Skip to content

chore(ci): create the GitHub Release from the release workflow - #455

Merged
manusa merged 2 commits into
mainfrom
ci/create-github-release
Nov 1, 2026
Merged

manusa merged 2 commits into
mainfrom
ci/create-github-release

Conversation

@manusa

@manusa manusa commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Summary

release.yml publishes to Maven Central, but the GitHub Release has always been created by hand afterwards. That step was missed for v0.0.22. This PR has the workflow create it.

Workflow (.github/workflows/release.yml)

  • A new github-release job (needs: release) runs only after the Maven Central deployment succeeded:
    gh release create "$GITHUB_REF_NAME" --verify-tag --title "$GITHUB_REF_NAME" --generate-notes --latest
    This gives the same format as earlier releases: title = tag, auto-generated notes (PR list + compare link), marked latest.
  • Least privilege: only github-release gets permissions: contents: write. The release job, which runs the build and deploy tooling, is pinned to contents: read. The repo default is already read-only, so the token those steps see is unchanged.
  • github-release has no checkout. GH_REPO names the repository, and --verify-tag checks that the tag exists on the remote.
  • Re-run safe: because the jobs are separate, "Re-run failed jobs" retries only the GitHub Release and never redeploys to Central. If gh release view already finds a release for the tag, the job logs a notice and skips.
  • The tag comes from the runner's GITHUB_REF_NAME env var, not ${{ }} interpolation inside run:. GH_TOKEN is github.token.

Docs

  • AGENTS.md (CLAUDE.md is a symlink to it): "Releasing" step 4 describes the github-release job and the "Re-run failed jobs" retry, plus a fallback for creating the release by hand. The CI summary line mentions the job.
  • README.md: the "Create GitHub Release" section says the same, including the gh release create fallback one-liner.

Testing

PR CI does not run release.yml; the next v* tag push is the first real run. Until then, the job's logic can be checked against v0.0.22: gh release view v0.0.22 finds the existing release, so a run on that tag would take the skip branch.

release.yml only published to Maven Central, and the GitHub Release
had to be created by hand. That step was missed for v0.0.22. Add a
final step that creates it with gh after a successful deploy: title is
the tag, notes are generated, and it is marked latest, matching
earlier releases. It skips if the release already exists. Document the
automatic step and a manual fallback in AGENTS.md and the README.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Signed-off-by: Marc Nuri <[email protected]>

@manusa manusa left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Pull Request Review: #455

State: OPEN, not a draft, merge state UNKNOWN (not yet computed), head c2246b1

Summary

Adds a final Create GitHub Release step to .github/workflows/release.yml. After make maven-deploy succeeds, the step runs gh release create "$GITHUB_REF_NAME" --verify-tag --title "$GITHUB_REF_NAME" --generate-notes --latest, and skips if a release for the tag already exists. The job gets permissions: contents: write. AGENTS.md (Releasing step 4 and the CI line) and README.md ("Create GitHub Release") now describe the automatic step and a manual fallback.

Linked Issues and Tickets

None referenced, so spec compliance was checked against the description only. The diff does everything the description claims, and nothing else:

  • The step runs after Maven Release (.github/workflows/release.yml:45-54), so it only runs when the deploy succeeded.
  • The tag comes from the GITHUB_REF_NAME env var, not ${{ }} interpolation (release.yml:47-50).
  • GH_TOKEN and GH_REPO are set (release.yml:52-54).
  • The job-level contents: write is at release.yml:15-16.
  • The description says the token was read-only before. Confirmed: gh api repos/manusa/helm-java/actions/permissions/workflow returns "default_workflow_permissions":"read", so without this block gh release create would get a 403.
  • gh release list shows v0.0.22 exists (Latest), so the next run's --generate-notes compares against v0.0.22 as intended.

Review Verdict

COMMENT

Findings

Critical Issues (Must Fix)

None.

Important Issues (Should Fix)

  1. Put the release in its own job instead of raising the build job's token to contents: write (.github/workflows/release.yml:15-16, :45-54). Only the last step needs write, but job-level permissions applies to every step. Two consequences:

    • Least privilege. actions/checkout persists the job token for later git calls by default (persist-credentials: true). make build-all and make maven-deploy then run third-party code with a token that can now push to the repo: Go modules, the xgo containers, Maven plugins. Before this PR that token was read-only.
    • Re-runs. "Re-run failed jobs" re-runs the whole job. That's why AGENTS.md step 4 has to warn "Don't re-run the whole job just to get the release: that would also redeploy to Maven Central". With a separate job (needs: release, permissions: contents: write, just the gh step, no checkout needed since GH_REPO is set and --verify-tag checks the remote tag), a failed release step re-runs on its own without touching Central. The release job can then drop to contents: read (or keep no block and inherit the repo's read default). This also makes the existence check at :47 the real re-run guard it's meant to be, and the doc caveat becomes optional.

    If you keep a single job, consider persist-credentials: false on the checkout at :18-19 at least. Nothing after checkout runs git: build/release.mk:4-5 is just mvn -Prelease clean deploy.

