Skip to content

Commit 5618f9f

Browse files
committed
Merge remote-tracking branch 'origin/main' into vaultsv2
2 parents d115a19 + 8374c73 commit 5618f9f

File tree

18 files changed

+250
-105
lines changed

18 files changed

+250
-105
lines changed

cmd/internal/cache.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func cacheSetFunc(ctx *context.Context, _ *cobra.Command, args []string) {
7979
if err != nil {
8080
ctx.Logger.FatalErr(err)
8181
}
82-
if err = s.CreateBucket(store.EnvironmentBucket()); err != nil {
82+
if _, err = s.CreateAndSetBucket(store.EnvironmentBucket()); err != nil {
8383
ctx.Logger.FatalErr(err)
8484
}
8585
defer func() {

cmd/internal/exec.go

Lines changed: 39 additions & 12 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,10 +137,12 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
134137
envMap = make(map[string]string)
135138
}
136139

137-
if ctx.Config.CurrentVault == nil || *ctx.Config.CurrentVault == vault.LegacyVaultReservedName {
138-
setAuthEnv(ctx, cmd, e)
139-
}
140-
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)
141146
if len(textInputs) > 0 {
142147
form, err := views.NewForm(io.Theme(ctx.Config.Theme.String()), ctx.StdIn(), ctx.StdOut(), textInputs...)
143148
if err != nil {
@@ -150,6 +155,10 @@ func execFunc(ctx *context.Context, cmd *cobra.Command, verb executable.Verb, ar
150155
envMap[key] = fmt.Sprintf("%v", val)
151156
}
152157
}
158+
159+
if ctx.Config.CurrentVault == nil || *ctx.Config.CurrentVault == vault.LegacyVaultReservedName {
160+
setAuthEnv(ctx, cmd, e)
161+
}
153162
startTime := time.Now()
154163
eng := engine.NewExecEngine()
155164
if err := runner.Exec(ctx, e, eng, envMap); err != nil {
@@ -306,36 +315,43 @@ func authRequired(ctx *context.Context, rootExec *executable.Executable) bool {
306315
}
307316

308317
//nolint:gocognit
309-
func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []*views.FormField {
318+
func pendingFormFields(
319+
ctx *context.Context, rootExec *executable.Executable, envMap map[string]string,
320+
) []*views.FormField {
310321
pending := make([]*views.FormField, 0)
311322
switch {
312323
case rootExec.Exec != nil:
313324
for _, param := range rootExec.Exec.Params {
314-
if param.Prompt != "" {
325+
_, exists := envMap[param.EnvKey]
326+
if param.Prompt != "" && !exists {
315327
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
316328
}
317329
}
318330
case rootExec.Launch != nil:
319331
for _, param := range rootExec.Launch.Params {
320-
if param.Prompt != "" {
332+
_, exists := envMap[param.EnvKey]
333+
if param.Prompt != "" && !exists {
321334
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
322335
}
323336
}
324337
case rootExec.Request != nil:
325338
for _, param := range rootExec.Request.Params {
326-
if param.Prompt != "" {
339+
_, exists := envMap[param.EnvKey]
340+
if param.Prompt != "" && !exists {
327341
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
328342
}
329343
}
330344
case rootExec.Render != nil:
331345
for _, param := range rootExec.Render.Params {
332-
if param.Prompt != "" {
346+
_, exists := envMap[param.EnvKey]
347+
if param.Prompt != "" && !exists {
333348
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
334349
}
335350
}
336351
case rootExec.Serial != nil:
337352
for _, param := range rootExec.Serial.Params {
338-
if param.Prompt != "" {
353+
_, exists := envMap[param.EnvKey]
354+
if param.Prompt != "" && !exists {
339355
pending = append(pending, &views.FormField{Key: param.EnvKey, Title: param.Prompt})
340356
}
341357
}
@@ -345,7 +361,7 @@ func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []
345361
if err != nil {
346362
continue
347363
}
348-
childPending := pendingFormFields(ctx, childExec)
364+
childPending := pendingFormFields(ctx, childExec, envMap)
349365
pending = append(pending, childPending...)
350366
}
351367
}
@@ -361,14 +377,25 @@ func pendingFormFields(ctx *context.Context, rootExec *executable.Executable) []
361377
if err != nil {
362378
continue
363379
}
364-
childPending := pendingFormFields(ctx, childExec)
380+
childPending := pendingFormFields(ctx, childExec, envMap)
365381
pending = append(pending, childPending...)
366382
}
367383
}
368384
}
369385
return pending
370386
}
371387

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

cmd/internal/flags/types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,14 @@ var StoreAllFlag = &Metadata{
176176
Default: false,
177177
}
178178

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+
}
186+
179187
var VaultTypeFlag = &Metadata{
180188
Name: "type",
181189
Shorthand: "t",

desktop/src-tauri/src/command_runner.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -181,20 +181,21 @@ impl CommandRunner {
181181
args: &[&str],
182182
params: Option<std::collections::HashMap<String, String>>,
183183
) -> CommandResult<()> {
184-
let mut cmd_args = vec![verb, executable_id];
185-
cmd_args.extend(args);
184+
let mut cmd_args = vec![verb.to_string(), executable_id.to_string()];
185+
cmd_args.extend(args.iter().map(|&s| s.to_string()));
186186

187187
let mut cmd =
188188
TokioCommand::new("/Users/jahvon/workspaces/github.com/jahvon/flow/.bin/flow");
189-
cmd.args(&cmd_args);
190-
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
191189

192-
// TODO: ensure that the CLI supports this
193190
if let Some(params) = params {
194191
for (key, value) in params {
195-
cmd.env(key, value);
192+
cmd_args.push("--param".to_string());
193+
let param_arg = format!("{}={}", key, value);
194+
cmd_args.push(param_arg);
196195
}
197196
}
197+
cmd.args(&cmd_args);
198+
cmd.stdout(Stdio::piped()).stderr(Stdio::piped());
198199

199200
println!("streaming cmd: {:?}", cmd);
200201

docs/cli/flow_exec.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ flow exec EXECUTABLE_ID [args...] [flags]
5050
### Options
5151

5252
```
53-
-h, --help help for exec
53+
-h, --help help for exec
54+
-p, --param stringArray Set a parameter value by env key. (i.e. KEY=value) Use multiple times to set multiple parameters.This will override any existing parameter values defined for the executable.
5455
```
5556

5657
### Options inherited from parent commands

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

internal/services/store/store.go

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ func (s *BoltStore) CreateAndSetBucket(id string) (string, error) {
9393

9494
func EnvironmentBucket() string {
9595
id := RootBucket
96-
if val, set := os.LookupEnv(BucketEnv); set {
96+
if val, set := os.LookupEnv(BucketEnv); set && val != "" {
9797
id = val
9898
}
9999
replacer := strings.NewReplacer(":", "_", "/", "_", " ", "_")
@@ -163,11 +163,38 @@ func (s *BoltStore) GetKeys() ([]string, error) {
163163
if bucket == nil {
164164
return fmt.Errorf("bucket %s not found", s.processBucket)
165165
}
166-
bucket.Stats()
167-
return bucket.ForEach(func(k, _ []byte) error {
168-
keys = append(keys, string(k))
166+
167+
// Collect keys from process bucket
168+
processKeys := make(map[string]bool)
169+
err := bucket.ForEach(func(k, _ []byte) error {
170+
key := string(k)
171+
keys = append(keys, key)
172+
processKeys[key] = true
169173
return nil
170174
})
175+
if err != nil {
176+
return err
177+
}
178+
179+
// If not using root bucket, also collect keys from root bucket
180+
if s.processBucket != RootBucket {
181+
rBucket := tx.Bucket([]byte(RootBucket))
182+
if rBucket == nil {
183+
return nil
184+
}
185+
err := rBucket.ForEach(func(k, _ []byte) error {
186+
// Only add keys that aren't already in the process bucket
187+
if key := string(k); !processKeys[key] {
188+
keys = append(keys, key)
189+
}
190+
return nil
191+
})
192+
if err != nil {
193+
return err
194+
}
195+
}
196+
197+
return nil
171198
})
172199
return keys, err
173200
}
@@ -186,10 +213,36 @@ func (s *BoltStore) GetAll() (map[string]string, error) {
186213
if bucket == nil {
187214
return fmt.Errorf("bucket %s not found", s.processBucket)
188215
}
189-
return bucket.ForEach(func(k, v []byte) error {
216+
217+
// First, collect all key-value pairs from process bucket
218+
err := bucket.ForEach(func(k, v []byte) error {
190219
m[string(k)] = string(v)
191220
return nil
192221
})
222+
if err != nil {
223+
return err
224+
}
225+
226+
// If not using root bucket, also collect from root bucket
227+
if s.processBucket != RootBucket {
228+
rBucket := tx.Bucket([]byte(RootBucket))
229+
if rBucket == nil {
230+
return nil
231+
}
232+
err := rBucket.ForEach(func(k, v []byte) error {
233+
key := string(k)
234+
// Only add if key doesn't already exist in process bucket
235+
if _, exists := m[key]; !exists {
236+
m[key] = string(v)
237+
}
238+
return nil
239+
})
240+
if err != nil {
241+
return err
242+
}
243+
}
244+
245+
return nil
193246
})
194247
return m, err
195248
}

tests/browse_cmds_e2e_test.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ import (
66
. "github.com/onsi/ginkgo/v2"
77
. "github.com/onsi/gomega"
88

9-
"github.com/jahvon/flow/internal/context"
109
"github.com/jahvon/flow/tests/utils"
1110
)
1211

1312
var _ = Describe("browse e2e", Ordered, func() {
1413
var (
15-
ctx *context.Context
14+
ctx *utils.Context
1615
run *utils.CommandRunner
1716
)
1817

@@ -33,7 +32,7 @@ var _ = Describe("browse e2e", Ordered, func() {
3332
func(args []string) {
3433
stdOut := ctx.StdOut()
3534
cmdArgs := append([]string{"browse", "--list"}, args...)
36-
Expect(run.Run(ctx, cmdArgs...)).To(Succeed())
35+
Expect(run.Run(ctx.Context, cmdArgs...)).To(Succeed())
3736
out, err := readFileContent(stdOut)
3837
Expect(err).NotTo(HaveOccurred())
3938
Expect(out).To(ContainSubstring("executables:"))
@@ -50,7 +49,7 @@ var _ = Describe("browse e2e", Ordered, func() {
5049

5150
It("should show executable details by verb and name", func() {
5251
stdOut := ctx.StdOut()
53-
Expect(run.Run(ctx, "browse", "exec", "examples:simple-print")).To(Succeed())
52+
Expect(run.Run(ctx.Context, "browse", "exec", "examples:simple-print")).To(Succeed())
5453
out, err := readFileContent(stdOut)
5554
Expect(err).NotTo(HaveOccurred())
5655
Expect(out).To(ContainSubstring("name: simple-print"))

0 commit comments

Comments
 (0)