diff --git a/.github/workflows/api-parity-audit.yml b/.github/workflows/api-parity-audit.yml index 28f10c6..425d71d 100644 --- a/.github/workflows/api-parity-audit.yml +++ b/.github/workflows/api-parity-audit.yml @@ -5,6 +5,11 @@ name: Weekly API parity PR # when the public API and first-class CLI command surface change. It does not # claim to regenerate hand-written command UX from OpenAPI. # +# It never resets the branch of an open rolling PR that carries commits from +# anyone but the bot: reconciliation pushed onto the PR is left alone and the +# run comments on the PR instead. Once that PR is merged or closed, the next run +# rebuilds the branch from main. +# # Prerequisite: # REGEN_PR_TOKEN: a token with Contents and Pull requests write access to this # repository. The organization does not allow GITHUB_TOKEN to create PRs. @@ -18,6 +23,8 @@ on: permissions: contents: read issues: write + # Lists the open rolling PR to comment on when hand commits are present. + pull-requests: read concurrency: group: weekly-api-parity-pr @@ -208,7 +215,78 @@ jobs: --write-markdown api-coverage-report.md --allow-drift + - name: Check the rolling branch for hand commits + id: rolling_branch + env: + GH_TOKEN: ${{ github.token }} + BRANCH: chore/weekly-api-parity + BOT_COMMITTER: 41898282+github-actions[bot]@users.noreply.github.com + run: | + set -euo pipefail + + # create-pull-request rebuilds the branch from main and force-pushes, + # which would discard reconciliation someone pushed onto the PR. + refs="$( + gh api "repos/${GITHUB_REPOSITORY}/git/matching-refs/heads/${BRANCH}" \ + --jq "map(select(.ref == \"refs/heads/${BRANCH}\")) | length" + )" + if [ "$refs" = "0" ]; then + echo "hand_commits=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + # A squash merge never makes the branch's commits ancestors of main, + # so a merged or closed rolling PR leaves "hand commits" behind + # forever. Only an open PR has reconciliation left to protect. + open_prs="$( + gh api "repos/${GITHUB_REPOSITORY}/pulls?head=${GITHUB_REPOSITORY_OWNER}:${BRANCH}&state=open" \ + --jq "length" + )" + if [ "$open_prs" = "0" ]; then + echo "hand_commits=false" >> "$GITHUB_OUTPUT" + exit 0 + fi + + hand_commits="$( + gh api "repos/${GITHUB_REPOSITORY}/compare/main...${BRANCH}" \ + --jq "[.commits[] | select(.commit.committer.email != \"${BOT_COMMITTER}\")] | length" + )" + if [ "$hand_commits" = "0" ]; then + echo "hand_commits=false" >> "$GITHUB_OUTPUT" + else + echo "::notice::${BRANCH} has ${hand_commits} commit(s) not made by the bot; leaving it unchanged." + echo "hand_commits=true" >> "$GITHUB_OUTPUT" + fi + + - name: Comment instead of resetting a hand-reconciled PR + if: steps.rolling_branch.outputs.hand_commits == 'true' + uses: actions/github-script@v7 + with: + script: | + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; + const { data: pulls } = await github.rest.pulls.list({ + owner: context.repo.owner, + repo: context.repo.repo, + head: `${context.repo.owner}:chore/weekly-api-parity`, + state: 'open', + }); + const body = [ + 'The weekly refresh left this branch unchanged because it has commits not made by the bot.', + 'Merge this PR, or rebase it onto `main` and regenerate `api-coverage-report.md`, so the next run can pick up new API changes.', + '', + `Run: ${runUrl}`, + ].join('\n'); + for (const pull of pulls) { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pull.number, + body, + }); + } + - name: Open or update the parity PR + if: steps.rolling_branch.outputs.hand_commits != 'true' uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.REGEN_PR_TOKEN }} diff --git a/README.md b/README.md index 67f940e..40b9567 100644 --- a/README.md +++ b/README.md @@ -365,8 +365,12 @@ A repository-owned GitHub workflow runs every Monday and refreshes the deterministic `api-coverage-report.md`. When coverage changes, it opens or updates one rolling PR on `chore/weekly-api-parity`; the PR's CI remains blocked until the command implementation or an explicitly reviewed manifest exception -reconciles the drift. A GitHub issue is used only if the automation itself -fails before it can create or update that PR. +reconciles the drift. Push reconciliation commits onto the rolling PR. While +that PR is open, the next weekly run leaves a branch with commits not made by +the bot unchanged and comments on the PR instead of resetting it, so merge or +rebase that PR to let later runs pick up new API changes. Once the PR is merged +or closed, the next run rebuilds the branch from `main`. A GitHub issue is used +only if the automation itself fails before it can create or update that PR. The schedule is Monday 2:00 AM PST (10:00 UTC; 3:00 AM during daylight saving time). GitHub Actions schedules can start later during busy periods. The audit