Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Debug logging #157

Merged
merged 2 commits into from
Feb 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading