Skip to content

Commit 99ad69b

Browse files
committed
fix: pipeline stringArray flag usage
ENG-2735 Signed-off-by: Russell Centanni <russell.centanni@gmail.com>
1 parent 088997e commit 99ad69b

6 files changed

Lines changed: 230 additions & 11 deletions

File tree

docs/pages/configuration/pipelines/README.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,8 @@ pipelines:
326326
short: e
327327
type: stringArray
328328
run: |-
329-
extraEnv=$(get_flag "env") # Retrieve the value of the `env` flag and store it in a variable
330-
echo ${extraEnv[1]}
329+
extraEnv=($(get_flag "env")) # Retrieve the value of the `env` flag and store it in an array variable
330+
echo ${extraEnv[0]} # Arrays are zero indexed
331331

332332
TERMINAL_ENABLED=true
333333
if [ $(get_flag "logs") == "true" ]; then # Test if --logs/-l flag is used or not

e2e/tests/pipelines/pipelines.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ var _ = DevSpaceDescribe("pipelines", func() {
7575
framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
7676
framework.ExpectLocalFileContentsImmediately("other2.txt", "false\n")
7777
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
78+
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "one\n")
79+
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "two\n")
7880
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
7981
framework.ExpectLocalFileContentsImmediately("dep1-test.txt", "test\n")
8082
framework.ExpectLocalFileContentsImmediately("dep1-test2.txt", "true\n")
@@ -85,6 +87,143 @@ var _ = DevSpaceDescribe("pipelines", func() {
8587
framework.ExpectLocalFileContentsImmediately("dep1-other-profile.txt", "profile1\n")
8688
})
8789

