mirror of
https://github.com/dokku/dokku.git
synced 2026-05-18 05:05:46 +02:00
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:
@@ -58,11 +58,17 @@ Multiple phases can be specified by using a comma when specifying phases:
|
||||
dokku docker-options:add node-js-app deploy,run "--ulimit nofile=12"
|
||||
```
|
||||
|
||||
The `docker-options:add` does not support setting multiple options in a single call. To specify multiple options, call `docker-options:add` multiple times.
|
||||
Multiple docker options can also be specified in a single call. Each `--flag [value]` group is detected on flag boundaries, shell-tokenized for quoting safety, and stored as its own entry so it round-trips through `docker-options:report` and `docker-options:list`:
|
||||
|
||||
```shell
|
||||
dokku docker-options:add node-js-app deploy "--ulimit nofile=12"
|
||||
dokku docker-options:add node-js-app deploy "--shm-size 256m"
|
||||
dokku docker-options:add node-js-app deploy "--ulimit nofile=12" "--shm-size 256m"
|
||||
```
|
||||
|
||||
A misplaced `--process PROC` (i.e. one specified after the app name instead of before it) is honored as a subcommand flag rather than stored as a docker option, so the example above and the equivalent process-scoped form below behave identically:
|
||||
|
||||
```shell
|
||||
dokku docker-options:add --process web node-js-app deploy "--ulimit nofile=12" "--shm-size 256m"
|
||||
dokku docker-options:add node-js-app deploy "--ulimit nofile=12" "--shm-size 256m" --process web
|
||||
```
|
||||
|
||||
#### Remove a Docker option
|
||||
@@ -79,11 +85,10 @@ Multiple phases can be specified by using a comma when specifying phases:
|
||||
dokku docker-options:remove node-js-app deploy,run "--ulimit nofile=12"
|
||||
```
|
||||
|
||||
The `docker-options:remove` does not support setting multiple options in a single call. To specify multiple options, call `docker-options:remove` multiple times.
|
||||
Multiple docker options can also be removed in a single call, mirroring the splitting that `docker-options:add` performs:
|
||||
|
||||
```shell
|
||||
dokku docker-options:remove node-js-app deploy "--ulimit nofile=12"
|
||||
dokku docker-options:remove node-js-app deploy "--shm-size 256m"
|
||||
dokku docker-options:remove node-js-app deploy "--ulimit nofile=12" "--shm-size 256m"
|
||||
```
|
||||
|
||||
#### Clear all Docker options for an app
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/dokku/dokku/plugins/common"
|
||||
"mvdan.cc/sh/v3/shell"
|
||||
)
|
||||
|
||||
// DefaultProcessType is the sentinel process-type key used for options that
|
||||
@@ -13,6 +14,117 @@ import (
|
||||
// Procfile process type).
|
||||
const DefaultProcessType = "_default_"
|
||||
|
||||
// SplitOptionString shell-tokenizes input, groups tokens on flag boundaries,
|
||||
// and returns one re-serialized option per group. A docker-options subcommand
|
||||
// flag (currently just --process) that lands inside the option content -
|
||||
// because the user typed it after the app name, where pflag's
|
||||
// SetInterspersed(false) hands it back as positional - is lifted into the
|
||||
// returned processes slice rather than stored as a docker option. The caller
|
||||
// merges those processes with whatever pflag already captured. Empty or
|
||||
// whitespace-only input returns empty slices.
|
||||
func SplitOptionString(input string) (options []string, processes []string, err error) {
|
||||
if strings.TrimSpace(input) == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
fields, err := shell.Fields(input, func(string) string { return "" })
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("Unable to parse docker option: %s", err.Error())
|
||||
}
|
||||
|
||||
var current []string
|
||||
flush := func() error {
|
||||
if len(current) == 0 {
|
||||
return nil
|
||||
}
|
||||
head := current[0]
|
||||
if head == "--process" {
|
||||
if len(current) < 2 {
|
||||
return fmt.Errorf("--process requires a value")
|
||||
}
|
||||
if len(current) > 2 {
|
||||
return fmt.Errorf("--process accepts a single value, got %d", len(current)-1)
|
||||
}
|
||||
processes = append(processes, current[1])
|
||||
current = nil
|
||||
return nil
|
||||
}
|
||||
if strings.HasPrefix(head, "--process=") {
|
||||
if len(current) > 1 {
|
||||
return fmt.Errorf("--process=value cannot be followed by additional tokens")
|
||||
}
|
||||
processes = append(processes, head[len("--process="):])
|
||||
current = nil
|
||||
return nil
|
||||
}
|
||||
options = append(options, joinShellTokens(current))
|
||||
current = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, tok := range fields {
|
||||
if isFlagToken(tok) && len(current) > 0 {
|
||||
if err := flush(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
current = append(current, tok)
|
||||
}
|
||||
if err := flush(); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return options, processes, nil
|
||||
}
|
||||
|
||||
// isFlagToken reports whether tok looks like a CLI flag (long or short) rather
|
||||
// than a value. Treating any token that begins with `-` and has more than one
|
||||
// character as a flag matches docker's flag conventions and avoids the need
|
||||
// for a per-flag whitelist.
|
||||
func isFlagToken(tok string) bool {
|
||||
return len(tok) > 1 && tok[0] == '-'
|
||||
}
|
||||
|
||||
// joinShellTokens joins tokens into a single string suitable for storage,
|
||||
// shell-quoting any token that contains characters the bash scheduler's
|
||||
// `eval` re-tokenization would interpret. The stored line round-trips through
|
||||
// `eval set -- "$line"` back to the original token slice.
|
||||
func joinShellTokens(tokens []string) string {
|
||||
parts := make([]string, len(tokens))
|
||||
for i, tok := range tokens {
|
||||
parts[i] = quoteShellArg(tok)
|
||||
}
|
||||
return strings.Join(parts, " ")
|
||||
}
|
||||
|
||||
// quoteShellArg returns s wrapped in single quotes when it contains characters
|
||||
// the shell would otherwise interpret (whitespace, quotes, expansion sigils,
|
||||
// globs, redirections, etc.). Embedded single quotes are escaped with the
|
||||
// standard `'\''` close-escape-open sequence. Tokens free of such characters
|
||||
// are returned verbatim so the stored representation stays human-readable for
|
||||
// the common case.
|
||||
func quoteShellArg(s string) string {
|
||||
if s == "" {
|
||||
return "''"
|
||||
}
|
||||
if !needsShellQuoting(s) {
|
||||
return s
|
||||
}
|
||||
return "'" + strings.ReplaceAll(s, "'", `'\''`) + "'"
|
||||
}
|
||||
|
||||
func needsShellQuoting(s string) bool {
|
||||
for _, r := range s {
|
||||
switch r {
|
||||
case ' ', '\t', '\n', '"', '\'', '$', '`', '\\',
|
||||
'*', '?', '[', ']', '<', '>', '|', '&', ';',
|
||||
'(', ')', '{', '}', '!', '#', '~':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func propertyKey(processType, phase string) string {
|
||||
if processType == "" {
|
||||
processType = DefaultProcessType
|
||||
|
||||
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
|
||||
}
|
||||
@@ -15,6 +15,12 @@ import (
|
||||
// to every container in the app); otherwise it is added to each named
|
||||
// process type. The default and process flows are mutually exclusive
|
||||
// because process scoping is only valid for the deploy phase.
|
||||
//
|
||||
// The option string is split on flag boundaries via SplitOptionString so a
|
||||
// single invocation may carry multiple flags (e.g. `--build-arg X=Y --link a
|
||||
// --link b`); each split flag becomes a separate stored option. A misplaced
|
||||
// `--process` inside the option content is lifted into the process slice
|
||||
// rather than stored as a docker option.
|
||||
func CommandAdd(appName string, processes []string, phasesArg string, option string) error {
|
||||
if err := common.VerifyAppName(appName); err != nil {
|
||||
return err
|
||||
@@ -25,10 +31,17 @@ func CommandAdd(appName string, processes []string, phasesArg string, option str
|
||||
return err
|
||||
}
|
||||
|
||||
if option == "" {
|
||||
options, extractedProcesses, err := SplitOptionString(option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
return errors.New("Please specify docker options to add to the phase")
|
||||
}
|
||||
|
||||
processes = dedupeProcesses(append(processes, extractedProcesses...))
|
||||
|
||||
if err := ValidateProcessFlag(processes, phases); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -37,15 +50,24 @@ func CommandAdd(appName string, processes []string, phasesArg string, option str
|
||||
WarnIfProcessNotInProcfile(appName, processType)
|
||||
}
|
||||
|
||||
if len(processes) == 0 {
|
||||
return AddDockerOptionToPhases(appName, phases, option)
|
||||
for _, opt := range options {
|
||||
if len(processes) == 0 {
|
||||
if err := AddDockerOptionToPhases(appName, phases, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := AddDockerOptionToProcessPhases(appName, processes, phases, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return AddDockerOptionToProcessPhases(appName, processes, phases, option)
|
||||
return nil
|
||||
}
|
||||
|
||||
// CommandRemove removes a docker option from the specified phases for an app.
|
||||
// Process-flag handling matches CommandAdd.
|
||||
// Process-flag handling matches CommandAdd, including splitting a multi-flag
|
||||
// option string and lifting a misplaced `--process` into the process slice.
|
||||
func CommandRemove(appName string, processes []string, phasesArg string, option string) error {
|
||||
if err := common.VerifyAppName(appName); err != nil {
|
||||
return err
|
||||
@@ -56,19 +78,50 @@ func CommandRemove(appName string, processes []string, phasesArg string, option
|
||||
return err
|
||||
}
|
||||
|
||||
if option == "" {
|
||||
options, extractedProcesses, err := SplitOptionString(option)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(options) == 0 {
|
||||
return errors.New("Please specify docker options to remove from the phase")
|
||||
}
|
||||
|
||||
processes = dedupeProcesses(append(processes, extractedProcesses...))
|
||||
|
||||
if err := ValidateProcessFlag(processes, phases); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(processes) == 0 {
|
||||
return RemoveDockerOptionFromPhases(appName, phases, option)
|
||||
for _, opt := range options {
|
||||
if len(processes) == 0 {
|
||||
if err := RemoveDockerOptionFromPhases(appName, phases, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
if err := RemoveDockerOptionFromProcessPhases(appName, processes, phases, opt); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return RemoveDockerOptionFromProcessPhases(appName, processes, phases, option)
|
||||
return nil
|
||||
}
|
||||
|
||||
// dedupeProcesses preserves order while collapsing repeated process names,
|
||||
// which can occur when --process is specified both before the app (captured
|
||||
// by pflag) and after (lifted by SplitOptionString).
|
||||
func dedupeProcesses(processes []string) []string {
|
||||
seen := map[string]bool{}
|
||||
result := make([]string, 0, len(processes))
|
||||
for _, p := range processes {
|
||||
if seen[p] {
|
||||
continue
|
||||
}
|
||||
seen[p] = true
|
||||
result = append(result, p)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// CommandClear removes all docker options for an app, optionally limited to
|
||||
|
||||
@@ -5,6 +5,7 @@ go 1.26.2
|
||||
require (
|
||||
github.com/dokku/dokku/plugins/common v0.0.0-00010101000000-000000000000
|
||||
github.com/spf13/pflag v1.0.10
|
||||
mvdan.cc/sh/v3 v3.13.1
|
||||
)
|
||||
|
||||
require (
|
||||
|
||||
@@ -4,6 +4,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA=
|
||||
@@ -12,6 +14,10 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
@@ -30,6 +36,8 @@ github.com/pkg/sftp v1.13.10 h1:+5FbKNTe5Z9aspU88DPIKJ9z2KZoaGCu6Sr6kKR/5mU=
|
||||
github.com/pkg/sftp v1.13.10/go.mod h1:bJ1a7uDhrX/4OII+agvy28lzRvQrmIQuaHrcI1HbeGA=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
|
||||
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
@@ -53,3 +61,5 @@ golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
mvdan.cc/sh/v3 v3.13.1 h1:DP3TfgZhDkT7lerUdnp6PTGKyxxzz6T+cOlY/xEvfWk=
|
||||
mvdan.cc/sh/v3 v3.13.1/go.mod h1:lXJ8SexMvEVcHCoDvAGLZgFJ9Wsm2sulmoNEXGhYZD0=
|
||||
|
||||
@@ -362,3 +362,75 @@ teardown() {
|
||||
assert_success
|
||||
assert_output_contains "TOKEN is: hello" 2
|
||||
}
|
||||
|
||||
@test "(docker-options:add) splits multi-flag input into separate options" {
|
||||
run /bin/bash -c "dokku docker-options:add $TEST_APP build --build-arg X=Y --link foo --link bar"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku docker-options:list $TEST_APP --phase build"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "--build-arg X=Y"
|
||||
assert_output_contains "--link foo"
|
||||
assert_output_contains "--link bar"
|
||||
|
||||
run /bin/bash -c "dokku docker-options:report $TEST_APP --docker-options-build"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "--build-arg X=Y"
|
||||
assert_output_contains "--link foo"
|
||||
assert_output_contains "--link bar"
|
||||
}
|
||||
|
||||
@test "(docker-options:remove) symmetrically removes multi-flag input" {
|
||||
run /bin/bash -c "dokku docker-options:add $TEST_APP build --build-arg X=Y --link foo --link bar"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku docker-options:remove $TEST_APP build --build-arg X=Y --link foo --link bar"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku docker-options:list $TEST_APP --phase build"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output ""
|
||||
}
|
||||
|
||||
@test "(docker-options:add) lifts misplaced --process out of option content" {
|
||||
run /bin/bash -c "dokku docker-options:add $TEST_APP deploy --link foo --process web"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run /bin/bash -c "dokku docker-options:list $TEST_APP --process web --phase deploy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output "--link foo"
|
||||
|
||||
run /bin/bash -c "dokku docker-options:list $TEST_APP --phase deploy"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
assert_output_contains "--link foo" 0
|
||||
}
|
||||
|
||||
@test "(docker-options) dockerfile build skips unsupported flags from multi-flag input" {
|
||||
run /bin/bash -c "dokku docker-options:add $TEST_APP build --build-arg PAYPAL_CLIENT_ID=abc --link postgres"
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
|
||||
run deploy_app dockerfile
|
||||
echo "output: $output"
|
||||
echo "status: $status"
|
||||
assert_success
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user