Skip to content

feat(template): harden the optional Docker Hub publish channel - #51

Merged
hasansezertasan merged 3 commits into
mainfrom
feat/shelled-cairnsmore
Jul 5, 2026
Merged

feat(template): harden the optional Docker Hub publish channel#51
hasansezertasan merged 3 commits into
mainfrom
feat/shelled-cairnsmore

Conversation

@hasansezertasan

@hasansezertasan hasansezertasan commented Jul 5, 2026

Copy link
Copy Markdown
Owner

Summary

Hardens the optional Docker Hub publish channel in the generated release-please.yml (rendered when include_web is enabled) and brings the documentation on both sides of the template up to date.

Workflow

  • Preflight job gating all publishing: a new docker-publish-preflight job (permissions: {}) validates the DOCKERHUB_USERNAME/DOCKERHUB_TOKEN secret pair up front. Both pypi-publish and docker-publish depend on it, so a half-configured pair (exactly one secret set) fails fast with an actionable ::error:: before the first irreversible upload — previously the failure surfaced as a cryptic registry auth error deep inside docker-publish, and worse, pypi-publish could complete concurrently, stranding a partial release. Recovery: fix the secrets → Re-run failed jobs.
  • Least-privilege secret handling: the token is surfaced into env only as a DOCKERHUB_TOKEN_SET presence flag, never as its value.
  • Visible skip: when neither secret is set, a ::notice:: annotation records that the run publishes to GHCR only, instead of leaving only a grey skipped step.
  • Namespace bug fix: the Docker Hub image name was docker.io/${{ github.repository }}, which assumes the Docker Hub username equals the GitHub owner; the push is denied whenever they differ. The image name is now built from the secret: docker.io/<DOCKERHUB_USERNAME>/<repo-name>.

Behavior matrix: both secrets → GHCR + Docker Hub; neither → GHCR with a notice; exactly one → loud failure, nothing published anywhere.

Documentation

  • Generated CONTRIBUTING.md: new optional step 6 in the one-time repository-setup section documenting the all-or-nothing secret pair, with gh secret set commands and the required token scope.
  • Template README.md: the Docker Hub section was stale — it linked to the retired hub.docker.com/settings/security token page and predated the fail-fast behavior. Rewritten as a step-by-step walkthrough matching the PyPI trusted-publishing section (current token URL, Read & Write least-privilege scope, expiry, both secrets, resulting image path).

