Compare commits

1 Commits
Author SHA1 Message Date
sneak 78bd936b15 Re-vendor the canonical files from sneak/prompts at dd4027b (closes #13)
check / check (push) Successful in 2m43s
`.dockerignore`, `.gitignore`, `.golangci.yml` and the workflow are their
copies at that commit. This repo's own entries are kept after the
canonical content: the anchored `/attrsum` binary, the Go entries in
`.gitignore`, and tabs for `*.go` in the new `.editorconfig`.
`REPO_POLICIES.md` is new. The workflow keeps `fetch-depth: 0`, which the
policies require of a repo that stamps a tag-derived version.

The lint phase moves to golangci-lint v2.14.0, which finds nothing in
the code. `script/fmt` now runs goimports with `go run` at its pinned
commit. A version-checked install by `script/bootstrap` would have to be
found on `PATH`, and the CI runner's `PATH` lacks Go's bin directory.

Model: opus-5-5
2026-10-06 06:17:14 +00:00
11 changed files with 107 additions and 357 deletions
-2
View File
@@ -1,2 +0,0 @@
node_modules/
yarn.lock
-4
View File
@@ -1,4 +0,0 @@
{
"tabWidth": 4,
"proseWrap": "always"
}
+25 -31
View File
@@ -1,7 +1,7 @@
[**attrsum**](https://git.eeqj.de/sneak/attrsum/) is a **Go 1.22** command-line [**attrsum**](https://git.eeqj.de/sneak/attrsum/) is a **Go
utility that **adds, updates, verifies, and clears per-file file content 1.22** command-line utility that **adds, updates, verifies, and clears per-file
checksums stored in extended attributes (xattrs) on macOS (APFS) and Linux**, file content checksums stored in extended attributes (xattrs) on macOS (APFS) and
released under the [WTFPL v2](http://www.wtfpl.net/). Linux**, released under the [WTFPL v2](http://www.wtfpl.net/).
Original release 2025-05-08. Original release 2025-05-08.
@@ -54,32 +54,29 @@ attrsum -q sum add DIR
``` ```
| xattr key | meaning | | xattr key | meaning |
| ---------------------------------------- | ------------------------------ | |---------------------------------------------|--------------------------------|
| `user.berlin.sneak.app.attrsum.checksum` | base-58 multihash (sha2-256) | | `user.berlin.sneak.app.attrsum.checksum` | base-58 multihash (sha2-256) |
| `user.berlin.sneak.app.attrsum.sumtime` | RFC 3339 timestamp of checksum | | `user.berlin.sneak.app.attrsum.sumtime` | RFC 3339 timestamp of checksum |
Flags: Flags:
- `-v, --verbose` — per-file log output * `-v, --verbose` — per-file log output
- `-q, --quiet` — suppress all output except errors (no progress bar or summary) * `-q, --quiet` — suppress all output except errors (no progress bar or summary)
- `--exclude PATTERN` — skip paths matching rsync/Doublestar glob * `--exclude PATTERN` — skip paths matching rsync/Doublestar glob
- `--exclude-dotfiles` — skip any path component that starts with `.` * `--exclude-dotfiles` — skip any path component that starts with `.`
All commands display a progress bar with ETA and print a summary report to All commands display a progress bar with ETA and print a summary report to stderr on completion (unless `--quiet` is specified).
stderr on completion (unless `--quiet` is specified).
`attrsum` **never follows symlinks** and skips non-regular files (sockets, `attrsum` **never follows symlinks** and skips non-regular files (sockets, devices, …).
devices, …).
--- ---
## Why? ## Why?
Apple APFS and Linux ext3/ext4 **store no per-file content checksums**, so Apple APFS and Linux ext3/ext4 **store no per-file content checksums**, so
silent data corruption can pass unnoticed. `attrsum` keeps a portable checksum silent data corruption can pass unnoticed. `attrsum` keeps a portable checksum **inside each file’s xattrs**, providing integrity
**inside each file’s xattrs**, providing integrity verification that travels verification that travels with the file itself—no external database
with the file itself—no external database required. Now you can trust a USB required. Now you can trust a USB stick didn't eat your data.
stick didn't eat your data.
--- ---
@@ -87,25 +84,21 @@ stick didn't eat your data.
Future improvements under consideration: Future improvements under consideration:
- **Dry-run mode (`--dry-run`, `-n`)** — show what would be done without making - **Dry-run mode (`--dry-run`, `-n`)** — show what would be done without making changes
changes - **JSON output (`--json`)** — machine-readable output for scripting and integration
- **JSON output (`--json`)** — machine-readable output for scripting and - **Parallel processing (`-j N`)** — use multiple goroutines for faster checksumming on large trees
integration
- **Parallel processing (`-j N`)** — use multiple goroutines for faster
checksumming on large trees
- **Exit code documentation** — formalize and document exit codes for scripting - **Exit code documentation** — formalize and document exit codes for scripting
--- ---
## Contributing ## Contributing
- Author & maintainer: **sneak** – <sneak@sneak.berlin> * Author & maintainer: **sneak** – <sneak@sneak.berlin>
- Issues / PRs: <https://git.eeqj.de/sneak/attrsum/> * Issues / PRs: <https://git.eeqj.de/sneak/attrsum/>
- Code must pass `make check`, which runs the tests and golangci-lint as phases * Code must pass `make check`, which runs the tests and golangci-lint as
of the `Dockerfile` (Docker is required) and checks formatting with `gofmt -s` phases of the `Dockerfile` (Docker is required) and checks formatting
and goimports for Go and prettier for Markdown. `make bootstrap` installs with `gofmt`.
goimports and prettier, and `make fmt` fixes what the check reports. * No CLA; contributions are under WTFPL v2.
- No CLA; contributions are under WTFPL v2.
--- ---
@@ -119,4 +112,5 @@ No formal Code of Conduct; be excellent to each other.
## License ## License
_Everything is permitted._ See [WTFPL v2](http://www.wtfpl.net/txt/copying/). *Everything is permitted.*
See [WTFPL v2](http://www.wtfpl.net/txt/copying/).
+63 -67
View File
@@ -1,83 +1,79 @@
# Workflow # Workflow
- branch (from `main`) * branch (from `main`)
- do the work in Next Step * do the work in Next Step
- move Next Step to the top of Completed Steps * move Next Step to the top of Completed Steps
- move the top item of Future Steps into Next Step * move the top item of Future Steps into Next Step
- commit (`TODO.md` changes in the same commit as the work) * commit (`TODO.md` changes in the same commit as the work)
- merge to `main` if the branch is not protected, otherwise open a PR * merge to `main` if the branch is not protected, otherwise open a PR
- push * push
# Status # Status
1.0+ 1.0+
Tagged 1.0.0 (2025-05-08). Substantial correctness fixes and features have Tagged 1.0.0 (2025-05-08). Substantial correctness fixes and features
landed since the tag. have landed since the tag.
# Next Step # Next Step
Restructure README.md into the standard sections: Description, Getting Started, Restructure README.md into the standard sections: Description, Getting
Entrypoints, Rationale, Design, TODO, License, Author (Getting Started, Why?, Started, Entrypoints, Rationale, Design, TODO, License, Author (Getting
TODO, License exist; Description, Entrypoints, Design, Author are missing) Started, Why?, TODO, License exist; Description, Entrypoints, Design,
Author are missing)
# Completed Steps # Completed Steps
- 2026-10-06: `make fmt` formats the Markdown files with prettier (four-space * 2026-10-06: canonical files re-vendored from `sneak/prompts` at
indents, `proseWrap: always`) and `make fmt-check` fails on one it would `dd4027b`: `REPO_POLICIES.md` and `.editorconfig` added;
change; prettier is pinned in `package.json` and `yarn.lock`, and `.dockerignore`, `.gitignore`, `.golangci.yml` and the workflow
`script/bootstrap` installs it, along with the pinned node and yarn when the refreshed, keeping this repo's own entries and `fetch-depth: 0`;
yarn on hand is not the pinned version; the Markdown files reformatted once the lint phase runs golangci-lint v2.14.0; `script/fmt` runs
- 2026-10-06: `script/fmt-check` checks what `script/fmt` writes: it fails and goimports with `go run` at its pinned commit instead of
lists the files when `gofmt -s` or goimports would change one, so a file that `script/bootstrap` installing it
passes `make check` no longer changes on the next `make fmt` * 2026-10-06: a path that `--exclude` or `--exclude-dotfiles` excludes
- 2026-10-06: canonical files re-vendored from `sneak/prompts` at `dd4027b`: is skipped even when it cannot be read, so an excluded directory
`REPO_POLICIES.md` and `.editorconfig` added; `.dockerignore`, `.gitignore`, that cannot be listed no longer fails the run
`.golangci.yml` and the workflow refreshed, keeping this repo's own entries * 2026-10-06: lint and test run as phases of the `Dockerfile`, and the
and `fetch-depth: 0`; the lint phase runs golangci-lint v2.14.0; build stage depends on both; `script/lint` and `script/test` each
`script/bootstrap` installs goimports unless the installed one has the pinned build their phase with `--no-cache`; `script/cibuild` bootstraps,
version, and it and `script/fmt` put Go's bin directory on `PATH` runs `script/check`, then builds the image; golangci-lint is no
- 2026-10-06: the summary line after `sum`, `check` and `clear` prints the byte longer installed on the host
unit once (`1.5 KiB`, not `1.5 KiB bytes`) * 2026-10-06: `check --continue` keeps going past a file or directory
- 2026-10-06: a path that `--exclude` or `--exclude-dotfiles` excludes is it cannot read: it counts it as failed, prints the error and the
skipped even when it cannot be read, so an excluded directory that cannot be path on stderr, and checks the rest of the tree
listed no longer fails the run * 2026-10-05: golangci-lint settings take effect: canonical
- 2026-10-06: lint and test run as phases of the `Dockerfile`, and the build `.golangci.yml` (v2 layout, settings under `linters.settings`),
stage depends on both; `script/lint` and `script/test` each build their phase golangci-lint pinned at v2.12.2 in `Dockerfile` and
with `--no-cache`; `script/cibuild` bootstraps, runs `script/check`, then `script/bootstrap`, and the code fixed for what the settings now
builds the image; golangci-lint is no longer installed on the host report (long lines in `attrsum.go` rewrapped)
- 2026-10-06: `check --continue` keeps going past a file or directory it cannot * 2026-10-05: `make try` runs on three small files in a temporary
read: it counts it as failed, prints the error and the path on stderr, and directory that it removes afterwards, also when a step fails,
checks the rest of the tree instead of on a fixed directory on one person's machine
- 2026-10-05: golangci-lint settings take effect: canonical `.golangci.yml` (v2 * 2026-10-02: `attrsum --version` reports the git tag or short commit,
layout, settings under `linters.settings`), golangci-lint pinned at v2.12.2 in stamped by `make build` and by a plain `docker build .` of a clone;
`Dockerfile` and `script/bootstrap`, and the code fixed for what the settings `.dockerignore` sends `.git` without `.git/config` and keeps a
now report (long lines in `attrsum.go` rewrapped) host-built `attrsum` out; CI checks out full history so it stamps
- 2026-10-05: `make try` runs on three small files in a temporary directory that the same version
it removes afterwards, also when a step fails, instead of on a fixed directory * 2026-02-02: correctness pass: track actual bytes read instead of
on one person's machine stale file size, atomic failure tracking in ProcessCheck, detect
- 2026-10-02: `attrsum --version` reports the git tag or short commit, stamped file modification during checksum (TOCTOU), propagate countFiles
by `make build` and by a plain `docker build .` of a clone; `.dockerignore` errors, single progress bar across paths, error on empty stdin,
sends `.git` without `.git/config` and keeps a host-built `attrsum` out; CI dead code removal
checks out full history so it stamps the same version * 2026-02-01: added quiet mode, progress bar, summary report, and stdin
- 2026-02-02: correctness pass: track actual bytes read instead of stale file path input; multiple file/directory arguments for all commands
size, atomic failure tracking in ProcessCheck, detect file modification during * 2025-07-12: README update
checksum (TOCTOU), propagate countFiles errors, single progress bar across * 2025-05-08: initial working tool with passing tests, skips
paths, error on empty stdin, dead code removal non-regular files, Makefile, README; tagged 1.0.0
- 2026-02-01: added quiet mode, progress bar, summary report, and stdin path
input; multiple file/directory arguments for all commands
- 2025-07-12: README update
- 2025-05-08: initial working tool with passing tests, skips non-regular files,
Makefile, README; tagged 1.0.0
# Future Steps # Future Steps
- Add a `LICENSE` file matching the README's WTFPL v2; sneak's to add, not an * Add a `LICENSE` file matching the README's WTFPL v2; sneak's to add,
agent's not an agent's
- Tag a patch release to ship the 2026-02-02 correctness fixes * Tag a patch release to ship the 2026-02-02 correctness fixes
- Dry-run mode (--dry-run, -n): show what would be done without making changes * Dry-run mode (--dry-run, -n): show what would be done without making
(from README TODO) changes (from README TODO)
- JSON output (--json) for scripting and integration (from README TODO) * JSON output (--json) for scripting and integration (from README TODO)
- Parallel processing (-j N) with multiple goroutines for faster checksumming on * Parallel processing (-j N) with multiple goroutines for faster
large trees (from README TODO) checksumming on large trees (from README TODO)
- Formalize and document exit codes for scripting (from README TODO) * Formalize and document exit codes for scripting (from README TODO)
+6 -6
View File
@@ -71,13 +71,13 @@ func (s *Stats) Duration() time.Duration {
return time.Since(s.StartTime) return time.Since(s.StartTime)
} }
func (s *Stats) Print(w io.Writer, opts *options, operation string) { func (s *Stats) Print(opts *options, operation string) {
if opts.quiet { if opts.quiet {
return return
} }
_, _ = fmt.Fprintf(w, fmt.Fprintf(os.Stderr,
"\n%s complete: %d files processed, %d skipped, %d failed, %s in %s\n", "\n%s complete: %d files processed, %d skipped, %d failed, %s bytes in %s\n",
operation, operation,
s.FilesProcessed, s.FilesProcessed,
s.FilesSkipped, s.FilesSkipped,
@@ -227,7 +227,7 @@ func runOverPaths(
} }
finishBar(bar) finishBar(bar)
stats.Print(os.Stderr, opts, op) stats.Print(opts, op)
return nil return nil
} }
@@ -481,7 +481,7 @@ func runCheck(opts *options, args []string, cont bool) error {
if perr != nil { if perr != nil {
if !cont { if !cont {
finishBar(bar) finishBar(bar)
stats.Print(os.Stderr, opts, "check") stats.Print(opts, "check")
return perr return perr
} }
@@ -491,7 +491,7 @@ func runCheck(opts *options, args []string, cont bool) error {
} }
finishBar(bar) finishBar(bar)
stats.Print(os.Stderr, opts, "check") stats.Print(opts, "check")
return finalErr return finalErr
} }
-25
View File
@@ -1,7 +1,6 @@
package main package main
import ( import (
"bytes"
"errors" "errors"
"os" "os"
"path/filepath" "path/filepath"
@@ -377,27 +376,3 @@ func TestCheckContinuePastUnreadable(t *testing.T) {
t.Fatalf("expected verification error from runCheck, got %v", err) t.Fatalf("expected verification error from runCheck, got %v", err)
} }
} }
func TestSummaryPrintsByteUnitOnce(t *testing.T) {
t.Parallel()
tests := []struct {
bytesProcessed int64
want string
}{
{9, ", 9 B in "},
{1536, ", 1.5 KiB in "},
}
for _, tt := range tests {
var out bytes.Buffer
stats := newTestStats()
stats.BytesProcessed = tt.bytesProcessed
stats.Print(&out, &options{}, "check")
if !strings.Contains(out.String(), tt.want) {
t.Errorf("summary %q does not contain %q", out.String(), tt.want)
}
}
}
-6
View File
@@ -1,6 +0,0 @@
{
"private": true,
"devDependencies": {
"prettier": "3.8.1"
}
}
+3 -150
View File
@@ -3,30 +3,13 @@
# this repo. Idempotent: every install is guarded by a check so already # this repo. Idempotent: every install is guarded by a check so already
# installed tools are skipped. Base tooling comes from nix, apt, brew, # installed tools are skipped. Base tooling comes from nix, apt, brew,
# or apk (detected in that order); assumes nothing is present. # or apk (detected in that order); assumes nothing is present.
# goimports is installed via `go install` at a pinned commit (never # goimports is not installed: script/fmt runs it with `go run` at a
# "latest"), unless the installed one already has the pinned version. # pinned commit. The linter is not installed: it runs only as the lint
# The linter is not installed: it runs only as the lint phase of the # phase of the Dockerfile.
# Dockerfile. yarn is installed via corepack unless the yarn that
# script/fmt runs already has the pinned version. It is installed under
# the node on PATH if that has the pinned version; otherwise the pinned
# node is installed via nvm (installing nvm itself first, from a
# hash-verified release archive, never curl | sh).
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Pinned versions, 2026-10-05
# GOIMPORTS_REF is the commit tagged GOIMPORTS_VERSION.
GOIMPORTS_VERSION="v0.42.0"
GOIMPORTS_REF="golang.org/x/tools/cmd/goimports@009367f5c17a8d4c45a961a3a509277190a9a6f0"
# Pinned versions, 2026-07-06
NODE_VERSION="22.17.0"
NVM_VERSION="0.40.3"
# sha256 of https://github.com/nvm-sh/nvm/archive/refs/tags/v0.40.3.tar.gz
NVM_SHA256="5f4d6aaa04a177dc93c985e31dbc411ab6b8c6e1e21d8015dbc1372625fcd1d0"
YARN_VERSION="1.22.22"
PKGMGR="" PKGMGR=""
SUDO="" SUDO=""
APT_UPDATED="" APT_UPDATED=""
@@ -74,127 +57,6 @@ missing() {
! command -v "$1" >/dev/null 2>&1 ! command -v "$1" >/dev/null 2>&1
} }
# Print the version the goimports on PATH was built from, or nothing.
# goimports has no version flag; `go version -m` reads the binary's
# build info, whose "mod" line names the module and its version.
goimports_version() {
if missing goimports; then return 0; fi
go version -m "$(command -v goimports)" 2>/dev/null |
awk '$1 == "mod" { print $3 }'
}
ensure_goimports() {
if [ "$(goimports_version)" = "$GOIMPORTS_VERSION" ]; then
echo "goimports $GOIMPORTS_VERSION already installed"
return 0
fi
go install "$GOIMPORTS_REF"
hash -r
if [ "$(goimports_version)" != "$GOIMPORTS_VERSION" ]; then
echo "bootstrap: goimports on PATH is not $GOIMPORTS_VERSION:" \
"$(command -v goimports)" >&2
exit 1
fi
echo "goimports $GOIMPORTS_VERSION installed"
}
# verify_sha256 <file> <expected-hash>
verify_sha256() {
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "$1" | cut -d' ' -f1)"
else
actual="$(shasum -a 256 "$1" | cut -d' ' -f1)"
fi
if [ "$actual" != "$2" ]; then
echo "bootstrap: sha256 mismatch for $1" >&2
echo " expected: $2" >&2
echo " actual: $actual" >&2
exit 1
fi
}
# nvm is a bash script; run a command in a bash with nvm loaded
nvm_sh() {
bash -c ". \"\$HOME/.nvm/nvm.sh\" && $*"
}
ensure_nvm() {
[ -s "$HOME/.nvm/nvm.sh" ] && return 0
# nvm prerequisites; nvm itself requires bash
if missing bash; then pkg_install bash bash bash bash; fi
if missing curl; then pkg_install curl curl curl curl; fi
if missing git; then pkg_install git git git git; fi
tmp="$(mktemp -d)"
curl -fsSL -o "$tmp/nvm.tar.gz" \
"https://github.com/nvm-sh/nvm/archive/refs/tags/v${NVM_VERSION}.tar.gz"
verify_sha256 "$tmp/nvm.tar.gz" "$NVM_SHA256"
mkdir -p "$HOME/.nvm"
tar -xzf "$tmp/nvm.tar.gz" -C "$HOME/.nvm" --strip-components=1
rm -rf "$tmp"
}
# Print the version of the node on PATH, such as v22.17.0, or nothing.
node_version() {
if missing node; then return 0; fi
node --version 2>/dev/null
}
ensure_node() {
if [ "$(node_version)" = "v$NODE_VERSION" ]; then
echo "node $NODE_VERSION already installed"
return 0
fi
ensure_nvm
nvm_sh "nvm install $NODE_VERSION"
echo "node $NODE_VERSION installed via nvm"
}
# Print the version of the yarn that script/fmt and script/fmt-check
# run, or nothing: the yarn on PATH, else the one under the pinned node
# in nvm.
yarn_version() {
if ! missing yarn; then
yarn --version 2>/dev/null
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && yarn --version" \
2>/dev/null
fi
}
# A yarn that already has the pinned version is used with the node that
# runs it. Otherwise yarn is installed via corepack under the pinned
# node.
ensure_yarn() {
if [ "$(yarn_version)" = "$YARN_VERSION" ]; then
echo "yarn $YARN_VERSION already installed"
return 0
fi
ensure_node
if [ "$(node_version)" = "v$NODE_VERSION" ]; then
corepack enable
corepack prepare "yarn@$YARN_VERSION" --activate
else
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
fi
hash -r
if [ "$(yarn_version)" != "$YARN_VERSION" ]; then
echo "bootstrap: the yarn script/fmt runs is not $YARN_VERSION:" \
"$(command -v yarn || echo "none on PATH")" >&2
exit 1
fi
echo "yarn $YARN_VERSION installed"
}
install_js_deps() {
if missing yarn && [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && cd \"$ROOT\" && \
yarn install --frozen-lockfile"
else
yarn install --frozen-lockfile
fi
}
main() { main() {
cd "$ROOT" cd "$ROOT"
@@ -202,17 +64,8 @@ main() {
if missing make; then pkg_install gnumake make make make; fi if missing make; then pkg_install gnumake make make make; fi
if missing go; then pkg_install go golang go go; fi if missing go; then pkg_install go golang go go; fi
# go install writes to Go's bin directory, which need not be on PATH.
gobin="$(go env GOBIN)"
[ -n "$gobin" ] || gobin="$(go env GOPATH)/bin"
PATH="$gobin:$PATH"
ensure_goimports
go mod download go mod download
ensure_yarn
install_js_deps
echo "bootstrap complete" echo "bootstrap complete"
} }
+4 -26
View File
@@ -4,36 +4,14 @@ set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap. # goimports v0.42.0, 2026-10-05. Run with `go run` at this commit rather
NODE_VERSION="22.17.0" # than installed, so it is always this version, whatever is on PATH.
GOIMPORTS_REF="golang.org/x/tools/cmd/goimports@009367f5c17a8d4c45a961a3a509277190a9a6f0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
yarn "$@"
return
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt: no yarn; run script/bootstrap first" >&2
exit 1
fi
bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() { main() {
cd "$ROOT" cd "$ROOT"
# script/bootstrap installs goimports into Go's bin directory, which
# need not be on PATH.
gobin="$(go env GOBIN)"
[ -n "$gobin" ] || gobin="$(go env GOPATH)/bin"
PATH="$gobin:$PATH"
gofmt -s -w . gofmt -s -w .
goimports -w . go run "$GOIMPORTS_REF" -w .
run_yarn run prettier --write '**/*.md' --tab-width 4 --prose-wrap always
} }
main "$@" main "$@"
+3 -29
View File
@@ -5,40 +5,14 @@ set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# Must match the pin in script/bootstrap.
NODE_VERSION="22.17.0"
# script/bootstrap installs node and yarn under nvm and leaves neither
# on the PATH of the shell that called it, so resolve the pinned
# toolchain here the way bootstrap's own install step does. nvm is a
# bash script, hence the subshell.
run_yarn() {
if command -v yarn >/dev/null 2>&1; then
yarn "$@"
return
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt-check: no yarn; run script/bootstrap first" >&2
exit 1
fi
bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() { main() {
cd "$ROOT" cd "$ROOT"
# script/bootstrap installs goimports into Go's bin directory, which files="$(gofmt -l .)"
# need not be on PATH.
gobin="$(go env GOBIN)"
[ -n "$gobin" ] || gobin="$(go env GOPATH)/bin"
PATH="$gobin:$PATH"
files="$(gofmt -s -l .; goimports -l .)"
if [ -n "$files" ]; then if [ -n "$files" ]; then
echo "files that make fmt would change:" >&2 echo "gofmt: files not formatted:" >&2
echo "$files" | sort -u >&2 echo "$files" >&2
exit 1 exit 1
fi fi
run_yarn run prettier --check '**/*.md' --tab-width 4 --prose-wrap always
} }
main "$@" main "$@"
-8
View File
@@ -1,8 +0,0 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
prettier@3.8.1:
version "3.8.1"
resolved "https://registry.yarnpkg.com/prettier/-/prettier-3.8.1.tgz#edf48977cf991558f4fcbd8a3ba6015ba2a3a173"
integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==