Skip to content

feat(ci): publish a pre-built box image to GHCR on release tags - #272

Open
danielrmerskine wants to merge 1 commit into
mainfrom
de/pr263-workflow
Open

feat(ci): publish a pre-built box image to GHCR on release tags#272
danielrmerskine wants to merge 1 commit into
mainfrom
de/pr263-workflow

Conversation

@danielrmerskine

Copy link
Copy Markdown
Collaborator

Summary

Adds a Release: Publish Box Image workflow. On every v* tag it builds
box/lager/docker/box.Dockerfile and pushes ghcr.io/lagerdata/lager-box:vX.Y.Z
(plus a bare :X.Y.Z alias). Also dispatchable against an existing tag, so images can
be backfilled for releases that predate it.

Authored by @juul-charles-w. This is the publisher half of #263; the client half is
deliberately not here — see below.

Nothing consumes the image, and no box behavior changes. lager update still
builds locally on every path.

Why only half

#263 also taught lager update to docker pull the published image instead of
building. That half is held back for four reasons:

  1. It cannot be tested in the order it was written. A pull cannot be exercised
    until real images exist on the registry, and images cannot exist until this
    workflow is on main. Landing both together ships the consuming half completely
    unexercised.
  2. The buildx preflight blocks the path that does not need it.
    _docker_supports_buildkit() hard-exits before Step 8 when a box's Docker is >= 23
    without buildx. A docker pull needs no buildx — and a box that cannot build is
    exactly the box a pre-built image exists for.
  3. A bare docker pull is a box-down risk on arm64. This workflow builds
    linux/amd64 only. Without an explicit --platform, Docker on an arm64 host can
    pull the amd64 image anyway, tag it lager, and leave start_box.sh failing with
    exec format error — worse than the local build it replaces, which at least
    produces something that runs. The deploy tooling already branches for
    aarch64|arm64|armv7l (cli/deployment/scripts/setup_and_deploy_box.sh).
  4. It changes what the box trusts, from source it checked out and hashed itself to
    a registry blob run privileged with raw /dev and /sys/bus/usb access — pulled by
    mutable tag, with no digest pin or signature check. Worth an explicit decision
    rather than arriving as a side effect.

Two smaller items for that follow-up: store_build_hash is called unconditionally with
the local tree's hash, asserting "this image was built from this tree" when it came
from a registry; and the build-failure hint strings are docker build-specific, so
pull failures (auth, manifest unknown, disk) get none.

Changes from the original

  • Tag resolution moved into step env. ${{ inputs.tag }} was interpolated directly
    into a run block, where ${{ }} is substituted into the script text before any
    shell parses it — so a crafted workflow_dispatch input would execute rather than
    compare, and the vX.Y.Z case guard below runs too late to help. This job holds
    packages: write against the registry the fleet would later pull from. (zizmor
    template-injection, high confidence / high severity — the only thing making
    static-checks red on feat(update): pull pre-built box images from GHCR on release tags #263.)
  • Header comment no longer references _try_pull_box_image, which does not exist in
    any version of this change.
  • Docs describe what actually ships: an image that is published and not yet consumed.

Test plan

  • zizmor --no-online-audits --min-severity low clean
  • actionlint clean
  • COVERAGE.md counts match (no test changes in this PR)
  • After merge: workflow_dispatch against an existing tag and confirm the image
    appears on GHCR
  • Make the package public, once, by hand — until then even a deliberate pull fails
  • Confirm an anonymous docker pull works from a host with no GHCR credentials

A red publish does not block a PyPI upload, since no box depends on the image.

Add a Release: Publish Box Image workflow that fires on every v* tag, builds
box/lager/docker/box.Dockerfile, and pushes ghcr.io/lagerdata/lager-box:vX.Y.Z
plus a bare :X.Y.Z alias. Also dispatchable against an existing tag, so images
can be backfilled for releases that predate it.

Nothing consumes the image yet, and no box behavior changes here. The client
half -- teaching `lager update` to docker pull a release instead of building
on the box -- is deliberately not in this commit. It cannot be tested until
real images exist on a registry to pull, and it has open questions that should
not ride along unexamined: the buildx preflight hard-exits before a path that
needs no buildx, and a bare `docker pull` of an amd64-only image onto an arm64
box tags something that cannot execute, which is worse than the local build it
replaces. Publishing first makes both testable against something real while
leaving the fleet untouched.

The GHCR package must be made public once, by hand, before any box could pull
anonymously. Until that happens a pull would fail even if a client asked for
one -- another reason this half is safe to land alone.

Resolve the tag through step env instead of interpolating github.event_name
and inputs.tag directly into the run block. `${{ }}` is substituted into the
script text before a shell ever parses it, so a crafted workflow_dispatch
input would execute rather than compare, and the vX.Y.Z `case` guard below it
runs too late to help. This job holds packages:write against the registry the
fleet would later pull from (zizmor: template-injection, high confidence,
high severity).

Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants