Skip to content

ci: build the image on every pull request - #6

Merged
sajonaro merged 1 commit into
mainfrom
ci-image-build-check
Aug 24, 2026
Merged

ci: build the image on every pull request#6
sajonaro merged 1 commit into
mainfrom
ci-image-build-check

Conversation

@sajonaro

Copy link
Copy Markdown
Contributor

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 COPY per library, and that list is deliberately the check that writ and writ-mcp are 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.yml fires only on a v* tag or by hand, so the release was the first thing to look.

The job

image-check.yml builds on every pull request and pushes nothing.

A second workflow, not a job in image.yml — that one holds packages: write so 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 the COPY list it guards — a hand-kept enumeration of what matters — and this drift arrived in tooling/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 RUN in the Dockerfile's second stage, so producing the image is the test — no registry, no login, no load.

One check moved

git presence was checked in image.yml as a post-push step. It is a property of the image, not of the registry, so it moves into the Dockerfile's smoke RUN, where make image and this new job check it too — not just the publish.

What stays in image.yml is the one check only a publish can make: the tag pulls back down and runs.

Verification

Built locally three ways:

build result
as committed image builds, all smoke checks pass, writ 0.1.0
git install deleted fails: no git: writ compare --git would not run
tooling/sql COPY removed fails: Error: 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-check run below is the job doing its job.

🤖 Generated with Claude Code

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>
@sajonaro
sajonaro merged commit 390a527 into main Aug 24, 2026
2 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Aug 24, 2026
@sajonaro
sajonaro deleted the ci-image-build-check branch August 24, 2026 09:55
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant