Format Markdown with prettier in make fmt (closes #43)
check / check (push) Failing after 4s

script/fmt and script/fmt-check now run prettier on every Markdown file
after their Go steps, as the standard scripts from sneak/prompts at
dd4027b do. script/bootstrap gains the standard node, nvm and yarn
install next to its goimports install, and the last Dockerfile stage
copies package.json and yarn.lock before running it. .prettierrc,
.prettierignore and yarn.lock are the standard copies. package.json is
the standard one without its licence field and marked private, because
this repo's licence is the WTFPL. .dockerignore keeps the log of a failed
yarn install out of the build context. The existing Markdown is
reformatted in the next commit.

Model: opus-5-5
This commit is contained in:
2026-10-06 14:03:51 +00:00
parent 17796569b8
commit d827a7e65c
11 changed files with 159 additions and 13 deletions
+3 -1
View File
@@ -72,10 +72,12 @@
**/*.sublime-* **/*.sublime-*
# This repository's host-built artifacts: the example program's binary, # This repository's host-built artifacts: the example program's binary,
# test binaries and coverage output. # test binaries, coverage output, and the log yarn writes when an install
# fails.
/cmd/example/example /cmd/example/example
/*.test /*.test
/*.out /*.out
/yarn-error.log
# aider's files, among them a config file that can hold an API key. # aider's files, among them a config file that can hold an API key.
**/.aider* **/.aider*
+2
View File
@@ -0,0 +1,2 @@
node_modules/
yarn.lock
+4
View File
@@ -0,0 +1,4 @@
{
"tabWidth": 4,
"proseWrap": "always"
}
+1 -1
View File
@@ -31,6 +31,6 @@ COPY --from=lint /src/go.sum /dev/null
COPY --from=test /src/go.sum /dev/null COPY --from=test /src/go.sum /dev/null
WORKDIR /src WORKDIR /src
COPY script/ script/ COPY script/ script/
COPY go.mod go.sum ./ COPY go.mod go.sum package.json yarn.lock ./
RUN script/bootstrap RUN script/bootstrap
COPY . . COPY . .
+7 -5
View File
@@ -143,8 +143,10 @@ alpine. We provide:
- `script/bootstrap` — install all dependencies (go if missing, goimports with - `script/bootstrap` — install all dependencies (go if missing, goimports with
`go install` at a pinned commit unless the installed one already reports the `go install` at a pinned commit unless the installed one already reports the
pinned version, then `go mod download`); golangci-lint is not installed, pinned version, then `go mod download`; node through nvm at a pinned version
since it runs only in Docker if node is missing, yarn at a pinned version if it is missing, then the
`prettier` that `package.json` and `yarn.lock` pin); golangci-lint is not
installed, since it runs only in Docker
- `script/setup` — set up the repo for development after a fresh clone: runs - `script/setup` — set up the repo for development after a fresh clone: runs
`script/bootstrap`, then `script/install-precommit` `script/bootstrap`, then `script/install-precommit`
- `script/projectname` — output the project name (our own extension); used by - `script/projectname` — output the project name (our own extension); used by
@@ -155,10 +157,10 @@ alpine. We provide:
- `script/lint` — run golangci-lint in Docker by building only the `lint` - `script/lint` — run golangci-lint in Docker by building only the `lint`
stage of the `Dockerfile`, without the build cache, so every run lints; the stage of the `Dockerfile`, without the build cache, so every run lints; the
image is tagged `simplelog-lint` image is tagged `simplelog-lint`
- `script/fmt` — format all files (writes) with the goimports that - `script/fmt` — format all files (writes): Go with the goimports that
`script/bootstrap` installs `script/bootstrap` installs, then every `*.md` file with `prettier`
- `script/fmt-check` — check formatting (read-only); fails if `gofmt -l` - `script/fmt-check` — check formatting (read-only); fails if `gofmt -l`
reports files reports files, or if `prettier` would change any `*.md` file
- `script/check` — run all checks: `test`, `lint`, `fmt-check` (our own - `script/check` — run all checks: `test`, `lint`, `fmt-check` (our own
extension) extension)
- `script/docker` — build the Docker image without the build cache, tagged - `script/docker` — build the Docker image without the build cache, tagged
+4
View File
@@ -22,6 +22,10 @@ Started, Rationale, Design, TODO, License, Author
# Completed Steps # Completed Steps
* 2026-10-06: `make fmt` and `make fmt-check` also format Markdown, with
`prettier` at the standard settings, pinned through `package.json` and
`yarn.lock`; `script/bootstrap` installs node and yarn for it, and the
existing Markdown was formatted once
* 2026-10-06: re-vendored the standard files from `sneak/prompts` at * 2026-10-06: re-vendored the standard files from `sneak/prompts` at
commit `dd4027b`: `REPO_POLICIES.md`, the CI workflow, `.gitignore`, commit `dd4027b`: `REPO_POLICIES.md`, the CI workflow, `.gitignore`,
and `.golangci.yml` together with the lint stage's new image, plus and `.golangci.yml` together with the lint stage's new image, plus
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"devDependencies": {
"prettier": "3.8.1"
}
}
+78 -1
View File
@@ -2,11 +2,21 @@
# script/bootstrap: install all dependencies needed to build and develop # script/bootstrap: install all dependencies needed to build and develop
# 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. 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).
set -eu set -eu
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
# 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"
# goimports from golang.org/x/tools v0.30.0, 2025-02-10: the last release # goimports from golang.org/x/tools v0.30.0, 2025-02-10: the last release
# that Go 1.22, the Go of go.mod and the Dockerfile, can build. # that Go 1.22, the Go of go.mod and the Dockerfile, can build.
GOIMPORTS_COMMIT="09747cdf594a7924dcecb506312be3bd6e437962" GOIMPORTS_COMMIT="09747cdf594a7924dcecb506312be3bd6e437962"
@@ -81,6 +91,69 @@ ensure_goimports() {
echo "goimports $GOIMPORTS_VERSION at $(command -v goimports)" echo "goimports $GOIMPORTS_VERSION at $(command -v goimports)"
} }
# 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"
}
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() { main() {
cd "$ROOT" cd "$ROOT"
@@ -93,6 +166,10 @@ main() {
go mod download go mod download
ensure_node
ensure_yarn
install_js_deps
echo "bootstrap complete" echo "bootstrap complete"
} }
+24 -4
View File
@@ -1,17 +1,37 @@
#!/bin/sh #!/bin/sh
# script/fmt: format all files (writes) with the goimports that # script/fmt: format all files (writes): Go with the goimports that
# script/bootstrap installs. It puts Go's bin directory first on PATH, as # script/bootstrap installs, then Markdown with prettier. It puts Go's bin
# script/bootstrap does, so it runs that goimports even when the directory # directory first on PATH, as script/bootstrap does, so it runs that
# is not on the caller's PATH. # goimports even when the directory is not on the caller's PATH.
set -eu 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
exec yarn "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() { main() {
cd "$ROOT" cd "$ROOT"
gobin="$(go env GOBIN)" gobin="$(go env GOBIN)"
PATH="${gobin:-$(go env GOPATH)/bin}:$PATH" PATH="${gobin:-$(go env GOPATH)/bin}:$PATH"
goimports -l -w . goimports -l -w .
run_yarn run prettier --write '**/*.md' --tab-width 4 --prose-wrap always
} }
main "$@" main "$@"
+22 -1
View File
@@ -1,10 +1,30 @@
#!/bin/sh #!/bin/sh
# script/fmt-check: check formatting (read-only). Fails and lists the # script/fmt-check: check formatting (read-only). Fails and lists the
# offending files if gofmt would reformat anything. # offending files if gofmt would reformat any Go file; otherwise fails if
# prettier would reformat any Markdown file.
set -eu 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
exec yarn "$@"
fi
if [ ! -s "$HOME/.nvm/nvm.sh" ]; then
echo "fmt-check: no yarn; run script/bootstrap first" >&2
exit 1
fi
exec bash -c '. "$HOME/.nvm/nvm.sh" && nvm use "$1" >/dev/null &&
shift && exec yarn "$@"' bash "$NODE_VERSION" "$@"
}
main() { main() {
cd "$ROOT" cd "$ROOT"
unformatted="$(gofmt -l .)" unformatted="$(gofmt -l .)"
@@ -13,6 +33,7 @@ main() {
echo "$unformatted" echo "$unformatted"
exit 1 exit 1
fi fi
run_yarn run prettier --check '**/*.md' --tab-width 4 --prose-wrap always
} }
main "$@" main "$@"
+8
View File
@@ -0,0 +1,8 @@
# 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==