fix: split multi-flag input in docker-options
Multi-flag inputs (e.g. `--build-arg X=Y --link a --link b`) used to be stored as a single line, which bypassed the per-line filter that drops `--link` and similar flags for dockerfile-based builders. Each `--flag [value]` group is now stored as its own entry, and a `--process` typed after the app name is lifted into the subcommand flag instead of being stored as a docker option.
This commit is contained in:
156
plugins/docker-options/dockeroptions_test.go
Normal file
156
plugins/docker-options/dockeroptions_test.go
Normal file
@@ -0,0 +1,156 @@
|
||||
package dockeroptions
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSplitOptionString(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
input string
|
||||
wantOptions []string
|
||||
wantProcesses []string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "single flag with value",
|
||||
input: "--link foo",
|
||||
wantOptions: []string{"--link foo"},
|
||||
},
|
||||
{
|
||||
name: "single flag with equals value",
|
||||
input: "--build-arg FOO=bar",
|
||||
wantOptions: []string{"--build-arg FOO=bar"},
|
||||
},
|
||||
{
|
||||
name: "two link flags split",
|
||||
input: "--link a --link b",
|
||||
wantOptions: []string{"--link a", "--link b"},
|
||||
},
|
||||
{
|
||||
name: "build-arg followed by two link flags",
|
||||
input: "--build-arg X=Y --link a --link b",
|
||||
wantOptions: []string{"--build-arg X=Y", "--link a", "--link b"},
|
||||
},
|
||||
{
|
||||
name: "boolean flag then key+value flag",
|
||||
input: "--rm --link foo",
|
||||
wantOptions: []string{"--rm", "--link foo"},
|
||||
},
|
||||
{
|
||||
name: "short flag with value",
|
||||
input: "-v /tmp",
|
||||
wantOptions: []string{"-v /tmp"},
|
||||
},
|
||||
{
|
||||
name: "restart with embedded equals and colon",
|
||||
input: "--restart=on-failure:5",
|
||||
wantOptions: []string{"--restart=on-failure:5"},
|
||||
},
|
||||
{
|
||||
name: "empty input",
|
||||
input: "",
|
||||
},
|
||||
{
|
||||
name: "whitespace only",
|
||||
input: " \t ",
|
||||
},
|
||||
{
|
||||
name: "value with whitespace gets shell-quoted",
|
||||
input: `--label "foo bar"`,
|
||||
wantOptions: []string{"--label 'foo bar'"},
|
||||
},
|
||||
{
|
||||
name: "equals value with whitespace gets shell-quoted",
|
||||
input: `--label key="hello world"`,
|
||||
wantOptions: []string{"--label 'key=hello world'"},
|
||||
},
|
||||
{
|
||||
name: "process flag in option content is lifted",
|
||||
input: "--process web",
|
||||
wantProcesses: []string{"web"},
|
||||
},
|
||||
{
|
||||
name: "process equals form is lifted",
|
||||
input: "--process=web",
|
||||
wantProcesses: []string{"web"},
|
||||
},
|
||||
{
|
||||
name: "process lifted from middle of multi-flag input",
|
||||
input: "--link foo --process web --link bar",
|
||||
wantOptions: []string{"--link foo", "--link bar"},
|
||||
wantProcesses: []string{"web"},
|
||||
},
|
||||
{
|
||||
name: "multiple processes lifted",
|
||||
input: "--process web --process worker",
|
||||
wantProcesses: []string{"web", "worker"},
|
||||
},
|
||||
{
|
||||
name: "process without value errors",
|
||||
input: "--process",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unbalanced quote errors",
|
||||
input: `--build-arg FOO="bar`,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
gotOptions, gotProcesses, err := SplitOptionString(tc.input)
|
||||
if tc.wantErr {
|
||||
if err == nil {
|
||||
t.Fatalf("SplitOptionString(%q) succeeded, want error", tc.input)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("SplitOptionString(%q): %v", tc.input, err)
|
||||
}
|
||||
if !equalStringSlice(gotOptions, tc.wantOptions) {
|
||||
t.Errorf("options = %q, want %q", gotOptions, tc.wantOptions)
|
||||
}
|
||||
if !equalStringSlice(gotProcesses, tc.wantProcesses) {
|
||||
t.Errorf("processes = %q, want %q", gotProcesses, tc.wantProcesses)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestQuoteShellArg(t *testing.T) {
|
||||
cases := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"", "''"},
|
||||
{"plain", "plain"},
|
||||
{"FOO=bar", "FOO=bar"},
|
||||
{"hello world", "'hello world'"},
|
||||
{"it's", `'it'\''s'`},
|
||||
{"$VAR", "'$VAR'"},
|
||||
{"a|b", "'a|b'"},
|
||||
}
|
||||
for _, tc := range cases {
|
||||
if got := quoteShellArg(tc.in); got != tc.want {
|
||||
t.Errorf("quoteShellArg(%q) = %q, want %q", tc.in, got, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func equalStringSlice(a, b []string) bool {
|
||||
if len(a) == 0 && len(b) == 0 {
|
||||
return true
|
||||
}
|
||||
if len(a) != len(b) {
|
||||
return false
|
||||
}
|
||||
for i := range a {
|
||||
if a[i] != b[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user