Minor Comments (Nice to Have)

  1. The README fallback covers less than AGENTS.md (README.md:891-892 vs AGENTS.md Releasing step 4). The README fallback only covers "if that step fails". It leaves out the "deploy was finished by hand" case, the gh release create ... --generate-notes --latest one-liner, and the warning not to re-run the whole job (which would redeploy to Central). A short note in the README would keep the two in step. This goes away if finding 1 is adopted.
  2. gh release view ... > /dev/null 2>&1 (release.yml:47) treats any failure (auth, network) as "no release" and falls through to create. That's acceptable: create then fails loudly with the real error. Not worth changing, noted only so the behaviour is intentional.

Security Concerns

  • Good: the tag reaches the script via $GITHUB_REF_NAME env, quoted, not ${{ github.ref_name }} inside run:, so there's no expression injection (release.yml:47-50).
  • The elevated token's scope is the one concern (Important #1).

Testing Assessment

release.yml only runs on v* tag pushes (release.yml:3-6), so PR CI can't exercise it. The next release is the first real run. I didn't run anything: diff-reading review only. The PR is your own and touches CI config and AGENTS.md, so per policy I didn't build it, and a build doesn't exercise the workflow anyway. The step's logic is straightforward, and its gh flags match the documented gh release create options (--verify-tag, --title, --generate-notes, --latest).

Overall Assessment

A small, well-motivated change that closes a real gap (the missed v0.0.22 release), with correct injection-safe scripting and docs that match the workflow. My main suggestion is structural: move the step into its own job so the build doesn't get a write token and a failed release re-runs on its own. Mergeable as-is if you accept the wider token scope.

Comment thread .github/workflows/release.yml Outdated
name: Release
runs-on: ubuntu-latest
permissions:
contents: write # gh release create

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Important: this raises the token for every step in the job. actions/checkout persists it by default, so make build-all and make maven-deploy, which run third-party Go modules, xgo containers and Maven plugins, now hold a token that can push. The repo default is read (default_workflow_permissions: read).

Suggestion: move the release step to its own job:

  github-release:
    name: GitHub Release
    needs: release
    runs-on: ubuntu-latest
    permissions:
      contents: write
    steps:
      - name: Create GitHub Release
        run: |
          ...
        env:
          GH_TOKEN: ${{ github.token }}
          GH_REPO: ${{ github.repository }}

The release job then needs no write block. As a bonus, "Re-run failed jobs" re-runs only this job and never redeploys to Central.

Comment thread README.md Outdated

