Skip to content

Commit e2b5cfd

Browse files
committed
feat: add ability to set params via exec arg
1 parent 4a8303c commit e2b5cfd

File tree

7 files changed

+87
-14
lines changed

7 files changed

+87
-14
lines changed

cmd/internal/exec.go

Lines changed: 37 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/jahvon/tuikit/views"
1212
"github.com/spf13/cobra"
1313

14+
"github.com/jahvon/flow/cmd/internal/flags"
1415
"github.com/jahvon/flow/internal/cache"
1516
"github.com/jahvon/flow/internal/context"
1617
"github.com/jahvon/flow/internal/io"
@@ -61,6 +62,7 @@ func RegisterExecCmd(ctx *context.Context, rootCmd *cobra.Command) {
6162
execFunc(ctx, cmd, verb, args)
6263
},
6364
}
65+
RegisterFlag(ctx, subCmd, *flags.ParameterValueFlag)
6466
rootCmd.AddCommand(subCmd)
6567
}
6668

@@ -114,6 +116,7 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
114116
))
115117
}
116118

119+
// add args to the env map
117120
execArgs := make([]string, 0)
118121
if len(args) >= 2 {
119122
execArgs = args[1:]
@@ -134,8 +137,12 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
134137
envMap = make(map[string]string)
135138
}
136139