90+
ginkgo.It("should resolve pipeline override array flags", func() {
91+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
92+
framework.ExpectNoError(err)
93+
defer framework.CleanupTempDir(initialDir, tempDir)
94+
95+
ns, err := kubeClient.CreateNamespace("pipelines")
96+
framework.ExpectNoError(err)
97+
defer framework.ExpectDeleteNamespace(kubeClient, ns)
98+
99+
rootCmd := cmd.NewRootCmd(f)
100+
persistentFlags := rootCmd.PersistentFlags()
101+
globalFlags := flags.SetGlobalFlags(persistentFlags)
102+
globalFlags.NoWarn = true
103+
globalFlags.Namespace = ns
104+
globalFlags.Profiles = []string{"profile1"}
105+
106+
cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
107+
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{
108+
"other": "test",
109+
"other2": "false",
110+
"other3": "true",
111+
"other4": "three four",
112+
})
113+
114+
devCmd := &cmd.RunPipelineCmd{
115+
GlobalFlags: globalFlags,
116+
Pipeline: "other",
117+
Ctx: cmdCtx,
118+
}
119+
err = devCmd.RunDefault(f)
120+
framework.ExpectNoError(err)
121+
122+
framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
123+
framework.ExpectLocalFileContentsImmediately("other2.txt", "false\n")
124+
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
125+
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
126+
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "three\n")
127+
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "four\n")
128+
})
129+
130+
ginkgo.It("should resolve pipeline override with --set-flags", func() {
131+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
132+
framework.ExpectNoError(err)
133+
defer framework.CleanupTempDir(initialDir, tempDir)
134+
135+
ns, err := kubeClient.CreateNamespace("pipelines")
136+
framework.ExpectNoError(err)
137+
defer framework.ExpectDeleteNamespace(kubeClient, ns)
138+
139+
rootCmd := cmd.NewRootCmd(f)
140+
persistentFlags := rootCmd.PersistentFlags()
141+
globalFlags := flags.SetGlobalFlags(persistentFlags)
142+
globalFlags.NoWarn = true
143+
globalFlags.Namespace = ns
144+
globalFlags.Profiles = []string{"profile1"}
145+
146+
cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
147+
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})
148+
149+
devCmd := &cmd.RunPipelineCmd{
150+
GlobalFlags: globalFlags,
151+
Pipeline: "other-override",
152+
Ctx: cmdCtx,
153+
}
154+
err = devCmd.RunDefault(f)
155+
framework.ExpectNoError(err)
156+
157+
framework.ExpectLocalFileContentsImmediately("other.txt", "test\n")
158+
framework.ExpectLocalFileContentsImmediately("other2.txt", "true\n")
159+
framework.ExpectLocalFileContentsImmediately("other3.txt", "true\n")
160+
framework.ExpectLocalFileContentsImmediately("other-profile.txt", "profile1\n")
161+
framework.ExpectLocalFileContentsImmediately("other4-0.txt", "five\n")
162+
framework.ExpectLocalFileContentsImmediately("other4-1.txt", "six\n")
163+
})
164+
165+
ginkgo.It("should resolve dependency pipeline flag defaults", func() {
166+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
167+
framework.ExpectNoError(err)
168+
defer framework.CleanupTempDir(initialDir, tempDir)
169+
170+
ns, err := kubeClient.CreateNamespace("pipelines")
171+
framework.ExpectNoError(err)
172+
defer framework.ExpectDeleteNamespace(kubeClient, ns)
173+
174+
rootCmd := cmd.NewRootCmd(f)
175+
persistentFlags := rootCmd.PersistentFlags()
176+
globalFlags := flags.SetGlobalFlags(persistentFlags)
177+
globalFlags.NoWarn = true
178+
globalFlags.Namespace = ns
179+
globalFlags.Profiles = []string{"profile1"}
180+
181+
cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
182+
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})
183+
184+
devCmd := &cmd.RunPipelineCmd{
185+
GlobalFlags: globalFlags,
186+
Pipeline: "arr-dep1",
187+
Ctx: cmdCtx,
188+
}
189+
err = devCmd.RunDefault(f)
190+
framework.ExpectNoError(err)
191+
192+
framework.ExpectLocalFileContentsImmediately("arr-0.txt", "one")
193+
framework.ExpectLocalFileContentsImmediately("arr-1.txt", "two")
194+
})
195+
196+
ginkgo.It("should resolve dependency pipeline flag defaults", func() {
197+
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/flags")
198+
framework.ExpectNoError(err)
199+
defer framework.CleanupTempDir(initialDir, tempDir)
200+
201+
ns, err := kubeClient.CreateNamespace("pipelines")
202+
framework.ExpectNoError(err)
203+
defer framework.ExpectDeleteNamespace(kubeClient, ns)
204+
205+
rootCmd := cmd.NewRootCmd(f)
206+
persistentFlags := rootCmd.PersistentFlags()
207+
globalFlags := flags.SetGlobalFlags(persistentFlags)
208+
globalFlags.NoWarn = true
209+
globalFlags.Namespace = ns
210+
globalFlags.Profiles = []string{"profile1"}
211+
212+
cmdCtx := values.WithCommandFlags(context.Background(), globalFlags.Flags)
213+
cmdCtx = values.WithFlagsMap(cmdCtx, map[string]string{})
214+
215+
devCmd := &cmd.RunPipelineCmd{
216+
GlobalFlags: globalFlags,
217+
Pipeline: "arr-dep1-override",
218+
Ctx: cmdCtx,
219+
}
220+
err = devCmd.RunDefault(f)
221+
framework.ExpectNoError(err)
222+
223+
framework.ExpectLocalFileContentsImmediately("arr-0.txt", "three")
224+
framework.ExpectLocalFileContentsImmediately("arr-1.txt", "")
225+
})
226+
88227
ginkgo.It("should exec container", func() {
89228
tempDir, err := framework.CopyToTempDir("tests/pipelines/testdata/exec_container")
90229
framework.ExpectNoError(err)

e2e/tests/pipelines/testdata/flags/dep1.yaml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,15 @@ pipelines:
3535
echo $(get_flag test3) > dep1-test2.txt
3636
echo $(get_flag profile) > dep1-dev-profile.txt
3737
run_pipelines other --set-flag other2=false
38+
39+
array:
40+
flags:
41+
- name: arr
42+
type: stringArray
43+
default:
44+
- one
45+
- two
46+
run: |-
47+
arr=($(get_flag arr))
48+
echo -n ${arr[0]} > arr-0.txt
49+
echo -n ${arr[1]} > arr-1.txt

e2e/tests/pipelines/testdata/flags/devspace.yaml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ pipelines:
1515
default: true
1616
- name: other3
1717
default: true
18+
- name: other4
19+
type: stringArray
20+
default:
21+
- one
22+
- two
1823
run: |-
1924
if get_flag test; then
2025
exit 1
@@ -24,6 +29,22 @@ pipelines:
2429
echo $(get_flag other2) > other2.txt
2530
echo $(get_flag other3) > other3.txt
2631
echo $(get_flag profile) > other-profile.txt
32+
33+
other4=($(get_flag other4))
34+
echo ${other4[0]} > other4-0.txt
35+
echo ${other4[1]} > other4-1.txt
36+
37+
other-override:
38+
run: |-
39+
run_pipelines other --set-flag other4=five --set-flag other4=six
40+
41+
arr-dep1:
42+
run: |-
43+
run_dependency_pipelines dep1 --pipeline array
44+
45+
arr-dep1-override:
46+
run: |-
47+
run_dependency_pipelines dep1 --pipeline array --set-flag arr=three
2748
2849
dev:
2950
flags:

pkg/devspace/pipeline/flags.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pipeline
22

33
import (
44
"fmt"
5+
"strconv"
56

67
"github.com/loft-sh/devspace/pkg/devspace/config/versions/latest"
78
)
@@ -31,18 +32,42 @@ func GetDefaultValue(pipelineFlag latest.PipelineFlag) (interface{}, error) {
3132
case latest.PipelineFlagTypeInteger:
3233
val := 0
3334
if pipelineFlag.Default != nil {
34-
val, ok = pipelineFlag.Default.(int)
35-
if !ok {
36-
return nil, fmt.Errorf("default is not an integer")
35+
switch pipelineFlag.Default.(type) {
36+
case float64:
37+
floatVal, ok := pipelineFlag.Default.(float64)
38+
if !ok {
39+
return nil, fmt.Errorf("default is not an integer")
40+
}
41+
return int(floatVal), nil
42+
case int:
43+
intVal, ok := pipelineFlag.Default.(int)
44+
if !ok {
45+
return nil, fmt.Errorf("default is not an integer")
46+
}
47+
return intVal, nil
48+
case string:
49+
strVal, ok := pipelineFlag.Default.(string)
50+
if !ok {
51+
return nil, fmt.Errorf("default is not an integer")
52+
}
53+
intVal, err := strconv.ParseInt(strVal, 10, 0)
54+
if err != nil {
55+
return nil, err
56+
}
57+
return int(intVal), nil
3758
}
59+
return nil, fmt.Errorf("default is not an integer")
3860
}
3961
return val, nil
4062
case latest.PipelineFlagTypeStringArray:
4163
val := []string{}
4264
if pipelineFlag.Default != nil {
43-
val, ok = pipelineFlag.Default.([]string)
44-
if !ok {
45-
return nil, fmt.Errorf("default is not a string array")
65+
for _, anyVal := range pipelineFlag.Default.([]interface{}) {
66+
strVal, ok := anyVal.(string)
67+
if !ok {
68+
return nil, fmt.Errorf("default is not a string array")
69+
}
70+
val = append(val, strVal)
4671
}
4772
}
4873
return val, nil

pkg/devspace/pipeline/pipeline.go

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -423,22 +423,44 @@ func (p *pipeline) startNewDependency(ctx devspacecontext.Context, dependency ty
423423
}
424424

425425
func applyFlags(ctx devspacecontext.Context, pipeline *latest.Pipeline, setFlags []string) (devspacecontext.Context, error) {
426-
newFlags := map[string]string{}
426+
defaultFlags := map[string]string{}
427427
for _, flag := range pipeline.Flags {
428428
val, err := GetDefaultValue(flag)
429429
if err != nil {
430430
return nil, err
431431
}
432432

433-
newFlags[flag.Name] = fmt.Sprintf("%v", val)
433+
switch flag.Type {
434+
case latest.PipelineFlagTypeStringArray:
435+
defaultFlags[flag.Name] = fmt.Sprintf("%v", strings.Join(val.([]string), " "))
436+
default:
437+
defaultFlags[flag.Name] = fmt.Sprintf("%v", val)
438+
}
434439
}
440+
441+
newFlags := map[string]string{}
435442
for _, v := range setFlags {
436443
splitted := strings.Split(v, "=")
437444
if len(splitted) <= 1 {
438445
return nil, fmt.Errorf("error parsing flag %s: expected format flag=value", v)
439446
}
440447

441-
newFlags[splitted[0]] = strings.Join(splitted[1:], "=")
448+
flagName := splitted[0]
449+
flagVal := strings.Join(splitted[1:], "=")
450+
flagVals := strings.Join(strings.Split(flagVal, ","), " ")
451+
452+
if newFlags[flagName] != "" {
453+
newFlags[flagName] = strings.Join([]string{newFlags[flagName], flagVals}, " ")
454+
} else {
455+
newFlags[flagName] = flagVals
456+
}
457+
458+
}
459+
460+
for name, value := range defaultFlags {
461+
if _, ok := newFlags[name]; !ok {
462+
newFlags[name] = value
463+
}
442464
}
443465

444466
return ctx.WithContext(values.WithFlagsMap(ctx.Context(), newFlags)), nil

0 commit comments

Comments
 (0)