Add daily OpenAPI PR reviewer agentic workflow - #6742
Conversation
Adds a gh-aw (GitHub Agentic Workflow) that runs the openapi-pr-reviewer logic daily: reviews the latest "Update OpenAPI 3.0/3.1 Descriptions" PRs, merges them and closes older superseded PRs when there are no breaking changes, and notifies #api-platform on Slack. Ships in staged (dry-run) mode until secrets and branch-protection bypass are provisioned. Tracked in github/api-platform#3408. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new scheduled gh-aw (GitHub Agentic Workflow) to automatically review and (when safe) merge the latest OpenAPI 3.0/3.1 description update PRs from github-openapi-bot, otherwise alerting #api-platform on Slack when breaking changes are detected.
Changes:
- Introduces a daily
openapi-pr-revieweragentic workflow definition (.md) with two gated safe-output jobs (merge/close + Slack notify; breaking-change Slack alert). - Adds the compiled workflow lockfile (
.lock.yml) generated bygh aw compile(v0.79.6) for Actions execution.
Show a summary per file
| File | Description |
|---|---|
| .github/workflows/openapi-pr-reviewer.md | Defines the agentic workflow, including safe-output jobs for merging PRs locally and posting alerts to Slack. |
| .github/workflows/openapi-pr-reviewer.lock.yml | Generated compiled workflow that GitHub Actions will run. |
Review details
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Files reviewed: 1/2 changed files
- Comments generated: 3
- Review effort level: Low
| # The #api-platform Slack channel ID. Update this to the real channel ID before enabling. | ||
| env: | ||
| GH_AW_SLACK_CHANNEL_ID: "${{ vars.API_PLATFORM_SLACK_CHANNEL_ID }}" |
There was a problem hiding this comment.
Good catch — fixed in 4edc3aa. gh-aw doesn't inject GH_AW_SAFE_OUTPUTS_STAGED into custom safe-output jobs (only its own built-in jobs), so it was unset. I now set it explicitly in each custom job's env: and, because a step's own step-level env isn't visible to that same step's if:, gate the destructive actions in-script ($GH_AW_SAFE_OUTPUTS_STAGED for run steps, process.env for github-script) rather than via step if:.
| env: | ||
| GH_AW_SLACK_CHANNEL_ID: ${{ vars.API_PLATFORM_SLACK_CHANNEL_ID }} | ||
|
|
There was a problem hiding this comment.
Fixed in 4edc3aa — same root cause as the .md comment. The compiled custom jobs now carry GH_AW_SAFE_OUTPUTS_STAGED: "true" in their step env and gate merge/close/post in-script, so staged (dry-run) mode actually previews instead of executing.
| - name: Checkout default branch | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| fetch-depth: 0 | ||
| token: ${{ env.MERGE_TOKEN }} | ||
| - name: Merge PRs locally and push | ||
| if: ${{ env.GH_AW_SAFE_OUTPUTS_STAGED != 'true' }} | ||
| env: | ||
| PR_30: ${{ steps.req.outputs.pr_30 }} | ||
| PR_31: ${{ steps.req.outputs.pr_31 }} | ||
| run: | | ||
| set -euo pipefail | ||
| git config user.name "github-actions[bot]" | ||
| git config user.email "github-actions[bot]@users.noreply.github.com" | ||
| DEFAULT_BRANCH="${GITHUB_REF_NAME}" | ||
| git checkout "$DEFAULT_BRANCH" |
There was a problem hiding this comment.
Addressed in 4edc3aa. The job no longer derives the branch from GITHUB_REF_NAME or pushes directly — it merges each PR via gh pr merge --merge, which targets the PR's base (the default branch) regardless of the ref the workflow was dispatched from. The staged preview now echoes github.event.repository.default_branch.
Replace the direct Slack Web API calls (chat.postMessage + SLACK_BOT_TOKEN /
channel ID) with the repo's existing chatterbox convention, matching
linter-failure-notifier.yml: POST to ${CHATTERBOX_URL}/topics/%23api-platform
with basic auth (CHATTERBOX_TOKEN). Renames the post-to-slack-channel safe
output to post-to-chatterbox and drops the SLACK_BOT_TOKEN /
API_PLATFORM_SLACK_CHANNEL_ID secrets/vars. Recompiled the lock with gh aw.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: dcc1f083-b349-4398-ae7e-e2b90b1924b7
Replace the local git merge + direct push to the protected default branch with `gh pr merge` on each of the two latest PRs. Merging a PR is the compliant path through the "require a pull request" branch rule, so the job no longer needs a protected-branch push or any ruleset/branch-protection bypass — OPENAPI_MERGE_TOKEN only needs pull-requests:write + contents:write. The synchronous merge call can gateway-timeout (502/504) on these ~250K-line diffs, so merge_pr retries and polls the PR's merged state after each failure (the merge usually completes server-side despite the timeout). Drops the actions/checkout-with-token step. Recompiled the lock with gh aw. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dcc1f083-b349-4398-ae7e-e2b90b1924b7
- Staged mode was a no-op: gh-aw does not inject GH_AW_SAFE_OUTPUTS_STAGED into custom safe-output jobs, and a step's own step-level env is not visible to that same step's `if:`. Set the flag explicitly in each custom job's env and gate destructive actions in-script (shell `$GH_AW_SAFE_OUTPUTS_STAGED` for run steps, `process.env` for github-script) instead of via step `if:`. - Merge target: merges now go through `gh pr merge` (targets each PR's base = default branch, not GITHUB_REF_NAME), so workflow_dispatch from a non-default branch can't merge into the wrong branch. Staged preview echoes github.event.repository.default_branch. - Reference secrets.OPENAPI_MERGE_TOKEN directly for GH_TOKEN (the previous `env.MERGE_TOKEN` indirection resolved empty for custom jobs). - Close-older-PRs now also requires success() so a failed merge won't close PRs. Recompiled the lock with gh aw. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: dcc1f083-b349-4398-ae7e-e2b90b1924b7
|
Closing in favour of #6993, which automates the same task. Both find the newest 3.0 and 3.1 description update pull requests, merge them and close the superseded ones; only one should land. #6993 is being taken forward because it adds safeguards this one does not have: the head commit is pinned by SHA and revalidated immediately before the merge, both OpenAPI lint checks must be green on that commit, the merge is refused if anything outside the description directories changed, and the run holds during a GHES release-candidate window or while an open Two things this pull request got right have been adopted there. It correctly identified that the merge has to go through the pull request API rather than a push to the default branch, which is what the branch rules allow; #6993 originally pushed a local merge commit and would have failed on every run. It also reported outcomes to chat, which #6993 now does for the cases that need someone to act. Thanks for it, and sorry for the duplicated effort. |
What
Adds a daily gh-aw (GitHub Agentic Workflow) that automates the API Platform team's
openapi-pr-reviewerprocess, tracked in github/api-platform#3408.Every day the workflow:
github-openapi-bot.changed_files: 0on these ~64-file, 250K-line PRs) to detect breaking changes.main, closes the older superseded PRs, and posts a summary to #api-platform on Slack.Files
.github/workflows/openapi-pr-reviewer.md— the agentic workflow (source of truth)..github/workflows/openapi-pr-reviewer.lock.yml— compiled workflow (gh aw compile, v0.79.6). This is what Actions runs; regenerate it whenever the.mdchanges.Design
The agent runs read-only (
contents: read,pull-requests: read). All privileged writes happen in gated safe-output jobs that hold the write tokens — gh-aw's defense against prompt injection:merge-openapi-prs(contents: write,pull-requests: write): merges the two latest PRs into the default branch via the GitHub merge API (gh pr merge --merge) and closes the older PRs. We can't use the built-inmerge-pull-requestsafe output because it always refuses merges to the default branch, so this is a custom job. Merging a PR is the compliant path through the "require a pull request" branch rule, so no protected-branch push or branch-protection bypass is needed. The synchronous merge call can gateway-timeout (502/504) on these ~250K-line diffs, so the job retries and polls the PR's merged state (the merge usually completes server-side despite the timeout).post-to-chatterbox: posts the breaking-change alert to #api-platform (≤200 chars) via chatterbox.🔒 Security review (required for new secrets)
This workflow introduces the following new secrets / variables. Each has been reviewed:
CHATTERBOX_URL${CHATTERBOX_URL}/topics/%23api-platform. Same secret already used by this repo'slinter-failure-notifier.yml.CHATTERBOX_TOKENchat:writeequivalent)-u "$CHATTERBOX_TOKEN:"). Same secret already used bylinter-failure-notifier.yml.OPENAPI_MERGE_TOKENmerge-openapi-prsjob. Needscontents: write+pull-requests: writeon this repo. Because the job merges PRs (rather than pushing to the default branch), it does not need any protected-branch bypass. Recommend a GitHub App / service-account token, not a personal PAT.No third-party GitHub Actions beyond
actions/checkout@v5andactions/github-script@v9are used. The agent has no network access to write anywhere except through these jobs.The workflow is committed with
safe-outputs.staged: true, so every safe-output job previews its action in the run summary instead of actually merging/closing/posting. This lets us validate the daily run end-to-end before it can change anything.To go live (follow-up, owner action)
CHATTERBOX_URL,CHATTERBOX_TOKEN,OPENAPI_MERGE_TOKEN) and the Copilot engine token (COPILOT_GITHUB_TOKEN/COPILOT_CLI_TOKENper the org's gh-aw setup).CHATTERBOX_URL/CHATTERBOX_TOKENalready exist in this repo forlinter-failure-notifier.yml.OPENAPI_MERGE_TOKENidentity haspull-requests: write+contents: writeon this repo and can merge PRs intomain. No branch-protection bypass is required, since the job merges the PRs rather than pushing to the default branch. (maincurrently has no required status checks, so no CodeQL exemption is needed.)#api-platform, hardcoded in the safe-output jobs) is correct.safe-outputs.stagedtofalse(or remove it) and setGH_AW_SAFE_OUTPUTS_STAGEDto"false"(or remove it) in both custom jobs'env:in the.md, then re-rungh aw compile openapi-pr-reviewer.See github/api-platform#3408 for the full design/tracking notes.