ci: build the image on pull requests #142

Merged
coilysiren merged 3 commits from fix/pr-image-build into main 2026-08-12 11:00:10 +00:00
Member

Closes #91 and #129: the image now builds on every pull request, verified against real logs.

Forgejo 16 serves the Actions log endpoints that returned 404 before, which is what made this diagnosable at all.

What the historic failure was

Run 17155, job 26996, the one #91 could not read:

Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

#91 hypothesis 1 was right that the job container never saw a usable DOCKER_HOST, and hypothesis 2 would not have worked as written. The runner container has DOCKER_HOST=tcp://localhost:2375, but that is its own loopback and a job container has a different one. A probe confirmed: agentic-os:release carries a docker client at 28.5.2, no socket is mounted, DOCKER_HOST is unset, and the daemon answers on the job container default gateway because dockerd listens on 0.0.0.0:2375 in the runner pod.

What landed

scripts/ci-image-build.sh derives the daemon address from /proc/net/route, falls back to the historic bridge and the socket, and verifies each candidate with a version call before use. No push, no registry credential, and the deploy runner stays the only publisher.

First run on this pull request: building against tcp://172.18.0.1:2375, all 35 Dockerfile steps including the compose stage clone, image build succeeded. The derived address was used, not the hardcoded fallback.

Proof it catches a fault

Reproduced the first of the two faults #129 describes, an input present in the repository but missing from the stage, by removing the compose stage COPY of the expander binary.

  • the static guard from #140 passes, since the class is invisible to it
  • the image build fails, exit code 127 at Dockerfile line 33

Run locally against the same Dockerfile and reverted. The two checks are complementary rather than overlapping.

The probe is kept as a failure-only diagnostic step, so the next red run reports what it can see without needing a commit to ask.

Closes #91 and #129: the image now builds on every pull request, verified against real logs. Forgejo 16 serves the Actions log endpoints that returned 404 before, which is what made this diagnosable at all. ## What the historic failure was Run 17155, job 26996, the one #91 could not read: ``` Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? ``` #91 hypothesis 1 was right that the job container never saw a usable DOCKER_HOST, and hypothesis 2 would not have worked as written. The runner container has DOCKER_HOST=tcp://localhost:2375, but that is its own loopback and a job container has a different one. A probe confirmed: agentic-os:release carries a docker client at 28.5.2, no socket is mounted, DOCKER_HOST is unset, and the daemon answers on the job container default gateway because dockerd listens on 0.0.0.0:2375 in the runner pod. ## What landed scripts/ci-image-build.sh derives the daemon address from /proc/net/route, falls back to the historic bridge and the socket, and verifies each candidate with a version call before use. No push, no registry credential, and the deploy runner stays the only publisher. First run on this pull request: `building against tcp://172.18.0.1:2375`, all 35 Dockerfile steps including the compose stage clone, `image build succeeded`. The derived address was used, not the hardcoded fallback. ## Proof it catches a fault Reproduced the first of the two faults #129 describes, an input present in the repository but missing from the stage, by removing the compose stage COPY of the expander binary. * the static guard from #140 passes, since the class is invisible to it * the image build fails, exit code 127 at Dockerfile line 33 Run locally against the same Dockerfile and reverted. The two checks are complementary rather than overlapping. The probe is kept as a failure-only diagnostic step, so the next red run reports what it can see without needing a commit to ask.
Temporary. #91 could not diagnose its build job because this Forgejo returned
404 for the Actions log endpoints. Forgejo 16 serves them, so the job can now
report what it sees instead of failing blind.

The runner manifest sets DOCKER_HOST to tcp://localhost:2375 on the runner
container, with dind as a sidecar in the same pod. Inside a job container that
address is the job container's own localhost, which is the likeliest reason the
first attempt failed. This prints the env, the client, the socket, the routes,
and each candidate daemon address so the real job can be written from facts.

Every step is continue-on-error, so the probe cannot fail the pull request.

Refs #91, #129

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
ci: move the probe body into a tracked script
All checks were successful
ci / test (pull_request) Successful in 55s
ci / publish-echo-image (pull_request) Has been skipped
ci / image-build-probe (pull_request) Successful in 3s
136b4f4f4d
The previous commit put the probe in a YAML block scalar, which the
actions-run-one-line hook rejects. Same content, invoked as one line, plus the
checkout the probe needs to reach it.

Refs #91, #129

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
ci: build the image on pull requests
All checks were successful
ci / test (pull_request) Successful in 54s
ci / publish-echo-image (pull_request) Has been skipped
ci / image-build (pull_request) Successful in 23s
2b600895c5
The probe answered every open question from #91, which could not be answered
before because this Forgejo returned 404 for the Actions log endpoints.

What the job container actually sees: agentic-os:release carries a docker
client at 28.5.2, no socket is mounted, and DOCKER_HOST is unset. The historic
failure in run 17155 was therefore the client falling back to
unix:///var/run/docker.sock and finding nothing, which the log now states in
one line.

The runner's own DOCKER_HOST of tcp://localhost:2375 would not have helped. That
is the runner container's loopback, and a job container has its own. dockerd
listens on 0.0.0.0:2375 in the runner pod, so the daemon answers on the job
container's default gateway. The probe reached 28.5.2 there.

The script derives that address from /proc/net/route rather than hardcoding it,
falls back to the historic bridge and the socket, and verifies each candidate
with a version call before use. A renumbered bridge changes nothing. When no
candidate answers it says so and points at the probe rather than failing with
docker's default socket message.

The build carries no push, no registry credential, and a tag nothing ships. The
deploy runner remains the only thing that publishes.

The probe stays as a failure-only diagnostic step, so the next person to see
this job go red gets the same evidence without adding a commit for it.

Refs #91, #129

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-gaming/sirens-echo!142
No description provided.