Once the release is published to Maven Central, create a new [GitHub release](https://github.com/manusa/helm-java/releases/new) for the released tag.
The `Release to Maven Central` workflow creates the GitHub release for the pushed tag, with generated release notes, after the Maven Central deployment succeeds.
If that step fails, create a new [GitHub release](https://github.com/manusa/helm-java/releases/new) for the released tag manually and use "Generate release notes".

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Minor: the fallback here only covers "if that step fails". AGENTS.md step 4 also covers the deploy being finished by hand, gives the gh release create v0.0.X --verify-tag --title v0.0.X --generate-notes --latest one-liner, and warns not to re-run the whole job (it would redeploy to Central). A short mention here would keep the two docs in step, unless the release moves to its own job, which makes the re-run warning moot.

Raising the release job to contents: write gave that write token to
every step, including the third-party build and deploy code run by
make build-all and make maven-deploy, through the credentials checkout
persists. Move the release creation into its own github-release job
(needs: release) that alone gets contents: write, and pin the build
job to contents: read. A failed GitHub Release can now be retried with
"Re-run failed jobs" without redeploying to Maven Central, so update
AGENTS.md and the README to match.

Co-authored-by: Claude Opus 5.5 <[email protected]>
Signed-off-by: Marc Nuri <[email protected]>

@manusa manusa left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Pull Request Review: #455 (re-review)

State: OPEN, not a draft, merge state CLEAN, head 5946803

This re-reviews the full current diff at 5946803. The previous review was at c2246b1.

Summary

The release now runs as two jobs in .github/workflows/release.yml:

  • release (build and Maven Central deploy) is pinned to contents: read.
  • A new github-release job (needs: release, contents: write, no checkout) runs gh release create "$GITHUB_REF_NAME" --verify-tag --title "$GITHUB_REF_NAME" --generate-notes --latest. It skips if the release already exists.

AGENTS.md (CI line, Releasing step 4) and README.md ("Create GitHub Release") describe the job, the "Re-run failed jobs" retry, and a manual fallback.

Earlier findings: status at 5946803

# Earlier finding Status Evidence
Important 1 Separate job / token scope Resolved See below.
Minor 1 README fallback narrower than AGENTS.md Resolved README.md:891-897 now matches AGENTS.md:78.
Minor 2 gh release view swallows every error Unchanged, as intended release.yml:54. No change was requested.

Important 1, separate job / token scope: resolved.

  • Write is scoped to one job. contents: write is now only on github-release (release.yml:49-50). The build/deploy job is explicitly contents: read (release.yml:15-16), so the token persisted by actions/checkout (release.yml:18-19) is read-only again. That matches the repo default, re-checked now: gh api repos/manusa/helm-java/actions/permissions/workflow returns "default_workflow_permissions":"read".
  • Re-runs no longer touch Central. The release is its own job (release.yml:45-61) with needs: release (release.yml:47). "Re-run failed jobs" retries a failed github-release on its own, without redeploying to Central.
  • The doc caveat is gone. The old "don't re-run the whole job" warning was replaced by the retry guidance (AGENTS.md:78, README.md:892).

Minor 1, README vs AGENTS.md fallback: resolved. README.md:891-897 and AGENTS.md:78 now cover the same things:

  • the automatic job;
  • the skip when the release already exists;
  • the "Re-run failed jobs" retry, with the note that it doesn't redeploy to Central;
  • the fallback when the re-run fails too or the deploy was finished by hand;
  • the gh release create ... --verify-tag --title ... --generate-notes --latest one-liner.

The README writes it as vX.Y.Z (README.md:896), matching the README's own vX.Y.Z convention in the section above. AGENTS.md uses v0.0.X.

Minor 2, gh release view error handling: unchanged, as intended. The check is the same at release.yml:54. The earlier note said it needed no change: a non-404 failure (auth, network) falls through to gh release create (release.yml:57), which then fails loudly with the real error. Nothing to do.

What the restructure introduced

I checked the new structure and found nothing new to fix:

  • Job dependency. needs: release (release.yml:47) means github-release only runs after the whole release job succeeds, including make maven-deploy (release.yml:39-44). If release fails, github-release is skipped. Re-running the failed release job also re-runs its dependent github-release, which is the right order.
  • Permissions on both jobs.
    • release (release.yml:15-16): job-level permissions sets every unlisted scope to none, so it gets contents: read and nothing else. Its steps need nothing more: checkout reads contents, the setup-go/setup-java caches use the runtime cache token rather than GITHUB_TOKEN scopes, and the deploy authenticates with the Central secrets.
    • github-release (release.yml:49-50): gh release create needs only contents: write.
  • No-checkout job. gh doesn't need a local git repo here:
    • GH_REPO (release.yml:61) names the repository, and GH_TOKEN is github.token (release.yml:60).
    • --verify-tag (release.yml:57) aborts if the tag isn't on the remote.
    • --generate-notes is computed server-side.
    • gh is preinstalled on GitHub-hosted ubuntu-latest runners (release.yml:48).
    • Without a checkout, no third-party code runs in the job that holds the write token.
  • Injection safety is unchanged. The tag still comes from the quoted $GITHUB_REF_NAME env var, not ${{ }} inside run: (release.yml:54-57).
  • The docs match the workflow.
    • AGENTS.md:68 names the github-release job.
    • AGENTS.md:78 correctly says it is needs: release and "the only job with contents: write" (release.yml:15-16 vs :49-50).
    • README.md:891 refers to the job by its display name GitHub Release (release.yml:46) and the workflow by its name Release to Maven Central (release.yml:1). Both are correct.
    • AGENTS.md:77 (step 3, "don't re-run blindly" after a partial deploy) and AGENTS.md:78 (step 4, "if only github-release failed") don't contradict each other: step 4's retry advice applies only when the deploy job succeeded.

Linked Issues and Tickets

None referenced, so spec compliance was checked against the replaced PR description only. The diff matches it:

  • the separate job with needs: release;
  • contents: write only on github-release, contents: read on release;
  • no checkout, GH_REPO plus --verify-tag;
  • the "Re-run failed jobs" behaviour;
  • the env-var tag;
  • the docs in both files.

There are no unrelated changes: the diff touches only .github/workflows/release.yml, AGENTS.md and README.md.

Review Verdict

COMMENT

Findings

Critical Issues (Must Fix)

None.

Important Issues (Should Fix)

None. The earlier Important finding is resolved (see above).

Minor Comments (Nice to Have)

None new. Minor 1 is resolved. Minor 2 needed no change.

Security Concerns

None. The write-scoped token now lives only in a job that runs one gh command and no third-party code (release.yml:45-61). The build/deploy job is read-only (release.yml:15-16).

Testing Assessment

release.yml only runs on v* tag pushes (release.yml:3-6), so PR CI can't exercise it. The next release is the first real run. This is a diff-reading review only: nothing was built or run. The only commands were read-only GETs: the repo's default workflow permissions and the PR data. The skip branch can be predicted against v0.0.22, which already exists as a release.

Overall Assessment

All earlier findings are addressed. The release is now least-privilege, re-runs are safe, and the docs agree with each other and with the workflow. Ready to merge from my side.

@manusa
manusa merged commit 8697197 into main Nov 1, 2026
3 checks passed
@manusa
manusa deleted the ci/create-github-release branch November 1, 2026 17:54
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