Skip to content

Commit 5ae57e7

Browse files
authoredOct 21, 2024
Update ie interactive to overwrite an executed document with the environment variables that were used. (#232)
This PR updates `ie interactive` to overwrite the source of an executed document with the environment variables that were used and provide it to the portal before executing. These changes do not affect the standard execution and will not overwrite the actual document.
1 parent 7f13491 commit 5ae57e7

File tree

8 files changed

+115
-36
lines changed

8 files changed

+115
-36
lines changed
 
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,30 @@
11
name: build-test-release-tagged
2-
32
on:
43
push:
54
tags:
65
- v*
7-
86
jobs:
97
build-test-release:
108
permissions:
119
contents: write
1210
runs-on: ubuntu-latest
1311
steps:
14-
- uses: actions/checkout@v2
15-
- name: Build all targets.
16-
run: |
17-
make build-all
18-
- name: Run unit tests across all targets.
19-
run: |
20-
make test-all
21-
- name: Prepare scenarios to be released.
22-
run: |
23-
sudo apt install zip
24-
zip -r scenarios.zip scenarios
25-
- name: Release Innovation Engine
26-
uses: softprops/action-gh-release@v1
27-
with:
28-
token: ${{ secrets.GITHUB_TOKEN }}
29-
generate_release_notes: true
30-
files: |
31-
./bin/ie
32-
./scenarios.zip
12+
- uses: actions/checkout@v4
13+
- name: Build all targets.
14+
run: |
15+
make build-all
16+
- name: Run unit tests across all targets.
17+
run: |
18+
make test-all
19+
- name: Prepare scenarios to be released.
20+
run: |
21+
sudo apt install zip
22+
zip -r scenarios.zip scenarios
23+
- name: Release Innovation Engine
24+
uses: softprops/action-gh-release@v1
25+
with:
26+
token: ${{ secrets.GITHUB_TOKEN }}
27+
generate_release_notes: true
28+
files: |
29+
./bin/ie
30+
./scenarios.zip

‎.github/workflows/build-test-release.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ on:
55
- main
66
jobs:
77
build-test-release:
8-
permissions:
9-
contents: write
8+
permissions:
9+
contents: write
1010
runs-on: ubuntu-latest
1111
steps:
12-
- uses: actions/checkout@v2
12+
- uses: actions/checkout@v4
1313
- name: Build all targets.
1414
run: |
1515
make build-all

‎.github/workflows/scenario-testing.yaml

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ jobs:
1616
test-ie-installation:
1717
runs-on: ubuntu-latest
1818
steps:
19-
- uses: actions/checkout@v2
19+
- uses: actions/checkout@v4
2020
- name: Check ie installation
2121
run: |
2222
set -e
@@ -38,14 +38,14 @@ jobs:
3838
# the testing subscription for any branch in this repository.
3939
environment: ScenarioTesting
4040
steps:
41-
- uses: actions/checkout@v2
41+
- uses: actions/checkout@v4
4242
- name: Build & test all targets.
4343
run: |
4444
make build-all
4545
make test-all WITH_COVERAGE=true
4646
make test-local-scenarios ENVIRONMENT=github-action
4747
- name: Upload test coverage
48-
uses: actions/upload-artifact@v2
48+
uses: actions/upload-artifact@v4
4949
if: github.event_name == 'pull_request'
5050
with:
5151
name: coverage

‎internal/engine/common/scenario.go

+9-2
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@ import (
66
"net/http"
77
"os"
88
"path/filepath"
9-
"regexp"
109
"strings"
1110

1211
"github.com/Azure/InnovationEngine/internal/lib"
1312
"github.com/Azure/InnovationEngine/internal/lib/fs"
1413
"github.com/Azure/InnovationEngine/internal/logging"
1514
"github.com/Azure/InnovationEngine/internal/parsers"
15+
"github.com/Azure/InnovationEngine/internal/patterns"
1616
"github.com/yuin/goldmark/ast"
1717
)
1818

@@ -29,6 +29,12 @@ type Scenario struct {
2929
Steps []Step
3030
Properties map[string]interface{}
3131
Environment map[string]string
32+
Source []byte
33+
}
34+
35+
// Get the markdown source for the scenario as a string.
36+
func (s *Scenario) GetSourceAsString() string {
37+
return string(s.Source)
3238
}
3339

3440
// Groups the codeblocks into steps based on the header of the codeblock.
@@ -132,7 +138,7 @@ func CreateScenarioFromMarkdown(
132138
for key, value := range environmentVariableOverrides {
133139
environmentVariables[key] = value
134140
logging.GlobalLogger.Debugf("Attempting to override %s with %s", key, value)
135-
exportRegex := regexp.MustCompile(fmt.Sprintf(`(?m)export %s\s*=\s*(.*?)(;|&&|$)`, key))
141+
exportRegex := patterns.ExportVariableRegex(key)
136142

137143
for index, codeBlock := range codeBlocks {
138144
matches := exportRegex.FindAllStringSubmatch(codeBlock.Content, -1)
@@ -206,6 +212,7 @@ func CreateScenarioFromMarkdown(
206212
Steps: steps,
207213
Properties: properties,
208214
MarkdownAst: markdown,
215+
Source: source,
209216
}, nil
210217
}
211218

‎internal/engine/engine.go

+3
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,7 @@ func (e *Engine) InteractWithScenario(scenario *common.Scenario) error {
157157
e.Configuration.Environment,
158158
stepsToExecute,
159159
lib.CopyMap(scenario.Environment),
160+
scenario.GetSourceAsString(),
160161
)
161162
if err != nil {
162163
return err
@@ -181,9 +182,11 @@ func (e *Engine) InteractWithScenario(scenario *common.Scenario) error {
181182

182183
switch e.Configuration.Environment {
183184
case environments.EnvironmentsAzure, environments.EnvironmentsOCD:
185+
184186
logging.GlobalLogger.Info(
185187
"Cleaning environment variable file located at /tmp/env-vars",
186188
)
189+
187190
err := lib.CleanEnvironmentStateFile(lib.DefaultEnvironmentStateFile)
188191
if err != nil {
189192
logging.GlobalLogger.Errorf("Error cleaning environment variables: %s", err.Error())

‎internal/engine/environments/azure.go

+55-6
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package environments
33
import (
44
"encoding/json"
55
"fmt"
6+
"strings"
67

78
"github.com/Azure/InnovationEngine/internal/az"
89
"github.com/Azure/InnovationEngine/internal/logging"
10+
"github.com/Azure/InnovationEngine/internal/patterns"
911
"github.com/Azure/InnovationEngine/internal/ui"
1012
)
1113

@@ -23,12 +25,13 @@ type AzureStep struct {
2325

2426
// The status of a one-click deployment or learn mode deployment.
2527
type AzureDeploymentStatus struct {
26-
Steps []AzureStep `json:"steps"`
27-
CurrentStep int `json:"currentStep"`
28-
Status string `json:"status"`
29-
ResourceURIs []string `json:"resourceURIs"`
30-
Error string `json:"error"`
31-
Output string `json:"output"`
28+
Steps []AzureStep `json:"steps"`
29+
CurrentStep int `json:"currentStep"`
30+
Status string `json:"status"`
31+
ResourceURIs []string `json:"resourceURIs"`
32+
Error string `json:"error"`
33+
Output string `json:"output"`
34+
ConfiguredMarkdown string `json:"configuredMarkdown"`
3235
}
3336

3437
func NewAzureDeploymentStatus() AzureDeploymentStatus {
@@ -72,6 +75,52 @@ func (status *AzureDeploymentStatus) SetOutput(output string) {
7275
status.Output = output
7376
}
7477

78+
// Given a markdown string, replace the environment variables with the values
79+
// provided in the environmentVariables map. Currently this is only used
80+
// by the portal.
81+
func (status *AzureDeploymentStatus) ConfigureMarkdownForDownload(
82+
markdown string,
83+
environmentVariables map[string]string,
84+
environment string,
85+
) {
86+
if !IsAzureEnvironment(environment) {
87+
return
88+
}
89+
90+
for key, value := range environmentVariables {
91+
exportRegex := patterns.ExportVariableRegex(key)
92+
93+
matches := exportRegex.FindAllStringSubmatch(markdown, -1)
94+
95+
if len(matches) != 0 {
96+
logging.GlobalLogger.Debugf(
97+
"Found %d matches for the environment variable %s, Replacing them in markdown source.",
98+
len(matches),
99+
key,
100+
)
101+
}
102+
103+
for _, match := range matches {
104+
oldLine := match[0]
105+
oldValue := match[1]
106+
107+
// Replace the old export with the new export statement
108+
newLine := strings.Replace(oldLine, oldValue, value+" ", 1)
109+
logging.GlobalLogger.Debugf("Replacing '%s' with '%s'", oldLine, newLine)
110+
111+
// Update the code block with the new export statement
112+
markdown = strings.Replace(
113+
markdown,
114+
oldLine,
115+
newLine,
116+
1,
117+
)
118+
}
119+
}
120+
121+
status.ConfiguredMarkdown = markdown
122+
}
123+
75124
// Print out the status JSON for azure/cloudshell if in the correct environment.
76125
func ReportAzureStatus(status AzureDeploymentStatus, environment string) {
77126
if !IsAzureEnvironment(environment) {

‎internal/engine/interactive/interactive.go

+15
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ type InteractiveModeModel struct {
6060
scenarioCompleted bool
6161
components interactiveModeComponents
6262
ready bool
63+
markdownSource string
6364
CommandLines []string
6465
}
6566

@@ -344,6 +345,18 @@ func (model InteractiveModeModel) Update(message tea.Msg) (tea.Model, tea.Cmd) {
344345
model.resourceGroupName,
345346
model.environment,
346347
)
348+
349+
environmentVariables, err := lib.LoadEnvironmentStateFile(lib.DefaultEnvironmentStateFile)
350+
if err != nil {
351+
logging.GlobalLogger.Errorf("Failed to load environment state file: %s", err)
352+
model.azureStatus.SetError(err)
353+
}
354+
355+
model.azureStatus.ConfigureMarkdownForDownload(
356+
model.markdownSource,
357+
environmentVariables,
358+
model.environment,
359+
)
347360
model.azureStatus.SetOutput(strings.Join(model.CommandLines, "\n"))
348361
commands = append(
349362
commands,
@@ -570,6 +583,7 @@ func NewInteractiveModeModel(
570583
environment string,
571584
steps []common.Step,
572585
env map[string]string,
586+
markdownSource string,
573587
) (InteractiveModeModel, error) {
574588
// TODO: In the future we should just set the current step for the azure status
575589
// to one as the default.
@@ -666,6 +680,7 @@ func NewInteractiveModeModel(
666680
environment: environment,
667681
scenarioCompleted: false,
668682
ready: false,
683+
markdownSource: markdownSource,
669684
CommandLines: commandLines,
670685
}, nil
671686
}

‎internal/patterns/regex.go

+8-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package patterns
22

3-
import "regexp"
3+
import (
4+
"fmt"
5+
"regexp"
6+
)
47

58
var (
69
// An SSH command regex where there must be a username@host somewhere present in the command.
@@ -18,4 +21,8 @@ var (
1821
// ARM regex
1922
AzResourceURI = regexp.MustCompile(`\"id\": \"(/subscriptions/[^\"]+)\"`)
2023
AzResourceGroupName = regexp.MustCompile(`resourceGroups/([^\"\\/\ ]+)`)
24+
25+
ExportVariableRegex = func(key string) *regexp.Regexp {
26+
return regexp.MustCompile(fmt.Sprintf(`(?m)export %s\s*=\s*(.*?)(;|&&|$)`, key))
27+
}
2128
)

0 commit comments

Comments
 (0)