ci: add Docker Hub + GHCR publish workflow on release#204
Conversation
Wires up automatic container image publishing to both Docker Hub
(`bluet/proxybroker2`) and GitHub Container Registry
(`ghcr.io/bluet/proxybroker2`) on every GitHub release. Mirrors the
trigger model of `python-publish.yml` so a single release event
ships PyPI, Docker Hub, and GHCR artifacts together.
Choices and rationale:
- **Multi-arch (amd64 + arm64)**: Apple Silicon and AWS Graviton
users were previously stuck with QEMU-emulated amd64 images.
Native arm64 builds via `docker/setup-qemu-action` +
`docker/setup-buildx-action`.
- **Push to both registries simultaneously**: GHCR is free, has
no rate limits for our scale, and uses the built-in GITHUB_TOKEN
(no extra secret to manage). Docker Hub remains the primary
endpoint per README. `docker/build-push-action` writes both in
one pass - no double build cost.
- **Tag strategy via `docker/metadata-action`**:
- `:VERSION` always (e.g. `:2.0.0b2`).
- `:major`, `:major.minor`, `:latest` ONLY on stable releases
(`!github.event.release.prerelease`). A beta release won't
silently advance the `:latest` tag - users pinning `:latest`
stay on the last stable.
- `workflow_dispatch` runs get a `:manual-<timestamp>` tag so
they don't collide with release tags.
- **GHA cache backend**: layer cache survives between runs so
reruns on the same SHA finish in seconds.
- **`workflow_dispatch` with ref input**: lets a maintainer re-run
after a transient registry failure or token rotation without
cutting a fresh release.
Required repo secrets (one-time setup before first run):
DOCKERHUB_USERNAME - Docker Hub account/org name
DOCKERHUB_TOKEN - Hub access token, scoped to proxybroker2
GHCR needs no setup; uses the built-in GITHUB_TOKEN.
Manual trigger for the just-published v2.0.0b2 (after secrets land):
gh workflow run docker-publish.yml \
--repo bluet/proxybroker2 --ref master \
--field ref=v2.0.0b2
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
|
Note Gemini is unable to generate a review for this pull request due to the file types involved not being currently supported. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
🚧 Files skipped from review as they are similar to previous changes (1)
WalkthroughAdds a GitHub Actions workflow that builds and publishes multi-architecture Docker images to Docker Hub and GHCR on release or manual dispatch, and adds a Dependabot configuration to monthly-update GitHub Actions dependencies. ChangesDocker Image Publication Workflow
Dependabot GitHub Actions Config
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
No IssuesNo application code changes were detected to scan. The PR contains only configuration files, documentation, or other non-code files. fossabot analyzed this PR using SAST security analysis (changed files only). |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/docker-publish.yml:
- Around line 75-79: Add a fallback tag rule to preserve non-SemVer release tags
by inserting a raw ref/tag passthrough before the semver rules: add a token like
type=ref,event=tag (or equivalent raw-preserve rule) immediately before the
existing type=semver entries so tags such as 2.0.0b2 are emitted unchanged;
update the tag-generation block that currently contains the type=semver lines
(the four tag lines shown) to prepend the ref passthrough rule so non-SemVer
tags are not skipped by the semver parser.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 14cc828e-c27f-4b7e-b4f4-b31ba842c4bf
📒 Files selected for processing (1)
.github/workflows/docker-publish.yml
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 42aeed7bc0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Original guard was over-conservative: `:latest`, `:major`, and `:major.minor` only fired on STABLE releases. Problem: this project has been in beta for over a year. Locking those tags to stable releases means users pulling `:latest` would be stuck on a year-old beta (`v2.0.0b1`, May 2025) until 2.0.0 ships - which defeats the purpose of having a floating `:latest`. Drop the prerelease guard on `:latest` / `:major` / `:major.minor`. A user pulling `:2` wants the most recent 2.x.y; a user pulling `:latest` wants the most recent published version. Both meanings include prereleases. `:latest` keeps a narrower guard (`event_name == 'release'`) so manual-trigger reruns don't accidentally move it; that guard is about source of truth, not stability. Acknowledged side-effect documented in the comment block: if we ever release a patch on an old version line (e.g. 1.5.1 after 2.0.0), `:latest` would move backward. Not maintaining old lines today; switch to `flavor: latest=auto` if that changes. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
No IssuesNo application code changes were detected to scan. The PR contains only configuration files, documentation, or other non-code files. fossabot analyzed this PR using SAST security analysis (changed files only). |
…ly bumps SonarCloud Quality Gate failed on PR #204 with 6 hotspots from rule `githubactions:S7637` ("Use full commit SHA hash for this dependency") on the new docker-publish.yml. Tag-pinned actions (`@v3`, `@v5`) are mutable - the publisher can re-point them, and a compromised publisher account would let an attacker swap in a malicious commit. The reviewdog/action-setup compromise (March 2025) is a recent example of why this is a real concern, not just a theoretical one. The project's existing `python-publish.yml` already SHA-pins its one external action; bringing docker-publish.yml in line with that convention. Resolved each tag to its commit SHA via the GitHub API; kept the human-readable tag in a trailing comment so the version is still scannable in code review. Adding `.github/dependabot.yml` to keep the SHA-pinned actions from silently rotting: - Bumps grouped into ONE PR per month (avoids the per-action-per-release flood that put me off SHA-pinning in the past). - Security advisories fire IMMEDIATELY regardless of the monthly schedule - that path is internal to Dependabot and not affected by `interval`. So a CVE in one of these actions becomes a same-day PR, not a 30-day-old one. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
No IssuesNo application code changes were detected to scan. The PR contains only configuration files, documentation, or other non-code files. fossabot analyzed this PR using SAST security analysis (changed files only). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 149645c8e5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
There was a problem hiding this comment.
♻️ Duplicate comments (1)
.github/workflows/docker-publish.yml (1)
85-87:⚠️ Potential issue | 🟠 Major | ⚡ Quick winNon-SemVer release tags can be dropped by
type=semverrulesLine 85–87 still rely only on
type=semver; if a release tag isv2.0.0b2(non-SemVer), metadata generation can skip expected version-derived tags. Add a tag passthrough fallback before semver rules.Suggested minimal fix
tags: | + type=ref,event=tag type=semver,pattern={{version}} type=semver,pattern={{major}}.{{minor}} type=semver,pattern={{major}} type=raw,value=latest,enable=${{ github.event_name == 'release' }} type=raw,value=manual-{{date 'YYYYMMDDHHmmss'}},enable=${{ github.event_name == 'workflow_dispatch' }}For docker/metadata-action at commit c299e40c65443455700f0fdfc63efafe5b349051 (v5), does `type=semver` parse release tags like `v2.0.0b2`? If not, what is the recommended fallback to preserve exact tag names?🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.github/workflows/docker-publish.yml around lines 85 - 87, Add a passthrough raw-tag rule before the existing semver rules so non-SemVer release tags (e.g., v2.0.0b2) are preserved; specifically, insert a rule like "type=raw,pattern={{version}}" before the lines that reference "type=semver,pattern={{version}}", "type=semver,pattern={{major}}.{{minor}}", and "type=semver,pattern={{major}}" so the action emits the exact tag if it cannot be parsed by the semver rules.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Duplicate comments:
In @.github/workflows/docker-publish.yml:
- Around line 85-87: Add a passthrough raw-tag rule before the existing semver
rules so non-SemVer release tags (e.g., v2.0.0b2) are preserved; specifically,
insert a rule like "type=raw,pattern={{version}}" before the lines that
reference "type=semver,pattern={{version}}",
"type=semver,pattern={{major}}.{{minor}}", and "type=semver,pattern={{major}}"
so the action emits the exact tag if it cannot be parsed by the semver rules.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 844cc87b-32d2-4bd5-b26e-ff727db305f5
📒 Files selected for processing (2)
.github/dependabot.yml.github/workflows/docker-publish.yml
PR #204 review surfaced three honest bugs in the workflow that all would have made the v2.0.0b2 publish silently fail or produce wrong tags. Fixing them per reviewer guidance. (a) PEP 440 vs SemVer: this project tags releases in PEP 440 form (`v2.0.0b2`), not SemVer (`v2.0.0-beta.2`). docker/metadata-action's `type=semver,pattern={{version}}` only parses SemVer and would silently SKIP `v2.0.0b2`. Switch to `type=pep440,pattern={{version}}` which is documented for Python prerelease forms (`b2`, `rc1`, etc.). (b) Floating aliases for prereleases: metadata-action's `{{major}}` and `{{major}}.{{minor}}` patterns deliberately hold back floating aliases for prerelease versions - they emit the full prerelease version instead of the major/minor truncation. We explicitly want these aliases to advance for prereleases (per the existing comment block: this project has been in beta for a year; locking floating tags to stable would freeze them on `v2.0.0b1` from May 2025). Use `type=match` with regex against the git tag name instead; `type=match` doesn't apply the prerelease guard, so floating aliases advance correctly. (c) workflow_dispatch + metadata-action ref mismatch: previously the workflow took an `inputs.ref` parameter and passed it to checkout. But docker/metadata-action reads GITHUB_REF (which stays at the workflow file's branch = master for workflow_dispatch), not the checkout's ref. So a manual rerun with `--field ref=v2.0.0b2` would only generate `:manual-<timestamp>`, not the version tags - silently mis-publish. Fix by removing the `inputs.ref` input entirely. workflow_dispatch now always builds from master HEAD with a `:manual-<timestamp>` tag - useful for testing the workflow itself. To publish a specific tagged release, re-fire the release event by deleting + recreating the GitHub release. Documented in the trigger comment block. Net effect: the v2.0.0b2 retroactive publish (delete + recreate GitHub release) will correctly produce: - bluet/proxybroker2:2.0.0b2 (exact pin, type=pep440) - bluet/proxybroker2:2.0 (major.minor, type=match) - bluet/proxybroker2:2 (major, type=match) - bluet/proxybroker2:latest (type=raw + release event guard) - same five on ghcr.io/bluet/proxybroker2 Refs: - CodeRabbit comment on docker-publish.yml:89 (PEP 440 vs SemVer) - chatgpt-codex on docker-publish.yml:33 (workflow_dispatch ref) - chatgpt-codex on docker-publish.yml:85 (PEP 440 parsing) - chatgpt-codex on docker-publish.yml:87 (floating aliases for prereleases) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
No IssuesNo application code changes were detected to scan. The PR contains only configuration files, documentation, or other non-code files. fossabot analyzed this PR using SAST security analysis (changed files only). |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e37ac28fe
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Codex caught a real correctness bug in my previous comment block. I
claimed `workflow_dispatch` cannot publish version tags - that was
wrong. GitHub documents `GITHUB_REF` for `workflow_dispatch` as the
ref the workflow was dispatched with, so:
- workflow_dispatch with `--ref v2.0.0b2` sets GITHUB_REF to
`refs/tags/v2.0.0b2`, and `type=pep440,pattern={{version}}` DOES
match - the version tag would be published.
- For STABLE pep440 tags, metadata-action's default
`flavor: latest=auto` would also auto-add `:latest`, contradicting
my "manual runs never move :latest" claim.
Two fixes:
(a) Add `flavor: latest=false` to disable the auto-`:latest`
behavior. We control `:latest` explicitly via the type=raw rule
with `enable=${{ event_name == 'release' }}`, so only release
events ever advance it - not manual rebuilds.
(b) Rewrite the trigger comment block to honestly describe what
workflow_dispatch DOES. The capability is actually useful: a
maintainer can do a one-shot retroactive rebuild of a tagged
release via `gh workflow run ... --ref v...` and get JUST the
version tag pushed (no floating-alias advancement). That's the
safer path for retroactive publishes than re-firing the release
event (which would advance every floating tag).
Net effect on the v2.0.0b2 retroactive publish:
- Path A: delete + recreate v2.0.0b2 release
Publishes :2.0.0b2 + :2.0 + :2 + :latest (full set).
- Path B: gh workflow run docker-publish.yml --ref v2.0.0b2
Publishes :2.0.0b2 only. No floating-tag movement. Lower
blast radius if you only need the version pin.
Both are valid; user picks based on intent.
Refs Codex review on docker-publish.yml:96.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
No IssuesNo application code changes were detected to scan. The PR contains only configuration files, documentation, or other non-code files. fossabot analyzed this PR using SAST security analysis (changed files only). |
|
Two related fixes uncovered while debugging the v2.0.0b2 Docker
publish:
(a) GHCR 403 Forbidden on first push:
Per the official GitHub Container Registry docs, the workflow's
GITHUB_TOKEN cannot push to a brand-new ghcr.io package unless
the image is connected to the source repo via the OCI label
`org.opencontainers.image.source`. Add the label to the
Dockerfile pointing at the proxybroker2 GitHub repo. Quote
from the docs (linked in the inline comment):
"the GITHUB_TOKEN will not have permission to push the package
if you have previously pushed a package to the same namespace,
but have not connected the package to the repository ... we
recommend adding the label org.opencontainers.image.source
to your Dockerfile."
This is the actual root cause - I had previously guessed it was
a workflow-permissions setting, which the maintainer correctly
refuted (the setting was already "Read and write").
(b) Node.js 20 deprecation:
GitHub announced that Node.js 20 actions will be forced to
Node 24 starting 2026-06-02, with Node 20 fully removed
2026-09-16. Bump every action used across all four workflows
to its latest major (which uses Node 24):
actions/checkout v4 -> v6.0.2
actions/setup-python v5 -> v6.2.0
actions/upload-artifact v4 -> v7.0.1
docker/setup-qemu-action v3 -> v4.0.0
docker/setup-buildx-action v3 -> v4.0.0
docker/login-action v3 -> v4.1.0
docker/metadata-action v5 -> v6.0.0
docker/build-push-action v5 -> v7.1.0
snok/install-poetry v1.4.1 (already latest, just SHA-pin)
All bumps are SHA-pinned with a trailing tag comment for
readability. Dependabot is configured to bundle future bumps
into a single monthly PR (added in PR bluet#204).
Files changed:
.github/workflows/docker-publish.yml
.github/workflows/python-publish.yml
.github/workflows/python-test-versions.yml
.github/workflows/py-to-exe.yml
.github/workflows/claude.yml (checkout only - leaves the beta
anthropics/claude-code-action floating tag alone, since
beta tracking is intentional)
After this lands:
- GHCR push works (no more 403 on ghcr.io/bluet/proxybroker2)
- All workflows survive past 2026-06-02 / 2026-09-16
- SHA-pinning posture is consistent across the entire CI surface
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>



Summary
.github/workflows/docker-publish.yml. Triggers onrelease: published(same hook as PyPI publish) and onworkflow_dispatchfor manual reruns.bluet/proxybroker2(Docker Hub) andghcr.io/bluet/proxybroker2(GHCR) in one build.Tag policy
:VERSION(:2.0.0b2):major(:2):major.minor(:2.0):latest:manual-{timestamp}Why floating tags include prereleases: This project has been in beta for over a year. If
:latestonly moved on stable releases, users pulling:latestwould be stuck onv2.0.0b1(May 2025) until 2.0.0 ships. A user pulling:2wants the most recent 2.x.y, regardless of pre-release status.Acknowledged side-effect: if we ever release a patch on an old version line (e.g. 1.5.1 after 2.0.0),
:latestwould move backward. Not a concern today since this project doesn't maintain old version lines. Documented in the workflow comment.One-time setup before this can publish
✅
DOCKERHUB_USERNAME— set✅
DOCKERHUB_TOKEN— setGHCR needs no setup — uses the built-in
GITHUB_TOKENand thepackages: writepermission declared in the workflow.After merge — publishing v2.0.0b2 to Docker
docker/metadata-actionreadsGITHUB_REFfor semver detection. Forworkflow_dispatch,GITHUB_REFstays atmaster(where the workflow file lives), so semver-based tags won't generate from the user-suppliedrefinput — only:manual-{timestamp}would land.Recommended path: delete and re-create the GitHub release. This fires a
release: publishedevent withGITHUB_REF=refs/tags/v2.0.0b2, and metadata-action generates all the semver tags correctly:That re-fires both
python-publish.yml(already failed; would still fail on the stale token unless that's fixed too) anddocker-publish.yml(will succeed once this PR is merged + secrets are in place).Alternative: trigger workflow_dispatch and accept the limited tag set:
This only pushes
:manual-{timestamp}(and not the version tags). Useful for build smoke-tests but doesn't replace a real publish.Test plan
DOCKERHUB_USERNAME+DOCKERHUB_TOKENin repo secrets (✅ done)bluet/proxybroker2:2.0.0b2,:2.0,:2,:latestall on https://hub.docker.com/r/bluet/proxybroker2/tagsghcr.io/bluet/proxybroker2on https://github.com/bluet/proxybroker2/pkgs/container/proxybroker2docker run --rm bluet/proxybroker2:2.0.0b2 --helpanddocker run --rm bluet/proxybroker2:latest --help🤖 Generated with Claude Code
Summary by CodeRabbit