From d4aaba9dc2c6680327da7a9c1a810b242ccece71 Mon Sep 17 00:00:00 2001 From: Brian McGee Date: Thu, 1 Aug 2024 16:48:23 +0100 Subject: [PATCH] feat: ci mode Adds a ci flag which does the following: - ensures INFO level logging at a minimum - --no-cache - --fail-on-change This also adds a delay on startup which is intended to ensure we do not start doing anything until we have moved into the second after the one in which the process started. This helps to ensure accurate comparisons of `modtime` using truncated second-level precision for change detection. This is only an issue in scenarios in which treefmt is being executed quickly in succession or directly after an automatic checkout, such as in CI. Signed-off-by: Brian McGee --- cli/cli.go | 2 ++ cli/format.go | 31 +++++++++++++++++++++++++++++++ docs/usage.md | 14 ++++++++++++-- 3 files changed, 45 insertions(+), 2 deletions(-) diff --git a/cli/cli.go b/cli/cli.go index bf6ff04b..243a8439 100644 --- a/cli/cli.go +++ b/cli/cli.go @@ -37,6 +37,8 @@ type Format struct { CpuProfile string `optional:"" help:"The file into which a cpu profile will be written."` + Ci bool `help:"Runs treefmt in a CI mode, enabling --no-cache, --fail-on-change and adjusting some other settings best suited to a CI use case."` + formatters map[string]*format.Formatter globalExcludes []glob.Glob diff --git a/cli/format.go b/cli/format.go index 1aaa0e59..dc1d6732 100644 --- a/cli/format.go +++ b/cli/format.go @@ -34,6 +34,37 @@ func (f *Format) Run() (err error) { // set log level and other options f.configureLogging() + // ci mode + if f.Ci { + f.NoCache = true + f.FailOnChange = true + + // ensure INFO level + if f.Verbosity < 1 { + f.Verbosity = 1 + } + // reconfigure logging + f.configureLogging() + + log.Info("ci mode enabled") + + startAfter := time.Now(). + // truncate to second precision + Truncate(time.Second). + // add one second + Add(1 * time.Second). + // a little extra to ensure we don't start until the next second + Add(10 * time.Millisecond) + + log.Debugf("waiting until %v before continuing", startAfter) + + // Wait until we tick over into the next second before processing to ensure our EPOCH level modtime comparisons + // for change detection are accurate. + // This can fail in CI between checkout and running treefmt if everything happens too quickly. + // For humans, the second level precision should not be a problem as they are unlikely to run treefmt in sub-second succession. + <-time.After(time.Until(startAfter)) + } + // cpu profiling if f.CpuProfile != "" { cpuProfile, err := os.Create(f.CpuProfile) diff --git a/docs/usage.md b/docs/usage.md index 2e09bd2c..c9f4fbeb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -31,6 +31,7 @@ Flags: . --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. ``` ## Arguments @@ -112,13 +113,22 @@ Format the context passed in via stdin. The file into which a cpu profile will be written. +### `--ci` + +Runs treefmt in a CI mode which does the following: + +- 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`. + ### `-V, --version` Print version. ## CI integration -Typically, you would use `treefmt` in CI with the `--fail-on-change` and `--no-cache flags`. +Typically, you would use `treefmt` in CI with the `--ci` flag. You can configure a `treefmt` job in a GitHub pipeline for Ubuntu with `nix-shell` like this: @@ -141,5 +151,5 @@ jobs: name: nix-community authToken: "${{ secrets.CACHIX_AUTH_TOKEN }}" - name: treefmt - run: nix-shell -p treefmt --run "treefmt --fail-on-change --no-cache" + run: nix-shell -p treefmt --run "treefmt --ci" ```