Test plan

  • Rendered the template with include_web=trueactionlint passes; pypi-publish and docker-publish both gate on docker-publish-preflight
  • Rendered with .example-input.yml defaults (web disabled) — actionlint passes; zero Docker leftovers in workflow, README, or CONTRIBUTING
  • prek run hooks pass on the changed files (pre-existing MD060 errors in docs/adr/*.md are unrelated — introduced by the markdownlint-cli2 v0.22.1 bump on main)

Harden the optional Docker Hub publish channel in the generated
release-please workflow:

- Fail the docker-publish job immediately with an actionable error when
  exactly one of DOCKERHUB_USERNAME/DOCKERHUB_TOKEN is set, instead of
  letting docker/login-action fail cryptically after QEMU/Buildx setup.
  The token is surfaced into env only as a presence flag, never its value.
- Emit a ::notice:: annotation when neither secret is set, so the
  GHCR-only behavior is visible in the run summary rather than only a
  skipped step.
- Fix the Docker Hub image namespace: it was built from
  github.repository, which silently assumes the Docker Hub username
  equals the GitHub owner and fails the push when it does not. Build it
  from DOCKERHUB_USERNAME instead.
- Document the secret pair as an optional step 6 in the generated
  CONTRIBUTING.md repository-setup section (rendered when include_web).
@hasansezertasan hasansezertasan added the no-issue Bypass the linked-issue requirement for PRs that need no issue label Jul 5, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Template changes harden the optional Docker Hub publishing path in the generated release workflow, adding explicit preflight gating and clearer behavior, fixing the Docker Hub namespace construction, and documenting the all-or-nothing secret configuration in CONTRIBUTING.md.

File-Level Changes

Change Details Files
Add explicit preflight logic to the docker-publish job to fail fast on half-configured Docker Hub secrets and visibly note GHCR-only publishing when Docker Hub is not configured.
  • Expose DOCKERHUB_USERNAME via env for gating Docker Hub login and add a DOCKERHUB_TOKEN_SET env flag that indicates token presence without surfacing its value.
  • Add a dedicated step that fails the job early with an ::error:: message when exactly one of DOCKERHUB_USERNAME or DOCKERHUB_TOKEN is set.
  • Add a notice step that emits an ::notice:: annotation when DOCKERHUB_USERNAME is empty, clarifying that the run will publish to GHCR only.
template/.github/workflows/release-please.yml.jinja
Fix Docker Hub image naming to derive the namespace from the Docker Hub username instead of assuming it matches the GitHub repository owner.
  • Update docker/metadata-action images configuration to build the Docker Hub image as docker.io/<DOCKERHUB_USERNAME>/ using env.DOCKERHUB_USERNAME and github.event.repository.name.
  • Retain GHCR publishing configuration while making Docker Hub channel explicitly opt-in by emitting an empty line when DOCKERHUB_USERNAME is unset.
template/.github/workflows/release-please.yml.jinja
Document optional Docker Hub publishing setup in CONTRIBUTING, including the all-or-nothing secret pair and required token scope.
  • Add an include_web-gated section describing that GHCR publishing is always enabled and Docker Hub is optional.
  • Provide gh secret set commands for DOCKERHUB_USERNAME and DOCKERHUB_TOKEN and describe their all-or-nothing behavior and failure/notice outcomes.
  • Clarify that the Docker Hub image is pushed to docker.io/<DOCKERHUB_USERNAME>/ and that the namespace follows the Docker Hub account rather than the GitHub owner.
template/CONTRIBUTING.md.jinja

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • The if condition for failing on half-configured Docker Hub credentials ((env.DOCKERHUB_USERNAME != '') != (env.DOCKERHUB_TOKEN_SET == 'true')) is correct but non-obvious; consider rewriting it as a clearer XOR or splitting into two explicit checks for readability and easier maintenance.
  • Because DOCKERHUB_TOKEN_SET is derived from secrets.DOCKERHUB_TOKEN != '', it might be worth explicitly handling the case where the secret is missing vs. present-but-empty to avoid future confusion about how an empty token string is treated.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `if` condition for failing on half-configured Docker Hub credentials (`(env.DOCKERHUB_USERNAME != '') != (env.DOCKERHUB_TOKEN_SET == 'true')`) is correct but non-obvious; consider rewriting it as a clearer XOR or splitting into two explicit checks for readability and easier maintenance.
- Because `DOCKERHUB_TOKEN_SET` is derived from `secrets.DOCKERHUB_TOKEN != ''`, it might be worth explicitly handling the case where the secret is missing vs. present-but-empty to avoid future confusion about how an empty token string is treated.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a4d4fc1b6

ℹ️ 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".

Comment thread template/.github/workflows/release-please.yml.jinja
The README's Docker Hub section predated the workflow hardening: it
linked to the retired hub.docker.com/settings/security token page, did
not name the Read & Write scope, and still described username-only
configuration as sufficient. Rewrite it as a step-by-step walkthrough
matching the PyPI trusted-publishing section, covering the all-or-nothing
secret pair and the namespace following the Docker Hub account.
Address review: the half-configured-secrets check ran inside
docker-publish, but pypi-publish fans out from build concurrently, so
the irreversible PyPI upload could complete before the check blocked
finalize-release, stranding a partial release. Hoist the check into a
dedicated docker-publish-preflight job (permissions: {}) that both
pypi-publish and docker-publish depend on, so an inconsistent secret
pair blocks every publish channel before anything is uploaded.
@hasansezertasan hasansezertasan changed the title feat(template): fail fast on half-configured Docker Hub secrets feat(template): harden the optional Docker Hub publish channel Jul 5, 2026
@hasansezertasan
hasansezertasan merged commit f48250e into main Jul 5, 2026
11 checks passed
@hasansezertasan
hasansezertasan deleted the feat/shelled-cairnsmore branch July 5, 2026 02:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-issue Bypass the linked-issue requirement for PRs that need no issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant