From b5a3072dde9b14527d6cef5f5a7928d64a23610d Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 21:14:46 +0000 Subject: [PATCH 01/10] ci: use draft releases to support immutable GitHub releases --- .github/workflows/release-please.yml | 54 +++++++++++++++++++++++----- release-please-config.json | 3 +- 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 95a4b0d..ff1c895 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -5,37 +5,73 @@ on: branches: [main] jobs: - release-package: + release-please: runs-on: ubuntu-latest permissions: - id-token: write # Needed if using OIDC to get release secrets. - contents: write # Contents and pull-requests are for release-please to make releases. - packages: write # needed for ghcr access + contents: write pull-requests: write + outputs: + releases_created: ${{ steps.release.outputs.releases_created }} + tag_name: ${{ steps.release.outputs.tag_name }} steps: - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4 id: release + release-package: + needs: release-please + if: ${{ needs.release-please.outputs.releases_created == 'true' }} + runs-on: ubuntu-latest + permissions: + id-token: write # Needed if using OIDC to get release secrets. + contents: write # Contents and pull-requests are for release-please to make releases. + packages: write # needed for ghcr access + steps: - uses: actions/checkout@v4 - if: ${{ steps.release.outputs.releases_created == 'true' }} with: fetch-depth: 0 # Full history is required for proper changelog generation + - name: Create release tag + env: + TAG_NAME: ${{ needs.release-please.outputs.tag_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if gh api "repos/${{ github.repository }}/git/ref/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} already exists, skipping creation." + else + echo "Creating tag ${TAG_NAME}." + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "${TAG_NAME}" + git push origin "${TAG_NAME}" + fi + - uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 - if: ${{ steps.release.outputs.releases_created == 'true' }} - name: Run quality control checks - if: ${{ steps.release.outputs.releases_created == 'true' }} uses: ./.github/actions/ci - uses: ./.github/actions/publish-gh-pages - if: ${{ steps.release.outputs.releases_created == 'true' }} with: dry_run: false token: ${{ secrets.GITHUB_TOKEN }} - uses: ./.github/actions/publish-ghcr - if: ${{ steps.release.outputs.releases_created == 'true' }} with: dry_run: false token: ${{ secrets.GITHUB_TOKEN }} + + publish-release: + needs: ['release-please', 'release-package'] + if: ${{ needs.release-please.outputs.releases_created == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ needs.release-please.outputs.tag_name }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/release-please-config.json b/release-please-config.json index 85a6e84..f9dd859 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -4,7 +4,8 @@ "release-type": "helm", "versioning": "default", "include-v-in-tag": false, - "include-component-in-tag": false + "include-component-in-tag": false, + "draft": true } } } From 95dabda26c9fadb05a60c6914679b6bd78c5729f Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 21:42:42 +0000 Subject: [PATCH 02/10] ci: add force-tag-creation and publish_release option --- .github/workflows/manual-publish.yml | 25 +++++++++++++++++++++++++ release-please-config.json | 3 ++- 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index f80b797..d0e9bcb 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -16,6 +16,15 @@ on: type: boolean required: false default: false + tag: + description: 'Tag of an existing draft release to upload artifacts to.' + type: string + required: false + publish_release: + description: 'Publish (un-draft) the release after all artifacts are uploaded?' + type: boolean + required: false + default: true jobs: build-publish: @@ -44,3 +53,19 @@ jobs: with: dry_run: ${{ inputs.dry_run }} token: ${{ secrets.GITHUB_TOKEN }} + + publish-release: + needs: ['build-publish'] + if: ${{ !inputs.dry_run && inputs.publish_release }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ inputs.tag }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/release-please-config.json b/release-please-config.json index f9dd859..749e031 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,7 +5,8 @@ "versioning": "default", "include-v-in-tag": false, "include-component-in-tag": false, - "draft": true + "draft": true, + "force-tag-creation": true } } } From d414baccac21257f1e3ec062b22234c3b2956045 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 22:06:51 +0000 Subject: [PATCH 03/10] ci: simplify for attestation-only releases (no draft needed) 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 --- .github/workflows/manual-publish.yml | 25 ---------------------- .github/workflows/release-please.yml | 31 ---------------------------- release-please-config.json | 1 - 3 files changed, 57 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index d0e9bcb..f80b797 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -16,15 +16,6 @@ on: type: boolean required: false default: false - tag: - description: 'Tag of an existing draft release to upload artifacts to.' - type: string - required: false - publish_release: - description: 'Publish (un-draft) the release after all artifacts are uploaded?' - type: boolean - required: false - default: true jobs: build-publish: @@ -53,19 +44,3 @@ jobs: with: dry_run: ${{ inputs.dry_run }} token: ${{ secrets.GITHUB_TOKEN }} - - publish-release: - needs: ['build-publish'] - if: ${{ !inputs.dry_run && inputs.publish_release }} - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Publish release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ inputs.tag }} - run: > - gh release edit "$TAG_NAME" - --repo ${{ github.repository }} - --draft=false diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index ff1c895..b1a924a 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -30,21 +30,6 @@ jobs: with: fetch-depth: 0 # Full history is required for proper changelog generation - - name: Create release tag - env: - TAG_NAME: ${{ needs.release-please.outputs.tag_name }} - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - run: | - if gh api "repos/${{ github.repository }}/git/ref/tags/${TAG_NAME}" >/dev/null 2>&1; then - echo "Tag ${TAG_NAME} already exists, skipping creation." - else - echo "Creating tag ${TAG_NAME}." - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git tag "${TAG_NAME}" - git push origin "${TAG_NAME}" - fi - - uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 - name: Run quality control checks @@ -59,19 +44,3 @@ jobs: with: dry_run: false token: ${{ secrets.GITHUB_TOKEN }} - - publish-release: - needs: ['release-please', 'release-package'] - if: ${{ needs.release-please.outputs.releases_created == 'true' }} - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - name: Publish release - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - TAG_NAME: ${{ needs.release-please.outputs.tag_name }} - run: > - gh release edit "$TAG_NAME" - --repo ${{ github.repository }} - --draft=false diff --git a/release-please-config.json b/release-please-config.json index 749e031..4b548ad 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -5,7 +5,6 @@ "versioning": "default", "include-v-in-tag": false, "include-component-in-tag": false, - "draft": true, "force-tag-creation": true } } From 85fb9df374815320816332cff80a10d9f4789e74 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Tue, 31 Mar 2026 22:33:50 +0000 Subject: [PATCH 04/10] ci: remove force-tag-creation from attestation-only repo 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. --- release-please-config.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/release-please-config.json b/release-please-config.json index 4b548ad..85a6e84 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -4,8 +4,7 @@ "release-type": "helm", "versioning": "default", "include-v-in-tag": false, - "include-component-in-tag": false, - "force-tag-creation": true + "include-component-in-tag": false } } } From 823fabdb61a22428140b3848f5450ce8e04f0533 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 16:22:19 +0000 Subject: [PATCH 05/10] ci: use draft releases with split release-please pattern for immutable 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 --- .github/workflows/manual-publish.yml | 27 ++++++++++++++++ .github/workflows/release-please.yml | 48 +++++++++++++++++++++++++++- release-please-config.json | 4 ++- 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index f80b797..cf9c0f1 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -2,6 +2,10 @@ name: Publish Package on: workflow_dispatch: inputs: + tag: + description: 'Tag of an existing draft release to upload artifacts to.' + type: string + required: true dry_run: description: "Is this a dry run? If so no package will be published." type: boolean @@ -16,6 +20,11 @@ on: type: boolean required: false default: false + publish_release: + description: "Publish (un-draft) the release after all artifacts are uploaded?" + type: boolean + required: false + default: true jobs: build-publish: @@ -27,6 +36,8 @@ jobs: packages: write # needed for ghcr access steps: - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} - uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 @@ -44,3 +55,19 @@ jobs: with: dry_run: ${{ inputs.dry_run }} token: ${{ secrets.GITHUB_TOKEN }} + + publish-release: + needs: build-publish + if: ${{ format('{0}', inputs.publish_release) == 'true' && format('{0}', inputs.dry_run) == 'false' }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ inputs.tag }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index b1a924a..24d650c 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -11,11 +11,41 @@ jobs: contents: write pull-requests: write outputs: + release_created: ${{ steps.release.outputs.release_created }} releases_created: ${{ steps.release.outputs.releases_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: - - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4 + # Create any releases first, then create tags, and then optionally create any new PRs. + - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0 id: release + with: + skip-github-pull-request: true + + # Need the repository content to be able to create and push a tag. + - uses: actions/checkout@v4 + if: ${{ steps.release.outputs.release_created == 'true' }} + + - name: Create release tag + if: ${{ steps.release.outputs.release_created == 'true' }} + env: + TAG_NAME: ${{ steps.release.outputs.tag_name }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + if gh api "repos/${{ github.repository }}/git/ref/tags/${TAG_NAME}" >/dev/null 2>&1; then + echo "Tag ${TAG_NAME} already exists, skipping creation." + else + echo "Creating tag ${TAG_NAME}." + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git tag "${TAG_NAME}" + git push origin "${TAG_NAME}" + fi + + - uses: googleapis/release-please-action@16a9c90856f42705d54a6fda1823352bdc62cf38 # v4.4.0 + if: ${{ steps.release.outputs.release_created != 'true' }} + id: release-prs + with: + skip-github-release: true release-package: needs: release-please @@ -44,3 +74,19 @@ jobs: with: dry_run: false token: ${{ secrets.GITHUB_TOKEN }} + + publish-release: + needs: ['release-please', 'release-package'] + if: ${{ needs.release-please.outputs.release_created == 'true' }} + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Publish release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + TAG_NAME: ${{ needs.release-please.outputs.tag_name }} + run: > + gh release edit "$TAG_NAME" + --repo ${{ github.repository }} + --draft=false diff --git a/release-please-config.json b/release-please-config.json index 85a6e84..749e031 100644 --- a/release-please-config.json +++ b/release-please-config.json @@ -4,7 +4,9 @@ "release-type": "helm", "versioning": "default", "include-v-in-tag": false, - "include-component-in-tag": false + "include-component-in-tag": false, + "draft": true, + "force-tag-creation": true } } } From 8e3b89e59edf7ccf8e853dd329e952dbb9eb55b4 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 17:04:33 +0000 Subject: [PATCH 06/10] ci: fix inconsistent release_created vs releases_created in publish-release gate --- .github/workflows/release-please.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 24d650c..8df8edf 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -77,7 +77,7 @@ jobs: publish-release: needs: ['release-please', 'release-package'] - if: ${{ needs.release-please.outputs.release_created == 'true' }} + if: ${{ needs.release-please.outputs.releases_created == 'true' }} runs-on: ubuntu-latest permissions: contents: write From ef3cbcdd2084643c33a87d8dcb66434f7035c61b Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 17:13:15 +0000 Subject: [PATCH 07/10] ci: use release_created consistently, remove unused releases_created output --- .github/workflows/release-please.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index 8df8edf..efddfeb 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -12,7 +12,6 @@ jobs: pull-requests: write outputs: release_created: ${{ steps.release.outputs.release_created }} - releases_created: ${{ steps.release.outputs.releases_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: # Create any releases first, then create tags, and then optionally create any new PRs. @@ -49,7 +48,7 @@ jobs: release-package: needs: release-please - if: ${{ needs.release-please.outputs.releases_created == 'true' }} + if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest permissions: id-token: write # Needed if using OIDC to get release secrets. @@ -77,7 +76,7 @@ jobs: publish-release: needs: ['release-please', 'release-package'] - if: ${{ needs.release-please.outputs.releases_created == 'true' }} + if: ${{ needs.release-please.outputs.release_created == 'true' }} runs-on: ubuntu-latest permissions: contents: write From 2fb7b68e1da588e4293705e5be7cf1c86fef74bb Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 17:24:36 +0000 Subject: [PATCH 08/10] ci: fix publish-ghcr checkout ref mismatch in manual-publish workflow --- .github/actions/publish-ghcr/action.yml | 6 ++++++ .github/workflows/manual-publish.yml | 1 + 2 files changed, 7 insertions(+) diff --git a/.github/actions/publish-ghcr/action.yml b/.github/actions/publish-ghcr/action.yml index aca0252..db100cc 100644 --- a/.github/actions/publish-ghcr/action.yml +++ b/.github/actions/publish-ghcr/action.yml @@ -7,11 +7,17 @@ inputs: token: description: "The GitHub token used to upload artifacts to the published release" required: true + ref: + description: "Git ref to checkout. Defaults to the triggering ref." + required: false + default: '' runs: using: composite steps: - uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} - name: Login to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index cf9c0f1..bc3f5a9 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -55,6 +55,7 @@ jobs: with: dry_run: ${{ inputs.dry_run }} token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ inputs.tag }} publish-release: needs: build-publish From 142b2925152993b1a680a27e4880740a44322984 Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 20:34:08 +0000 Subject: [PATCH 09/10] ci: pass tag directly to composite actions and fix stale permission comments --- .github/actions/publish-ghcr/action.yml | 2 +- .github/workflows/manual-publish.yml | 2 +- .github/workflows/release-please.yml | 4 +++- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/actions/publish-ghcr/action.yml b/.github/actions/publish-ghcr/action.yml index db100cc..20b2ada 100644 --- a/.github/actions/publish-ghcr/action.yml +++ b/.github/actions/publish-ghcr/action.yml @@ -17,7 +17,7 @@ runs: steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.ref || github.ref }} + ref: ${{ inputs.ref || github.sha }} - name: Login to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 diff --git a/.github/workflows/manual-publish.yml b/.github/workflows/manual-publish.yml index bc3f5a9..8225e14 100644 --- a/.github/workflows/manual-publish.yml +++ b/.github/workflows/manual-publish.yml @@ -32,7 +32,7 @@ jobs: # Needed to get tokens during publishing. permissions: id-token: write # Needed if using OIDC to get release secrets. - contents: write # Contents and pull-requests are for release-please to make releases. + contents: write # Needed for publish-gh-pages to upload release assets. packages: write # needed for ghcr access steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml index efddfeb..72d277e 100644 --- a/.github/workflows/release-please.yml +++ b/.github/workflows/release-please.yml @@ -52,11 +52,12 @@ jobs: runs-on: ubuntu-latest permissions: id-token: write # Needed if using OIDC to get release secrets. - contents: write # Contents and pull-requests are for release-please to make releases. + contents: write # Needed for publish-gh-pages to upload release assets. packages: write # needed for ghcr access steps: - uses: actions/checkout@v4 with: + ref: ${{ needs.release-please.outputs.tag_name }} fetch-depth: 0 # Full history is required for proper changelog generation - uses: azure/setup-helm@29960d0f5f19214b88e1d9ba750a9914ab0f1a2f # v4.0.0 @@ -73,6 +74,7 @@ jobs: with: dry_run: false token: ${{ secrets.GITHUB_TOKEN }} + ref: ${{ needs.release-please.outputs.tag_name }} publish-release: needs: ['release-please', 'release-package'] From fe926a90cb5bc0dddd805c4fdab323d707221fbe Mon Sep 17 00:00:00 2001 From: "mkeeler@launchdarkly.com" Date: Mon, 6 Apr 2026 20:47:02 +0000 Subject: [PATCH 10/10] ci: make publish-ghcr ref input required instead of optional with fallback --- .github/actions/publish-ghcr/action.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/actions/publish-ghcr/action.yml b/.github/actions/publish-ghcr/action.yml index 20b2ada..0d70b14 100644 --- a/.github/actions/publish-ghcr/action.yml +++ b/.github/actions/publish-ghcr/action.yml @@ -8,16 +8,15 @@ inputs: description: "The GitHub token used to upload artifacts to the published release" required: true ref: - description: "Git ref to checkout. Defaults to the triggering ref." - required: false - default: '' + description: "Git ref to checkout (e.g. a tag name)." + required: true runs: using: composite steps: - uses: actions/checkout@v4 with: - ref: ${{ inputs.ref || github.sha }} + ref: ${{ inputs.ref }} - name: Login to GitHub Container Registry uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0