diff --git a/.envrc b/.envrc index b36cb446..3d51f8c2 100644 --- a/.envrc +++ b/.envrc @@ -4,3 +4,4 @@ dotenv_if_exists .env.local watch_file nix/devshell.nix use flake +use flake .#docs \ No newline at end of file diff --git a/cmd/init/init.toml b/cmd/init/init.toml index af4bf343..57baf463 100644 --- a/cmd/init/init.toml +++ b/cmd/init/init.toml @@ -1,45 +1,61 @@ # One CLI to format the code tree - https://github.com/numtide/treefmt -# Do not exit with error if a configured formatter is missing (env $TREEFMT_ALLOW_MISSING_FORMATTER) +# Do not exit with error if a configured formatter is missing +# Env $TREEFMT_ALLOW_MISSING_FORMATTER # allow-missing-formatter = true -# The file into which a cpu profile will be written (env $TREEFMT_CPU_PROFILE) +# The file into which a cpu profile will be written +# Env $TREEFMT_CPU_PROFILE # cpu-profile = ./cpu.pprof -# Exclude files or directories matching the specified globs (env $TREEFMT_EXCLUDES) +# Exclude files or directories matching the specified globs +# Env $TREEFMT_EXCLUDES # excludes = ["*.md", "*.gif"] -# Exit with error if any changes were made. Useful for CI (env $TREEFMT_FAIL_ON_CHANGE) +# Exit with error if any changes were made during execution +# Useful for CI +# Env $TREEFMT_FAIL_ON_CHANGE # fail-on-change = true -# Specify formatters to apply. Defaults to all configured formatters. (env $TREEFMT_FORMATTERS) +# A list of formatters to apply +# Defaults to all configured formatters +# Env $TREEFMT_FORMATTERS # formatters = ["gofmt", "prettier"] -# Log paths that did not match any formatters at the specified log level. -# Possible values are . (env $TREEFMT_ON_UNMATCHED) +# Log paths that did not match any formatters at the specified log level +# Possible values are +# Env $TREEFMT_ON_UNMATCHED # on-unmatched = "info" -# The root directory from which treefmt will start walking the filesystem (defaults to the directory containing the -# config file). (env $TREEFMT_TREE_ROOT) +# The root directory from which treefmt will start walking the filesystem +# Defaults to the directory containing the config file +# Env $TREEFMT_TREE_ROOT # tree-root = "/tmp/foo" -# File to search for to find the tree root (if --tree-root is not passed). (env $TREEFMT_TREE_ROOT_FILE) +# File to search for to find the tree root (if tree-root is not set) +# Env $TREEFMT_TREE_ROOT_FILE # tree-root-file = ".git/config" -# Set the verbosity of logs e.g. -# 0 = warn, 1 = info, 2 = debug (env $TREEFMT_VERBOSE) +# Set the verbosity of logs +# 0 = warn, 1 = info, 2 = debug +# Env $TREEFMT_VERBOSE # verbose = 2 -# The method used to traverse the files within the tree root. -# Currently supports 'auto', 'git' or 'filesystem'. (env $TREEFMT_WALK) +# The method used to traverse the files within the tree root +# Currently, we support 'auto', 'git' or 'filesystem' +# Env $TREEFMT_WALK # walk = "filesystem" [formatter.mylanguage] -# Formatter to run +# Command to execute command = "command-to-run" # Command-line arguments for the command options = [] # Glob pattern of files to include includes = [ "*." ] # Glob patterns of files to exclude -excludes = [] \ No newline at end of file +excludes = [] +# Controls the order of application when multiple formatters match the same file +# Lower the number, the higher the precedence +# Default is 0 +priority = 0 \ No newline at end of file diff --git a/config/config.go b/config/config.go index 46051eeb..a5035f15 100644 --- a/config/config.go +++ b/config/config.go @@ -14,7 +14,7 @@ import ( // Config is used to represent the list of configured Formatters. type Config struct { AllowMissingFormatter bool `mapstructure:"allow-missing-formatter" toml:"allow-missing-formatter,omitempty"` - CI bool `mapstructure:"ci" toml:"ci,omitempty"` + CI bool `mapstructure:"ci" toml:"-"` // not allowed in config ClearCache bool `mapstructure:"clear-cache" toml:"-"` // not allowed in config CPUProfile string `mapstructure:"cpu-profile" toml:"cpu-profile,omitempty"` Excludes []string `mapstructure:"excludes" toml:"excludes,omitempty"` diff --git a/docs/content/getting-started/configure.md b/docs/content/getting-started/configure.md index 051fe6a7..e1b3383a 100644 --- a/docs/content/getting-started/configure.md +++ b/docs/content/getting-started/configure.md @@ -1,52 +1,419 @@ # Configure -The `treefmt.toml` configuration file consists of a mixture of global options and formatter sections: +`treefmt`'s behaviour can be influenced in one of three ways: -```toml -[global] -excludes = ["*.md", "*.dat"] - -[formatter.elm] -command = "elm-format" -options = ["--yes"] -includes = ["*.elm"] - -[formatter.go] -command = "gofmt" -options = ["-w"] -includes = ["*.go"] - -[formatter.python] -command = "black" -includes = ["*.py"] - -# use the priority field to control the order of execution - -# run shellcheck first -[formatter.shellcheck] -command = "shellcheck" -includes = ["*.sh"] -priority = 0 # default is 0, but we set it here for clarity - -# shfmt second -[formatter.shfmt] -command = "shfmt" -options = ["-s", "-w"] -includes = ["*.sh"] -priority = 1 +1. Process flags and arguments +2. Environment variables +3. A [TOML] based config file + +There is an order of precedence between these mechanisms as listed above, with process flags having the highest +precedence and values in the configuration file having the lowest. + +!!!note + + Some options can **only be configured as process flags**, + others may support **process flags and environment variables**, + and others still may support **all three mechanisms**. + +## Config File + +The `treefmt` configuration file is a mixture of global options and formatter sections. + +It should be named `treefmt.toml` or `.treefmt.toml`, and typically resides at the root of a repository. + +When executing `treefmt` within a subdirectory, `treefmt` will search upwards in the directory structure, looking for +`treefmt.toml` or `.treefmt.toml`. +You can change this behaviour using the [config-file](#config-file_1) options + +!!! tip + + When starting a new project you can generate an initial config file using `treefmt --init` + +```nix title="treefmt.toml" +--8<-- "cmd/init/init.toml" ``` ## Global Options -- `excludes` - an optional list of [glob patterns](#glob-patterns-format) used to exclude certain files from all formatters. +### `allow-missing-formatter` + +Do not exit with error if a configured formatter is missing. + +=== "Flag" + + ```console + treefmt --allow-missing-formatter true + ``` + +=== "Env" + + ```console + TREEFMT_ALLOW_MISSING_FORMATTER=true treefmt + ``` + +=== "Config" + + ```toml + allow-missing-formatter = true + ``` + +### `ci` + +Runs treefmt in a CI mode, enabling [no-cache](#no-cache), [fail-on-change](#fail-on-change) and adjusting some other settings best suited to a +continuous integration environment. + +=== "Flag" + + ```console + treefmt --ci + ``` + +=== "Env" + + ```console + TREEFMT_CI=true treefmt + ``` + +### `clear-cache` + +Reset the evaluation cache. Use in case the cache is not precise enough. + +=== "Flag" + + ```console + treefmt -c + treefmt --clear-cache + ``` + +=== "Env" + + ```console + TREEFMT_CLEAR_CACHE=true treefmt + ``` + +### `config-file` + +=== "Flag" + + ```console + treefmt --config-file /tmp/treefmt.toml + ``` + +=== "Env" + + ```console + TREEFMT_CONFIG=/tmp/treefmt.toml treefmt + ``` + +### `cpu-profile` + +The file into which a [pprof](https://github.com/google/pprof) cpu profile will be written. + +=== "Flag" + + ```console + treefmt --cpu-profile ./cpu.pprof + ``` + +=== "Env" + + ```console + TREEFMT_CPU_PROFILE=./cpu.pprof treefmt + ``` + +=== "Config" + + ```toml + cpu-profile = "./cpu.pprof" + ``` + +### `excludes` + +An optional list of [glob patterns](#glob-patterns-format) used to exclude files from all formatters. + +=== "Flag" + + ```console + treefmt --excludes *.toml,*.php,README + ``` + +=== "Env" + + ```console + TREEFMT_EXCLUDES="*.toml,*.php,README" treefmt + ``` + +=== "Config" + + ```toml + excludes = ["*.toml", "*.php", "README"] + ``` + +### `fail-on-change` + +Exit with error if any changes were made during execution. + +=== "Flag" + + ```console + treefmt --fail-on-change true + ``` + +=== "Env" + + ```console + TREEFMT_FAIL_ON_CHANGE=true treefmt + ``` + +=== "Config" + + ```toml + fail-on-change = true + ``` + +### `formatters` + +A list of formatters to apply. +Defaults to all configured formatters. + +=== "Flag" + + ```console + treefmt -f go,toml,haskell + treefmt --formatters go,toml,haskell + ``` + +=== "Env" + + ```console + TREEFMT_FORMATTERS=go,toml,haskell treefmt + ``` + +=== "Config" + + ```toml + formatters = ["go", "toml", "haskell"] + + [formatter.go] + ... + + [formatter.toml] + ... + + [formatter.haskell] + ... + + [formatter.ruby] + ... + + [formatter.shellcheck] + ... + ``` + +### `no-cache` + +Ignore the evaluation cache entirely. Useful for CI. + +=== "Flag" + + ```console + treefmt --no-cache + ``` + +=== "Env" + + ```console + TREEFMT_NO_CACHE=true treefmt + ``` + +### `on-unmatched` + +Log paths that did not match any formatters at the specified log level. +Possible values are ``. + +=== "Flag" + + ```console + treefmt -u debug + treefmt --on-unmatched debug + ``` + +=== "Env" + + ```console + TREEFMT_ON_UNMACTHED=info treefmt + ``` + +=== "Config" + + ```toml + on-unmatched = "debug" + ``` + +### `stdin` + +Format the context passed in via stdin. + +!!! note +You must provide a single path argument, the value of which is used to match against the configured formatters. + +=== "Flag" + + ```console + cat ../test.go | treefmt --stdin foo.go + ``` + +### `tree-root` + +The root directory from which treefmt will start walking the filesystem. +Defaults to the directory containing the config file. + +=== "Flag" + + ```console + treefmt --tree-root /tmp/foo + ``` + +=== "Env" + + ```console + TREEFMT_TREE_ROOT=/tmp/foo treefmt + ``` + +=== "Config" + + ```toml + tree-root = "/tmp/foo" + ``` + +### `tree-root-file` + +File to search for to find the tree root (if `tree-root` is not set) + +=== "Flag" + + ```console + treefmt --tree-root-file .git/config + ``` + +=== "Env" + + ```console + TREEFMT_TREE_ROOT_FILE=.git/config treefmt + ``` + +=== "Config" + + ```toml + tree-root-file = ".git/config" + ``` + +### `verbose` + +Set the verbosity level of logs: + +- `0` => `warn` +- `1` => `info` +- `2` => `debug` + +=== "Flag" + + The number of `v`'s passed matches the level set. + + ```console + treefmt -vv + ``` + +=== "Env" + + ```console + TREEFMT_VERBOSE=1 treefmt + ``` + +=== "Config" + + ```toml + verbose = 2 + ``` + +### `walk` + +The method used to traverse the files within the tree root. +Currently, we support 'auto', 'git' or 'filesystem' + +=== "Flag" + + ```console + treefmt --walk filesystem + ``` + +=== "Env" + + ```console + TREEFMT_WALK=filesystem treefmt + ``` + +=== "Config" + + ```toml + walk = "filesystem" + ``` + +### `working-dir` + +Run as if `treefmt` was started in the specified working directory instead of the current working directory. + +=== "Flag" + + ```console + treefmt -C /tmp/foo + treefmt --working-dir /tmp/foo + ``` + +=== "Env" + + ```console + TREEFMT_WORKING_DIR=/tmp/foo treefmt + ``` ## Formatter Options -- `command` - the command to invoke when applying the formatter. -- `options` - an optional list of args to be passed to `command`. -- `includes` - a list of [glob patterns](#glob-patterns-format) used to determine whether the formatter should be applied against a given path. -- `excludes` - an optional list of [glob patterns](#glob-patterns-format) used to exclude certain files from this formatter. -- `priority` - influences the order of execution. Greater precedence is given to lower numbers, with the default being `0`. +Formatters are configured using a [table](https://toml.io/en/v1.0.0#table) entry in `treefmt.toml` of the form +`[formatter.]`: + +```toml +[formatter.alejandra] +command = "alejandra" +includes = ["*.nix"] +excludes = ["examples/nix/sources.nix"] +priority = 1 + +[formatter.deadnix] +command = "deadnix" +options = ["-e"] +includes = ["*.nix"] +priority = 2 +``` + +### `command` + +The command to invoke when applying the formatter. + +### `options` + +An optional list of args to be passed to `command`. + +### `includes` + +A list of [glob patterns](#glob-patterns-format) used to determine whether the formatter should be applied against a given path. + +### `excludes` + +An optional list of [glob patterns](#glob-patterns-format) used to exclude certain files from this formatter. + +### `priority` + +Influences the order of execution. Greater precedence is given to lower numbers, with the default being `0`. ## Same file, multiple formatters? @@ -83,3 +450,4 @@ To find examples, take a look at , which uses Nix to pull in the right formatter package and seamlessly integrates both together. [spec]: ../reference/formatter-spec.md +[TOML]: https://toml.io diff --git a/docs/content/getting-started/usage.md b/docs/content/getting-started/usage.md index c9f4fbeb..d9953cbf 100644 --- a/docs/content/getting-started/usage.md +++ b/docs/content/getting-started/usage.md @@ -7,128 +7,128 @@ outline: deep `treefmt` has the following specification: ``` -Usage: treefmt [ ...] [flags] - -Arguments: - [ ...] Paths to format. Defaults to formatting the whole tree. +Usage: + treefmt [flags] Flags: - -h, --help Show context-sensitive help. - --allow-missing-formatter Do not exit with error if a configured formatter is missing. - -C, --working-directory="." Run as if treefmt was started in the specified working directory instead of the current working directory. - --no-cache Ignore the evaluation cache entirely. Useful for CI. - -c, --clear-cache Reset the evaluation cache. Use in case the cache is not precise enough. - --config-file=STRING Load the config file from the given path (defaults to searching upwards for treefmt.toml). - --fail-on-change Exit with error if any changes were made. Useful for CI. - -f, --formatters=FORMATTERS,... Specify formatters to apply. Defaults to all formatters. - --tree-root=STRING The root directory from which treefmt will start walking the filesystem (defaults to the directory containing the config file) ($PRJ_ROOT). - --tree-root-file=STRING File to search for to find the project root (if --tree-root is not passed). - --walk="auto" The method used to traverse the files within --tree-root. Currently supports 'auto', 'git' or 'filesystem'. - -v, --verbose Set the verbosity of logs e.g. -vv ($LOG_LEVEL). - -V, --version Print version. - -i, --init Create a new treefmt.toml. - -u, --on-unmatched=warn Log paths that did not match any formatters at the specified log level, with fatal exiting the process with an error. Possible values are - . - --stdin Format the context passed in via stdin. - --cpu-profile=STRING The file into which a cpu profile will be written. - --ci Runs treefmt in a CI mode, enabling --no-cache, --fail-on-change and adjusting some other settings best suited to a CI use case. + --allow-missing-formatter Do not exit with error if a configured formatter is missing. (env $TREEFMT_ALLOW_MISSING_FORMATTER) + --ci Runs treefmt in a CI mode, enabling --no-cache, --fail-on-change and adjusting some other settings best suited to a CI use case. (env $TREEFMT_CI) + -c, --clear-cache Reset the evaluation cache. Use in case the cache is not precise enough. (env $TREEFMT_CLEAR_CACHE) + --config-file string Load the config file from the given path (defaults to searching upwards for treefmt.toml or .treefmt.toml). + --cpu-profile string The file into which a cpu profile will be written. (env $TREEFMT_CPU_PROFILE) + --excludes strings Exclude files or directories matching the specified globs. (env $TREEFMT_EXCLUDES) + --fail-on-change Exit with error if any changes were made. Useful for CI. (env $TREEFMT_FAIL_ON_CHANGE) + -f, --formatters strings Specify formatters to apply. Defaults to all configured formatters. (env $TREEFMT_FORMATTERS) + -h, --help help for treefmt + -i, --init Create a treefmt.toml file in the current directory. + --no-cache Ignore the evaluation cache entirely. Useful for CI. (env $TREEFMT_NO_CACHE) + -u, --on-unmatched string Log paths that did not match any formatters at the specified log level. Possible values are . (env $TREEFMT_ON_UNMATCHED) (default "warn") + --stdin Format the context passed in via stdin. + --tree-root string The root directory from which treefmt will start walking the filesystem (defaults to the directory containing the config file). (env $TREEFMT_TREE_ROOT) + --tree-root-file string File to search for to find the tree root (if --tree-root is not passed). (env $TREEFMT_TREE_ROOT_FILE) + -v, --verbose count Set the verbosity of logs e.g. -vv. (env $TREEFMT_VERBOSE) + --version version for treefmt + --walk string The method used to traverse the files within the tree root. Currently supports . (env $TREEFMT_WALK) (default "auto") + -C, --working-dir string Run as if treefmt was started in the specified working directory instead of the current working directory. (env $TREEFMT_WORKING_DIR) (default ".") ``` -## Arguments - -### `[ ...]` - -Paths to format. Defaults to formatting the whole tree - -## Flags - -### `-h, --help` - -Prints available flags and options - -### `--allow-missing-formatter` - -Do not exit with an error if some of the configured formatters are missing. - -### `-C, --working-directory="."` - -Run as if `treefmt` was started in the specified working directory instead of the current working directory - -### `--no-cache` - -Tells `treefmt` to ignore the evaluation cache entirely. - -With this flag, you can avoid cache invalidation issues, if any. Typically, the machine that is running `treefmt` in -CI is starting with a fresh environment each time, so any calculated cache is lost. - -The `--no-cache` flag eliminates unnecessary work in CI. - -### `--config-file ` - -Run with the specified config file. - -### `--fail-on-change` - -Exit with error if any changes were made. +Typically, you will execute `treefmt` from the root of your repository with no arguments: -This is useful for CI if you want to detect if someone forgot to format their code. - -### `-f, --formatters ...` - -Specify formatters to apply. Defaults to all formatters. - -### `--tree-root="."` - -The root directory from which `treefmt` will start walking the filesystem. - -### `--walk ` - -The method used to traverse the files within `--tree-root`. Currently supports `auto`, `git` or `filesystem`. - -Default is `auto`, where we will detect if the `` is a git repository and use the `git` walker for -traversal. If not we will fall back to the `filesystem` walker. - -### `-v, --verbose` - -Set the verbosity of logs e.g. `-vv`. Can also be set with an integer value in an env variable `$LOG_LEVEL`. - -Log verbosity is based off the number of 'v' used. With one `-v`, your logs will display `[INFO]` and `[ERROR]` messages, -while `-vv` will also show `[DEBUG]` messages. - -### `--init` +```console +❯ treefmt +traversed 106 files +emitted 9 files for processing +formatted 6 files (2 changed) in 184ms +``` -Create a new `treefmt.toml`. +## Clear Cache -### `-u --on-unmatched` +To force re-evaluation of the entire tree, you run `treefmt` with the `-c` or `--clear-cache` flag: -Log paths that did not match any formatters at the specified log level, with fatal exiting the process with an error. Possible values are . +```console +❯ treefmt -c +traversed 106 files +emitted 106 files for processing +formatted 56 files (0 changed) in 363ms -[default: warn] +❯ treefmt --clear-cache +traversed 106 files +emitted 106 files for processing +formatted 56 files (0 changed) in 351ms +``` -### `--stdin` +## Change working directory -Format the context passed in via stdin. +Similar to [git](https://git-scm.com/), `treefmt` has an option to [change working directory](./configure.md#working-dir) +before executing: -### `--cpu-profile` +```console +❯ treefmt -C test/examples --allow-missing-formatter +traversed 106 files +emitted 56 files for processing +formatted 46 files (1 changed) in 406ms +``` -The file into which a cpu profile will be written. +## Format files & directories -### `--ci` +To format one or more specific files, you can pass them as arguments. -Runs treefmt in a CI mode which does the following: +```console +> treefmt default.nix walk/walk.go nix/devshells/renovate.nix +traversed 3 files +emitted 3 files for processing +formatted 3 files (0 changed) in 144ms +``` -- ensures `INFO` level logging at a minimum -- enables `--no-cache` and `--fail-on-change` -- introduces a small startup delay so we do not start processing until the second after the process started, thereby - ensuring the accuracy of our change detection based on second-level `modtime`. +You can also pass directories: -### `-V, --version` +```console +> treefmt nix walk/cache +traversed 9 files +emitted 8 files for processing +formatted 7 files (0 changed) in 217ms +``` -Print version. +!!!note + + When passing directories as arguments, `treefmt` will traverse them using the configured [walk](./configure.md#walk) + strategy. + +## Format stdin + +Using the [stdin](./configure.md#stdin) option, `treefmt` can format content passed via `stdin`, forwarding its +output to `stdout`: + +```console +❯ cat default.nix | treefmt --stdin foo.nix +# This file provides backward compatibility to nix < 2.4 clients +{system ? builtins.currentSystem}: let + lock = builtins.fromJSON (builtins.readFile ./flake.lock); + + inherit + (lock.nodes.flake-compat.locked) + owner + repo + rev + narHash + ; + + flake-compat = fetchTarball { + url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; + sha256 = narHash; + }; + + flake = import flake-compat { + inherit system; + src = ./.; + }; +in + flake.defaultNix +``` ## CI integration -Typically, you would use `treefmt` in CI with the `--ci` flag. +We recommend using the [CI option](./configure.md#ci) in continuous integration environments. You can configure a `treefmt` job in a GitHub pipeline for Ubuntu with `nix-shell` like this: diff --git a/docs/content/reference/formatter-spec.md b/docs/content/reference/formatter-spec.md index 3a7ad0a9..d4ac53d9 100644 --- a/docs/content/reference/formatter-spec.md +++ b/docs/content/reference/formatter-spec.md @@ -4,22 +4,24 @@ outline: deep # Formatter Specification -In order to keep the design of `treefmt` simple, we only supports formatters that adhere to a certain standard. This -document outlines that standard. - -If the formatter you would like to use doesn't comply with the rules, it's often possible to create a wrapper script -that transforms the usage to match the specification. +To keep the design of `treefmt` simple, we only support formatters that adhere to a certain standard. +This document outlines that standard. In this design, we rely on `treefmt` to do the tree traversal, and only invoke the code formatter on the selected files. +!!! note + + If the formatter you would like to use doesn't comply with the rules, it's often possible to create a wrapper script + that transforms the usage to match the specification. + ## Rules -In order for the formatter to comply to this spec, it **MUST** comply with the following: +In order for the formatter to comply with this spec, it **MUST** satisfy the following: ### 1. Files passed as arguments -In order to be integrated with `treefmt`'s workflow, the formatter's CLI must be of the form: +The formatter's CLI must be of the form: ``` [options] [...] @@ -37,17 +39,18 @@ Example: $ rustfmt --edition 2018 src/main.rs src/lib.rs ``` -> [!IMPORTANT] -> It _MUST_ process the specified files. For example, it _MUST_ NOT ignore files because they are not tracked by a VCS. -> -> It _SHOULD_ processes only the specified files. Files that are not passed _SHOULD_ never be formatted. +!!! note + + It _MUST_ process the specified files. For example, it _MUST_ NOT ignore files because they are not tracked by a VCS. + + It _SHOULD_ processes only the specified files. Files that are not passed _SHOULD_ never be formatted. ### 2. Write to changed files -Whenever there is a change to the code formatting, the code formatter **MUST** write to the changes back to the +Whenever there is a change to the code formatting, the code formatter **MUST** write those changes back to the original location. -If there is no changes to the original file, the formatter **MUST** NOT write to the original location. +If there are no changes to the original file, the formatter **MUST NOT** write to the original location. ### 3. Idempotent diff --git a/test/examples/treefmt.toml b/test/examples/treefmt.toml index e70f30fc..3c80758d 100644 --- a/test/examples/treefmt.toml +++ b/test/examples/treefmt.toml @@ -1,5 +1,4 @@ # One CLI to format the code tree - https://github.com/numtide/treefmt - excludes = ["*.toml"] [formatter.python]