137-
setAuthEnv(ctx, cmd, e)
138-
textInputs := pendingFormFields(ctx, e)
140+
// add --param overrides to the env map
141+
paramOverrides := flags.ValueFor[[]string](ctx, cmd, *flags.ParameterValueFlag, false)
142+
applyParameterOverrides(paramOverrides, envMap)
143+
144+
// add values from the prompt param type to the env map
145+
textInputs := pendingFormFields(ctx, e, envMap)
139146
if len(textInputs) > 0 {
140147
form, err := views.NewForm(io.Theme(ctx.Config.Theme.String()), ctx.StdIn(), ctx.StdOut(), textInputs...)
141148
if err != nil {
@@ -148,6 +155,8 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
148155
envMap[key] = fmt.Sprintf("%v", val)
149156
}
150157
}
158+
159+
setAuthEnv(ctx, cmd, e)
151160
startTime := time.Now()
152161
eng := engine.NewExecEngine()
153162
if err := runner.Exec(ctx, e, eng, envMap); err != nil {
@@ -304,36 +313,43 @@ func authRequired(ctx *context.Context, rootExec *executable.Executable) bool {
304313
}
305314

306315
//nolint:gocognit
307-
func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []*views.FormField {
316+
func pendingFormFields(
317+
ctx *context.Context, rootExec *executable.Executable, envMap map[string]string,
318+
) []*views.FormField {
308319
pending := make([]*views.FormField, 0)
309320
switch {
310321
case rootExec.Exec != nil:
311322
for _, param := range rootExec.Exec.Params {
312-
if param.Prompt != "" {
323+
_, exists := envMap[param.EnvKey]
324+
if param.Prompt != "" && !exists {
313325
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
314326
}
315327
}
316328
case rootExec.Launch != nil:
317329
for _, param := range rootExec.Launch.Params {
318-
if param.Prompt != "" {
330+
_, exists := envMap[param.EnvKey]
331+
if param.Prompt != "" && !exists {
319332
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
320333
}
321334
}
322335
case rootExec.Request != nil:
323336
for _, param := range rootExec.Request.Params {
324-
if param.Prompt != "" {
337+
_, exists := envMap[param.EnvKey]
338+
if param.Prompt != "" && !exists {
325339
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
326340
}
327341
}
328342
case rootExec.Render != nil:
329343
for _, param := range rootExec.Render.Params {
330-
if param.Prompt != "" {
344+
_, exists := envMap[param.EnvKey]
345+
if param.Prompt != "" && !exists {
331346
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
332347
}
333348
}
334349
case rootExec.Serial != nil:
335350
for _, param := range rootExec.Serial.Params {
336-
if param.Prompt != "" {
351+
_, exists := envMap[param.EnvKey]
352+
if param.Prompt != "" && !exists {
337353
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
338354
}
339355
}
@@ -343,7 +359,7 @@ func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []
343359
if err != nil {
344360
continue
345361
}
346-
childPending := pendingFormFields(ctx, childExec)
362+
childPending := pendingFormFields(ctx, childExec, envMap)
347363
pending = append(pending, childPending...)
348364
}
349365
}
@@ -359,14 +375,25 @@ func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []
359375
if err != nil {
360376
continue
361377
}
362-
childPending := pendingFormFields(ctx, childExec)
378+
childPending := pendingFormFields(ctx, childExec, envMap)
363379
pending = append(pending, childPending...)
364380
}
365381
}
366382
}
367383
return pending
368384
}
369385

386+
func applyParameterOverrides(overrides []string, envMap map[string]string) {
387+
for _, override := range overrides {
388+
parts := strings.SplitN(override, "=", 2)
389+
if len(parts) != 2 {
390+
continue // skip invalid overrides
391+
}
392+
key, value := parts[0], parts[1]
393+
envMap[key] = value
394+
}
395+
}
396+
370397
var (
371398
//nolint:lll
372399
execDocumentation = `

cmd/internal/flags/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,3 +175,11 @@ var StoreAllFlag = &Metadata{
175175
Usage: "Force clear all stored data",
176176
Default: false,
177177
}
178+
179+
var ParameterValueFlag = &Metadata{
180+
Name: "param",
181+
Shorthand: "p",
182+
Usage: "Set a parameter value by env key. (i.e. KEY=value) Use multiple times to set multiple parameters." +
183+
"This will override any existing parameter values defined for the executable.",
184+
Default: []string{},
185+
}

docs/guide/executable.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,13 @@ parameter type sets a static value for the environment variable.
157157
158158
_This example used the `exec` type, but the `params` field can be used with any executable type._
159159
160+
You can override any environment variable defined in the `params` or provide additional ones by using the `--param` flag
161+
when running the executable:
162+
163+
```shell
164+
flow deploy devbox --param API_TOKEN=token --param DRY_RUN=true --param VERBOSE=value
165+
```
166+
160167
**Args**
161168
162169

internal/runner/env.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,11 @@ func ResolveParameterValue(
3838
param executable.Parameter,
3939
promptedEnv map[string]string,
4040
) (string, error) {
41+
if val, found := promptedEnv[param.EnvKey]; found {
42+
// existing values win - these could come in as a param override from the CLI
43+
return val, nil
44+
}
45+
4146
switch {
4247
case param.Text == "" && param.SecretRef == "" && param.Prompt == "":
4348
return "", nil

tests/exec_cmd_e2e_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,19 @@ var _ = Describe("exec e2e", func() {
3434
Entry("nameless example", ""),
3535
Entry("request with transformation", "examples:request-with-transform"),
3636
)
37+
38+
When("param overrides are provided", func() {
39+
It("should run the executable with the provided overrides", func() {
40+
runner := utils.NewE2ECommandRunner()
41+
stdOut := ctx.StdOut()
42+
Expect(runner.Run(
43+
ctx, "exec", "examples:with-params",
44+
"--param", "PARAM1=value1", "--param", "PARAM2=value2", "--param", "PARAM3=value3",
45+
)).To(Succeed())
46+
out, _ := readFileContent(stdOut)
47+
Expect(out).To(ContainSubstring("value1"))
48+
Expect(out).To(ContainSubstring("value2"))
49+
Expect(out).To(ContainSubstring("value3"))
50+
})
51+
})
3752
})

tests/utils/context.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
stdCtx "context"
55
"os"
66
"path/filepath"
7+
"runtime"
78
"strings"
89

910
tuikitIO "github.com/jahvon/tuikit/io"
@@ -42,7 +43,14 @@ func NewContext(ctx stdCtx.Context, t ginkgo.FullGinkgoTInterface) *context.Cont
4243
tuikitIO.WithTheme(io.Theme("")),
4344
tuikitIO.WithMode(tuikitIO.Text),
4445
tuikitIO.WithExitFunc(func() {
45-
t.Fatalf("logger exit called")
46+
// include the colling function/line
47+
_, file, line, ok := runtime.Caller(1)
48+
if ok {
49+
file = filepath.Base(file)
50+
t.Logf("logger exit called from %s:%d", file, line)
51+
} else {
52+
t.Logf("logger exit called")
53+
}
4654
}),
4755
)
4856
ctxx := newTestContext(ctx, t, logger, stdIn, stdOut)

tools/builder/exec.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,16 @@ func ExecWithParams(opts ...Option) *executable.Executable {
180180
for _, param := range params {
181181
switch {
182182
case param.Text != "":
183-
paramCmds = append(paramCmds, fmt.Sprintf("echo 'key=%s, value=%s'", param.EnvKey, param.Text))
183+
paramCmds = append(paramCmds, fmt.Sprintf(`echo "key=%s, value=$%[1]s"`, param.EnvKey))
184184
case param.SecretRef != "":
185-
paramCmds = append(paramCmds, fmt.Sprintf("echo 'key=%s, secret=%s'", param.EnvKey, param.SecretRef))
185+
paramCmds = append(
186+
paramCmds,
187+
fmt.Sprintf(`echo "key=%s, secret=%s, value=$%[1]s"`, param.EnvKey, param.SecretRef),
188+
)
186189
case param.Prompt != "":
187190
paramCmds = append(
188191
paramCmds,
189-
fmt.Sprintf("echo 'key=%s, prompt=%s', value=$%[1]s", param.EnvKey, param.Prompt),
192+
fmt.Sprintf(`echo "key=%s, prompt=%s value=$%[1]s"`, param.EnvKey, param.Prompt),
190193
)
191194
}
192195
}

0 commit comments

Comments
 (0)