Skip to content

Commit

Permalink
engine: add output format support (#170)
Browse files Browse the repository at this point in the history
* engine: add output format support

This PR adds support for engine output format, along with a new single
line output option that instead prints lint/dry run output on a single
line for each file.

Signed-off-by: braydonk <braydonk@google.com>

* change integration test until I have a way to make it hermetic

---------

Signed-off-by: braydonk <braydonk@google.com>
  • Loading branch information
braydonk authored May 3, 2024
1 parent e713bb3 commit fc5b1e4
Show file tree
Hide file tree
Showing 26 changed files with 289 additions and 146 deletions.
12 changes: 10 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ YAMLFMT_BIN ?= $(shell pwd)/dist/yamlfmt
.PHONY: integrationtest
integrationtest:
$(MAKE) build
go test -tags=integration_test ./integrationtest/command
go test -v -tags=integration_test ./integrationtest/command

.PHONY: integrationtest_v
integrationtest_v:
Expand All @@ -28,11 +28,19 @@ integrationtest_stdout:
$(MAKE) build
go test -v -tags=integration_test ./integrationtest/command -stdout

.PHONY: integrationtest_local_update
.PHONY: integrationtest_update
integrationtest_update:
$(MAKE) build
go test -tags=integration_test ./integrationtest/command -update

.PHONY: command_test_case
command_test_case:
ifndef TESTNAME
$(error "TESTNAME undefined")
endif
mkdir -p integrationtest/command/testdata/$(TESTNAME)/before && \
mkdir -p integrationtest/command/testdata/$(TESTNAME)/stdout

.PHONY: install
install:
go install ./cmd/yamlfmt
Expand Down
12 changes: 6 additions & 6 deletions cmd/yamlfmt/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ func validatePath(path string) error {
}

func makeCommandConfigFromData(configData map[string]any) (*command.Config, error) {
config := command.NewConfig()
config := command.Config{FormatterConfig: command.NewFormatterConfig()}
err := mapstructure.Decode(configData, &config)
if err != nil {
return nil, err
Expand Down Expand Up @@ -253,23 +253,23 @@ func makeCommandConfigFromData(configData map[string]any) (*command.Config, erro
config.Extensions = []string{"yaml", "yml"}
}

// Default to doublestar flag if not set in config
// Apply the general rule that the config takes precedence over
// the command line flags.
if !config.Doublestar {
config.Doublestar = *flagDoublestar
}

// Default to continue_on_error flag if not set in config
if !config.ContinueOnError {
config.ContinueOnError = *flagContinueOnError
}

if !config.GitignoreExcludes {
config.GitignoreExcludes = *flagGitignoreExcludes
}

if config.GitignorePath == "" {
config.GitignorePath = *flagGitignorePath
}
if config.OutputFormat == "" {
config.OutputFormat = getOutputFormatFromFlag()
}

// Overwrite config if includes are provided through args
if len(flag.Args()) > 0 {
Expand Down
18 changes: 12 additions & 6 deletions cmd/yamlfmt/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ import (
"runtime"
"strings"

"github.com/google/yamlfmt/command"
"github.com/google/yamlfmt"
"github.com/google/yamlfmt/engine"
)

var (
Expand All @@ -38,6 +39,7 @@ operation without performing it.`)
flagContinueOnError *bool = flag.Bool("continue_on_error", false, "Continue to format files that didn't fail instead of exiting with code 1.")
flagGitignoreExcludes *bool = flag.Bool("gitignore_excludes", false, "Use a gitignore file for excludes")
flagGitignorePath *string = flag.String("gitignore_path", ".gitignore", "Path to gitignore file to use")
flagOutputFormat *string = flag.String("output_format", "default", "The engine output format")
flagExclude = arrayFlag{}
flagFormatter = arrayFlag{}
flagExtensions = arrayFlag{}
Expand Down Expand Up @@ -85,17 +87,21 @@ func configureHelp() {
}
}

func getOperationFromFlag() command.Operation {
func getOperationFromFlag() yamlfmt.Operation {
if *flagIn || isStdinArg() {
return command.OperationStdin
return yamlfmt.OperationStdin
}
if *flagLint {
return command.OperationLint
return yamlfmt.OperationLint
}
if *flagDry {
return command.OperationDry
return yamlfmt.OperationDry
}
return command.OperationFormat
return yamlfmt.OperationFormat
}

func getOutputFormatFromFlag() engine.EngineOutputFormat {
return engine.EngineOutputFormat(*flagOutputFormat)
}

func isStdinArg() bool {
Expand Down
4 changes: 3 additions & 1 deletion cmd/yamlfmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"flag"
"fmt"
"log"
"os"

"github.com/google/yamlfmt"
"github.com/google/yamlfmt/command"
Expand All @@ -29,7 +30,8 @@ var version string = "0.11.0"

func main() {
if err := run(); err != nil {
log.Fatal(err)
l := log.New(os.Stderr, "", 0)
l.Fatal(err)
}
}

Expand Down
69 changes: 29 additions & 40 deletions command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,53 +19,38 @@ import (
"errors"
"fmt"
"io"
"log"
"os"

"github.com/google/yamlfmt"
"github.com/google/yamlfmt/engine"
)

type Operation int

const (
OperationFormat Operation = iota
OperationLint
OperationDry
OperationStdin
)

type FormatterConfig struct {
Type string `mapstructure:"type"`
FormatterSettings map[string]any `mapstructure:",remain"`
}

// NewFormatterConfig returns an empty formatter config with all fields initialized.
func NewFormatterConfig() FormatterConfig {
return FormatterConfig{FormatterSettings: make(map[string]any)}
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"`
}

// NewConfig returns an empty config with all fields initialized.
func NewConfig() Config {
formatterConfig := NewFormatterConfig()
return Config{FormatterConfig: &formatterConfig}
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"`
}

type Command struct {
Operation Operation
Operation yamlfmt.Operation
Registry *yamlfmt.Registry
Config *Config
Quiet bool
Expand Down Expand Up @@ -113,6 +98,7 @@ func (c *Command) Run() error {
Formatter: formatter,
Quiet: c.Quiet,
ContinueOnError: c.Config.ContinueOnError,
OutputFormat: c.Config.OutputFormat,
}

collectedPaths, err := c.collectPaths()
Expand All @@ -129,17 +115,20 @@ func (c *Command) Run() error {

paths, err := c.analyzePaths(collectedPaths)
if err != nil {
log.Printf("path analysis found the following errors:\n%v", err)
log.Println("Continuing...")
fmt.Printf("path analysis found the following errors:\n%v", err)
fmt.Println("Continuing...")
}

switch c.Operation {
case OperationFormat:
err := eng.Format(paths)
case yamlfmt.OperationFormat:
out, err := eng.Format(paths)
if out != nil {
fmt.Print(out)
}
if err != nil {
return err
}
case OperationLint:
case yamlfmt.OperationLint:
out, err := eng.Lint(paths)
if err != nil {
return err
Expand All @@ -150,17 +139,17 @@ func (c *Command) Run() error {
// component of the lint functionality.
return errors.New(out.String())
}
case OperationDry:
case yamlfmt.OperationDry:
out, err := eng.DryRun(paths)
if err != nil {
return err
}
if out.Message == "" && out.Files.ChangedCount() == 0 {
log.Print("No files will be changed.")
if out != nil {
fmt.Print(out)
} else {
log.Print(out)
fmt.Print("No files will be changed.")
}
case OperationStdin:
case yamlfmt.OperationStdin:
stdinYaml, err := readFromStdin()
if err != nil {
return err
Expand All @@ -169,7 +158,7 @@ func (c *Command) Run() error {
if err != nil {
return err
}
fmt.Print(string(out))
fmt.Print(out)
}

return nil
Expand Down
25 changes: 13 additions & 12 deletions docs/command-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,18 +74,19 @@ These flags will configure the underlying behaviour of the command.

The string array flags can be a bit confusing. See the [String Array Flags](#string-array-flags) section for more information.

| Name | Flag | Type | Example | Description |
|:----------------------|:----------------------|:---------|:----------------------------------------------------------|:------------|
| Config File Path | `-conf` | string | `yamlfmt -conf ./config/.yamlfmt` | Specify a path to read a [configuration file](./config-file.md) from. |
| Global Config | `-global_conf` | bool | `yamlfmt -global_conf` | Force yamlfmt to use the configuration file from the system config directory. |
| Disable Global Config | `-no_global_conf` | bool | `yamlfmt -no_global_conf` | Disable looking for the configuration file from the system config directory. |
| Doublestar | `-dstar` | bool | `yamlfmt -dstar "**/*.yaml"` | Enable [Doublestar](./paths.md#doublestar) path collection mode. Note that doublestar patterns should be specified with quotes in bash to prevent shell expansion. |
| Exclude | `-exclude` | []string | `yamlfmt -exclude ./not/,these_paths.yaml` | Patterns to exclude from path collection. These are in addition to the exclude patterns specified in the [config file](./config-file.md) |
| Gitignore Excludes | `-gitignore_excludes` | bool | `yamlfmt -gitignore_excludes` | Use a gitignore file to exclude paths. This is in addition to otherwise specified exclude patterns. |
| Gitignore Path | `-gitignore_path` | string | `yamlfmt -gitignore_path .special_gitignore` | Specify a path to a gitignore file to use. Defaults to `.gitignore` (in working directory). |
| Extensions | `-extensions` | []string | `yamlfmt -extensions yaml,yml` | Extensions to use in standard path collection. Has no effect in Doublestar mode. These add to extensions specified in the [config file](./config-file.md)
| Formatter Config | `-formatter` | []string | `yamlfmt -formatter indent=2,include_document_start=true` | Provide configuration values for the formatter. See [Formatter Configuration Options](./config-file.md#basic-formatter) for options. Each field is specified as `configkey=value`. |
| Debug Logging | `-debug` | []string | `yamlfmt -debug paths,config` | Enable debug logging. See [Debug Logging](#debug-logging) for more information. |
| Name | Flag | Type | Example | Description |
|:----------------------|:----------------------|:------------------|:----------------------------------------------------------|:------------|
| Config File Path | `-conf` | string | `yamlfmt -conf ./config/.yamlfmt` | Specify a path to read a [configuration file](./config-file.md) from. |
| Global Config | `-global_conf` | bool | `yamlfmt -global_conf` | Force yamlfmt to use the configuration file from the system config directory. |
| Disable Global Config | `-no_global_conf` | bool | `yamlfmt -no_global_conf` | Disable looking for the configuration file from the system config directory. |
| Doublestar | `-dstar` | bool | `yamlfmt -dstar "**/*.yaml"` | Enable [Doublestar](./paths.md#doublestar) path collection mode. Note that doublestar patterns should be specified with quotes in bash to prevent shell expansion. |
| Exclude | `-exclude` | []string | `yamlfmt -exclude ./not/,these_paths.yaml` | Patterns to exclude from path collection. These are in addition to the exclude patterns specified in the [config file](./config-file.md) |
| Gitignore Excludes | `-gitignore_excludes` | bool | `yamlfmt -gitignore_excludes` | Use a gitignore file to exclude paths. This is in addition to otherwise specified exclude patterns. |
| Gitignore Path | `-gitignore_path` | string | `yamlfmt -gitignore_path .special_gitignore` | Specify a path to a gitignore file to use. Defaults to `.gitignore` (in working directory). |
| Extensions | `-extensions` | []string | `yamlfmt -extensions yaml,yml` | Extensions to use in standard path collection. Has no effect in Doublestar mode. These add to extensions specified in the [config file](./config-file.md)
| Formatter Config | `-formatter` | []string | `yamlfmt -formatter indent=2,include_document_start=true` | Provide configuration values for the formatter. See [Formatter Configuration Options](./config-file.md#basic-formatter) for options. Each field is specified as `configkey=value`. |
| Debug Logging | `-debug` | []string | `yamlfmt -debug paths,config` | Enable debug logging. See [Debug Logging](#debug-logging) for more information. |
| Output Format | `-output_format` | `default`, `line` | `yamlfmt -output_format line` | Choose a different output format. Defaults to `default`. See [Output docs](./output.md) for more information. |

#### String Array Flags

Expand Down
19 changes: 10 additions & 9 deletions docs/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,16 @@ The command package defines the main command engine that `cmd/yamlfmt` uses. It
| Key | Type | Default | Description |
|:-------------------------|:---------------|:-------------|:------------|
| `line_ending` | `lf` or `crlf` | `crlf` on Windows, `lf` otherwise | Parse and write the file with "lf" or "crlf" line endings. This global setting will override any formatter `line_ending` options. |
| `doublestar` | bool | false | Use [doublestar](https://github.com/bmatcuk/doublestar) for include and exclude paths. (This was the default before 0.7.0) |
| `continue_on_error` | bool | false | Continue formatting and don't exit with code 1 when there is an invalid yaml file found. |
| `include` | []string | [] | The paths for the command to include for formatting. See [Specifying Paths][] for more details. |
| `exclude` | []string | [] | The paths for the command to exclude from formatting. See [Specifying Paths][] for more details. |
| `gitignore_excludes` | bool | false | Use gitignore files for exclude paths. This is in addition to the patterns from the `exclude` option. |
| `gitignore_path` | string | `.gitignore` | The path to the gitignore file to use. |
| `regex_exclude` | []string | [] | Regex patterns to match file contents for, if the file content matches the regex the file will be excluded. Use [Golang regexes](https://regex101.com/). |
| `extensions` | []string | [] | The extensions to use for standard mode path collection. See [Specifying Paths][] for more details. |
| `formatter` | map[string]any | `type: basic` | Formatter settings. See [Formatter](#formatter) for more details. |
| `doublestar` | bool | false | Use [doublestar](https://github.com/bmatcuk/doublestar) for include and exclude paths. (This was the default before 0.7.0) |
| `continue_on_error` | bool | false | Continue formatting and don't exit with code 1 when there is an invalid yaml file found. |
| `include` | []string | [] | The paths for the command to include for formatting. See [Specifying Paths][] for more details. |
| `exclude` | []string | [] | The paths for the command to exclude from formatting. See [Specifying Paths][] for more details. |
| `gitignore_excludes` | bool | false | Use gitignore files for exclude paths. This is in addition to the patterns from the `exclude` option. |
| `gitignore_path` | string | `.gitignore` | The path to the gitignore file to use. |
| `regex_exclude` | []string | [] | Regex patterns to match file contents for, if the file content matches the regex the file will be excluded. Use [Golang regexes](https://regex101.com/). |
| `extensions` | []string | [] | The extensions to use for standard mode path collection. See [Specifying Paths][] for more details. |
| `formatter` | map[string]any | `type: basic` | Formatter settings. See [Formatter](#formatter) for more details. |
| `output_format` | `default` or `line` | `default` | The output format to use. See [Output docs](./output.md) for more details. |

## Formatter

Expand Down
34 changes: 34 additions & 0 deletions docs/output.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Output

The tool supports different output formats when writing to stdout/stderr. This can be configured through either a command line flag `-output_format` or through the `output_format` configuration field in the [config file](./config-file.md).

The following values are supported:

## `default`

Example:
```
The following formatting differences were found:
y.yaml:
a: a:
- b: 1 b: 1
+
z.yaml:
a: a:
- b: 1 b: 1
+
x.yaml:
a: a:
- b: 1 b: 1
+
```

## `line`

Example:
```
x.yaml: formatting difference found
y.yaml: formatting difference found
z.yaml: formatting difference found
```
Loading

0 comments on commit fc5b1e4

Please sign in to comment.