Skip to content

ci: publish multi-arch container image to GHCR on release#70

Merged
deveshctl merged 3 commits into
mainfrom
feat/ghcr-image-publishing
Jul 7, 2026
Merged

ci: publish multi-arch container image to GHCR on release#70
deveshctl merged 3 commits into
mainfrom
feat/ghcr-image-publishing

Conversation

@deveshctl

Copy link
Copy Markdown
Owner

Summary

Adds a ghcr job to the release workflow that packages the same GoReleaser-built Linux binaries into a multi-arch OCI image (linux/amd64 + linux/arm64) and pushes it to ghcr.io/deveshctl/layerx on every tagged release.

The image reuses the exact binary produced by GoReleaser, so layerx --version is bit-identical across brew, scoop, deb, rpm, direct-download, and the container image.

What's in the change

  • Dockerfile — distroless-static:nonroot base, exec-form ENTRYPOINT, uid 65532, WORKDIR /home/nonroot. Copies the platform-specific binary out of dist/ (GoReleaser's layout) rather than building Go inside the image.
  • .github/workflows/release.yml — new ghcr job. Uploads the Linux binaries as a workflow artifact from the goreleaser job, downloads them, builds and pushes with docker/build-push-action and QEMU for arm64. Tags: latest (non-prerelease only), vX.Y.Z, vX.Y, vX. All action SHAs pinned. packages: write is scoped to this job only.
  • workflow_dispatch trigger — added so the Dockerfile + buildx wiring can be dry-run on a feature branch before cutting a real tag. On dispatch, GoReleaser runs --snapshot --skip=publish --skip=sign, the SLSA provenance job is skipped, and the image is built with push: false.
  • README — new "Container image" section with docker run / podman run recipes for Docker on Linux (with the --group-add \$DOCKER_GID note for the distroless base), Docker Desktop, rootless Podman, and archive mode. New FAQ entry on when to reach for the container vs. the native binary.
  • docs/releasing.md — new file documenting the tag → release flow, the first-time GHCR "flip to public" step, and the workflow_dispatch dry-run procedure.
  • CHANGELOG.md[Unreleased] entry.

Trade-offs called out in the docs

  • Container image needs the engine's socket bind-mounted (or a bind-mounted archive); it's not usable in a fully-sandboxed environment.
  • Docker on Linux needs --group-add \$DOCKER_GID because the distroless base has no /etc/group for name resolution.
  • File extraction (x key) writes into the container FS unless the user bind-mounts an output dir.

Notes

  • GHCR packages default to private. After the first successful push, the maintainer flips visibility to public from the package page. docs/releasing.md documents this.
  • No changes to the Go source or any user-visible CLI behaviour.

deveshctl added 2 commits July 6, 2026 18:23
Every tagged release now publishes a linux/amd64 + linux/arm64 image to
ghcr.io/deveshctl/layerx alongside the existing archives, deb/rpm
packages, Homebrew tap, and Scoop bucket. The image reuses the exact
binary produced by GoReleaser so `layerx --version` stays identical
across every install path.

- Dockerfile: multi-stage build on gcr.io/distroless/static-debian12
  :nonroot. Runs as uid 65532, WORKDIR /home/nonroot, exec-form
  ENTRYPOINT so signals reach the TUI cleanly. Per-arch binary picked
  from dist/layerx_linux_<arch>*/layerx via buildx TARGETARCH.
- release.yml: new ghcr job (needs: goreleaser) that downloads the
  goreleaser-built binaries as a workflow artifact, sets up QEMU +
  Buildx, logs in to ghcr.io with GITHUB_TOKEN, computes tags/labels
  via docker/metadata-action, and pushes via docker/build-push-action
  with GHA cache. Tag scheme: latest, vX.Y.Z, vX.Y, vX (pre-release
  tags skip latest + the major/minor rollups). workflow_dispatch
  trigger runs the full build with push: false for dry-run testing.
- README: new 'Container image' install section with Docker, Podman,
  and archive-mode examples, plus the --group-add note for reaching
  Docker's root:docker socket from the nonroot user inside a
  distroless image (which has no /etc/group to resolve the name).
- docs/releasing.md: new file documenting the release flow and the
  one-time public-visibility flip required after the first GHCR
  publish.
docs/releasing.md is a maintainer-only runbook — its contents (cutting a
tag, flipping GHCR visibility, dry-running the workflow) are only
actionable by someone with push access to this repo, so shipping it in
the public tree and linking to it from the user-facing README sends
readers to a page that is not for them.

Remove the file and the README link. Keep the one-sentence user-visible
note about the first-release private-GHCR behaviour in the README, but
drop the dead link to the removed doc. Update the workflow comment that
pointed at the deleted file to reference the gh CLI command directly.

CONTRIBUTING.md still contains a reference to docs/releasing.md; that
link predates this PR and points at a file that also did not exist on
main before this change, so it is a pre-existing broken link and left
untouched here.
@deveshctl deveshctl force-pushed the feat/ghcr-image-publishing branch from 5d18e50 to 35c317d Compare July 6, 2026 12:55
distroless/static ships no terminfo database. Without TERM set, termenv
(used by lipgloss v2) sees a dumb terminal and disables all colour output.
COLORTERM=truecolor is required to activate 24-bit hex colour codes —
without it, the palette defined in tui/styles.go gets quantized or dropped.
@deveshctl deveshctl merged commit 53bb576 into main Jul 7, 2026
24 checks passed
@deveshctl deveshctl mentioned this pull request Jul 8, 2026
3 tasks
@deveshctl deveshctl deleted the feat/ghcr-image-publishing branch July 9, 2026 08:12
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.

1 participant