diff --git a/Dockerfile b/Dockerfile index 82c49a1..7bf54d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,6 +11,14 @@ COPY . . # Touch .pb.go so make does not try to regenerate via protoc (file is committed) RUN touch mfer/mf.pb.go +# Cache buster. script/cibuild passes a fresh CHECK_EPOCH on every invocation, +# which invalidates every layer below this line so the checks always really +# execute. Layers ABOVE it (base image, go mod download) keep their cache, so +# this costs nothing but the checks themselves. Every stage that runs a check +# needs its own copy: ARG is scoped per stage, and a stage without one silently +# serves a cached pass. See https://git.eeqj.de/sneak/mfer/issues/89. +ARG CHECK_EPOCH + # Go half of fmt-check only: this image has no node, so no prettier. The # markdown half runs in the mdfmt stage below. RUN make fmt-check-go @@ -27,6 +35,9 @@ RUN yarn install --frozen-lockfile COPY . . +# Cache buster, so the check below always executes; see the lint stage. +ARG CHECK_EPOCH + # No make in this image; call the script entrypoint directly. RUN script/prettier --check @@ -47,6 +58,9 @@ COPY . . # Touch .pb.go so make does not try to regenerate via protoc (file is committed) RUN touch mfer/mf.pb.go +# Cache buster, so the check below always executes; see the lint stage. +ARG CHECK_EPOCH + RUN make test RUN cd cmd/mfer && go build -tags urfave_cli_no_docs -o /mfer . diff --git a/README.md b/README.md index ec05d47..17d1846 100644 --- a/README.md +++ b/README.md @@ -23,8 +23,12 @@ javascript library is planned. # Build Status -CI runs via `script/cibuild` (`docker build .`), which executes `make check` -(formatting, linting, tests). The `main` branch must always be green. +CI runs via `script/cibuild`, which builds the Dockerfile. Every stage that runs +a check declares an `ARG CHECK_EPOCH` above it, and `script/cibuild` passes a +fresh value on every invocation, so the check layers cannot be served from the +Docker layer cache. A successful build therefore implies that formatting, +linting, and tests actually ran and passed on the current tree. The `main` +branch must always be green. # Entrypoints diff --git a/script/cibuild b/script/cibuild index 3da5857..d5ae6a6 100755 --- a/script/cibuild +++ b/script/cibuild @@ -6,9 +6,14 @@ set -eu ROOT="$(cd "$(dirname "$0")/.." && pwd -P)" +# A bare "docker build ." serves the check layers straight from the Docker +# layer cache when the tree has not changed, so the build exits 0 without ever +# running the checks. CHECK_EPOCH changes on every invocation and the +# Dockerfile declares it above the checks in every stage that runs one, which +# forces them to execute while leaving the dependency layers cached. main() { cd "$ROOT" - docker build . + docker build --build-arg CHECK_EPOCH="$(date +%s)" . } main "$@"