Skip to content

Commit 03ae339

Browse files
authored
Interactive mode (#165)
The changes introduced by this PR as follows: * Adds `ie interactive` implementation to support executing markdown documents interactively. * Adds `ie inspect` to view the parsed data of a markdown document produced by Innovation engines parser. This feature is still experimental and only shows some of the parsing output for now. * Updates the markdown parser to associate the last paragraph of markdown document as the description for a codeblock. * Adds a completely new rendering system using `bubble-tea` to support interactive mode. Both `ie execute` and `ie test` will be rewritten in the future to utilize a similar rendering implementation. * Changes the status update for integration with the azure extensionto nest codeblocks underneath steps and for codeblocks to include their descriptions for learn mode.
1 parent 7faac03 commit 03ae339

25 files changed

+1210
-345
lines changed

Makefile

+2-60
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: build-ie build-api build-all run-ie run-api clean test-all test all
1+
.PHONY: build-ie build-all run-ie clean test-all test all
22

33
BINARY_DIR := bin
44
IE_BINARY := $(BINARY_DIR)/ie
@@ -10,15 +10,8 @@ build-ie:
1010
@echo "Building the Innovation Engine CLI..."
1111
@CGO_ENABLED=0 go build -o "$(IE_BINARY)" cmd/ie/ie.go
1212

13-
build-api:
14-
@echo "Building the Innovation Engine API..."
15-
@CGO_ENABLED=0 go build -o "$(API_BINARY)" cmd/api/main.go
1613

17-
build-runner: build-ie build-api
18-
@echo "Building the Innovation Engine Runner..."
19-
@CGO_ENABLED=0 go build -o "$(BINARY_DIR)/runner" cmd/runner/main.go
20-
21-
build-all: build-ie build-api build-runner
14+
build-all: build-ie
2215

2316
# ------------------------------ Install targets -------------------------------
2417

@@ -67,58 +60,7 @@ run-ie: build-ie
6760
@echo "Running the Innovation Engine CLI"
6861
@"$(IE_BINARY)"
6962

70-
run-api: build-api
71-
@echo "Running the Innovation Engine API"
72-
@"$(API_BINARY)"
73-
7463
clean:
7564
@echo "Cleaning up"
7665
@rm -rf "$(BINARY_DIR)"
7766

78-
# ----------------------------- Docker targets ---------------------------------
79-
80-
API_IMAGE_TAG ?= latest
81-
82-
# Builds the API container.
83-
build-api-container:
84-
@echo "Building the Innovation Engine API container"
85-
@docker build -t innovation-engine-api:$(API_IMAGE_TAG) -f infra/api/Dockerfile .
86-
87-
88-
# ----------------------------- Kubernetes targets -----------------------------
89-
90-
# Applies the ingress controller to the cluster and waits for it to be ready.
91-
k8s-deploy-ingress-controller:
92-
@echo "Deploying the ingress controller to your local cluster..."
93-
@kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml
94-
@kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=120s
95-
96-
# Deploys the API deployment, service, and ingress specifications to the
97-
# cluster, allowing the API to be accessed via the ingress controller.
98-
k8s-deploy-api: build-api-container
99-
@echo "Deploying the Innovation Engine API container to your local cluster..."
100-
@kubectl apply -f infra/api/deployment.yaml
101-
@kubectl apply -f infra/api/service.yaml
102-
@kubectl apply -f infra/api/ingress.yaml
103-
104-
k8s-initialize-cluster: k8s-deploy-ingress-controller k8s-deploy-api
105-
@echo "Set up Kubernetes cluster for local development."
106-
107-
k8s-delete-ingress-controller:
108-
@echo "Deleting the ingress controller from your local cluster..."
109-
@kubectl delete -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.7.1/deploy/static/provider/cloud/deploy.yaml
110-
111-
k8s-delete-api:
112-
@echo "Deleting the Innovation Engine API container from your local cluster..."
113-
@kubectl delete -f infra/api/deployment.yaml
114-
@kubectl delete -f infra/api/service.yaml
115-
@kubectl delete -f infra/api/ingress.yaml
116-
117-
k8s-refresh-api: k8s-delete-api k8s-deploy-api
118-
@echo "Refreshed the Innovation Engine API container in your local cluster..."
119-
120-
k8s-delete-cluster: k8s-delete-api k8s-delete-ingress-controller
121-
@echo "Deleted Kubernetes cluster for local development."
122-
123-
k8s-refresh-cluster: k8s-delete-cluster k8s-initialize-cluster
124-
@echo "Refreshed Kubernetes cluster for local development."

cmd/api/main.go

-55
This file was deleted.

cmd/api/types.go

-11
This file was deleted.

cmd/ie/commands/inspect.go

+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
package commands
2+
3+
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/Azure/InnovationEngine/internal/engine"
9+
"github.com/Azure/InnovationEngine/internal/logging"
10+
"github.com/Azure/InnovationEngine/internal/ui"
11+
"github.com/spf13/cobra"
12+
)
13+
14+
// Register the command with our command runner.
15+
func init() {
16+
rootCommand.AddCommand(inspectCommand)
17+
18+
// String flags
19+
inspectCommand.PersistentFlags().
20+
String("correlation-id", "", "Adds a correlation ID to the user agent used by a scenarios azure-cli commands.")
21+
inspectCommand.PersistentFlags().
22+
String("subscription", "", "Sets the subscription ID used by a scenarios azure-cli commands. Will rely on the default subscription if not set.")
23+
inspectCommand.PersistentFlags().
24+
String("working-directory", ".", "Sets the working directory for innovation engine to operate out of. Restores the current working directory when finished.")
25+
26+
// StringArray flags
27+
inspectCommand.PersistentFlags().
28+
StringArray("var", []string{}, "Sets an environment variable for the scenario. Format: --var <key>=<value>")
29+
}
30+
31+
var inspectCommand = &cobra.Command{
32+
Use: "inspect",
33+
Short: "Execute a document in inspect mode.",
34+
Run: func(cmd *cobra.Command, args []string) {
35+
markdownFile := args[0]
36+
if markdownFile == "" {
37+
logging.GlobalLogger.Errorf("Error: No markdown file specified.")
38+
cmd.Help()
39+
os.Exit(1)
40+
}
41+
42+
environmentVariables, _ := cmd.Flags().GetStringArray("var")
43+
// features, _ := cmd.Flags().GetStringArray("feature")
44+
45+
// Parse the environment variables from the command line into a map
46+
cliEnvironmentVariables := make(map[string]string)
47+
for _, environmentVariable := range environmentVariables {
48+
keyValuePair := strings.SplitN(environmentVariable, "=", 2)
49+
if len(keyValuePair) != 2 {
50+
logging.GlobalLogger.Errorf(
51+
"Error: Invalid environment variable format: %s",
52+
environmentVariable,
53+
)
54+
fmt.Printf("Error: Invalid environment variable format: %s", environmentVariable)
55+
cmd.Help()
56+
os.Exit(1)
57+
}
58+
59+
cliEnvironmentVariables[keyValuePair[0]] = keyValuePair[1]
60+
}
61+
// Parse the markdown file and create a scenario
62+
scenario, err := engine.CreateScenarioFromMarkdown(
63+
markdownFile,
64+
[]string{"bash", "azurecli", "azurecli-inspect", "terraform"},
65+
cliEnvironmentVariables,
66+
)
67+
if err != nil {
68+
logging.GlobalLogger.Errorf("Error creating scenario: %s", err)
69+
fmt.Printf("Error creating scenario: %s", err)
70+
os.Exit(1)
71+
}
72+
73+
if err != nil {
74+
logging.GlobalLogger.Errorf("Error creating engine: %s", err)
75+
fmt.Printf("Error creating engine: %s", err)
76+
os.Exit(1)
77+
}
78+
79+
fmt.Println(ui.ScenarioTitleStyle.Render(scenario.Name))
80+
for stepNumber, step := range scenario.Steps {
81+
stepTitle := fmt.Sprintf(" %d. %s\n", stepNumber+1, step.Name)
82+
fmt.Println(ui.StepTitleStyle.Render(stepTitle))
83+
for codeBlockNumber, codeBlock := range step.CodeBlocks {
84+
fmt.Println(
85+
ui.InteractiveModeCodeBlockDescriptionStyle.Render(
86+
fmt.Sprintf(
87+
" %d.%d %s",
88+
stepNumber+1,
89+
codeBlockNumber+1,
90+
codeBlock.Description,
91+
),
92+
),
93+
)
94+
fmt.Print(
95+
ui.IndentMultiLineCommand(
96+
fmt.Sprintf(
97+
" %s",
98+
ui.InteractiveModeCodeBlockStyle.Render(
99+
codeBlock.Content,
100+
),
101+
),
102+
6),
103+
)
104+
fmt.Println()
105+
}
106+
}
107+
108+
},
109+
}

