Skip to content

Commit

Permalink
Debug logging (#157)
Browse files Browse the repository at this point in the history
* Debug logging

This PR adds debug logging. Debug logging can be enabled through flags
to the yamlfmt command, and will tell you in very noisy detail about
things that are going on. Debug groups are separated into codes, so that
only certain debug logs can be enabled if you are debugging a specific
problem.

I also fixed a bug I introduced in the last config PR with the `-conf`
flag. It is fixed before ever going out in a release, so it will only
affect someone who installed from that exact commit before I fix it
here.

* Fix integration test files, add -no_global_conf

The integration test files got accidentally formatted by some local
testing, this sets them back to what they should be. Also adds a new
flag that that disables usage of configuration file from system home.
  • Loading branch information
braydonk authored Feb 4, 2024
1 parent be7dd1b commit f33dc61
Show file tree
Hide file tree
Showing 13 changed files with 165 additions and 60 deletions.
9 changes: 7 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

.PHONY: build
build:
go build -o dist/yamlfmt ./cmd/yamlfmt
go build -o dist/yamlfmt ./cmd/yamlfmt

.PHONY: test
test:
Expand All @@ -23,10 +23,15 @@ integrationtest_v:
$(MAKE) build
go test -v -tags=integration_test ./integrationtest/command

.PHONY: integrationtest_stdout
integrationtest_stdout:
$(MAKE) build
go test -v -tags=integration_test ./integrationtest/command -stdout

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

.PHONY: install
install:
Expand Down
53 changes: 31 additions & 22 deletions cmd/yamlfmt/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/google/yamlfmt"
"github.com/google/yamlfmt/command"
"github.com/google/yamlfmt/internal/collections"
"github.com/google/yamlfmt/internal/logger"
"github.com/mitchellh/mapstructure"
)

