check / check (push) Successful in 2m43s
`make fmt` now runs prettier over the Markdown files after the Go formatters, and `make fmt-check` fails when prettier would change one. `package.json`, `yarn.lock`, `.prettierrc` and `.prettierignore` are the `sneak/prompts` copies at `dd4027b`, except for `"private": true` in `package.json` in place of the licence field. Both scripts find yarn the way that repo's scripts do. `script/bootstrap` installs prettier with `yarn install --frozen-lockfile`. When the yarn those scripts would run is not 1.22.22, it installs yarn through corepack under node 22.17.0: the node on `PATH` if it has that version, otherwise one installed through nvm from a hash-checked archive. `README.md` and `TODO.md` are reformatted once. Model: opus-5-5
220 lines
6.7 KiB
Bash
Executable File
220 lines
6.7 KiB
Bash
Executable File
#!/bin/sh
|
|
# script/bootstrap: install all dependencies needed to build and develop
|
|
# this repo. Idempotent: every install is guarded by a check so already
|
|
# installed tools are skipped. Base tooling comes from nix, apt, brew,
|
|
# or apk (detected in that order); assumes nothing is present.
|
|
# goimports is installed via `go install` at a pinned commit (never
|
|
# "latest"), unless the installed one already has the pinned version.
|
|
# The linter is not installed: it runs only as the lint phase of the
|
|
# 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
|
|
|
|
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=""
|
|
SUDO=""
|
|
APT_UPDATED=""
|
|
|
|
detect_pkgmgr() {
|
|
[ -n "$PKGMGR" ] && return 0
|
|
if command -v nix-env >/dev/null 2>&1; then
|
|
PKGMGR="nix"
|
|
elif command -v apt-get >/dev/null 2>&1; then
|
|
PKGMGR="apt"
|
|
elif command -v brew >/dev/null 2>&1; then
|
|
PKGMGR="brew"
|
|
elif command -v apk >/dev/null 2>&1; then
|
|
PKGMGR="apk"
|
|
else
|
|
echo "bootstrap: no supported package manager (nix, apt, brew, apk)" >&2
|
|
exit 1
|
|
fi
|
|
if [ "$PKGMGR" = "apt" ]; then
|
|
export DEBIAN_FRONTEND=noninteractive
|
|
if [ "$(id -u)" != "0" ]; then
|
|
SUDO="sudo"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# pkg_install <nix-attr> <apt-pkg> <brew-formula> <apk-pkg>
|
|
pkg_install() {
|
|
detect_pkgmgr
|
|
case "$PKGMGR" in
|
|
nix) nix-env -iA "nixpkgs.$1" ;;
|
|
apt)
|
|
if [ -z "$APT_UPDATED" ]; then
|
|
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get update
|
|
APT_UPDATED=1
|
|
fi
|
|
$SUDO env DEBIAN_FRONTEND=noninteractive apt-get install -y "$2"
|
|
;;
|
|
brew) brew install "$3" ;;
|
|
apk) apk add --no-cache "$4" ;;
|
|
esac
|
|
}
|
|
|
|
missing() {
|
|
! 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() {
|
|
cd "$ROOT"
|
|
|
|
if missing git; then pkg_install git git git git; fi
|
|
if missing make; then pkg_install gnumake make make make; 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
|
|
|
|
ensure_yarn
|
|
install_js_deps
|
|
|
|
echo "bootstrap complete"
|
|
}
|
|
|
|
main "$@"
|