Skip to content

ci: publish rustunnel-server image to GHCR on release tags#91

Merged
joaoh82 merged 1 commit into
mainfrom
feat/ghcr-server-image
Jun 13, 2026
Merged

ci: publish rustunnel-server image to GHCR on release tags#91
joaoh82 merged 1 commit into
mainfrom
feat/ghcr-server-image

Conversation

@joaoh82

@joaoh82 joaoh82 commented Jun 13, 2026

Copy link
Copy Markdown
Owner

What

Wire up GHCR-driven container publishing so every stable vX.Y.Z tag builds deploy/Dockerfile and pushes ghcr.io/joaoh82/rustunnel-server:X.Y.Z + :latest (multi-arch: linux/amd64 + linux/arm64). Closes the gap where the README promised a Docker deployment but no published image existed anywhere.

Ref: TUNNEL-20 (surfaced from TUNNEL-14 § 0.3).

Changes

  • .github/workflows/release-docker.yml (new) — reusable workflow modeled on the existing publish-mcp-image.yml. QEMU + buildx multi-arch build, GHA layer cache, GITHUB_TOKEN auth (packages: write). Pushes the immutable :X.Y.Z always; :latest only moves for stable (hyphen-free) tags. workflow_dispatch lets you build an image for an already-cut tag.
  • .github/workflows/release.yml — new publish-server-image job (needs: [publish], gated !contains(github.ref_name, '-'), stable-only). Runs automatically on v* tag push.
  • deploy/Dockerfile — OCI labels on the runtime stage (source, description, licenses, version, revision, title, url, documentation). image.source links the GHCR package to this repo so the package page renders the README; version/revision come from build-args. Labels are also re-asserted at the build step so seeding the package from an older tag (whose Dockerfile predates these labels) still links correctly.
  • Makefilemake docker-push (multi-arch buildx --push) + REGISTRY_IMAGE var.
  • README.md — new "Pull the published image" section (docker pull / docker run); the build path is now "Quick reference (build from source)".

Verification

  • actionlint clean on both workflows (also shellchecks inline run scripts).
  • Full native build of the real deploy/Dockerfile → exit 0, 139 MB linux/arm64 image. OCI labels resolve correctly (image.source=https://github.com/joaoh82/rustunnel, version=0.8.1, real revision, AGPL-3.0). Entrypoint binary runs: rustunnel-server 0.8.1.
  • make -n docker-push / docker-build resolve correctly.

Manual follow-ups after merge

  1. Seed the packagev0.8.1 predates this workflow, so it won't auto-build. Run once: gh workflow run release-docker.yml -f tag=v0.8.1 (creates :0.8.1 + :latest).
  2. Set package visibility → public (package settings UI → Change visibility) so docker pull works without auth — same one-time step as the MCP image.
  3. Bump TUNNEL-14 web/seo/backlinks.md § 2 (Docker Hub / GHCR rows) in the rustunnel-web repo once 1–2 land.

🤖 Generated with Claude Code

Wire up multi-arch container publishing so every stable `vX.Y.Z` tag builds
`deploy/Dockerfile` and pushes `ghcr.io/joaoh82/rustunnel-server:X.Y.Z` plus
`:latest`. Closes the gap where the README promised a Docker deployment but no
published image existed anywhere (TUNNEL-20).

- New reusable workflow `release-docker.yml` (modeled on publish-mcp-image.yml):
  QEMU + buildx, linux/amd64 + linux/arm64, GHA layer cache, GITHUB_TOKEN auth.
  `:latest` only moves for stable tags; `workflow_dispatch` seeds an existing tag.
- Wire it into release.yml as `publish-server-image` (needs: publish, stable-only).
- Add OCI labels to deploy/Dockerfile runtime stage. image.source links the GHCR
  package to this repo so the package page renders the README; version/revision
  come from build-args. Labels are also re-asserted at the build step so seeding
  from an older tag still links correctly.
- Add `make docker-push` (multi-arch buildx --push) + REGISTRY_IMAGE var.
- README: new "Pull the published image" section (docker pull / docker run).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@joaoh82 joaoh82 merged commit 58590c0 into main Jun 13, 2026
1 check passed
pull Bot pushed a commit to Stars1233/rustunnel that referenced this pull request Jun 14, 2026
The multi-arch image landed in joaoh82#91 built linux/arm64 under QEMU emulation,
which compiles the full Rust server + Next.js dashboard on an emulated CPU —
~1hr+ cold, and slow on *every* release (the source changes each tag, so the
emulated compile re-runs and the layer cache can't save it).

Switch to the canonical native-runner pattern: build each arch on its own
native runner (amd64 on ubuntu-latest, arm64 on the free ubuntu-24.04-arm
runners), push each by digest, then a merge job stitches them into one
multi-arch manifest list tagged :X.Y.Z (+ :latest for stable tags). No
emulation — each arch compiles at native speed, in parallel.

- Per-arch GHA cache scopes so the two legs don't clobber each other.
- OCI labels on each arch image (build-args + labels) AND index-level
  annotations on the manifest in the merge job, so image.source links the
  GHCR package to the repo and the package page renders the README.
- :latest stays stable-only (hyphen tags are pre-releases).

release.yml's publish-server-image job is unchanged (same reusable-workflow
call + permissions).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.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.

1 participant