diff --git a/README.md b/README.md index 3991bfc..169f3c7 100644 --- a/README.md +++ b/README.md @@ -123,9 +123,10 @@ alpine. We provide: - `script/check` — run all checks: `test`, `lint`, `fmt-check` (our own extension) - `script/docker` — build the Docker image, tagged via `script/projectname` - (byte-identical across repos) -- `script/cibuild` — cd to the repo root and `docker build .` (what CI runs; the - image build runs `script/check`) + (byte-identical across repos); builds with `--no-cache`, like `script/cibuild` +- `script/cibuild` — cd to the repo root and `docker build --no-cache .` (what + CI runs; the image build runs `script/check`, and `--no-cache` is what stops + Docker serving those checks from cache on an unchanged tree) - `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 diff --git a/TODO.md b/TODO.md index fac919c..e0f9b9a 100644 --- a/TODO.md +++ b/TODO.md @@ -21,6 +21,11 @@ fmt-check, and commit. # Completed Steps +- 2026-09-08: Closed the false green in the canonical CI gate: `script/cibuild` + and `script/docker` now build with `--no-cache`, so the Dockerfile's check + 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-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 diff --git a/prompts/CODE_STYLEGUIDE_GO.md b/prompts/CODE_STYLEGUIDE_GO.md index ce2b903..39c4957 100644 --- a/prompts/CODE_STYLEGUIDE_GO.md +++ b/prompts/CODE_STYLEGUIDE_GO.md @@ -1,6 +1,6 @@ --- title: Code Styleguide — Go -last_modified: 2026-03-18 +last_modified: 2026-09-08 --- 1. Try to hard wrap long lines at 77 characters or less. @@ -101,9 +101,12 @@ last_modified: 2026-03-18 `golangci-lint`. 1. Write a `Dockerfile` for every repo, even if it only runs the tests and - linting. `docker build .` should always make sure that the code is in an - able-to-be-compiled state, linted, and any tests run. The Docker build - should fail if linting doesn't pass. + linting. `script/cibuild` and `script/docker` should always make sure that + the code is in an able-to-be-compiled state, linted, and any tests run, and + the build should fail if linting doesn't pass. Go through those scripts + rather than a bare `docker build .`: they pass `--no-cache`, without which + an unchanged tree serves the check layers from cache and the build reports a + green it never ran. 1. Every repo must have a `Makefile`. See [Repository Policies](https://git.eeqj.de/sneak/prompts/raw/branch/main/prompts/REPO_POLICIES.md) diff --git a/prompts/EXISTING_REPO_CHECKLIST.md b/prompts/EXISTING_REPO_CHECKLIST.md index 53be732..037cda7 100644 --- a/prompts/EXISTING_REPO_CHECKLIST.md +++ b/prompts/EXISTING_REPO_CHECKLIST.md @@ -1,6 +1,6 @@ --- title: Existing Repo Checklist -last_modified: 2026-07-06 +last_modified: 2026-09-08 --- Use this checklist when beginning work in a repo that may not yet conform to our @@ -30,9 +30,10 @@ with your task. - [ ] `.editorconfig` exists — fetch from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.editorconfig` - [ ] `Dockerfile` and `.dockerignore` exist; Dockerfile runs `make check` as a - build step — fetch `.dockerignore` from + build step, and `script/cibuild` and `script/docker` build it with + `--no-cache` — fetch `.dockerignore` from `https://git.eeqj.de/sneak/prompts/raw/branch/main/.dockerignore` -- [ ] Gitea Actions workflow in `.gitea/workflows/` runs `docker build .` on +- [ ] Gitea Actions workflow in `.gitea/workflows/` runs `script/cibuild` on push — reference `https://git.eeqj.de/sneak/prompts/raw/branch/main/.gitea/workflows/check.yml` - [ ] Language-specific config: @@ -106,5 +107,6 @@ with your task. # Final - [ ] `make check` passes -- [ ] `docker build` succeeds +- [ ] `script/cibuild` succeeds and demonstrably executed the checks — a + sub-second build, or `CACHED` on a check layer, means nothing ran - [ ] Commit and merge fixes before starting your actual task diff --git a/prompts/NEW_REPO_CHECKLIST.md b/prompts/NEW_REPO_CHECKLIST.md index 5f46b40..6f3dc8b 100644 --- a/prompts/NEW_REPO_CHECKLIST.md +++ b/prompts/NEW_REPO_CHECKLIST.md @@ -1,6 +1,6 @@ --- title: New Repo Checklist -last_modified: 2026-07-06 +last_modified: 2026-09-08 --- Use this checklist when creating a new repository from scratch. Follow the steps @@ -90,8 +90,11 @@ are thin shims calling them. Model scripts: - [ ] `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) -- [ ] `script/cibuild` — cd to repo root, `docker build .` (what CI runs) + `script/projectname` (byte-identical across repos); passes `--no-cache` + like `script/cibuild` +- [ ] `script/cibuild` — cd to repo root, `docker build --no-cache .` (what CI + runs; without `--no-cache` an unchanged tree serves the check layers from + cache and the build reports a green it never ran) - [ ] `script/precommit` — called by the pre-commit hook; runs `script/check` - [ ] `script/install-precommit` — installs the pre-commit hook that runs `script/precommit` @@ -103,6 +106,8 @@ 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 check layer, means nothing ran - [ ] No secrets in repo - [ ] No mutable image/package references - [ ] No unnecessary files in repo root diff --git a/prompts/REPO_POLICIES.md b/prompts/REPO_POLICIES.md index c83afe7..0ce4531 100644 --- a/prompts/REPO_POLICIES.md +++ b/prompts/REPO_POLICIES.md @@ -1,6 +1,6 @@ --- title: Repository Policies -last_modified: 2026-08-19 +last_modified: 2026-09-08 --- This document covers repository structure, tooling, and workflow standards. Code @@ -60,10 +60,10 @@ style conventions are in separate documents: prerequisite since nvm requires bash. yarn is then pinned via `corepack prepare yarn@ --activate`. Never install "latest" or "lts"; always exact versions. `script/cibuild` runs the CI build: it changes to the - repo root and runs `docker build .`; 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`; `script/precommit` is - what the git pre-commit hook runs, and it calls `script/check`; + repo root and runs `docker build --no-cache .`; 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`; `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 name. Scripts that need the name call @@ -99,6 +99,15 @@ style conventions are in separate documents: `yarn.lock`, `go.mod` + `go.sum`, etc.) before running it so the bootstrap layer stays cached until dependencies change. +- **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/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. Never invalidate by pruning — + `docker builder prune` and friends destroy a build cache shared with every + other build on the host. + - **Dockerfiles must use a separate lint stage for fail-fast feedback.** Go repos use a multistage build where linting runs in an independent stage based on the `golangci/golangci-lint` image (pinned by hash). This stage runs @@ -167,9 +176,10 @@ style conventions are in separate documents: artifacts or heavier dependencies. - Every repo should have a Gitea Actions workflow (`.gitea/workflows/`) that - runs `script/cibuild` (which runs `docker build .`) on push. Since the - Dockerfile already runs `make check`, a successful build implies all checks - pass. + runs `script/cibuild` (which runs `docker build --no-cache .`) on push. Since + the Dockerfile already runs `make check`, a successful build implies all + checks pass — an implication that holds only because of the `--no-cache` + above. - Use platform-standard formatters: `black` for Python, `prettier` for JS/CSS/Markdown/HTML, `go fmt` for Go. Always use default configuration with diff --git a/script/cibuild b/script/cibuild index 75cc3e6..51d3b80 100755 --- a/script/cibuild +++ b/script/cibuild @@ -1,13 +1,14 @@ #!/bin/sh -# script/cibuild: run the CI build. The Dockerfile runs script/check, so -# a successful build implies all checks pass. +# script/cibuild: run the CI build. --no-cache because the checks are +# RUN steps: on an unchanged tree Docker serves them from cache and the +# build exits 0 having run nothing. set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" main() { cd "$ROOT" - docker build . + docker build --no-cache . } main "$@" diff --git a/script/docker b/script/docker index 9b9ea86..f72ee15 100755 --- a/script/docker +++ b/script/docker @@ -1,6 +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 for the same reason as script/cibuild: a cached check layer +# is a check that did not run. set -eu SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd -P)" @@ -8,7 +10,7 @@ ROOT="$(cd "$SCRIPT_DIR/.." && pwd -P)" main() { cd "$ROOT" - docker build -t "$("$SCRIPT_DIR/projectname")" . + docker build --no-cache -t "$("$SCRIPT_DIR/projectname")" . } main "$@"