ci: build the image on every pull request - #6
Merged
Conversation
Dockerfile stage 1 copies a hand-written list of directories rather than the repository, and that list is deliberately the check that `writ` and `writ-mcp` are a small closed set. Its cost is a failure mode: a library added to a dune file and not to the list builds everywhere except in the image. `writ_sql` did that, and since image.yml fires only on a v* tag or by hand, the publish was the first thing to notice. image-check.yml builds on every pull request and pushes nothing. It is a second workflow rather than a job in image.yml because that one holds `packages: write` to reach GHCR, and nothing triggered by a pull request should be able to publish. No `paths:` filter on the trigger, on purpose: a paths filter is the same kind of hand-kept enumeration as the COPY list it guards, and this drift arrived in tooling/cli/dune, so a filter naming the obvious files would have skipped the very commit that broke the image. Every pull request builds; the buildx cache is what keeps that cheap enough that nobody adds the filter back. The job builds and stops, because every check worth running is already a RUN in the Dockerfile's second stage. git was the exception — it lived in image.yml as a post-push step — so it moves into that RUN, where it is checked by `make image` and the new job as well as by the publish. What stays in image.yml is the one check only a publish can make: that the tag pulls back down and runs. Verified by building all three ways locally: the image builds and smokes clean; deleting the git install fails the build with "no git: writ compare --git would not run"; and removing the tooling/sql COPY reproduces "Library "writ_sql" not found", which is the regression this job exists to catch. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #5. That bug —
Library "writ_sql" not found— was invisible until the publish, because nothing builds the image before one.Why it was invisible
Dockerfile stage 1 copies a hand-written list of directories, one
COPYper library, and that list is deliberately the check thatwritandwrit-mcpare a small closed set. The cost of that design is a failure mode: a library added to a dune file and not to the list builds everywhere except in the image.image.ymlfires only on av*tag or by hand, so the release was the first thing to look.The job
image-check.ymlbuilds on every pull request and pushes nothing.A second workflow, not a job in
image.yml— that one holdspackages: writeso it can reach GHCR, and nothing triggered by a pull request should be able to publish. Two files, two permission sets.No
paths:filter, on purpose. A paths filter is the same kind of object as theCOPYlist it guards — a hand-kept enumeration of what matters — and this drift arrived intooling/cli/dune, not in the Dockerfile. A filter naming the obvious files would have skipped the very commit that broke the image. So every PR builds, and the buildx cache is what keeps that cheap enough that nobody is tempted to add the filter back.It builds and stops. Every check worth running is already a
RUNin the Dockerfile's second stage, so producing the image is the test — no registry, no login, noload.One check moved
gitpresence was checked inimage.ymlas a post-push step. It is a property of the image, not of the registry, so it moves into the Dockerfile's smokeRUN, wheremake imageand this new job check it too — not just the publish.What stays in
image.ymlis the one check only a publish can make: the tag pulls back down and runs.Verification
Built locally three ways:
writ 0.1.0no git: writ compare --git would not runtooling/sqlCOPY removedError: Library "writ_sql" not found.The third is the regression this job exists to catch, reproduced on demand.
This PR is its own first test — the
image-checkrun below is the job doing its job.🤖 Generated with Claude Code