From 8c0b0a75c5a6da8249629b631097af154c439c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?coffee=20=E2=98=95=EF=B8=8F?= Date: Sat, 18 Apr 2026 14:29:20 -0400 Subject: [PATCH 1/3] Consolidate publish gating into gatekeeper workflow - Run publish detection from gatekeeper on main - Convert publish.yml to a reusable workflow - Preserve release-surface and AGW client verification before publish --- .github/workflows/gatekeeper.yml | 63 +++++++++++++++++++++++++++++--- .github/workflows/publish.yml | 47 +----------------------- 2 files changed, 59 insertions(+), 51 deletions(-) diff --git a/.github/workflows/gatekeeper.yml b/.github/workflows/gatekeeper.yml index cfa2e7c..03e56d3 100644 --- a/.github/workflows/gatekeeper.yml +++ b/.github/workflows/gatekeeper.yml @@ -9,7 +9,7 @@ on: concurrency: group: ci-gatekeeper-${{ github.ref }} - cancel-in-progress: true + cancel-in-progress: ${{ github.ref != 'refs/heads/main' }} jobs: changes: @@ -77,6 +77,27 @@ jobs: - '.github/workflows/publish.yml' - '.github/workflows/gatekeeper.yml' + detect-publishable: + name: Detect publishable release + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + outputs: + should_publish: ${{ steps.detect.outputs.should_publish }} + packages_json: ${{ steps.detect.outputs.packages_json }} + summary_markdown: ${{ steps.detect.outputs.summary_markdown }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 24 + + - name: Detect publishable packages + id: detect + run: node .github/scripts/detect-publishable.mjs + changesets: name: Changeset Check if: github.event_name == 'pull_request' && !startsWith(github.head_ref, 'changeset-release/') && needs.changes.outputs.publish-surface == 'true' @@ -122,17 +143,46 @@ jobs: secrets: inherit agw-client: - needs: changes - if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.agw-client == 'true' + needs: + - changes + - detect-publishable + if: | + always() && + needs.changes.result == 'success' && + (github.event_name == 'workflow_dispatch' || + needs.changes.outputs.agw-client == 'true' || + needs.detect-publishable.outputs.should_publish == 'true') uses: ./.github/workflows/ci-agw-client.yml secrets: inherit release-surface: - needs: changes - if: github.event_name == 'workflow_dispatch' || needs.changes.outputs.publish-surface == 'true' + needs: + - changes + - detect-publishable + if: | + always() && + needs.changes.result == 'success' && + (github.event_name == 'workflow_dispatch' || + needs.changes.outputs.publish-surface == 'true' || + needs.detect-publishable.outputs.should_publish == 'true') uses: ./.github/workflows/ci-release-surface.yml secrets: inherit + publish: + name: Publish + needs: + - detect-publishable + - release-surface + - agw-client + if: | + github.event_name == 'push' && + github.ref == 'refs/heads/main' && + needs.detect-publishable.outputs.should_publish == 'true' && + needs.release-surface.result == 'success' && + needs.agw-client.result == 'success' + uses: ./.github/workflows/publish.yml + secrets: inherit + ci-gatekeeper: name: CI Gatekeeper runs-on: ubuntu-latest @@ -143,11 +193,12 @@ jobs: - contracts - agw-client - release-surface + - publish steps: - name: Verify job results run: | set -euo pipefail - results="${{ needs.changesets.result || 'skipped' }} ${{ needs.verify.result || 'skipped' }} ${{ needs.contracts.result || 'skipped' }} ${{ needs.agw-client.result || 'skipped' }} ${{ needs.release-surface.result || 'skipped' }}" + results="${{ needs.changesets.result || 'skipped' }} ${{ needs.verify.result || 'skipped' }} ${{ needs.contracts.result || 'skipped' }} ${{ needs.agw-client.result || 'skipped' }} ${{ needs.release-surface.result || 'skipped' }} ${{ needs.publish.result || 'skipped' }}" echo "Job results: $results" if [[ "$results" =~ failure || "$results" =~ cancelled ]]; then diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 1d6df66..fa83ca2 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,65 +1,22 @@ name: Publish on: - push: - branches: - - main + workflow_call: permissions: contents: read + id-token: write concurrency: group: publish-${{ github.ref }} cancel-in-progress: false jobs: - detect: - name: Detect publishable release - runs-on: ubuntu-latest - outputs: - should_publish: ${{ steps.detect.outputs.should_publish }} - packages_json: ${{ steps.detect.outputs.packages_json }} - summary_markdown: ${{ steps.detect.outputs.summary_markdown }} - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Setup Node.js - uses: actions/setup-node@v4 - with: - node-version: 24 - - - name: Detect publishable packages - id: detect - run: node .github/scripts/detect-publishable.mjs - - verify-release-surface: - name: Verify release surface - needs: detect - if: needs.detect.outputs.should_publish == 'true' - uses: ./.github/workflows/ci-release-surface.yml - secrets: inherit - - verify-agw-client: - name: Verify AGW client - needs: detect - if: needs.detect.outputs.should_publish == 'true' - uses: ./.github/workflows/ci-agw-client.yml - secrets: inherit - publish: name: Publish to npm - needs: - - detect - - verify-release-surface - - verify-agw-client - if: needs.detect.outputs.should_publish == 'true' runs-on: ubuntu-latest environment: name: npm - permissions: - contents: read - id-token: write env: NPM_CONFIG_PROVENANCE: "true" NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 3cc6dbffa96bbdfd6533d96df5127c8e32413c9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?coffee=20=E2=98=95=EF=B8=8F?= Date: Sat, 18 Apr 2026 14:33:03 -0400 Subject: [PATCH 2/3] Grant publish job OIDC permissions - Set `contents: read` and `id-token: write` on the reusable publish job - Allow gatekeeper to invoke the publish workflow with the required token scope --- .github/workflows/gatekeeper.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/gatekeeper.yml b/.github/workflows/gatekeeper.yml index 03e56d3..737a89a 100644 --- a/.github/workflows/gatekeeper.yml +++ b/.github/workflows/gatekeeper.yml @@ -180,6 +180,9 @@ jobs: needs.detect-publishable.outputs.should_publish == 'true' && needs.release-surface.result == 'success' && needs.agw-client.result == 'success' + permissions: + contents: read + id-token: write uses: ./.github/workflows/publish.yml secrets: inherit From 47c8404001dacbd780fa4f68da6120fb33a72ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?coffee=20=E2=98=95=EF=B8=8F?= Date: Sat, 18 Apr 2026 14:34:34 -0400 Subject: [PATCH 3/3] Include detect-publishable in gatekeeper checks - Add the new `detect-publishable` job to gatekeeper dependencies - Include its result in the workflow failure/cancellation check --- .github/workflows/gatekeeper.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/gatekeeper.yml b/.github/workflows/gatekeeper.yml index 737a89a..ec187f3 100644 --- a/.github/workflows/gatekeeper.yml +++ b/.github/workflows/gatekeeper.yml @@ -196,12 +196,13 @@ jobs: - contracts - agw-client - release-surface + - detect-publishable - publish steps: - name: Verify job results run: | set -euo pipefail - results="${{ needs.changesets.result || 'skipped' }} ${{ needs.verify.result || 'skipped' }} ${{ needs.contracts.result || 'skipped' }} ${{ needs.agw-client.result || 'skipped' }} ${{ needs.release-surface.result || 'skipped' }} ${{ needs.publish.result || 'skipped' }}" + results="${{ needs.changesets.result || 'skipped' }} ${{ needs.verify.result || 'skipped' }} ${{ needs.contracts.result || 'skipped' }} ${{ needs.agw-client.result || 'skipped' }} ${{ needs.release-surface.result || 'skipped' }} ${{ needs.detect-publishable.result || 'skipped' }} ${{ needs.publish.result || 'skipped' }}" echo "Job results: $results" if [[ "$results" =~ failure || "$results" =~ cancelled ]]; then