diff --git a/CHANGELOG.md b/CHANGELOG.md index 8561063518..0af3cb2144 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ UPGRADE NOTES: NEW FEATURES: ENHANCEMENTS: +* Added `-concise` flag to omit the refreshing state logs when tofu plan is run. ([#1225](https://github.com/opentofu/opentofu/pull/1225)) * `nonsensitive` function no longer returns error when applied to values that are not sensitive ([#369](https://github.com/opentofu/opentofu/pull/369)) * Managing large local terraform.tfstate files is now much faster. ([#579](https://github.com/opentofu/opentofu/pull/579)) - Previously, every call to state.Write() would also Persist to disk. This was not following the intended API and had longstanding TODOs in the code. diff --git a/internal/command/arguments/view.go b/internal/command/arguments/view.go index 37dbc15422..2d64e8bf60 100644 --- a/internal/command/arguments/view.go +++ b/internal/command/arguments/view.go @@ -15,6 +15,10 @@ type View struct { // level of noise when multiple instances of the same warning are raised // for a configuration. CompactWarnings bool + + // Concise is used to reduce the level of noise in the output and display + // only the important details. + Concise bool } // ParseView processes CLI arguments, returning a View value and a @@ -32,6 +36,8 @@ func ParseView(args []string) (*View, []string) { common.NoColor = true case "-compact-warnings": common.CompactWarnings = true + case "-concise": + common.Concise = true default: // Unsupported argument: move left to the current position, and // increment the index. diff --git a/internal/command/arguments/view_test.go b/internal/command/arguments/view_test.go index ff2ea872e8..de55c556a6 100644 --- a/internal/command/arguments/view_test.go +++ b/internal/command/arguments/view_test.go @@ -19,37 +19,57 @@ func TestParseView(t *testing.T) { }{ "nil": { nil, - &View{NoColor: false, CompactWarnings: false}, + &View{NoColor: false, CompactWarnings: false, Concise: false}, nil, }, "empty": { []string{}, - &View{NoColor: false, CompactWarnings: false}, + &View{NoColor: false, CompactWarnings: false, Concise: false}, []string{}, }, "none matching": { []string{"-foo", "bar", "-baz"}, - &View{NoColor: false, CompactWarnings: false}, + &View{NoColor: false, CompactWarnings: false, Concise: false}, []string{"-foo", "bar", "-baz"}, }, "no-color": { []string{"-foo", "-no-color", "-baz"}, - &View{NoColor: true, CompactWarnings: false}, + &View{NoColor: true, CompactWarnings: false, Concise: false}, []string{"-foo", "-baz"}, }, "compact-warnings": { []string{"-foo", "-compact-warnings", "-baz"}, - &View{NoColor: false, CompactWarnings: true}, + &View{NoColor: false, CompactWarnings: true, Concise: false}, []string{"-foo", "-baz"}, }, - "both": { + "concise": { + []string{"-foo", "-concise", "-baz"}, + &View{NoColor: false, CompactWarnings: false, Concise: true}, + []string{"-foo", "-baz"}, + }, + "no-color and compact-warnings": { []string{"-foo", "-no-color", "-compact-warnings", "-baz"}, - &View{NoColor: true, CompactWarnings: true}, + &View{NoColor: true, CompactWarnings: true, Concise: false}, []string{"-foo", "-baz"}, }, - "both, resulting in empty args": { - []string{"-no-color", "-compact-warnings"}, - &View{NoColor: true, CompactWarnings: true}, + "no-color and concise": { + []string{"-foo", "-no-color", "-concise", "-baz"}, + &View{NoColor: true, CompactWarnings: false, Concise: true}, + []string{"-foo", "-baz"}, + }, + "concise and compact-warnings": { + []string{"-foo", "-concise", "-compact-warnings", "-baz"}, + &View{NoColor: false, CompactWarnings: true, Concise: true}, + []string{"-foo", "-baz"}, + }, + "all three": { + []string{"-foo", "-no-color", "-compact-warnings", "-concise", "-baz"}, + &View{NoColor: true, CompactWarnings: true, Concise: true}, + []string{"-foo", "-baz"}, + }, + "all three, resulting in empty args": { + []string{"-no-color", "-compact-warnings", "-concise"}, + &View{NoColor: true, CompactWarnings: true, Concise: true}, []string{}, }, } diff --git a/internal/command/jsonformat/plan.go b/internal/command/jsonformat/plan.go index b26cebce79..729c1e0253 100644 --- a/internal/command/jsonformat/plan.go +++ b/internal/command/jsonformat/plan.go @@ -176,7 +176,6 @@ func (plan Plan) renderHuman(renderer Renderer, mode plans.Mode, opts ...plans.Q } } } - if haveRefreshChanges { renderer.Streams.Print(format.HorizontalRule(renderer.Colorize, renderer.Streams.Stdout.Columns())) renderer.Streams.Println() diff --git a/internal/command/plan.go b/internal/command/plan.go index 64f11bf4aa..43d9df1dfc 100644 --- a/internal/command/plan.go +++ b/internal/command/plan.go @@ -276,6 +276,9 @@ Other Options: -no-color If specified, output won't contain any color. + -concise Displays plan output in a concise way, skipping the + refreshing log lines. + -out=path Write a plan file to the given path. This can be used as input to the "apply" command. diff --git a/internal/command/views/hook_ui.go b/internal/command/views/hook_ui.go index 4d46498a02..f74d156660 100644 --- a/internal/command/views/hook_ui.go +++ b/internal/command/views/hook_ui.go @@ -276,10 +276,11 @@ func (h *UiHook) PreRefresh(addr addrs.AbsResourceInstance, gen states.Generatio if depKey, ok := gen.(states.DeposedKey); ok { addrStr = fmt.Sprintf("%s (deposed object %s)", addrStr, depKey) } - - h.println(fmt.Sprintf( - h.view.colorize.Color("[reset][bold]%s: Refreshing state...%s"), - addrStr, stateIdSuffix)) + if !h.view.concise { + h.println(fmt.Sprintf( + h.view.colorize.Color("[reset][bold]%s: Refreshing state...%s"), + addrStr, stateIdSuffix)) + } return tofu.HookActionContinue, nil } diff --git a/internal/command/views/hook_ui_test.go b/internal/command/views/hook_ui_test.go index a22accec55..b38f58eca7 100644 --- a/internal/command/views/hook_ui_test.go +++ b/internal/command/views/hook_ui_test.go @@ -442,6 +442,36 @@ func TestPreRefresh(t *testing.T) { } } +func TestPreRefresh_concise(t *testing.T) { + streams, done := terminal.StreamsForTesting(t) + view := NewView(streams) + view.concise = true + h := NewUiHook(view) + + addr := addrs.Resource{ + Mode: addrs.ManagedResourceMode, + Type: "test_instance", + Name: "foo", + }.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance) + + priorState := cty.ObjectVal(map[string]cty.Value{ + "id": cty.StringVal("test"), + "bar": cty.ListValEmpty(cty.String), + }) + + _, err := h.PreRefresh(addr, states.CurrentGen, priorState) + + if err != nil { + t.Fatal(err) + } + + result := done(t) + + if got, want := result.Stdout(), ""; got != want { + t.Fatalf("unexpected output\n got: %q\nwant: %q", got, want) + } +} + // Test that PreRefresh still works if no ID key and value can be determined // from state. func TestPreRefresh_noID(t *testing.T) { diff --git a/internal/command/views/view.go b/internal/command/views/view.go index 0442e050b4..178d949b16 100644 --- a/internal/command/views/view.go +++ b/internal/command/views/view.go @@ -29,6 +29,10 @@ type View struct { // the messages that users are most likely to see. runningInAutomation bool + // Concise is used to reduce the level of noise in the output and display + // only the important details. + concise bool + // This unfortunate wart is required to enable rendering of diagnostics which // have associated source code in the configuration. This function pointer // will be dereferenced as late as possible when rendering diagnostics in @@ -70,6 +74,7 @@ func (v *View) RunningInAutomation() bool { func (v *View) Configure(view *arguments.View) { v.colorize.Disable = view.NoColor v.compactWarnings = view.CompactWarnings + v.concise = view.Concise } // SetConfigSources overrides the default no-op callback with a new function