Promote main to release #59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Promote main to release | |
| # `main` is the integration branch (trunk); `release` is the release-ready branch. | |
| # Open one daily promotion PR and let the main ruleset's required `CI Passed` | |
| # check decide when it is safe to merge. A red run leaves the PR open for a | |
| # human to diagnose; a later fix on `main` re-runs the promotion CI. | |
| on: | |
| schedule: | |
| # After the 06:17 UTC nightly full run, and off the top of the hour to avoid | |
| # GitHub Actions' busiest cron window. | |
| - cron: '17 8 * * *' | |
| workflow_dispatch: | |
| concurrency: | |
| group: promote-main-to-release | |
| cancel-in-progress: false | |
| permissions: | |
| contents: read | |
| pull-requests: write | |
| jobs: | |
| promote: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Require promotion App credentials | |
| env: | |
| APP_ID: ${{ secrets.RELEASE_APP_ID }} | |
| APP_KEY: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| run: | | |
| if [ -z "$APP_ID" ] || [ -z "$APP_KEY" ]; then | |
| echo "RELEASE_APP_ID / RELEASE_APP_PRIVATE_KEY are required to open promotion PRs and trigger CI" >&2 | |
| exit 1 | |
| fi | |
| # copse-release-bot installation token in place of the SYNC_PR_TOKEN PAT. | |
| # Contents: write as well as Pull requests: write — opening the PR needs | |
| # only the latter, but enabling auto-merge is a merge and wants both. | |
| - uses: actions/create-github-app-token@v3 | |
| id: app-token | |
| with: | |
| app-id: ${{ secrets.RELEASE_APP_ID }} | |
| private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} | |
| permission-contents: write | |
| permission-pull-requests: write | |
| - name: Open promotion PR and enable auto-merge | |
| uses: actions/github-script@v9 | |
| with: | |
| # GITHUB_TOKEN-created PRs do not trigger downstream workflows, and | |
| # this organization blocks that token from creating PRs. The App is | |
| # a distinct actor, so neither restriction applies to it. | |
| github-token: ${{ steps.app-token.outputs.token }} | |
| script: | | |
| const { owner, repo } = context.repo; | |
| const base = 'release'; | |
| const head = 'main'; | |
| // A promotion is complete only when every `main` commit is an | |
| // ancestor of `release`. Comparing trees would mistake a squash merge | |
| // for a complete promotion even though it discards that ancestry. | |
| const comparison = await github.request( | |
| 'GET /repos/{owner}/{repo}/compare/{basehead}', | |
| { owner, repo, basehead: `${base}...${head}` }, | |
| ); | |
| if (comparison.data.ahead_by === 0) { | |
| core.info(`${head} has no commits to promote.`); | |
| return; | |
| } | |
| const open = await github.paginate(github.rest.pulls.list, { | |
| owner, | |
| repo, | |
| state: 'open', | |
| base, | |
| head: `${owner}:${head}`, | |
| per_page: 100, | |
| }); | |
| if (open.length > 1) { | |
| core.setFailed(`Found ${open.length} open ${head} -> ${base} pull requests.`); | |
| return; | |
| } | |
| let pull = open[0]; | |
| if (!pull) { | |
| const created = await github.rest.pulls.create({ | |
| owner, | |
| repo, | |
| base, | |
| head, | |
| title: 'Promote main to release', | |
| body: [ | |
| 'Daily automated promotion of `main` to the release-ready `release` branch.', | |
| '', | |
| 'The repository ruleset requires the full `CI Passed` gate. This PR will', | |
| 'merge automatically when that gate is green; failures leave it open', | |
| 'until the underlying issue is fixed on `main`.', | |
| ].join('\n'), | |
| }); | |
| pull = created.data; | |
| core.info(`Opened promotion PR #${pull.number}.`); | |
| } else { | |
| core.info(`Reusing promotion PR #${pull.number}.`); | |
| } | |
| if (pull.draft) { | |
| core.setFailed(`Promotion PR #${pull.number} is unexpectedly a draft.`); | |
| return; | |
| } | |
| if (pull.auto_merge) { | |
| core.info(`Auto-merge is already enabled on PR #${pull.number}.`); | |
| return; | |
| } | |
| await github.graphql( | |
| `mutation EnablePromotionAutoMerge($pullRequestId: ID!) { | |
| enablePullRequestAutoMerge(input: { | |
| pullRequestId: $pullRequestId | |
| mergeMethod: MERGE | |
| }) { | |
| pullRequest { number } | |
| } | |
| }`, | |
| { pullRequestId: pull.node_id }, | |
| ); | |
| core.info(`Enabled merge auto-merge on PR #${pull.number}.`); |