cmd/ie/commands/interactive.go

+96-4
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,107 @@
11
package commands
22

33
import (
4+
"fmt"
5+
"os"
6+
"strings"
7+
8+
"github.com/Azure/InnovationEngine/internal/engine"
9+
"github.com/Azure/InnovationEngine/internal/logging"
410
"github.com/spf13/cobra"
511
)
612

13+
// Register the command with our command runner.
14+
func init() {
15+
rootCommand.AddCommand(interactiveCommand)
16+
17+
// String flags
18+
interactiveCommand.PersistentFlags().
19+
String("correlation-id", "", "Adds a correlation ID to the user agent used by a scenarios azure-cli commands.")
20+
interactiveCommand.PersistentFlags().
21+
String("subscription", "", "Sets the subscription ID used by a scenarios azure-cli commands. Will rely on the default subscription if not set.")
22+
interactiveCommand.PersistentFlags().
23+
String("working-directory", ".", "Sets the working directory for innovation engine to operate out of. Restores the current working directory when finished.")
24+
25+
// StringArray flags
26+
interactiveCommand.PersistentFlags().
27+
StringArray("var", []string{}, "Sets an environment variable for the scenario. Format: --var <key>=<value>")
28+
}
29+
730
var interactiveCommand = &cobra.Command{
831
Use: "interactive",
932
Short: "Execute a document in interactive mode.",
10-
}
33+
Run: func(cmd *cobra.Command, args []string) {
34+
markdownFile := args[0]
35+
if markdownFile == "" {
36+
logging.GlobalLogger.Errorf("Error: No markdown file specified.")
37+
cmd.Help()
38+
os.Exit(1)
39+
}
1140

12-
// / Register the command with our command runner.
13-
func init() {
14-
rootCommand.AddCommand(interactiveCommand)
41+
verbose, _ := cmd.Flags().GetBool("verbose")
42+
doNotDelete, _ := cmd.Flags().GetBool("do-not-delete")
43+
44+
subscription, _ := cmd.Flags().GetString("subscription")
45+
correlationId, _ := cmd.Flags().GetString("correlation-id")
46+
environment, _ := cmd.Flags().GetString("environment")
47+
workingDirectory, _ := cmd.Flags().GetString("working-directory")
48+
49+
environmentVariables, _ := cmd.Flags().GetStringArray("var")
50+
// features, _ := cmd.Flags().GetStringArray("feature")
51+
52+
// Known features
53+
renderValues := false
54+
55+
// Parse the environment variables from the command line into a map
56+
cliEnvironmentVariables := make(map[string]string)
57+
for _, environmentVariable := range environmentVariables {
58+
keyValuePair := strings.SplitN(environmentVariable, "=", 2)
59+
if len(keyValuePair) != 2 {
60+
logging.GlobalLogger.Errorf(
61+
"Error: Invalid environment variable format: %s",
62+
environmentVariable,
63+
)
64+
fmt.Printf("Error: Invalid environment variable format: %s", environmentVariable)
65+
cmd.Help()
66+
os.Exit(1)
67+
}
68+
69+
cliEnvironmentVariables[keyValuePair[0]] = keyValuePair[1]
70+
}
71+
// Parse the markdown file and create a scenario
72+
scenario, err := engine.CreateScenarioFromMarkdown(
73+
markdownFile,
74+
[]string{"bash", "azurecli", "azurecli-interactive", "terraform"},
75+
cliEnvironmentVariables,
76+
)
77+
if err != nil {
78+
logging.GlobalLogger.Errorf("Error creating scenario: %s", err)
79+
fmt.Printf("Error creating scenario: %s", err)
80+
os.Exit(1)
81+
}
82+
83+
innovationEngine, err := engine.NewEngine(engine.EngineConfiguration{
84+
Verbose: verbose,
85+
DoNotDelete: doNotDelete,
86+
Subscription: subscription,
87+
CorrelationId: correlationId,
88+
Environment: environment,
89+
WorkingDirectory: workingDirectory,
90+
RenderValues: renderValues,
91+
})
92+
93+
if err != nil {
94+
logging.GlobalLogger.Errorf("Error creating engine: %s", err)
95+
fmt.Printf("Error creating engine: %s", err)
96+
os.Exit(1)
97+
}
98+
99+
// Execute the scenario
100+
err = innovationEngine.InteractWithScenario(scenario)
101+
if err != nil {
102+
logging.GlobalLogger.Errorf("Error executing scenario: %s", err)
103+
fmt.Printf("Error executing scenario: %s", err)
104+
os.Exit(1)
105+
}
106+
},
15107
}

cmd/runner/main.go

-13
This file was deleted.

0 commit comments

Comments
 (0)