|
22 | 22 |
|
23 | 23 | jobs: |
24 | 24 | release: |
25 | | - name: Release ${{ inputs.package }} |
| 25 | + name: Open release PR for ${{ inputs.package }} |
26 | 26 | runs-on: ubuntu-24.04 |
27 | | - environment: compact-npm-prod # Includes npm token and requires approval |
| 27 | + environment: compact-npm-prod # Requires approval before opening the release PR |
28 | 28 |
|
29 | 29 | permissions: |
30 | | - contents: write # Required to push commits and tags |
| 30 | + contents: write # create the release branch |
| 31 | + pull-requests: write # open the PR + enable auto-merge |
31 | 32 |
|
32 | 33 | steps: |
33 | 34 | - name: Get github app token |
@@ -74,35 +75,76 @@ jobs: |
74 | 75 | yarn version ${{ inputs.version_bump }} |
75 | 76 | NEW_VERSION=$(node -p "require('./package.json').version") |
76 | 77 | echo "new=$NEW_VERSION" >> $GITHUB_OUTPUT |
77 | | - echo "### Release Summary" >> $GITHUB_STEP_SUMMARY |
78 | | - echo "- Package: ${{ inputs.package }}" >> $GITHUB_STEP_SUMMARY |
79 | | - echo "- New version: $NEW_VERSION" >> $GITHUB_STEP_SUMMARY |
80 | | - echo "- Bump type: ${{ inputs.version_bump }}" >> $GITHUB_STEP_SUMMARY |
| 78 | + echo "branch=release/${{ inputs.package }}-v$NEW_VERSION" >> $GITHUB_OUTPUT |
| 79 | + { |
| 80 | + echo "### Release Summary" |
| 81 | + echo "- Package: ${{ inputs.package }}" |
| 82 | + echo "- New version: $NEW_VERSION" |
| 83 | + echo "- Bump type: ${{ inputs.version_bump }}" |
| 84 | + } >> $GITHUB_STEP_SUMMARY |
81 | 85 |
|
82 | 86 | - name: Verify package contents |
83 | 87 | run: | |
84 | 88 | cd packages/${{ steps.pkg.outputs.dir }} |
85 | 89 | yarn pack --dry-run |
86 | 90 |
|
87 | | - # Uses GitHub API to create signed commits for verification on protected branches |
| 91 | + # Branch protection blocks direct pushes to main, so route the version bump |
| 92 | + # through a PR: create branch → signed bot commit → open PR → auto-merge. |
| 93 | + - name: Create release branch |
| 94 | + env: |
| 95 | + GH_TOKEN: ${{ steps.gh-app-token.outputs.token }} |
| 96 | + BRANCH: ${{ steps.version.outputs.branch }} |
| 97 | + run: | |
| 98 | + if gh api "/repos/${{ github.repository }}/git/refs/heads/$BRANCH" >/dev/null 2>&1; then |
| 99 | + echo "branch $BRANCH already exists, reusing it" |
| 100 | + else |
| 101 | + SHA=$(gh api "/repos/${{ github.repository }}/git/refs/heads/${{ github.ref_name }}" -q .object.sha) |
| 102 | + gh api --method POST "/repos/${{ github.repository }}/git/refs" \ |
| 103 | + -f ref="refs/heads/$BRANCH" \ |
| 104 | + -f sha="$SHA" |
| 105 | + fi |
| 106 | +
|
88 | 107 | - name: Commit version bump |
89 | 108 | uses: iarekylew00t/verified-bot-commit@934fa64df2191ab067d0c0d73f422239b6933392 # v2.2.1 |
90 | 109 | with: |
91 | | - message: "Release ${{ inputs.package }} v${{ steps.version.outputs.new }}" |
| 110 | + message: "release: ${{ inputs.package }} v${{ steps.version.outputs.new }}" |
92 | 111 | token: ${{ steps.gh-app-token.outputs.token }} |
93 | | - ref: ${{ github.ref_name }} |
| 112 | + ref: ${{ steps.version.outputs.branch }} |
94 | 113 | files: | |
95 | 114 | packages/${{ steps.pkg.outputs.dir }}/package.json |
96 | 115 |
|
97 | | - - name: Create and push tag |
| 116 | + - name: Ensure release label exists |
| 117 | + env: |
| 118 | + GH_TOKEN: ${{ steps.gh-app-token.outputs.token }} |
98 | 119 | run: | |
99 | | - git tag "${{ inputs.package }}/v${{ steps.version.outputs.new }}" |
100 | | - git push origin "${{ inputs.package }}/v${{ steps.version.outputs.new }}" |
| 120 | + gh label create release \ |
| 121 | + --description "Automated release PR" \ |
| 122 | + --color ededed \ |
| 123 | + --force |
101 | 124 |
|
102 | | - - name: Publish to npm |
| 125 | + - name: Open release PR |
| 126 | + id: open-pr |
| 127 | + env: |
| 128 | + GH_TOKEN: ${{ steps.gh-app-token.outputs.token }} |
| 129 | + BRANCH: ${{ steps.version.outputs.branch }} |
103 | 130 | run: | |
104 | | - yarn config set npmAuthToken "$NPM_TOKEN" |
105 | | - cd packages/${{ steps.pkg.outputs.dir }} |
106 | | - yarn npm publish --access public --provenance |
| 131 | + cat > /tmp/pr-body.md <<EOF |
| 132 | + Automated release PR for **${{ inputs.package }}** v${{ steps.version.outputs.new }} (${{ inputs.version_bump }} bump). |
| 133 | +
|
| 134 | + This PR was opened by the release workflow. Once required checks pass (semgrep, CodeQL, code-owner review), it will auto-merge. Merging will trigger the publish workflow, which tags the release and publishes to npm. |
| 135 | + EOF |
| 136 | + PR_URL=$(gh pr create \ |
| 137 | + --base "${{ github.ref_name }}" \ |
| 138 | + --head "$BRANCH" \ |
| 139 | + --title "release: ${{ inputs.package }} v${{ steps.version.outputs.new }}" \ |
| 140 | + --label release \ |
| 141 | + --body-file /tmp/pr-body.md) |
| 142 | + echo "url=$PR_URL" >> $GITHUB_OUTPUT |
| 143 | + echo "- PR: $PR_URL" >> $GITHUB_STEP_SUMMARY |
| 144 | +
|
| 145 | + # If this step fails with "auto-merge is not allowed", enable it under |
| 146 | + # Settings → General → "Allow auto-merge", then re-run the workflow. |
| 147 | + - name: Enable auto-merge |
107 | 148 | env: |
108 | | - NPM_TOKEN: ${{ secrets.NPM_TOKEN }} |
| 149 | + GH_TOKEN: ${{ steps.gh-app-token.outputs.token }} |
| 150 | + run: gh pr merge "${{ steps.open-pr.outputs.url }}" --auto --squash |
0 commit comments