Skip to content

ci: use draft releases to support immutable GitHub releases#48

Merged
keelerm84 merged 7 commits intomainfrom
devin/1774991613-immutable-releases
Apr 6, 2026
Merged

ci: use draft releases to support immutable GitHub releases#48
keelerm84 merged 7 commits intomainfrom
devin/1774991613-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 with no functional code modifications.

Related issues

Supports the org-wide migration to immutable GitHub releases, which prevents modifying a release after it has been published.

Describe the solution you've provided

LaunchDarkly has enabled immutable GitHub releases. Once a release is published, it cannot be modified — which breaks workflows that upload artifacts after release-please creates and publishes the release. This repo uploads package.zip as a release artifact, so it needs the draft release pattern.

This PR makes the following changes:

  1. Draft releases & force-tag-creation (release-please-config.json): Added "draft": true so release-please creates draft (unpublished) releases, allowing artifacts to be uploaded before the release goes live. Also added "force-tag-creation": true (not yet supported by release-please, but included for forward compatibility once support lands).

  2. Split release-please pattern (release-please.yml): The release-please job now runs release-please in two passes to ensure tags exist before release-please checks for them:

    • First pass (skip-github-pull-request: true): Only creates releases (no PRs). If a release is created, the job checks out the repo and creates the Git tag manually (idempotent — skips if tag already exists).
    • Second pass (skip-github-release: true, conditional): Only runs if no release was created in the first pass, and only creates/updates PRs. This prevents release-please from creating duplicate release PRs because it depends on the tag existing when determining if a release PR is needed.
  3. Publish-release job (release-please.yml): A new publish-release job runs after release-package completes and un-drafts the release via gh release edit --draft=false.

  4. Manual publish workflow (manual-publish.yml): Added a publish_release boolean input (default: true) and a publish-release job that un-drafts the release after build-publish completes. Setting publish_release to false allows operators to keep the release in draft state after artifact uploads.

  5. Pinned action version: release-please-action pinned to @16a9c90856f42705d54a6fda1823352bdc62cf38 (v4.4.0).

The original single-job workflow was split into three jobs (release-pleaserelease-packagepublish-release). This follows the canonical pattern from launchdarkly/ld-relay commit 1581de9.

Key review points:

  • The original workflow used releases_created (plural) to gate checkout/CI and release_created (singular) to gate the upload step. This distinction is preserved: releases_created gates the release-package job, while release_created gates the upload step and publish-release job.
  • Tag names are passed via env: variables in run: blocks to avoid script injection. ${{ github.repository }} is used only in safe contexts (GitHub-controlled, not user input).
  • The publish-release job's needs array in release-please.yml includes both release-please and release-package, ensuring the release is only un-drafted after all artifacts are uploaded.
  • In manual-publish.yml, the publish-release job is gated on !inputs.dry_run && inputs.publish_release. If dry_run is true or publish_release is false, the release stays in draft.
  • Verify the second release-please pass condition (release_created != 'true') correctly covers the case where release-please has pending version bumps but no release was created this run.
  • Confirm the ./.github/actions/publish composite action uses gh release upload with --clobber or otherwise handles the draft release state correctly.
  • force-tag-creation has no effect today but will activate automatically once a supporting release-please version is available, at which point the manual tag creation step can be removed.

Describe alternatives you've considered

None — this follows the canonical pattern established in launchdarkly/ld-relay.

Additional context

  • The ./.github/actions/publish composite action uses gh release upload ${{ inputs.tag_name }} with inline interpolation rather than an env var — this is outside the scope of this PR but worth noting for a future hardening pass.
  • No SLSA/attestation changes were needed as this repo does not use SLSA provenance.

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


Note

Medium Risk
Changes the release automation workflow (release/tag creation and publishing order), which could affect release publishing/tagging if conditions or GH CLI steps misfire. No runtime code changes, but failures would block or incorrectly publish releases.

Overview
Moves the release pipeline to a draft-release pattern so release artifacts can be uploaded before a GitHub release is published/immutable.

release-please is split into a two-pass flow that creates draft releases first, then ensures the corresponding git tag exists (creating it if needed), and only creates/updates release PRs when no release was created. Packaging/artifact upload is moved into a dependent release-package job, followed by a new publish-release job that un-drafts the release via gh release edit --draft=false.

Manual publishing gains a publish_release input and a follow-on job to optionally un-draft the release after uploading artifacts, and release-please-config.json now enables draft: true (plus force-tag-creation: true for forward compatibility).

Reviewed by Cursor Bugbot for commit 967d0fe. 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

@kinyoklion kinyoklion marked this pull request as ready for review April 1, 2026 17:59
@kinyoklion kinyoklion requested a review from a team as a code owner April 1, 2026 17:59
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 f03cd4d. Configure here.

@keelerm84 keelerm84 merged commit 2bb113d into main Apr 6, 2026
7 checks passed
@keelerm84 keelerm84 deleted the devin/1774991613-immutable-releases branch April 6, 2026 20:18
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.

2 participants