Pin the remaining developer tool installs (closes #68)
check / check (push) Failing after 2s

gofumpt was pinned only by its version tag. It is now a tool of a
separate module in tools/, so `go tool` builds it from source checked
against the hashes in tools/go.sum, and mfer's own module gains no
dependencies. script/prettier no longer falls back to a prettier on
PATH, and fails when node_modules holds a different version than
package.json pins. The bootstrap comment now says what --frozen-lockfile
really does, package.json drops its made-up version, and the golang
image comment names its exact version.

Model: opus-5-5
This commit is contained in:
2026-10-04 17:09:48 +00:00
parent d3394bd2a2
commit ef56eeeae0
8 changed files with 71 additions and 42 deletions
+6 -3
View File
@@ -132,15 +132,18 @@ main() {
# ---- JS / docs repos ----
# This is a Go repo, but node and yarn are required anyway: prettier
# formats the Markdown and JSON, and script/fmt-check verifies it.
# The version is pinned by package.json/yarn.lock, whose integrity
# hashes --frozen-lockfile enforces.
# The version is pinned by package.json/yarn.lock: yarn checks every
# package it fetches against its yarn.lock integrity hash, and
# --frozen-lockfile fails instead of rewriting a yarn.lock that no
# longer matches package.json.
ensure_node
ensure_yarn
install_js_deps
# ---- Go repos ----
if missing go; then pkg_install go golang go go; fi
# No golangci-lint: script/lint runs it in Docker only.
# No golangci-lint: script/lint runs it in Docker only. No gofumpt:
# script/gofumpt builds the version tools/go.mod requires.
go mod download
# ---- Python repos ----
+8 -9
View File
@@ -10,10 +10,9 @@ set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# gofumpt v0.12.0, 2026-10-04. `go run` fetches and builds exactly this
# version, so neither a developer machine nor the lint image needs
# gofumpt installed.
GOFUMPT="mvdan.cc/gofumpt@v0.12.0"
# gofumpt v0.12.0, 2026-10-04, required by tools/go.mod. `go tool` run in
# tools/ builds it from source checked against the hashes in tools/go.sum,
# so neither a developer machine nor the lint image needs it installed.
usage() {
echo "usage: script/gofumpt --write|--check" >&2
@@ -22,15 +21,15 @@ usage() {
main() {
[ "$#" -eq 1 ] || usage
cd "$ROOT"
# Every Go file in the repo. gofumpt holds generated files, such as
# mfer/mf.pb.go, to gofmt's rules only.
cd "$ROOT/tools"
# Every Go file in the repo, from $ROOT down. gofumpt holds generated
# files, such as mfer/mf.pb.go, to gofmt's rules only.
case "$1" in
--write) go run "$GOFUMPT" -l -w . ;;
--write) go tool gofumpt -l -w "$ROOT" ;;
--check)
# Own line: a failing command inside `[ -n "$(...)" ]` does
# not trip `set -e`, so a gofumpt that never ran would pass.
unformatted="$(go run "$GOFUMPT" -l .)"
unformatted="$(go tool gofumpt -l "$ROOT")"
if [ -n "$unformatted" ]; then
echo "gofumpt: files need formatting (run make fmt):" >&2
echo "$unformatted" >&2
+16 -24
View File
@@ -18,24 +18,9 @@ usage() {
exit 2
}
# Prefer the version pinned by package.json/yarn.lock so that CI and
# developer machines format identically. Fall back to a prettier on PATH,
# but say so, because a different version formats differently.
find_prettier() {
if [ -x "$ROOT/node_modules/.bin/prettier" ]; then
printf '%s\n' "$ROOT/node_modules/.bin/prettier"
return 0
fi
if command -v prettier >/dev/null 2>&1; then
echo "prettier: node_modules/.bin/prettier is absent; using the" \
"prettier on PATH, which may be a different version than the" \
"one pinned in package.json. Run script/bootstrap to install" \
"the pinned version." >&2
command -v prettier
return 0
fi
return 1
}
# Only the prettier yarn installed from yarn.lock, never one on PATH: a
# different version formats differently.
PRETTIER="$ROOT/node_modules/.bin/prettier"
main() {
[ "$#" -eq 1 ] || usage
@@ -46,10 +31,17 @@ main() {
cd "$ROOT"
if ! prettier_bin="$(find_prettier)"; then
echo "prettier: not found." >&2
echo " Install it with: script/bootstrap" >&2
echo " (installs the version pinned in package.json/yarn.lock)" >&2
# node_modules keeps the old prettier after package.json moves to a new
# one, until script/bootstrap runs again, so compare the two. A prettier
# that runs also means node is on PATH, which reading package.json needs.
if ! installed="$("$PRETTIER" --version 2>/dev/null)"; then
echo "prettier: not installed; run script/bootstrap" >&2
exit 1
fi
pinned="$(node -p 'require("./package.json").devDependencies.prettier')"
if [ "$installed" != "$pinned" ]; then
echo "prettier: package.json pins $pinned but $installed is" \
"installed; run script/bootstrap" >&2
exit 1
fi
@@ -62,8 +54,8 @@ main() {
# patterns always match at least one tracked file (README.md,
# package.json), so an empty match means the glob broke, and prettier
# erroring out is exactly what we want rather than a vacuous pass.
"$prettier_bin" "$mode" "**/*.md"
"$prettier_bin" "$mode" "**/*.json"
"$PRETTIER" "$mode" "**/*.md"
"$PRETTIER" "$mode" "**/*.json"
}
main "$@"