Expand All @@ -28,7 +29,7 @@ const configHomeDir string = "yamlfmt"
var (
errNoConfFlag = errors.New("config path not specified in --conf")
errConfPathInvalid = errors.New("config path specified in --conf was invalid")
errConfPathNotExist = errors.New("config path does not exist")
errConfPathNotExist = errors.New("no config file found")
errConfPathIsDir = errors.New("config path is dir")
errNoConfigHome = errors.New("missing required env var for config home")
)
Expand All @@ -40,13 +41,13 @@ type configPathError struct {

func (e *configPathError) Error() string {
if errors.Is(e.err, errConfPathInvalid) {
return fmt.Sprintf("Config path %s was invalid", e.path)
return fmt.Sprintf("config path %s was invalid", e.path)
}
if errors.Is(e.err, errConfPathNotExist) {
return fmt.Sprintf("Config path %s does not exist", e.path)
return fmt.Sprintf("no config file found in directory %s", filepath.Dir(e.path))
}
if errors.Is(e.err, errConfPathIsDir) {
return fmt.Sprintf("Config path %s is a directory", e.path)
return fmt.Sprintf("config path %s is a directory", e.path)
}
return e.err.Error()
}
Expand Down Expand Up @@ -90,31 +91,36 @@ func getConfigPath() (string, error) {
return configPath, nil
}

// Third priority: in home config directory
configPath, err = getConfigPathFromConfigHome()
// In this scenario, no errors are considered a failure state,
// so we continue to the next fallback if there are no errors.
if err == nil {
return configPath, nil
if !*flagDisableGlobalConf {
// Third priority: in home config directory
configPath, err = getConfigPathFromConfigHome()
// In this scenario, no errors are considered a failure state,
// so we continue to the next fallback if there are no errors.
if err == nil {
return configPath, nil
}
}

// All else fails, no path and no error (signals to
// use default config).
logger.Debug(logger.DebugCodeConfig, "No config file found, using default config")
return "", nil
}

func getConfigPathFromFlag() (string, error) {
// If there is a path specified in the conf flag, that takes precedence
configPath := *flagConf
if configPath == "" {
logger.Debug(logger.DebugCodeConfig, "No config path specified in -conf")
return configPath, errNoConfFlag
}
// Then we check if we want the global config
if *flagGlobalConf {
logger.Debug(logger.DebugCodeConfig, "Using -global_conf flag")
return getConfigPathFromXdgConfigHome()
}

return "", validatePath(configPath)
logger.Debug(logger.DebugCodeConfig, "Using config path %s from -conf flag", configPath)
return configPath, validatePath(configPath)
}

// This function searches up the directory tree until it finds
Expand All @@ -132,23 +138,14 @@ func getConfigPathFromDirTree() (string, error) {
for dir != filepath.Dir(dir) {
configPath, err := getConfigPathFromDir(dir)
if err == nil {
logger.Debug(logger.DebugCodeConfig, "Found config at %s", configPath)
return configPath, nil
}
dir = filepath.Dir(dir)
}
return "", errConfPathNotExist
}

func getConfigPathFromDir(dir string) (string, error) {
for filename := range configFileNames {
configPath := filepath.Join(dir, filename)
if err := validatePath(configPath); err == nil {
return configPath, nil
}
}
return "", errConfPathNotExist
}

func getConfigPathFromConfigHome() (string, error) {
// Build tags are a veritable pain in the behind,
// I'm putting both config home functions in this
Expand Down Expand Up @@ -184,6 +181,18 @@ func getConfigPathFromAppDataLocal() (string, error) {
return getConfigPathFromDir(homeConfigPath)
}

func getConfigPathFromDir(dir string) (string, error) {
for filename := range configFileNames {
configPath := filepath.Join(dir, filename)
if err := validatePath(configPath); err == nil {
logger.Debug(logger.DebugCodeConfig, "Found config at %s", configPath)
return configPath, nil
}
}
logger.Debug(logger.DebugCodeConfig, "No config file found in %s", dir)
return "", errConfPathNotExist
}

func validatePath(path string) error {
info, err := os.Stat(path)
if err != nil {
Expand Down
30 changes: 16 additions & 14 deletions cmd/yamlfmt/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,22 +28,25 @@ var (
source yaml and formatted yaml.`)
flagDry *bool = flag.Bool("dry", false, `Perform a dry run; show the output of a formatting
operation without performing it.`)
flagIn *bool = flag.Bool("in", false, "Format yaml read from stdin and output to stdout")
flagVersion *bool = flag.Bool("version", false, "Print yamlfmt version")
flagConf *string = flag.String("conf", "", "Read yamlfmt config from this path")
flagGlobalConf *bool = flag.Bool("global_conf", false, globalConfFlagMessage())
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.")
flagExclude = arrayFlag{}
flagFormatter = arrayFlag{}
flagExtensions = arrayFlag{}
flagIn *bool = flag.Bool("in", false, "Format yaml read from stdin and output to stdout")
flagVersion *bool = flag.Bool("version", false, "Print yamlfmt version")
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()))
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.")
flagExclude = arrayFlag{}
flagFormatter = arrayFlag{}
flagExtensions = arrayFlag{}
flagDebug = arrayFlag{}
)

func bindArrayFlags() {
flag.Var(&flagExclude, "exclude", "Paths to exclude in the chosen format (standard or doublestar)")
flag.Var(&flagFormatter, "formatter", "Config value overrides to pass to the formatter")
flag.Var(&flagExtensions, "extensions", "File extensions to use for standard path collection")
flag.Var(&flagDebug, "debug", "Debug codes to activate for debug logging")
}

type arrayFlag []string
Expand Down Expand Up @@ -101,10 +104,9 @@ func isStdinArg() bool {
return arg == "-" || arg == "/dev/stdin"
}

func globalConfFlagMessage() string {
varName := "XDG_CONFIG_HOME"
func globalConfFlagVar() string {
if runtime.GOOS == "windows" {
varName = "LOCALAPPDATA"
return "LOCALAPPDATA"
}
return fmt.Sprintf("Use global yamlfmt config from %s", varName)
return "XDG_CONFIG_HOME"
}
5 changes: 5 additions & 0 deletions cmd/yamlfmt/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/google/yamlfmt"
"github.com/google/yamlfmt/command"
"github.com/google/yamlfmt/formatters/basic"
"github.com/google/yamlfmt/internal/logger"
)

var version string = "0.11.0"
Expand All @@ -42,6 +43,10 @@ func run() error {
return nil
}

for _, code := range flagDebug {
logger.ActivateDebugCode(code)
}

c := &command.Command{
Operation: getOperationFromFlag(),
Registry: getFullRegistry(),
Expand Down
28 changes: 20 additions & 8 deletions docs/command-usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,14 +74,16 @@ 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 ./config/.yamlfmt` | Force yamlfmt to use the configuration file from the system config directory. |
| Doublstar | `-dstar` | boolean | `yamlfmt -dstar "**/*.yaml"` | Enable [Doublstar](./paths.md#doublestar) path collection mode. |
| Exclude | `-exclude` | []string | `yamlfmt -exclude ./not/,these_paths.yaml` | Patterns to exclude from path collection. These add to exclude patterns specified in the [config file](./config-file.md) |
| 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`. |
| 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 | `-global_conf`| bool | `yamlfmt -no_global_conf` | Disable looking for the configuration file from the system config directory. |
| Doublstar | `-dstar` | boolean | `yamlfmt -dstar "**/*.yaml"` | Enable [Doublstar](./paths.md#doublestar) path collection mode. |
| Exclude | `-exclude` | []string | `yamlfmt -exclude ./not/,these_paths.yaml` | Patterns to exclude from path collection. These add to exclude patterns specified in the [config file](./config-file.md) |
| 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. |

#### String Array Flags

Expand All @@ -96,3 +98,13 @@ String array flags can be provided in two ways. For example with a flag called `
* Technically they can be combined but why would you?
- `-arrFlag a,b -arrFlag c`
- Result: `arrFlag: [a b c]`

## Debug Logging

Debug logging can be enabled through the `-debug` [array flag](#string-array-flags). The following is the list of supported debug codes:
* `paths`
- Log the details for the path discovery process. Use it to debug your include/exclude patterns.
* `config`
- Log the details for the configuration loading process. Use it to figure out which config file yamlfmt uses and why.
* `all`
- Enable all available debug codes.
6 changes: 4 additions & 2 deletions integrationtest/command/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,18 @@ import (

var (
updateFlag *bool = flag.Bool("update", false, "Whether to update the goldens.")
stdoutFlag *bool = flag.Bool("stdout", false, "Show stdout instead of diffing it.")
yamlfmtBin string
)

func init() {
func TestMain(m *testing.M) {
yamlfmtBinVar := os.Getenv("YAMLFMT_BIN")
if yamlfmtBinVar == "" {
fmt.Println("Must provide a YAMLFMT_BIN environment variable.")
os.Exit(1)
}
yamlfmtBin = yamlfmtBinVar
m.Run()
}

func TestPathArg(t *testing.T) {
Expand All @@ -42,5 +44,5 @@ func TestIncludeDocumentStart(t *testing.T) {
}

func yamlfmtWithArgs(args string) string {
return fmt.Sprintf("%s %s", yamlfmtBin, args)
return fmt.Sprintf("%s -no_global_conf %s", yamlfmtBin, args)
}
12 changes: 9 additions & 3 deletions integrationtest/command/testcase.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package command

import (
"bytes"
"fmt"
"os/exec"
"path/filepath"
"strings"
Expand All @@ -18,9 +19,10 @@ const (
)

type TestCase struct {
Dir string
Command string
Update bool
Dir string
Command string
Update bool
ShowStdout bool
}

func (tc TestCase) Run(t *testing.T) {
Expand Down Expand Up @@ -69,6 +71,10 @@ func (tc TestCase) command(wd string, stdoutBuf *bytes.Buffer) *exec.Cmd {
}

func (tc TestCase) goldenStdout(stdoutResult []byte) error {
if !tc.ShowStdout {
fmt.Printf("Output for test %s:\n%s", tc.Dir, stdoutResult)
return nil
}
goldenCtx := tempfile.GoldenCtx{
Dir: tc.testFolderStdoutPath(),
Update: tc.Update,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
hello:
world: 1
world: 1
2 changes: 1 addition & 1 deletion integrationtest/command/testdata/path_arg/before/x.yaml
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
6tark:
does: 64
does: 64
12 changes: 9 additions & 3 deletions internal/collections/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,18 @@ package collections

type Set[T comparable] map[T]struct{}

func (s Set[T]) Add(el T) {
s[el] = struct{}{}
func (s Set[T]) Add(el ...T) {
for _, el := range el {
s[el] = struct{}{}
}
}

func (s Set[T]) Remove(el T) {
func (s Set[T]) Remove(el T) bool {
if !s.Contains(el) {
return false
}
delete(s, el)
return true
}

func (s Set[T]) Contains(el T) bool {
Expand Down
37 changes: 37 additions & 0 deletions internal/logger/debug.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package logger

import (
"fmt"

"github.com/google/yamlfmt/internal/collections"
)

type DebugCode int

const (
DebugCodeAny DebugCode = iota
DebugCodeConfig
DebugCodePaths
)

var (
supportedDebugCodes = map[string][]DebugCode{
"config": {DebugCodeConfig},
"paths": {DebugCodePaths},
"all": {DebugCodeConfig, DebugCodePaths},
}
activeDebugCodes = collections.Set[DebugCode]{}
)

func ActivateDebugCode(code string) {
if debugCodes, ok := supportedDebugCodes[code]; ok {
activeDebugCodes.Add(debugCodes...)
}
}

// Debug prints a message if the given debug code is active.
func Debug(code DebugCode, msg string, args ...any) {
if activeDebugCodes.Contains(code) {
fmt.Printf("[DEBUG]: %s\n", fmt.Sprintf(msg, args...))
}
}
2 changes: 1 addition & 1 deletion internal/tempfile/golden.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func (g GoldenCtx) CompareGoldenFile(path string, gotContent []byte) error {
if gotContent == nil {
gotContent = []byte{}
}
diff := cmp.Diff(expectedContent, gotContent)
diff := cmp.Diff(string(expectedContent), string(gotContent))
// If there is no diff between the content, nothing to do in either mode.
if diff == "" {
return nil
Expand Down
Loading

0 comments on commit f33dc61

Please sign in to comment.