Compare commits
1 Commits
1c16d50d67
...
4baf2a1c78
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4baf2a1c78 |
12
README.md
12
README.md
@@ -37,13 +37,11 @@ broken. We provide:
|
|||||||
|
|
||||||
- `script/bootstrap` — install all dependencies, assuming nothing is present:
|
- `script/bootstrap` — install all dependencies, assuming nothing is present:
|
||||||
pinned node via nvm if needed, yarn via corepack,
|
pinned node via nvm if needed, yarn via corepack,
|
||||||
`yarn install --frozen-lockfile`, and the backend's toolchain — Go (an
|
`yarn install --frozen-lockfile`, and the backend's toolchain — Go (reused if
|
||||||
already-installed Go is reused only when its version falls inside the window
|
already new enough) and golangci-lint at the version `Dockerfile.backend`
|
||||||
the pinned golangci-lint can analyse; a newer Go is ignored, not preferred)
|
pins. Everything not installed by the system package manager comes from a
|
||||||
and golangci-lint at the version `Dockerfile.backend` pins. Everything not
|
hash-verified release archive and is symlinked onto `PATH`, so `make check`
|
||||||
installed by the system package manager comes from a hash-verified release
|
works in a plain shell afterwards
|
||||||
archive and is symlinked into `~/.local/bin`, so `make check` works in a plain
|
|
||||||
shell afterwards
|
|
||||||
- `script/setup` — make a fresh clone ready for development: bootstrap plus the
|
- `script/setup` — make a fresh clone ready for development: bootstrap plus the
|
||||||
git pre-commit hook
|
git pre-commit hook
|
||||||
- `script/projectname` — print the project name (used for the Docker image tags)
|
- `script/projectname` — print the project name (used for the Docker image tags)
|
||||||
|
|||||||
4
TODO.md
4
TODO.md
@@ -33,9 +33,7 @@ files, so merging it also closes most compliance gaps.
|
|||||||
golangci-lint cannot analyse packages built by a newer Go; it links only into
|
golangci-lint cannot analyse packages built by a newer Go; it links only into
|
||||||
`~/.local/bin`, never a system-wide prefix, and refuses to replace anything it
|
`~/.local/bin`, never a system-wide prefix, and refuses to replace anything it
|
||||||
did not create; and it exits non-zero rather than reporting success when the
|
did not create; and it exits non-zero rather than reporting success when the
|
||||||
tools on the caller's `PATH` are not the pinned ones — `gofmt` included, held
|
tools on the caller's `PATH` are not the pinned ones
|
||||||
to the same version agreement as `go` and relinked on every run so a deleted
|
|
||||||
link cannot leave another Go's `gofmt` gating the repo
|
|
||||||
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints, Makefile
|
||||||
shims, README Entrypoints section
|
shims, README Entrypoints section
|
||||||
- 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow
|
- 2026-02-27: backend with buffered zstd-compressed report storage; CI workflow
|
||||||
|
|||||||
103
script/bootstrap
103
script/bootstrap
@@ -5,11 +5,10 @@
|
|||||||
# or apk (detected in that order); assumes nothing is present. Node is
|
# or apk (detected in that order); assumes nothing is present. Node is
|
||||||
# used directly if installed; otherwise it is installed at a pinned
|
# used directly if installed; otherwise it is installed at a pinned
|
||||||
# version via nvm (installing nvm itself first, from a hash-verified
|
# version via nvm (installing nvm itself first, from a hash-verified
|
||||||
# release archive, never curl | sh). Go is used directly only if its
|
# release archive, never curl | sh). Go is used directly if it is
|
||||||
# version falls inside the pinned window described at GO_MAX_MINOR below
|
# already new enough, and golangci-lint is installed at the exact pinned
|
||||||
# -- a newer Go is ignored, not preferred -- and golangci-lint is
|
# version; both come from hash-verified official release archives, never
|
||||||
# installed at the exact pinned version; both come from hash-verified
|
# an install script.
|
||||||
# official release archives, never an install script.
|
|
||||||
#
|
#
|
||||||
# The backend's toolchain is bootstrapped here because script/check runs
|
# The backend's toolchain is bootstrapped here because script/check runs
|
||||||
# backend/script/test and backend/script/lint, so a machine that cannot
|
# backend/script/test and backend/script/lint, so a machine that cannot
|
||||||
@@ -23,13 +22,10 @@
|
|||||||
#
|
#
|
||||||
# Three rules govern what this script is allowed to touch:
|
# Three rules govern what this script is allowed to touch:
|
||||||
#
|
#
|
||||||
# 1. Everything it installs itself lands under $HOME, using $TMPDIR
|
# 1. It never writes outside $HOME. A per-repo bootstrap has no
|
||||||
# only for scratch downloads it then deletes. The one exception is
|
# business writing to /usr/local/bin, a Homebrew prefix, or any
|
||||||
# the system package manager, which it shells out to for base
|
# other system-wide location shared with other users and with a
|
||||||
# tooling (see pkg_install) and which owns those paths already. A
|
# package manager.
|
||||||
# per-repo bootstrap has no business writing to /usr/local/bin, a
|
|
||||||
# Homebrew prefix, or any other system-wide location behind that
|
|
||||||
# package manager's back.
|
|
||||||
# 2. It never replaces something it did not create. Only a symlink
|
# 2. It never replaces something it did not create. Only a symlink
|
||||||
# that already points into one of its own managed directories is
|
# that already points into one of its own managed directories is
|
||||||
# overwritten; anything else is left alone and bootstrap exits
|
# overwritten; anything else is left alone and bootstrap exits
|
||||||
@@ -385,32 +381,8 @@ go_ok() {
|
|||||||
ver_ge "$GO_MAX_MINOR" "$(echo "$have" | cut -d. -f1,2)"
|
ver_ge "$GO_MAX_MINOR" "$(echo "$have" | cut -d. -f1,2)"
|
||||||
}
|
}
|
||||||
|
|
||||||
# gofmt_ok: gofmt is a gate tool -- backend/script/fmt-check runs it --
|
|
||||||
# and its output is not guaranteed byte-identical across Go releases, so
|
|
||||||
# a gofmt from a different release than the go that compiles the code is
|
|
||||||
# treated exactly like a missing one, the same way a mismatched
|
|
||||||
# golangci-lint is. `go version <file>` prints the toolchain a Go binary
|
|
||||||
# was built with, so this compares the gofmt that resolves on PATH
|
|
||||||
# against the go that resolves on PATH, without depending on where
|
|
||||||
# either one lives. Anything it cannot read -- no go to ask, gofmt
|
|
||||||
# absent, not a Go binary -- fails closed.
|
|
||||||
gofmt_ok() {
|
|
||||||
if missing go; then return 1; fi
|
|
||||||
if missing gofmt; then return 1; fi
|
|
||||||
go_have="$(go version 2>/dev/null | awk '{print $3}')"
|
|
||||||
[ -n "$go_have" ] || return 1
|
|
||||||
fmt_have="$(go version "$(command -v gofmt)" 2>/dev/null | awk '{print $NF}')"
|
|
||||||
[ "$fmt_have" = "$go_have" ]
|
|
||||||
}
|
|
||||||
|
|
||||||
# ensure_go: reuse the host toolchain only when the go on PATH is inside
|
|
||||||
# the window AND a gofmt from that same release is on PATH with it. Both
|
|
||||||
# link_bin calls sit outside that early return, so whenever the pinned
|
|
||||||
# toolchain is the one in use they run on every bootstrap, not only on
|
|
||||||
# the run that unpacked the archive: a deleted or never-created gofmt
|
|
||||||
# link is restored rather than silently left to some other Go's gofmt.
|
|
||||||
ensure_go() {
|
ensure_go() {
|
||||||
if go_ok && gofmt_ok; then return 0; fi
|
if go_ok; then return 0; fi
|
||||||
if [ ! -x "$GO_DIR/bin/go" ]; then
|
if [ ! -x "$GO_DIR/bin/go" ]; then
|
||||||
plat="$(platform)"
|
plat="$(platform)"
|
||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
@@ -502,22 +474,7 @@ ensure_golangci_lint() {
|
|||||||
# warning buried in a long bootstrap log is not enough. So the checks
|
# warning buried in a long bootstrap log is not enough. So the checks
|
||||||
# re-run against the PATH the caller will have (theirs, plus $BIN_DIR at
|
# re-run against the PATH the caller will have (theirs, plus $BIN_DIR at
|
||||||
# the front if bootstrap had to ask for it), and a failure is fatal.
|
# the front if bootstrap had to ask for it), and a failure is fatal.
|
||||||
#
|
|
||||||
# Every gate tool that has a version constraint is checked with the same
|
|
||||||
# predicate its install used -- go_ok, gofmt_ok, golangci_lint_ok -- not
|
|
||||||
# with a bare presence test, because a wrong-version gate tool produces
|
|
||||||
# different results from the one CI runs, which is the failure this
|
|
||||||
# function exists to prevent. node and yarn have no pinned version to
|
|
||||||
# disagree about, so presence is the whole constraint for them.
|
|
||||||
verify_toolchain() {
|
verify_toolchain() {
|
||||||
# BIN_DIR is only set once something needed linking, but the remedy
|
|
||||||
# text must name a real directory in every reachable state, so fall
|
|
||||||
# back to the one ensure_bin_dir would have chosen.
|
|
||||||
bin_dir="${BIN_DIR:-$HOME/.local/bin}"
|
|
||||||
|
|
||||||
# Model the PATH the caller will actually have: their own, plus
|
|
||||||
# $BIN_DIR at the front only if bootstrap linked something there and
|
|
||||||
# therefore told them to add it.
|
|
||||||
verify_path="$ORIG_PATH"
|
verify_path="$ORIG_PATH"
|
||||||
if [ -n "$BIN_DIR" ]; then
|
if [ -n "$BIN_DIR" ]; then
|
||||||
case ":$ORIG_PATH:" in
|
case ":$ORIG_PATH:" in
|
||||||
@@ -531,9 +488,8 @@ verify_toolchain() {
|
|||||||
export PATH
|
export PATH
|
||||||
bad=""
|
bad=""
|
||||||
go_ok || bad="$bad go"
|
go_ok || bad="$bad go"
|
||||||
gofmt_ok || bad="$bad gofmt"
|
|
||||||
golangci_lint_ok || bad="$bad golangci-lint"
|
golangci_lint_ok || bad="$bad golangci-lint"
|
||||||
for t in node yarn; do
|
for t in gofmt node yarn; do
|
||||||
if missing "$t"; then bad="$bad $t"; fi
|
if missing "$t"; then bad="$bad $t"; fi
|
||||||
done
|
done
|
||||||
PATH="$saved_path"
|
PATH="$saved_path"
|
||||||
@@ -541,43 +497,20 @@ verify_toolchain() {
|
|||||||
|
|
||||||
[ -z "$bad" ] && return 0
|
[ -z "$bad" ] && return 0
|
||||||
|
|
||||||
# Two different faults land here and they need different remedies: a
|
|
||||||
# tool that resolves but is the wrong build is being shadowed, and
|
|
||||||
# telling the user to fix PATH is right; a tool that does not resolve
|
|
||||||
# at all is not being shadowed by anything, and saying so would send
|
|
||||||
# them hunting for a conflict that does not exist.
|
|
||||||
echo "bootstrap: the toolchain on your PATH cannot run the gate." >&2
|
echo "bootstrap: the toolchain on your PATH cannot run the gate." >&2
|
||||||
wrong=""
|
|
||||||
absent=""
|
|
||||||
for t in $bad; do
|
for t in $bad; do
|
||||||
where="$(
|
where="$(
|
||||||
export PATH="$verify_path"
|
export PATH="$verify_path"
|
||||||
command -v "$t" 2>/dev/null || true
|
command -v "$t" || echo "not found"
|
||||||
)"
|
)"
|
||||||
if [ -n "$where" ]; then
|
echo " $t: $where" >&2
|
||||||
echo " $t: $where (wrong version)" >&2
|
|
||||||
wrong="$wrong $t"
|
|
||||||
else
|
|
||||||
echo " $t: not found" >&2
|
|
||||||
absent="$absent $t"
|
|
||||||
fi
|
|
||||||
done
|
done
|
||||||
echo " The pinned toolchain is linked into $bin_dir." >&2
|
echo " Expected these to come from $BIN_DIR. Something earlier on" >&2
|
||||||
if [ -n "$wrong" ]; then
|
echo " your PATH is shadowing them, or PATH does not reach it." >&2
|
||||||
echo " The tools shown with a path resolve to a build this" >&2
|
echo " Put $BIN_DIR first in PATH, or remove the conflicting tool," >&2
|
||||||
echo " script did not provision: something earlier on your PATH" >&2
|
echo " then re-run bootstrap. Failing rather than leaving you a" >&2
|
||||||
echo " shadows $bin_dir. Put $bin_dir first in" >&2
|
echo " bootstrap that reports success and a \`make check\` that does" >&2
|
||||||
echo " PATH, or remove the conflicting tool, then re-run." >&2
|
echo " not run." >&2
|
||||||
fi
|
|
||||||
if [ -n "$absent" ]; then
|
|
||||||
echo " The tools shown as not found are on no directory of your" >&2
|
|
||||||
echo " PATH at all, so nothing is shadowing them. Add $bin_dir" >&2
|
|
||||||
echo " to PATH and re-run; if they are still not found after" >&2
|
|
||||||
echo " that, bootstrap failed to install them and that is a bug" >&2
|
|
||||||
echo " in this script, not in your environment." >&2
|
|
||||||
fi
|
|
||||||
echo " Failing rather than leaving you a bootstrap that reports" >&2
|
|
||||||
echo " success and a \`make check\` that does not run." >&2
|
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user