8 Commits
Author SHA1 Message Date
clawbotandsneak ed5ed236b1 Migrate canonical .golangci.yml to gomodguard_v2, with a block list (#55)
check / check (push) Successful in 27s
Option-2 answer to sneak's ruling of 2026-08-19 on
sneak/homoicon#4: migrate to the successor, with
settings. Closes #25.

## The change

`golangci-lint` v2.12.0 deprecated `gomodguard`, and this config sets
`linters.default: all`, so it is enabled everywhere and warns on every run.

- `gomodguard` joins `wsl` in `linters.disable` under a shared "deprecated"
  comment. The warning is attached to the old name, so disabling it is what
  silences it.
- `gomodguard_v2` is named in `linters.enable`, a no-op under `default: all`
  that gives the settings block a visible owner.
- Blocked, each restating a decision already recorded in the Go package
  defaults: `rs/zerolog` → `log/slog`; the pre-fork `go-redis/redis` →
  `redis/go-redis/v9`; `sergi/go-diff` and `hexops/gotextdiff` → `go-udiff`.
  Every entry matches the module path exactly, so the pre-fork go-redis takes
  three: `go-redis/redis`, `/v7`, `/v8`. A prefix would also cover
  `go-redis/redismock`, the test double for the successor recommended here.

Deliberately absent: recorded rejections that vendoring repos still require
(`mattn/go-sqlite3`, `gorm.io/gorm`, `pmezard/go-difflib`), plus `urfave/cli`
and unversioned `go-chi/chi`. Blocking those would redden repos mid-migration
on their next re-vendor.

## After merge

The file's sha256 moves from
`d10f47ef5e0d8620efd62275b016a7de8fd5abedf333922eec86fe4abc06176e` to
`a79b63a254602a5318db5d0e9a06bc71b84bf0c1d896305229d8bfed1d1b1776`, so every
vendoring repo mismatches. #60 is the
propagation brief; it and the record on
#25 carry this value.

## Disclosures

- Judgement call: `wsl` moved two lines down to share the "deprecated" comment.
  No behaviour change, but it widens the diff.
- The `go.mod` survey and the settings-block probe are on
  #25.
- Unverified: the linter was not run against each vendoring repo; the per-repo
  claim rests on reading their `go.mod` files.
- `make check` exit 0.

Model: opus-5
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #55
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-09-09 14:04:18 +02:00
sneak 6c489067ce Milestone next: check-cache busting, build-context hygiene, lint in a container (#34)
check / check (push) Successful in 24s
Reviewed-on: #34
2026-09-09 14:01:54 +02:00
sneak c4d5546e86 Gate the build on Docker lint and test phases (closes #40, closes #30)
check / check (push) Successful in 5m12s
Per the owner ruling on issue 40, linting and testing are phases of the
main Dockerfile rather than a separate lint file. script/lint and
script/test build one phase each by name with caching disabled, and the
final stage copies a harmless file from each so the image cannot be built
unless both passed. A stage that is not the last is built only when
something depends on it or --target names it, so the gates are invoked by
name and the edges kept. script/check runs the gates and builds no image
of its own; script/cibuild bootstraps first, because CI runs it alone and
fmt-check is native. fmt and fmt-check source nvm for the pinned node
before calling yarn, which bootstrap installs but leaves off its caller's
PATH. Every build in script/ is tagged and uncached. Issue 30 closes too:
a container has its own lint cache and lock.

Model: opus-5
2026-09-09 11:44:59 +00:00
sneak 7b55c444ae Keep in-repo agent scratch out of the build context and out of git (closes #27)
The canonical .dockerignore and .gitignore both omitted the in-repo agent
scratch directory, which holds one worktree per in-flight agent, so under
`COPY . .` an entire extra checkout of the repo reached the image. The
two entries are deliberately different shapes: anchored in .dockerignore,
where the `**/` form would also delete a legitimately named nested
directory, and unanchored in .gitignore, where a pattern already matches
at every depth. Anchoring leaves a gap where agents run in
subdirectories, stated in the vendored file itself. The second half is
the consequence of excluding .git: `git describe` in a build stage yields
an empty version without erroring, so the version is now computed on the
host and passed in.

Model: opus-5
2026-09-09 11:44:59 +00:00
sneak c3a504f647 Keep secrets out of the Docker build context at every depth (closes #29)
The canonical .dockerignore was three lines while the canonical
Dockerfile does `COPY . .`, so a local .env, *.pem or *.key shipped into
the build context and could land in an image layer, invisible to every
git-based check. Copying .gitignore's patterns across is not the repair:
.dockerignore anchors an unprefixed pattern at the context root, so that
form protects only the repository root while reading as solved. Every
depth-independent pattern here carries `**/`, and secret names are
character ranges because matching is case-sensitive and an ALL-CAPS twin
still misses `Server.Key`. Public certificates are deliberately left in
as a legitimate build input. Verified by enumerating a probe image.

Model: opus-5
2026-09-09 11:44:58 +00:00
sneak 85bea7681e Compare versions when bootstrap installs a pinned tool (closes #28)
The canonical `if missing <tool>; then install; fi` guard tests PATH
presence and never version, so on any already-provisioned machine a pin
is inert and a version bump is a no-op, while the Dockerfile installs the
pinned version into a clean image and CI then disagrees with local about
what the tool is. Comparing versions alone is not enough either: an
installer writes to its own directory while callers resolve through PATH,
so a shadowing binary lets the install succeed and change nothing anyone
sees. REPO_POLICIES.md now states the whole form — exact whole-token
comparison, mis-parse falling through to a reinstall, re-resolution
through PATH after installing, and a call site that prints the version.

Model: opus-5
2026-09-09 11:44:58 +00:00
sneak 58f75147be Build with --no-cache so the check layer actually runs (closes #26)
script/cibuild was a plain `docker build .` and the Dockerfile does
`COPY . .` followed by `RUN make check`, so on an unchanged tree Docker
served the check layer from cache: the suite never ran and the build
still exited 0. Measured here before the change, a second run on a
byte-identical tree returned in 0.286s with `RUN make check` CACHED.
script/cibuild and script/docker now pass --no-cache. The canonical text
asserting that a bare `docker build .` proves the checks ran was wrong in
REPO_POLICIES.md, both checklists and the Go styleguide, and is corrected
in all of them.

Model: opus-5
2026-09-09 11:44:58 +00:00
58eafaf4c2 Add -count=1 to the canonical Go make test example (#45)
check / check (push) Successful in 7s
Answers #44: the canonical Go `make test` target omitted `-count=1`, so Go replayed cached successful results and the target could exit 0 having executed no test. Every repository that copied it inherited that false green.

The change adds `-count=1` to both `go test` invocations in the example in `REPO_POLICIES.md`, with a short paragraph saying why, and records the step in `TODO.md`. It defeats only the test-result cache, not the build cache, so it costs the suite's runtime and no recompilation. It is independent of the Docker layer cache that #26 addresses.

Rebased onto current `main`; the check is green. Repositories pick it up the next time each vendors the canonical files.

Model: opus-5 (change); fable-5-1 (this description)
Co-authored-by: sneak <sneak@sneak.berlin>
Co-authored-by: clawbot <cai2025@acidhou.se>
Co-authored-by: Jeffrey Paul <sneak@noreply.example.org>
Reviewed-on: #45
Co-authored-by: clawbot <clawbot@noreply.example.org>
Co-committed-by: clawbot <clawbot@noreply.example.org>
2026-09-09 13:41:57 +02:00
11 changed files with 190 additions and 56 deletions
+40 -1
View File
@@ -10,13 +10,20 @@ run:
linters:
default: all
enable:
# Successor to the deprecated gomodguard. Named explicitly, rather than
# left to `default: all`, because it carries the module policy below.
- gomodguard_v2
disable:
# Genuinely incompatible with project patterns
- exhaustruct # Requires all struct fields
- godot # Requires comments to end with periods
- wsl # Deprecated, replaced by wsl_v5
- wrapcheck # Too verbose for internal packages
- varnamelen # Short names like db, id are idiomatic Go
# Deprecated: the warning is attached to the old name, so it is
# silenced by disabling that name, not by enabling the successor.
- wsl # Deprecated, replaced by wsl_v5
- gomodguard # Deprecated, replaced by gomodguard_v2
settings:
lll:
line-length: 88
@@ -53,6 +60,38 @@ linters:
desc: >-
Test-support code belongs in test files and in packages whose
directory name ends in test, not in the shipped binary.
# Only decisions already recorded in the Go package defaults are
# listed here. Every entry matches the module path exactly.
gomodguard_v2:
blocked:
- module: github.com/rs/zerolog
recommendations:
- log/slog
reason: "Structured logging is stdlib log/slog."
# One entry per pre-fork module path, because the later releases
# are separate paths. A prefix match would be shorter but would
# also reach github.com/go-redis/redismock, the test double for
# the successor these entries recommend.
- module: github.com/go-redis/redis
recommendations:
- github.com/redis/go-redis/v9
reason: "Pre-fork module; use the maintained go-redis v9."
- module: github.com/go-redis/redis/v7
recommendations:
- github.com/redis/go-redis/v9
reason: "Pre-fork module; use the maintained go-redis v9."
- module: github.com/go-redis/redis/v8
recommendations:
- github.com/redis/go-redis/v9
reason: "Pre-fork module; use the maintained go-redis v9."
- module: github.com/sergi/go-diff
recommendations:
- github.com/aymanbagabas/go-udiff
reason: "No unified diff output; use go-udiff."
- module: github.com/hexops/gotextdiff
recommendations:
- github.com/aymanbagabas/go-udiff
reason: "Unmaintained fork; use go-udiff."
issues:
max-issues-per-linter: 0
+8 -7
View File
@@ -123,16 +123,17 @@ alpine. We provide:
- `script/fmt` — format all markdown files with prettier (writes; native, not in
a container)
- `script/fmt-check` — check formatting (read-only; native)
- `script/check` — run all checks: `test`, `lint`, `fmt-check`, then
`docker build -t prompts .` (our own extension; no `--no-cache` there, because
the two phases that build depends on were just built without it)
- `script/check` — run all checks: `test`, `lint`, `fmt-check` (our own
extension); builds no image of its own
- `script/docker`
`docker build --no-cache --build-arg VERSION="$version" -t prompts .`, the tag
coming from `script/projectname` (byte-identical across repos)
- `script/cibuild` — cd to the repo root, compute `version` from `git describe`,
run `script/check`, then
`docker build --build-arg VERSION="$version" -t prompts .` (what CI runs; the
version is computed on the host because `.dockerignore` excludes `.git`)
- `script/cibuild` — cd to the repo root, run `script/bootstrap`, run
`script/check`, compute `version` from `git describe`, then
`docker build --no-cache --build-arg VERSION="$version" -t prompts .` (what CI
runs; it bootstraps because CI checks out and runs this alone while
`script/fmt-check` is native, and the version is computed on the host because
`.dockerignore` excludes `.git`)
- `script/precommit` — run by the git pre-commit hook (our own extension); calls
`script/check`
- `script/install-precommit` — installs the git pre-commit hook (our own
+11
View File
@@ -29,6 +29,8 @@ fmt-check, and commit.
they pass. This also closes issue 30: a container has its own result cache and
its own lock, so a lint verdict can no longer belong to another checkout. No
separate lint Dockerfile, and no `golangci-lint config verify` step.
`script/check` runs the gates and nothing else, and `script/cibuild`
bootstraps first, since it is all CI runs and `script/fmt-check` is native.
- 2026-09-08: Kept in-repo agent scratch out of the Docker build context and out
of version control: `.claude/` is one full checkout of the repo per in-flight
agent, and under `COPY . .` all of it was reaching the image. Also closed the
@@ -51,6 +53,15 @@ fmt-check, and commit.
layers cannot be served from cache on an unchanged tree, and the text claiming
a bare `docker build .` proves the checks ran is corrected in
`REPO_POLICIES.md`, both checklists and the Go styleguide.
- 2026-09-03: Added `-count=1` to both `go test` invocations in the canonical Go
`make test` example in `REPO_POLICIES.md`, so the target cannot report a
cached pass it did not earn, and documented that Go's test-result cache is a
second, independent cache stacked below the Docker layer cache.
- 2026-08-31: Migrated the canonical `.golangci.yml` from the deprecated
`gomodguard` to `gomodguard_v2`: the old linter is disabled by name (which is
what silences the deprecation warning), the successor is named explicitly in
`linters.enable`, and it carries a `blocked` module list drawn only from
decisions already recorded in the Go package defaults.
- 2026-08-07: Set the canonical `.golangci.yml` to the org-standard v2-schema
config already deployed byte-identical across the org's Go repos (settings
under `linters.settings` so thresholds like lll/funlen/cyclop/dupl actually
+13 -2
View File
@@ -99,6 +99,15 @@ with your task.
on the host.
- [ ] Every `docker build` in `script/` is tagged — an untagged one leaves a
dangling image behind on every run, on every host and CI runner
- [ ] `script/cibuild` runs `script/bootstrap` before `script/check`, and builds
the image with `--no-cache`. Without the bootstrap the CI run dies in
`script/fmt-check`, which runs the formatter on the host and finds nothing
installed.
- [ ] `script/fmt` and `script/fmt-check` source nvm for the pinned node version
before invoking `yarn`, as `script/bootstrap`'s own install step does.
`script/bootstrap` leaves the node and yarn it installs off the `PATH` of
the shell that called it, so a bare `yarn` exits 127 on a runner carrying
nothing but docker and git.
- [ ] `script/bootstrap` installs no linter of its own — delete the block, its
version variables and its call site. A JS repo's `yarn install` stays; it
brings a linter along with every other dependency, and no verdict is taken
@@ -151,8 +160,10 @@ with your task.
# Final
- [ ] `make check` passes
- [ ] `script/cibuild` succeeds and demonstrably executed the checks — a
sub-second build, or `CACHED` on a gate layer, means nothing ran
- [ ] `script/cibuild` succeeds in a fresh clone on a host carrying nothing but
docker and git, with no node or yarn on `PATH`, which is what CI has, and
demonstrably executed the checks — a sub-second build, or `CACHED` on a
gate layer, means nothing ran
- [ ] A planted lint violation fails both `make lint` and a plain
`docker build .`; revert it afterwards
- [ ] Commit and merge fixes before starting your actual task
+16 -6
View File
@@ -113,15 +113,23 @@ are thin shims calling them. Model scripts:
container)
- [ ] `script/fmt-check` / `make fmt-check` — checks formatting (read-only;
native)
- [ ] `script/check` / `make check` — runs `test`, `lint`, `fmt-check`, then
builds the image; must not modify files
- [ ] `script/check` / `make check` — runs `test`, `lint`, `fmt-check`; must not
modify files
- [ ] `script/projectname` — outputs the project name (used by `script/docker`
for the image tag)
- [ ] `script/docker` / `make docker` — builds Docker image, tagged via
`script/projectname` (byte-identical across repos); `--no-cache`, plus the
version as a build arg
- [ ] `script/cibuild` — cd to repo root, run `script/check`, then
`docker build --build-arg VERSION="$version" .` (what CI runs)
- [ ] `script/cibuild` — cd to repo root, run `script/bootstrap`, run
`script/check`, then
`docker build --no-cache --build-arg VERSION="$version" .` (what CI runs).
The bootstrap is required: CI checks out and runs this alone, and
`script/fmt-check` runs the formatter on the host.
- [ ] `script/fmt` and `script/fmt-check` source nvm for the pinned node version
before invoking `yarn`, as `script/bootstrap`'s own install step does.
`script/bootstrap` leaves the node and yarn it installs off the `PATH` of
the shell that called it, so a bare `yarn` exits 127 on a runner carrying
nothing but docker and git.
- [ ] Every `docker build` in `script/` is tagged, so no invocation leaves a
dangling image behind
- [ ] `script/precommit` — called by the pre-commit hook; runs `script/check`
@@ -135,8 +143,10 @@ are thin shims calling them. Model scripts:
- [ ] `make check` passes
- [ ] `make docker` succeeds
- [ ] `script/cibuild` succeeds and demonstrably executed the checks — a
sub-second build, or `CACHED` on a gate layer, means nothing ran
- [ ] `script/cibuild` succeeds in a fresh clone on a host carrying nothing but
docker and git, with no node or yarn on `PATH`, which is what CI has, and
demonstrably executed the checks — a sub-second build, or `CACHED` on a
gate layer, means nothing ran
- [ ] Plant a lint violation and confirm both `make lint` and a plain
`docker build .` fail on it; revert. A plain build that passes proves the
final stage is missing its `COPY --from=` edge to the gate phases.
+47 -20
View File
@@ -60,10 +60,20 @@ style conventions are in separate documents:
prerequisite since nvm requires bash. yarn is then pinned via
`corepack prepare yarn@<version> --activate`. Never install "latest" or "lts";
always exact versions. `script/cibuild` runs the CI build: it changes to the
repo root, runs `script/check`, and builds the image with the version; the
Gitea workflow calls it. Four further scripts are our own extensions to the
standard: `script/check` runs `script/test`, `script/lint` and
`script/fmt-check`, then builds the image; `script/precommit` is what the git
repo root, runs `script/bootstrap`, runs `script/check`, and builds the image
with the version; the Gitea workflow calls it. **`script/cibuild` runs
`script/bootstrap` first**, because the workflow checks out the repo and runs
nothing else, while `script/fmt-check` runs the formatter on the host: on a
pristine checkout with nothing installed the run dies there, after the
containerised gates have passed. **The bootstrap alone is not enough**:
`script/bootstrap` installs node and yarn under nvm and leaves neither on the
`PATH` of the shell that called it, so a bare `yarn` still exits 127. The host
entrypoints that need yarn — `script/fmt` and `script/fmt-check` — therefore
source nvm for the pinned node version before invoking it, exactly as
`script/bootstrap`'s own install step does. A runner carrying nothing but
docker and git then gets through `script/check`. Four further scripts are our
own extensions to the standard: `script/check` runs `script/test`,
`script/lint` and `script/fmt-check`; `script/precommit` is what the git
pre-commit hook runs, and it calls `script/check`; `script/install-precommit`
installs the git pre-commit hook (the `make hooks` target shims to it); and
`script/projectname` (literally that filename) simply outputs the project's
@@ -115,7 +125,7 @@ style conventions are in separate documents:
plain `docker build .` builds the last stage alone and exits 0 having linted
and tested nothing.
**Every `docker build` in `script/` is tagged**, here and in `script/check`,
**Every `docker build` in `script/` is tagged**, here and in
`script/cibuild` and `script/docker`. An untagged build leaves a dangling
image behind on every invocation, on every developer host and every CI
runner; a tagged one replaces the previous image.
@@ -139,14 +149,13 @@ style conventions are in separate documents:
- **Any build that runs checks is built with `--no-cache`.** Docker invalidates
a `COPY` layer only when the copied content changes, so on an unchanged tree
the check `RUN` is served from cache, nothing executes, and the build still
exits 0. `script/lint`, `script/test`, `script/cibuild` and `script/docker`
therefore pass `--no-cache`, and a bare `docker build .` is not evidence that
anything ran: a sub-second build reporting success is a cache hit, not a
result. The one build that may go through the cache is the image build inside
`script/check`, which runs immediately after the two gate phases were built
uncached and so reuses that run. Never invalidate by pruning —
`docker builder prune` and friends destroy a build cache shared with every
other build on the host.
exits 0. Every `docker build` in `script/` therefore passes `--no-cache`:
`script/lint`, `script/test`, `script/cibuild` and `script/docker` are the
four, and there is no fifth — `script/check` runs the two gate phases and
`script/fmt-check`, and builds no image of its own. A bare `docker build .` is
not evidence that anything ran: a sub-second build reporting success is a
cache hit, not a result. Never invalidate by pruning — `docker builder prune`
and friends destroy a build cache shared with every other build on the host.
- **The gate phases are separate stages, and the build stage depends on both.**
The lint phase is based on the `golangci/golangci-lint` image (pinned by
@@ -218,10 +227,13 @@ style conventions are in separate documents:
`script/docker` and `script/cibuild`; no stage may call `git describe`.
- Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that
runs `script/cibuild` on push. That script runs the gate phases and then the
image build, so a successful run means every check passed; a bare
`docker build .` does not carry the same guarantee, because its gate phases
may come from the cache.
runs `script/cibuild` on push, and checks out the repo as its only other step.
That script bootstraps, runs the gate phases, and then builds the image, so a
successful run means every check passed; a bare `docker build .` does not
carry the same guarantee, because its gate phases may come from the cache. The
image build is uncached and so runs the gate phases a second time. That is the
price of the rule above, and it is worth paying: the image that ships is built
from a run of its own gates rather than from a cache entry.
- Use platform-standard formatters: `black` for Python, `prettier` for
JS/CSS/Markdown/HTML, `go fmt` for Go. Always use default configuration with
@@ -268,11 +280,24 @@ style conventions are in separate documents:
```makefile
test:
@go test -timeout 90s -race -cover ./... || \
@go test -count=1 -timeout 90s -race -cover ./... || \
{ echo "--- Rerunning with -v for details ---"; \
go test -timeout 90s -race -v ./...; exit 1; }
go test -count=1 -timeout 90s -race -v ./...; exit 1; }
```
`-count=1` is required on both invocations: it defeats Go's test _result_
cache, so the target cannot report a pass it did not earn, and the rerun
reproduces a failure instead of replaying it. It leaves the build cache
alone, so it costs the runtime of the suite and no recompilation.
Note that this is a second, independent cache, stacked below the Docker
layer cache that [issue #26](https://git.eeqj.de/sneak/prompts/issues/26)
addresses. `CHECK_EPOCH` guarantees the `RUN make test` _step_ re-executes;
it does not guarantee `go test` inside that step does any work, because the
`GOCACHE` baked into earlier image layers survives into the re-executed
step. They are two separate defects requiring two separate fixes, and a fix
for one must not be recorded as covering the other.
Python example:
```makefile
@@ -352,7 +377,9 @@ style conventions are in separate documents:
# trip `set -e`, so the inline form degrades to an empty constant.
version="$(git describe --tags --always --dirty 2>/dev/null || true)"
[ -n "$version" ] || version="unknown"
docker build --no-cache --build-arg VERSION="$version" .
docker build --no-cache \
--build-arg VERSION="$version" \
-t "$(script/projectname)" .
```
`--always` makes an untagged repo yield an abbreviated commit hash rather
+4 -10
View File
@@ -1,22 +1,16 @@
#!/bin/sh
# script/check: run all checks (test, lint, fmt-check) and then the
# image build. Our own extension to scripts-to-rule-them-all. test and
# lint are Docker phases; fmt-check is native, because a formatter
# writes the working tree. Must not modify any files.
# script/check: run all checks (test, lint, fmt-check). Our own
# extension to scripts-to-rule-them-all. test and lint are Docker
# phases; fmt-check is native, because a formatter writes the working
# tree. Must not modify any files.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
"$SCRIPT_DIR/test"
"$SCRIPT_DIR/lint"
"$SCRIPT_DIR/fmt-check"
# No --no-cache here: the two phases this build depends on were just
# built uncached above, so what it reuses is that run and not an
# older one.
cd "$ROOT"
docker build -t "$("$SCRIPT_DIR/projectname")" .
}
main "$@"
+9 -5
View File
@@ -1,7 +1,10 @@
#!/bin/sh
# script/cibuild: run the CI build. script/check runs the gates and
# builds the image; this repeats the build with the version, which
# reuses the phases just built rather than re-running them.
# script/cibuild: run the CI build. It bootstraps first: a CI runner
# checks out and runs this and nothing else, and script/fmt-check runs
# the formatter on the host, which a pristine checkout cannot do.
# --no-cache for the same reason as script/docker: the gate phases the
# final stage depends on are RUN steps, and a cached one is a check that
# did not run.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
@@ -9,6 +12,8 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
cd "$ROOT"
"$SCRIPT_DIR/bootstrap"
"$SCRIPT_DIR/check"
# Own line: a failing command substitution inside an argument does
# not trip `set -e`, so the inline form degrades silently to an
# empty constant. VERSION is computed here because .dockerignore
@@ -16,8 +21,7 @@ main() {
# version without failing.
version="$(git describe --tags --always --dirty 2>/dev/null || true)"
[ -n "$version" ] || version="unknown"
"$SCRIPT_DIR/check"
docker build \
docker build --no-cache \
--build-arg VERSION="$version" \
-t "$("$SCRIPT_DIR/projectname")" .
}
+2 -3
View File
@@ -1,9 +1,8 @@
#!/bin/sh
# script/docker: build the Docker image tagged with the project name.
# Identical in all repos; the tag comes from script/projectname.
# --no-cache because this is a standalone entrypoint: the lint and test
# phases the final stage depends on must run rather than be served from
# an older build.
# --no-cache because the gate phases the final stage depends on are RUN
# steps, and a cached one is a check that did not run.
set -eu
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
+20 -1
View File
@@ -4,9 +4,28 @@ 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
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() {
cd "$ROOT"
yarn run prettier --write '**/*.md' --tab-width 4 --prose-wrap always
run_yarn run prettier --write '**/*.md' --tab-width 4 --prose-wrap always
}
main "$@"
+20 -1
View File
@@ -4,9 +4,28 @@ 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
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() {
cd "$ROOT"
yarn run prettier --check '**/*.md' --tab-width 4 --prose-wrap always
run_yarn run prettier --check '**/*.md' --tab-width 4 --prose-wrap always
}
main "$@"