diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index 1544c402e5..8bd0c163aa 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -508,7 +508,7 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C } detached, _ := cmd.Flags().GetBool("detach") - ep, err := selectEventProcessor(dockerCli, opts.Progress, ansi, detached) + ep, err := selectEventProcessor(dockerCli, opts.Progress, ansi, detached, dryRun) if err != nil { return err } @@ -653,37 +653,37 @@ func stdinfo(dockerCli command.Cli) io.Writer { // In auto mode we probe Err() (not Out()) because the renderer writes to stderr; // probing stdout would force plain mode whenever stdout is redirected (e.g. // `docker compose up | tee log`) while stderr is still a terminal. -func selectEventProcessor(dockerCli command.Cli, progress, ansi string, detached bool) (api.EventProcessor, error) { +func selectEventProcessor(dockerCli command.Cli, progress, ansi string, detached, dryRun bool) (api.EventProcessor, error) { switch progress { case "", display.ModeAuto: switch { case ansi == "never": display.Mode = display.ModePlain - return display.Plain(dockerCli.Err()), nil + return display.Plain(dockerCli.Err(), dryRun), nil case dockerCli.Err().IsTerminal(): - return display.Full(dockerCli.Err(), stdinfo(dockerCli), detached), nil + return display.Full(dockerCli.Err(), stdinfo(dockerCli), detached, dryRun), nil default: - return display.Plain(dockerCli.Err()), nil + return display.Plain(dockerCli.Err(), dryRun), nil } case display.ModeTTY: if ansi == "never" { return nil, fmt.Errorf("can't use --progress tty while ANSI support is disabled") } display.Mode = display.ModeTTY - return display.Full(dockerCli.Err(), stdinfo(dockerCli), detached), nil + return display.Full(dockerCli.Err(), stdinfo(dockerCli), detached, dryRun), nil case display.ModePlain: if ansi == "always" { return nil, fmt.Errorf("can't use --progress plain while ANSI support is forced") } display.Mode = display.ModePlain - return display.Plain(dockerCli.Err()), nil + return display.Plain(dockerCli.Err(), dryRun), nil case display.ModeQuiet, "none": display.Mode = display.ModeQuiet return display.Quiet(), nil case display.ModeJSON: display.Mode = display.ModeJSON logrus.SetFormatter(&logrus.JSONFormatter{}) - return display.JSON(dockerCli.Err()), nil + return display.JSON(dockerCli.Err(), dryRun), nil default: return nil, fmt.Errorf("unsupported --progress value %q", progress) } diff --git a/cmd/compose/compose_progress_test.go b/cmd/compose/compose_progress_test.go index c94760ec66..9189c4ced8 100644 --- a/cmd/compose/compose_progress_test.go +++ b/cmd/compose/compose_progress_test.go @@ -125,7 +125,7 @@ func TestSelectEventProcessor_AutoMode(t *testing.T) { saveGlobalState(t) cli := newMockCli(t, newStream(t, tc.outIsTTY), newStream(t, tc.errIsTTY)) - ep, err := selectEventProcessor(cli, "", tc.ansi, false) + ep, err := selectEventProcessor(cli, "", tc.ansi, false, false) assert.NilError(t, err) assert.Equal(t, fmt.Sprintf("%T", ep), tc.wantType) }) @@ -196,7 +196,7 @@ func TestSelectEventProcessor_ExplicitMode(t *testing.T) { // Explicit modes don't probe IsTerminal; pipes are fine for both. cli := newMockCli(t, newStream(t, false), newStream(t, false)) - ep, err := selectEventProcessor(cli, tc.progress, tc.ansi, false) + ep, err := selectEventProcessor(cli, tc.progress, tc.ansi, false, false) if tc.wantErrText != "" { assert.ErrorContains(t, err, tc.wantErrText) assert.Assert(t, ep == nil) diff --git a/cmd/display/json.go b/cmd/display/json.go index b887359637..9952a2cb80 100644 --- a/cmd/display/json.go +++ b/cmd/display/json.go @@ -25,9 +25,10 @@ import ( "github.com/docker/compose/v5/pkg/api" ) -func JSON(out io.Writer) api.EventProcessor { +func JSON(out io.Writer, dryRun bool) api.EventProcessor { return &jsonWriter{ - out: out, + out: out, + dryRun: dryRun, } } diff --git a/cmd/display/json_test.go b/cmd/display/json_test.go index 0f0dff23a6..9201486dea 100644 --- a/cmd/display/json_test.go +++ b/cmd/display/json_test.go @@ -60,3 +60,14 @@ func TestJsonWriter_Event(t *testing.T) { } assert.DeepEqual(t, expected, actual) } + +func TestJSON_DryRunWiring(t *testing.T) { + var out bytes.Buffer + ep := JSON(&out, true) + ep.On(api.Resource{ID: "service1", Text: api.StatusCreating}) + + var actual jsonMessage + err := json.Unmarshal(out.Bytes(), &actual) + assert.NilError(t, err) + assert.Equal(t, actual.DryRun, true) +} diff --git a/cmd/display/plain.go b/cmd/display/plain.go index 16f2816c01..626e7d377b 100644 --- a/cmd/display/plain.go +++ b/cmd/display/plain.go @@ -24,9 +24,10 @@ import ( "github.com/docker/compose/v5/pkg/api" ) -func Plain(out io.Writer) api.EventProcessor { +func Plain(out io.Writer, dryRun bool) api.EventProcessor { return &plainWriter{ - out: out, + out: out, + dryRun: dryRun, } } diff --git a/cmd/display/plain_test.go b/cmd/display/plain_test.go new file mode 100644 index 0000000000..668b25fe19 --- /dev/null +++ b/cmd/display/plain_test.go @@ -0,0 +1,43 @@ +/* + Copyright 2020 Docker Compose CLI authors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package display + +import ( + "bytes" + "strings" + "testing" + + "gotest.tools/v3/assert" + + "github.com/docker/compose/v5/pkg/api" +) + +func TestPlain_DryRun(t *testing.T) { + var out bytes.Buffer + ep := Plain(&out, true) + ep.On(api.Resource{ID: "service1", Text: api.StatusCreating}) + + assert.Assert(t, strings.Contains(out.String(), DRYRUN_PREFIX)) +} + +func TestPlain_NotDryRun(t *testing.T) { + var out bytes.Buffer + ep := Plain(&out, false) + ep.On(api.Resource{ID: "service1", Text: api.StatusCreating}) + + assert.Assert(t, !strings.Contains(out.String(), DRYRUN_PREFIX)) +} diff --git a/cmd/display/tty.go b/cmd/display/tty.go index 447ecd61e7..d8263a0b20 100644 --- a/cmd/display/tty.go +++ b/cmd/display/tty.go @@ -37,7 +37,7 @@ import ( // Full creates an EventProcessor that render advanced UI within a terminal. // On Start, TUI lists task with a progress timer -func Full(out io.Writer, info io.Writer, detached bool) api.EventProcessor { +func Full(out io.Writer, info io.Writer, detached, dryRun bool) api.EventProcessor { return &ttyWriter{ out: out, info: info, @@ -45,6 +45,7 @@ func Full(out io.Writer, info io.Writer, detached bool) api.EventProcessor { done: make(chan bool), mtx: &sync.Mutex{}, detached: detached, + dryRun: dryRun, } } @@ -56,7 +57,7 @@ type ttyWriter struct { numLines int done chan bool mtx *sync.Mutex - dryRun bool // FIXME(ndeloof) (re)implement support for dry-run + dryRun bool operation string ticker *time.Ticker suspended bool diff --git a/cmd/display/tty_test.go b/cmd/display/tty_test.go index c6d6165c31..6ab402e1bf 100644 --- a/cmd/display/tty_test.go +++ b/cmd/display/tty_test.go @@ -31,6 +31,14 @@ import ( "github.com/docker/compose/v5/pkg/api" ) +func TestFull_DryRunWiring(t *testing.T) { + var buf bytes.Buffer + ep := Full(&buf, &buf, false, true) + w, ok := ep.(*ttyWriter) + assert.Assert(t, ok) + assert.Equal(t, w.dryRun, true) +} + func newTestWriter() (*ttyWriter, *bytes.Buffer) { var buf bytes.Buffer w := &ttyWriter{