Skip to content

Build the container image in PR CI, not for the first time at release #258

Description

@BryanFRD

Every product repository builds its image on release and workflow_dispatch only:

Repo Triggers
FerrVault-Cloud, FerrTrack-Cloud, FerrGrowth-Cloud, FerrFleet-Cloud, FerrLens-Cloud, FerrGames-Cloud, FerrLabs-Cloud, LFSX release, workflow_dispatch

So the Dockerfile is exercised for the first time after a version has been tagged, its changelog written, its crate published and its chart pushed with an appVersion naming an image that may not exist. The release does not roll back when the image build fails — it just leaves a hole.

This is not hypothetical. Recent release-time image failures:

  • LFSX: 0.21.0, 0.22.0, 0.23.0, 0.23.1, 0.24.0 — five consecutive releases, no image. GHCR stops at 0.20.0. Cause: one export VAR="$(cat ...)" tripping hadolint's SC2155 at --failure-threshold warning. A lint error, invisible for five versions.
  • FerrTrack-Cloud: api@v2.14.2 failed, latest Docker run failed
  • FerrGrowth-Cloud: app@v4.21.0 failed
  • FerrGames-Cloud: gameserver@v2026.8.3 failed
  • FerrLabs-Cloud: bff@v3.2.0 and auth@v6.1.0 failed

The consequence is not limited to a missing artefact. Homelab pinned the LFSX chart to 0.23.1, the pod could not pull ghcr.io/ferrlabs/lfsx:0.23.1, and the service was down for seventeen minutes until Flux gave up and rolled back — for a version that had been "released" hours earlier.

What to build

Make the image prove itself on the pull request that changes it.

  • An enable-docker-build input on the language reusables (reusable-ci-rust.yml first, since every API is Rust), calling reusable-docker-build.yml with push: false
  • Single platform (linux/amd64) — the release keeps the multi-arch matrix. The point is proving the file, not the fan-out
  • Path-filtered by the caller so it runs on PRs touching Dockerfile or the build workflow, not on every PR
  • hadolint runs in that job the same way it does on release, since a lint failure is what actually cost the five LFSX releases
  • The smoke test stays on the release path: duplicating the script per caller is one more thing to drift

LFSX already carries a hand-rolled version of this (FerrLabs/LFSX#84) and should drop it once the shared one lands.

Related

The same investigation turned up a syntax error in reusable-docker-build.yml that breaks every caller pinned past 30a2f74, fixed in #257 with a bash -n guard over every run block. That one is about the reusable being broken; this issue is about product repositories not finding out until it is too late.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions