Skip to content

ci: use draft releases for immutable GitHub releases support#102

Merged
keelerm84 merged 10 commits into
mainfrom
devin/1774991618-immutable-releases
Apr 6, 2026
Merged

ci: use draft releases for immutable GitHub releases support#102
keelerm84 merged 10 commits into
mainfrom
devin/1774991618-immutable-releases

Conversation

@keelerm84
Copy link
Copy Markdown
Member

@keelerm84 keelerm84 commented Mar 31, 2026

Requirements

  • I have added test coverage for new or changed functionality
  • I have followed the repository's pull request submission guidelines
  • I have validated my changes against all supported platform versions

N/A — CI-only changes, no application code or tests affected.

Related issues

Supports the org-wide migration to immutable GitHub releases. Reference implementation: launchdarkly/ld-relay (branch v8).

Describe the solution you've provided

The .github/actions/publish-gh-pages composite action runs gh release upload to attach the packaged Helm chart .tgz to the GitHub release. Under immutable releases, this fails because the release is already published when the upload runs. This repo therefore needs the draft release pattern: create a draft release, upload all artifacts, then publish.

Changes across four files:

  1. release-please-config.json — Added "draft": true so release-please creates draft (unpublished) releases, and "force-tag-creation": true for forward compatibility once release-please supports it natively.

  2. release-please.yml — Implemented the split release-please pattern:

    • First pass (skip-github-pull-request: true): Only creates releases (no PRs). If a release is created, checks out the repo and creates/pushes the git tag inline (idempotent — skips if tag already exists).
    • Second pass (skip-github-release: true, conditional): Only runs if no release was created; creates/updates PRs. This ordering ensures the tag exists before release-please evaluates whether a release PR is still needed.
    • release-package job is gated on release_created == 'true' at the job level (per-step if guards removed). Checks out the tag directly via ref: ${{ needs.release-please.outputs.tag_name }} and passes it to publish-ghcr.
    • New publish-release job un-drafts the release after release-package completes, also gated on release_created.
    • Action pinned to googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 (v4.4.0).
  3. manual-publish.yml — Added:

    • tag input (required) — references an existing draft release. Checkout uses ref: ${{ inputs.tag }} so the built chart version matches the release. Tag is also forwarded to publish-ghcr.
    • publish_release input (boolean, default true) — controls whether the release is un-drafted after artifact uploads.
    • publish-release job gated on format('{0}', inputs.publish_release) == 'true' && format('{0}', inputs.dry_run) == 'false' for boolean/string safety across workflow_dispatch and workflow_call triggers.
  4. .github/actions/publish-ghcr/action.yml — Added optional ref input. When provided, the composite's internal checkout uses the specified ref; otherwise falls back to github.sha (the exact triggering commit). This ensures the GHCR artifact is always built from the same ref as the gh-pages artifact.

Updates since last revision

  • Consistent release_created usage: Switched all gates from releases_created (plural) to release_created (singular) since this repo only releases one package. Removed the unused releases_created output.
  • Tag passed directly to composite actions: Both release-please.yml and manual-publish.yml now explicitly pass the tag to the publish-ghcr action via its new ref input, and the release-package job checks out the tag directly. This eliminates reliance on github.ref (branch HEAD) which could drift if another commit lands between job start and checkout.
  • Fixed publish-ghcr fallback: Changed the default checkout fallback from github.ref to github.sha to preserve the original behavior of checking out the exact triggering commit rather than the branch HEAD.
  • Fixed stale permission comments: Updated contents: write comments from "Contents and pull-requests are for release-please to make releases" to "Needed for publish-gh-pages to upload release assets" in both workflow files.

Describe alternatives you've considered

An earlier revision assumed this repo was attestation-only (no release asset uploads), which would not require draft releases. This was incorrect — publish-gh-pages uploads the chart tarball as a release asset.

Additional context

Key review points:

  • Verify publish-release in release-please.yml waits on all artifact-uploading jobs. Currently needs: ['release-please', 'release-package'] — confirm release-package is the only job that uploads to the release.
  • The split release-please ordering: releases first, then tags, then PRs. Verify the if condition on the second call correctly prevents duplicate release PRs.
  • Consistent use of release_created (singular) across all job gates.
  • publish-gh-pages action uses gh release upload ${{ steps.version.outputs.CHART_VERSION }} — the version comes from helm show chart . on the checked-out code. In the automated workflow, this now reads from the tag ref. In the manual workflow, it reads from inputs.tag. Confirm both paths produce the correct version.
  • Pre-existing: publish-gh-pages action line 57 uses inline ${{ steps.version.outputs.* }} in a run: block. Low risk since the value comes from helm show chart, but worth noting for a future hardening pass.
  • force-tag-creation has no effect with the current release-please version — it is a forward-compatibility placeholder. Confirm it does not cause errors.

Link to Devin session: https://app.devin.ai/sessions/7d5bda4d9dbe4ae0b950b30a50485e60
Requested by: @keelerm84


Note

Medium Risk
Medium risk because it restructures the release automation (tag creation, artifact publishing, and release publication) and could break or mis-sequence releases if conditions/refs are wrong.

Overview
Switches the release pipeline to a draft-release model so Helm chart artifacts can be uploaded before the GitHub release is published.

release-please is split into a release-creation pass (then idempotent tag creation) and a PR-creation-only pass; downstream packaging now checks out and builds from the created tag, publishes to GH Pages/GHCR, and then a new job un-drafts the release via gh release edit.

Manual publishing now requires a tag (checks out that ref), forwards it into publish-ghcr, and can optionally publish (un-draft) the release after uploads. The publish-ghcr composite action is updated to accept a required ref and checkout that ref before building/pushing.

Reviewed by Cursor Bugbot for commit fe926a9. Bugbot is set up for automated code reviews on this repo. Configure here.

@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@devin-ai-integration devin-ai-integration Bot added the devin-pr PR created by Devin label Mar 31, 2026
Since actions/attest@v4 stores attestations via GitHub's attestation API
(not as release assets), repos that only use attestation don't need draft
releases. Release-please can publish the release directly.

Changes:
- Remove draft:true from release-please-config.json
- Remove create-tag job/steps (force-tag-creation handles this)
- Remove publish-release job (release is published directly)
- Remove publish_release input from manual workflows
@devin-ai-integration devin-ai-integration Bot changed the title ci: use draft releases to support immutable GitHub releases ci: add force-tag-creation and restructure release workflow Mar 31, 2026
force-tag-creation only operates in conjunction with draft releases.
Since this repo does not use draft releases (attestation-only, no
artifact uploads to the release), force-tag-creation is not needed.
@devin-ai-integration devin-ai-integration Bot changed the title ci: add force-tag-creation and restructure release workflow ci: restructure release workflow into separate jobs Mar 31, 2026
@kinyoklion kinyoklion marked this pull request as ready for review April 6, 2026 15:33
@kinyoklion kinyoklion requested a review from a team as a code owner April 6, 2026 15:33
@kinyoklion
Copy link
Copy Markdown
Member

Devin, the pages publish action uploads to the release, which means we do need the draft change.

Run gh release upload 3.6.0 build/ld-relay-3.6.0.tgz --clobber
HTTP 422: Cannot upload assets to an immutable release. (https://uploads.github.com/repos/launchdarkly/ld-relay-helm/releases/305661576/assets?label=&name=ld-relay-3.6.0.tgz)
Error: Process completed with exit code 1.

…e releases

The publish-gh-pages action uploads tarballs to the GitHub release via
gh release upload, which fails under immutable releases. This repo needs
the draft release pattern:

- release-please-config.json: added draft: true and force-tag-creation: true
- release-please.yml: split release-please into two passes (releases first,
  then PRs), inline tag creation, and publish-release job to un-draft
- manual-publish.yml: added tag input, publish_release input, and
  publish-release job with format() boolean/string safety
@devin-ai-integration devin-ai-integration Bot changed the title ci: restructure release workflow into separate jobs ci: use draft releases for immutable GitHub releases support Apr 6, 2026
@devin-ai-integration
Copy link
Copy Markdown
Contributor

@kinyoklion You're right — publish-gh-pages does gh release upload, so this repo needs the draft release pattern. I've updated the PR:

  1. release-please-config.json — Added "draft": true and "force-tag-creation": true
  2. release-please.yml — Split release-please pattern (two passes: releases first with inline tag creation, then PRs) + publish-release job to un-draft after release-package completes
  3. manual-publish.yml — Added tag input (references existing draft release), publish_release input (default true), and publish-release job with format() boolean/string safety

Comment thread .github/workflows/release-please.yml
Comment thread .github/workflows/release-please.yml
Comment thread .github/workflows/manual-publish.yml
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 2fb7b68. Configure here.

Comment thread .github/actions/publish-ghcr/action.yml Outdated
@keelerm84 keelerm84 merged commit 29fcaa9 into main Apr 6, 2026
8 checks passed
@keelerm84 keelerm84 deleted the devin/1774991618-immutable-releases branch April 6, 2026 21:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devin-pr PR created by Devin

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants