mirror of
https://github.com/opentffoundation/opentf.git
synced 2026-04-08 15:01:37 -04:00
We now have RunningInAutomation has a general concern in views.View, so we no longer need to specify it for each command-specific constructor separately. For this initial change I focused only on changing the exported interface of the views package and let the command-specific views go on having their own unexported fields containing a copy of the flag because it made this change less invasive and I wasn't feeling sure yet about whether we ought to have code within command-specific views directly access the internals of views.View. However, maybe we'll simplify this further in a later commit if we conclude that these copies of the flag are burdensome. The general version of this gets set directly inside the main package, which might at some future point allow us to make the command package itself unaware of this "running in automation" idea and thus reinforce that it's intended as a presentation-only thing rather than as a behavioral thing, but we'll save more invasive refactoring for another day.
163 lines
5.3 KiB
Go
163 lines
5.3 KiB
Go
package views
|
|
|
|
import (
|
|
"github.com/hashicorp/terraform/command/arguments"
|
|
"github.com/hashicorp/terraform/command/format"
|
|
"github.com/hashicorp/terraform/internal/terminal"
|
|
"github.com/hashicorp/terraform/tfdiags"
|
|
"github.com/mitchellh/colorstring"
|
|
)
|
|
|
|
// View is the base layer for command views, encapsulating a set of I/O
|
|
// streams, a colorize implementation, and implementing a human friendly view
|
|
// for diagnostics.
|
|
type View struct {
|
|
streams *terminal.Streams
|
|
colorize *colorstring.Colorize
|
|
|
|
compactWarnings bool
|
|
|
|
// When this is true it's a hint that Terraform is being run indirectly
|
|
// via a wrapper script or other automation and so we may wish to replace
|
|
// direct examples of commands to run with more conceptual directions.
|
|
// However, we only do this on a best-effort basis, typically prioritizing
|
|
// the messages that users are most likely to see.
|
|
runningInAutomation 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
|
|
// order to access the config loader cache.
|
|
configSources func() map[string][]byte
|
|
}
|
|
|
|
// Initialize a View with the given streams, a disabled colorize object, and a
|
|
// no-op configSources callback.
|
|
func NewView(streams *terminal.Streams) *View {
|
|
return &View{
|
|
streams: streams,
|
|
colorize: &colorstring.Colorize{
|
|
Colors: colorstring.DefaultColors,
|
|
Disable: true,
|
|
Reset: true,
|
|
},
|
|
configSources: func() map[string][]byte { return nil },
|
|
}
|
|
}
|
|
|
|
// SetRunningInAutomation modifies the view's "running in automation" flag,
|
|
// which causes some slight adjustments to certain messages that would normally
|
|
// suggest specific Terraform commands to run, to make more conceptual gestures
|
|
// instead for situations where the user isn't running Terraform directly.
|
|
//
|
|
// For convenient use during initialization (in conjunction with NewView),
|
|
// SetRunningInAutomation returns the reciever after modifying it.
|
|
func (v *View) SetRunningInAutomation(new bool) *View {
|
|
v.runningInAutomation = new
|
|
return v
|
|
}
|
|
|
|
func (v *View) RunningInAutomation() bool {
|
|
return v.runningInAutomation
|
|
}
|
|
|
|
// Configure applies the global view configuration flags.
|
|
func (v *View) Configure(view *arguments.View) {
|
|
v.colorize.Disable = view.NoColor
|
|
v.compactWarnings = view.CompactWarnings
|
|
}
|
|
|
|
// SetConfigSources overrides the default no-op callback with a new function
|
|
// pointer, and should be called when the config loader is initialized.
|
|
func (v *View) SetConfigSources(cb func() map[string][]byte) {
|
|
v.configSources = cb
|
|
}
|
|
|
|
// Diagnostics renders a set of warnings and errors in human-readable form.
|
|
// Warnings are printed to stdout, and errors to stderr.
|
|
func (v *View) Diagnostics(diags tfdiags.Diagnostics) {
|
|
diags.Sort()
|
|
|
|
if len(diags) == 0 {
|
|
return
|
|
}
|
|
|
|
diags = diags.ConsolidateWarnings(1)
|
|
|
|
// Since warning messages are generally competing
|
|
if v.compactWarnings {
|
|
// If the user selected compact warnings and all of the diagnostics are
|
|
// warnings then we'll use a more compact representation of the warnings
|
|
// that only includes their summaries.
|
|
// We show full warnings if there are also errors, because a warning
|
|
// can sometimes serve as good context for a subsequent error.
|
|
useCompact := true
|
|
for _, diag := range diags {
|
|
if diag.Severity() != tfdiags.Warning {
|
|
useCompact = false
|
|
break
|
|
}
|
|
}
|
|
if useCompact {
|
|
msg := format.DiagnosticWarningsCompact(diags, v.colorize)
|
|
msg = "\n" + msg + "\nTo see the full warning notes, run Terraform without -compact-warnings.\n"
|
|
v.streams.Print(msg)
|
|
return
|
|
}
|
|
}
|
|
|
|
for _, diag := range diags {
|
|
var msg string
|
|
if v.colorize.Disable {
|
|
msg = format.DiagnosticPlain(diag, v.configSources(), v.streams.Stderr.Columns())
|
|
} else {
|
|
msg = format.Diagnostic(diag, v.configSources(), v.colorize, v.streams.Stderr.Columns())
|
|
}
|
|
|
|
if diag.Severity() == tfdiags.Error {
|
|
v.streams.Eprint(msg)
|
|
} else {
|
|
v.streams.Print(msg)
|
|
}
|
|
}
|
|
}
|
|
|
|
// HelpPrompt is intended to be called from commands which fail to parse all
|
|
// of their CLI arguments successfully. It refers users to the full help output
|
|
// rather than rendering it directly, which can be overwhelming and confusing.
|
|
func (v *View) HelpPrompt(command string) {
|
|
v.streams.Eprintf(helpPrompt, command)
|
|
}
|
|
|
|
const helpPrompt = `
|
|
For more help on using this command, run:
|
|
terraform %s -help
|
|
`
|
|
|
|
// outputColumns returns the number of text character cells any non-error
|
|
// output should be wrapped to.
|
|
//
|
|
// This is the number of columns to use if you are calling v.streams.Print or
|
|
// related functions.
|
|
func (v *View) outputColumns() int {
|
|
return v.streams.Stdout.Columns()
|
|
}
|
|
|
|
// errorColumns returns the number of text character cells any error
|
|
// output should be wrapped to.
|
|
//
|
|
// This is the number of columns to use if you are calling v.streams.Eprint
|
|
// or related functions.
|
|
func (v *View) errorColumns() int {
|
|
return v.streams.Stderr.Columns()
|
|
}
|
|
|
|
// outputHorizRule will call v.streams.Println with enough horizontal line
|
|
// characters to fill an entire row of output.
|
|
//
|
|
// If UI color is enabled, the rule will get a dark grey coloring to try to
|
|
// visually de-emphasize it.
|
|
func (v *View) outputHorizRule() {
|
|
v.streams.Println(format.HorizontalRule(v.colorize, v.outputColumns()))
|
|
}
|