diff --git a/cmd/yamlfmt/flags.go b/cmd/yamlfmt/flags.go index 1780252..98de3c1 100644 --- a/cmd/yamlfmt/flags.go +++ b/cmd/yamlfmt/flags.go @@ -34,6 +34,7 @@ operation without performing it.`) flagConf *string = flag.String("conf", "", "Read yamlfmt config from this path") flagGlobalConf *bool = flag.Bool("global_conf", false, fmt.Sprintf("Use global yamlfmt config from %s", globalConfFlagVar())) flagDisableGlobalConf *bool = flag.Bool("no_global_conf", false, fmt.Sprintf("Disabled usage of global yamlfmt config from %s", globalConfFlagVar())) + flagResolvedConf *bool = flag.Bool("resolved_conf", false, "Print resolved config") flagDoublestar *bool = flag.Bool("dstar", false, "Use doublestar globs for include and exclude") flagQuiet *bool = flag.Bool("quiet", false, "Print minimal output to stdout") flagContinueOnError *bool = flag.Bool("continue_on_error", false, "Continue to format files that didn't fail instead of exiting with code 1.") @@ -97,6 +98,9 @@ func getOperationFromFlag() yamlfmt.Operation { if *flagDry { return yamlfmt.OperationDry } + if *flagResolvedConf { + return yamlfmt.OperationPrintConfig + } return yamlfmt.OperationFormat } diff --git a/command/command.go b/command/command.go index 9a9eab0..984fb83 100644 --- a/command/command.go +++ b/command/command.go @@ -23,6 +23,8 @@ import ( "github.com/google/yamlfmt" "github.com/google/yamlfmt/engine" + + "github.com/braydonk/yaml" ) type FormatterConfig struct { @@ -30,23 +32,52 @@ type FormatterConfig struct { FormatterSettings map[string]any `mapstructure:",remain"` } +func (f *FormatterConfig) flatten() map[string]any { + flat := make(map[string]any, len(f.FormatterSettings)+1) + if f.Type == "" { + flat["type"] = yamlfmt.BasicFormatterType + } else { + flat["type"] = f.Type + } + + for k, v := range f.FormatterSettings { + flat[k] = v + } + + return flat +} + // NewFormatterConfig returns an empty formatter config with all fields initialized. func NewFormatterConfig() *FormatterConfig { return &FormatterConfig{FormatterSettings: make(map[string]any)} } type Config struct { - Extensions []string `mapstructure:"extensions"` - Include []string `mapstructure:"include"` - Exclude []string `mapstructure:"exclude"` - RegexExclude []string `mapstructure:"regex_exclude"` - FormatterConfig *FormatterConfig `mapstructure:"formatter,omitempty"` - Doublestar bool `mapstructure:"doublestar"` - ContinueOnError bool `mapstructure:"continue_on_error"` - LineEnding yamlfmt.LineBreakStyle `mapstructure:"line_ending"` - GitignoreExcludes bool `mapstructure:"gitignore_excludes"` - GitignorePath string `mapstructure:"gitignore_path"` - OutputFormat engine.EngineOutputFormat `mapstructure:"output_format"` + Extensions []string `mapstructure:"extensions" yaml:"extensions"` + Include []string `mapstructure:"include" yaml:"include"` + Exclude []string `mapstructure:"exclude" yaml:"exclude"` + RegexExclude []string `mapstructure:"regex_exclude" yaml:"regex_exclude"` + FormatterConfig *FormatterConfig `mapstructure:"formatter,omitempty" yaml:"-"` + Doublestar bool `mapstructure:"doublestar" yaml:"doublestar"` + ContinueOnError bool `mapstructure:"continue_on_error" yaml:"continue_on_error"` + LineEnding yamlfmt.LineBreakStyle `mapstructure:"line_ending" yaml:"line_ending"` + GitignoreExcludes bool `mapstructure:"gitignore_excludes" yaml:"gitignore_excludes"` + GitignorePath string `mapstructure:"gitignore_path" yaml:"gitignore_path"` + OutputFormat engine.EngineOutputFormat `mapstructure:"output_format" yaml:"output_format"` +} + +func (c *Config) Marshal() ([]byte, error) { + conf, err := yaml.Marshal(c) + if err != nil { + return []byte{}, err + } + formatterConf, err := yaml.Marshal(struct { + Formatter map[string]any `yaml:"formatter"` + }{Formatter: c.FormatterConfig.flatten()}) + if err != nil { + return []byte{}, err + } + return append(conf, formatterConf...), nil } type Command struct { @@ -159,6 +190,16 @@ func (c *Command) Run() error { return err } fmt.Print(string(out)) + case yamlfmt.OperationPrintConfig: + conf, err := c.Config.Marshal() + if err != nil { + return err + } + formatted, err := formatter.Format(conf) + if err != nil { + return err + } + fmt.Println(string(formatted)) } return nil diff --git a/docs/command-usage.md b/docs/command-usage.md index 9565f36..30907a2 100644 --- a/docs/command-usage.md +++ b/docs/command-usage.md @@ -59,14 +59,15 @@ All flags must be specified **before** any path arguments. These flags adjust the command's mode of operation. All of these flags are booleans. -| Name | Flag | Example | Description | -| :------------ | :--------- | :-------------------------- | :-------------------------------------------------------- | -| Help | `-help` | `yamlfmt -help` | Print the command usage information. | -| Print Version | `-version` | `yamlfmt -version` | Print the yamlfmt version. | -| Dry Run | `-dry` | `yamlfmt -dry .` | Use [Dry Run](#dry-run) mode | -| Lint | `-lint` | `yamlfmt -lint .` | Use [Lint](#lint) mode | -| Quiet Mode | `-quiet` | `yamlfmt -dry -quiet .` | Use quiet mode. Only has effect in Dry Run or Lint modes. | -| Read Stdin | `-in` | `cat x.yaml \| yamlfmt -in` | Read input from stdin and output result to stdout. | +| Name | Flag | Example | Description | +| :------------ | :--------------- | :------------------------ | :-------------------------------------------------------- | +| Help | `-help` | `yamlfmt -help` | Print the command usage information. | +| Print Version | `-version` | `yamlfmt -version` | Print the yamlfmt version. | +| Print Config | `-resolved_conf` | `yamlfmt -resolved_conf` | Print the merged configuration to use. | +| Dry Run | `-dry` | `yamlfmt -dry .` | Use [Dry Run](#dry-run) mode | +| Lint | `-lint` | `yamlfmt -lint .` | Use [Lint](#lint) mode | +| Quiet Mode | `-quiet` | `yamlfmt -dry -quiet .` | Use quiet mode. Only has effect in Dry Run or Lint modes. | +| Read Stdin | `-in` | `cat x.yaml \| yamlfmt -in` | Read input from stdin and output result to stdout. | ### Configuration Flags diff --git a/docs/config-file.md b/docs/config-file.md index 45ba9ca..9edc0a8 100644 --- a/docs/config-file.md +++ b/docs/config-file.md @@ -26,6 +26,8 @@ If the flag `-global_conf` is passed, all other steps will be circumvented and t In the `-conf` flag, the config file can be named anything. As long as it's valid yaml, yamlfmt will read it as a config file. This can be useful for applying unique configs to different directories in a project. The automatic discovery paths do need to use one of the known names. +In the `-resolved_conf` flag, merged config values will be printed. + ## Command The command package defines the main command engine that `cmd/yamlfmt` uses. It uses the top level configuration that any run of the yamlfmt command will use. diff --git a/engine.go b/engine.go index da66578..b98ee89 100644 --- a/engine.go +++ b/engine.go @@ -29,6 +29,7 @@ const ( OperationLint OperationDry OperationStdin + OperationPrintConfig ) type Engine interface { diff --git a/formatter.go b/formatter.go index 52b3f5c..42cdedc 100644 --- a/formatter.go +++ b/formatter.go @@ -16,6 +16,8 @@ package yamlfmt import "fmt" +const BasicFormatterType string = "basic" + type Formatter interface { Type() string Format(yamlContent []byte) ([]byte, error) diff --git a/formatters/basic/factory.go b/formatters/basic/factory.go index eb536b0..147059b 100644 --- a/formatters/basic/factory.go +++ b/formatters/basic/factory.go @@ -22,7 +22,7 @@ import ( type BasicFormatterFactory struct{} func (f *BasicFormatterFactory) Type() string { - return BasicFormatterType + return yamlfmt.BasicFormatterType } func (f *BasicFormatterFactory) NewFormatter(configData map[string]interface{}) (yamlfmt.Formatter, error) { diff --git a/formatters/basic/formatter.go b/formatters/basic/formatter.go index c16d561..61f9eb4 100644 --- a/formatters/basic/formatter.go +++ b/formatters/basic/formatter.go @@ -23,8 +23,6 @@ import ( "github.com/google/yamlfmt" ) -const BasicFormatterType string = "basic" - type BasicFormatter struct { Config *Config Features yamlfmt.FeatureList @@ -34,7 +32,7 @@ type BasicFormatter struct { // yamlfmt.Formatter interface func (f *BasicFormatter) Type() string { - return BasicFormatterType + return yamlfmt.BasicFormatterType } func (f *BasicFormatter) Format(input []byte) ([]byte, error) { diff --git a/integrationtest/command/command_test.go b/integrationtest/command/command_test.go index 86f33df..413ce8c 100644 --- a/integrationtest/command/command_test.go +++ b/integrationtest/command/command_test.go @@ -100,3 +100,11 @@ func TestDryQuiet(t *testing.T) { Update: *updateFlag, }.Run(t) } + +func TestResolvedConf(t *testing.T) { + TestCase{ + Dir: "resolved_conf", + Command: yamlfmtWithArgs("-resolved_conf -continue_on_error=true -formatter retain_line_breaks=true"), + Update: *updateFlag, + }.Run(t) +} diff --git a/integrationtest/command/testdata/resolved_conf/after/a.yaml b/integrationtest/command/testdata/resolved_conf/after/a.yaml new file mode 100755 index 0000000..967c940 --- /dev/null +++ b/integrationtest/command/testdata/resolved_conf/after/a.yaml @@ -0,0 +1,2 @@ +a: + b: 1 \ No newline at end of file diff --git a/integrationtest/command/testdata/resolved_conf/before/a.yaml b/integrationtest/command/testdata/resolved_conf/before/a.yaml new file mode 100644 index 0000000..967c940 --- /dev/null +++ b/integrationtest/command/testdata/resolved_conf/before/a.yaml @@ -0,0 +1,2 @@ +a: + b: 1 \ No newline at end of file diff --git a/integrationtest/command/testdata/resolved_conf/stdout/stderr.txt b/integrationtest/command/testdata/resolved_conf/stdout/stderr.txt new file mode 100755 index 0000000..e69de29 diff --git a/integrationtest/command/testdata/resolved_conf/stdout/stdout.txt b/integrationtest/command/testdata/resolved_conf/stdout/stdout.txt new file mode 100644 index 0000000..48c5ff2 --- /dev/null +++ b/integrationtest/command/testdata/resolved_conf/stdout/stdout.txt @@ -0,0 +1,17 @@ +extensions: + - yaml + - yml +include: [] +exclude: [] +regex_exclude: [] +doublestar: false +continue_on_error: true +line_ending: lf +gitignore_excludes: false +gitignore_path: .gitignore +output_format: default +formatter: + line_ending: lf + retain_line_breaks: true + type: basic +