Vendor pinned prettier/format toolchain from prompts scaffold (closes #203)
Check / check (pull_request) Successful in 3m15s

script/fmt ran unpinned npx prettier with an inline --tab-width and no config, so formatting was not reproducible. This vendors the canonical format toolchain from the prompts scaffold: .prettierrc (tabWidth 4, proseWrap always), package.json and yarn.lock pinning prettier 3.8.1, and a script/bootstrap that installs node/yarn from a hash-verified nvm archive. script/fmt now reads the config over static/js and markdown; gofmt/goimports unchanged. .prettierignore keeps the *.min.js rule protecting the vendored alpine.min.js. Existing markdown was reflowed to house style.

Disclosure: make check does not gate prettier; the linter's pre-existing gomodguard deprecation warning is unrelated and left as-is.

Model: opus-4-8 (implementation and review)
This commit is contained in:
2026-09-22 18:43:58 +00:00
committed by sneak
parent 6026e3923d
commit 1d769834de
9 changed files with 277 additions and 136 deletions
+80 -2
View File
@@ -5,8 +5,12 @@
# or apk (detected in that order); assumes NOTHING is present (not git,
# make, or go). goimports is installed with `go install` at a pinned
# version (integrity via the Go module checksum database) into
# /usr/local/bin so it is on PATH. The linter is not installed here: it
# runs only in Docker via script/lint, so docker is its sole prerequisite.
# /usr/local/bin so it is on PATH. Node is used directly if installed;
# otherwise it is installed at a pinned version via nvm (installing nvm
# itself first, from a hash-verified release archive, never curl | sh),
# then the pinned prettier from yarn.lock. The linter is not installed
# here: it runs only in Docker via script/lint, so docker is its sole
# prerequisite.
set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
@@ -15,6 +19,12 @@ ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# golang.org/x/tools goimports, 2026-08-13. v0.49.0 requires Go 1.25 (matches
# go.mod); v0.50.0 needs Go 1.26. Integrity via the Go module checksum database.
GOIMPORTS_VERSION="v0.49.0"
# Node/yarn toolchain, 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=""
SUDO=""
@@ -56,6 +66,21 @@ missing() {
! command -v "$1" >/dev/null 2>&1
}
# 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
}
# goimports is not packaged uniformly across nix/apt/brew/apk, so install it
# with `go install` at a pinned version and place the binary in /usr/local/bin
# so it is on PATH regardless of shell config. Requires go, which main
@@ -69,6 +94,54 @@ ensure_goimports() {
rm -rf "$tmp"
}
# 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"
}
ensure_node() {
if ! missing node; then return 0; fi
ensure_nvm
nvm_sh "nvm install $NODE_VERSION"
}
ensure_yarn() {
if ! missing yarn; then return 0; fi
if ! missing corepack; then
corepack enable
corepack prepare "yarn@$YARN_VERSION" --activate
elif [ -s "$HOME/.nvm/nvm.sh" ]; then
nvm_sh "nvm use $NODE_VERSION >/dev/null && corepack enable && \
corepack prepare yarn@$YARN_VERSION --activate"
else
npm install -g "yarn@$YARN_VERSION"
fi
}
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() {
cd "$ROOT"
@@ -80,6 +153,11 @@ main() {
if missing go; then pkg_install go golang go go; fi
ensure_goimports
# Node toolchain and pinned prettier
ensure_node
ensure_yarn
install_js_deps
# The linter runs only in Docker (script/lint). Warn, don't fail: the
# rest of the repo works without it.
if missing docker; then
+24 -1
View File
@@ -4,11 +4,34 @@ set -eu
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: 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() {
cd "$ROOT"
gofmt -s -w .
goimports -w .
npx prettier --write --tab-width 4 static/js/*.js
# Pinned prettier reads settings from .prettierrc (tabWidth 4,
# proseWrap always); .prettierignore keeps alpine.min.js untouched.
# Globs are quoted so prettier expands them, not the shell.
run_yarn run prettier --write 'static/js/*.js' '**/*.md'
}
main "$@"