Re-sync script/docker and script/cibuild with the model scripts (closes #265)
check / check (push) Failing after 2s

script/docker and script/cibuild are again byte-identical to the shared model
scripts. Both compute the version on the host with git describe, pass it as the
VERSION build arg and build with --no-cache; script/cibuild runs
script/bootstrap and script/check first. The CI build now stamps a real version
instead of unknown.

make build compiles with -trimpath and -s -w, as the model Dockerfile does.
script/version stays: the Makefile and the Dockerfile still use it.

The README describes both scripts as they now are.

Model: opus-5-5
This commit is contained in:
2026-10-05 23:28:36 +00:00
parent 46fe7baed0
commit ea40b31e50
4 changed files with 62 additions and 29 deletions
+4 -1
View File
@@ -48,8 +48,11 @@ fmt-check:
check:
@script/check
# -trimpath keeps the build directory out of the binary, so two builds of one
# commit match wherever they ran; -s -w drop the symbol table and debug info,
# which panics and stack traces do not use.
build: assets
go build -ldflags '$(strip -X main.version=$(VERSION) $(GO_LDFLAGS))' -o bin/webhooker ./cmd/webhooker
go build -trimpath -ldflags '$(strip -s -w -X main.version=$(VERSION) $(GO_LDFLAGS))' -o bin/webhooker ./cmd/webhooker
run: build
./bin/webhooker
+26 -15
View File
@@ -1095,7 +1095,8 @@ field), in the UI footer, and in the startup log line (`msg=starting`,
The value is stamped in at build time by the linker; it is not read from a file
at runtime, so it identifies the build itself.
`script/version` produces the value and both build paths use it:
`script/version` produces the value for `make build`, from
`git describe --tags --always --dirty`:
| Build | What it reports |
| ----------------------- | --------------------------------------------------- |
@@ -1118,7 +1119,8 @@ https://git.eeqj.de/sneak/upaas/issues/274. git in the build reads the checkout
whoever owns its files, since a context sent as a tar archive keeps the sender's
owners and git otherwise refuses a checkout owned by another user. A `VERSION`
build arg (`--build-arg VERSION=...`) takes precedence; `script/docker` (and so
`make docker`) passes the one `script/version` resolves on the host. The image
`make docker`) and `script/cibuild` always pass one, from the same
`git describe` run on the host, or `unknown` when that gives nothing. The image
build fails if its context carries `.git` and the version still comes out
`unknown`, which means git is missing from the build or could not read the
checkout.
@@ -1134,8 +1136,9 @@ to a commit.
`""`, and resolves the way an absent one does.
Nothing that varies between two builds of the same commit is stamped — no
timestamp, no hostname, no builder identity — so two builds of one commit still
produce a byte-identical binary.
timestamp, no hostname, no builder identity — and `make build` compiles with
`-trimpath`, which keeps the build directory out of the binary, so two builds of
one commit still produce a byte-identical binary wherever they ran.
### Backups contain secrets
@@ -1240,10 +1243,13 @@ We provide:
- `script/check` — run test, lint, fmt-check, and css-check
- `script/version` — output the version to stamp into the binary (see
[Version stamping](#version-stamping))
- `script/docker` — build the Docker image tagged via `script/projectname`,
passing `script/version`'s output in as the `VERSION` build arg
- `script/cibuild` — CI entrypoint: `docker build .` (the Dockerfile runs the
checks, so a green build implies a green repo)
- `script/docker` — build the Docker image tagged via `script/projectname`, with
`--no-cache` so no check is replayed from cache, passing the version
`git describe --tags --always --dirty` gives on the host (`unknown` if none)
in as the `VERSION` build arg
- `script/cibuild` — CI entrypoint: run `script/bootstrap` and `script/check` on
the host, then build the image exactly as `script/docker` does (the Dockerfile
runs the checks again, so a green build implies a green repo)
- `script/ci-mark-superseded` — CI helper: mark the commits whose run a newer
push cancelled (see [CI gate honesty](#ci-gate-honesty))
- `script/precommit` — pre-commit checks (`go mod tidy` guard, then
@@ -1251,6 +1257,11 @@ We provide:
- `script/install-precommit` — install the git pre-commit hook that runs
`script/precommit`
`script/docker` and `script/cibuild` are byte-identical copies of the shared
model scripts at
`https://git.eeqj.de/sneak/prompts/raw/branch/main/script/<name>`. A change to
either is made there and copied here, never made here alone.
## Third-party browser assets
The web UI serves one third-party script, Alpine.js, in its CSP build: the npm
@@ -3439,13 +3450,13 @@ The lint and builder stages use Debian rather than Alpine because
not compile against musl. Only the final binary is statically linked, which is
what lets it run on the Alpine runtime image.
`script/cibuild` — `docker build .` — is the CI gate: the checks run inside the
image, so a build that succeeds is a repo that is formatted, linted, tested and
compiled, with a current stylesheet. `script/lint` also uses Docker
(`Dockerfile.lint` and the `js-lint` stage, see Linting above), so `make lint`
and `make check` run the same pinned linter versions the gate does; of the steps
`make check` runs, only `script/test` and the `gofmt` check in
`script/fmt-check` run on the host.
`script/cibuild` is the CI gate: it runs `script/check`, then builds the image
with `--no-cache`, and the checks run again inside the image, so a build that
succeeds is a repo that is formatted, linted, tested and compiled, with a
current stylesheet. `script/lint` also uses Docker (`Dockerfile.lint` and the
`js-lint` stage, see Linting above), so `make lint` and `make check` run the
same pinned linter versions the gate does; of the steps `make check` runs, only
`script/test` and the `gofmt` check in `script/fmt-check` run on the host.
#### CI gate honesty
+20 -6
View File
@@ -1,15 +1,29 @@
#!/bin/sh
# script/cibuild: run the CI build. The Dockerfile runs the checks (the
# gofmt check, golangci-lint, the stylesheet check, ESLint, the Markdown
# check, make test), so a successful build implies a green repo. Generic:
# needs no adaptation. The Gitea workflow runs this on push.
# 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
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)"
ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
cd "$ROOT"
docker build .
"$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
# excludes .git, so `git describe` in a build stage yields an empty
# version without failing.
version="$(git describe --tags --always --dirty 2>/dev/null || true)"
[ -n "$version" ] || version="unknown"
docker build --no-cache \
--build-arg VERSION="$version" \
-t "$("$SCRIPT_DIR/projectname")" .
}
main "$@"
+12 -7
View File
@@ -1,10 +1,8 @@
#!/bin/sh
# script/docker: build the Docker image tagged with the project name.
# The tag comes from script/projectname.
#
# The version script/version resolves here goes in as the VERSION build
# arg, which takes precedence over what the build would derive from the
# .git in its context.
# Identical in all repos; the tag comes from script/projectname.
# --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)"
@@ -12,8 +10,15 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)"
main() {
cd "$ROOT"
docker build \
--build-arg VERSION="$("$SCRIPT_DIR/version")" \
# 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
# excludes .git, so `git describe` in a build stage yields an empty
# version without failing.
version="$(git describe --tags --always --dirty 2>/dev/null || true)"
[ -n "$version" ] || version="unknown"
docker build --no-cache \
--build-arg VERSION="$version" \
-t "$("$SCRIPT_DIR/projectname")" .
}