Close invalid PR writer #1270
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: Close invalid PR writer | |
| on: | |
| workflow_run: | |
| workflows: [Close issue/PR on adding invalid label] | |
| types: [completed] | |
| # pull_request does not run for conflicted PRs, so reconcile from the trusted default branch. | |
| schedule: | |
| - cron: '*/5 * * * *' | |
| workflow_dispatch: | |
| permissions: {} | |
| jobs: | |
| close-invalid-pr-from-workflow-run: | |
| if: > | |
| github.repository == 'github/copilot-cli' && | |
| github.event_name == 'workflow_run' && | |
| github.event.workflow_run.event == 'pull_request' && | |
| github.event.workflow_run.repository.full_name == github.repository | |
| runs-on: ubuntu-latest | |
| permissions: | |
| actions: read | |
| pull-requests: write | |
| concurrency: | |
| group: close-invalid-pr-${{ github.event.workflow_run.pull_requests[0].number || github.run_id }} | |
| cancel-in-progress: false | |
| steps: | |
| - name: Close invalid PR | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| WORKFLOW_RUN_ID: ${{ github.event.workflow_run.id }} | |
| run: | | |
| set -euo pipefail | |
| trusted_workflow_id="$(gh api "repos/$GH_REPO/actions/workflows/close-invalid.yml" --jq .id)" | |
| workflow_run="$(gh api "repos/$GH_REPO/actions/runs/$WORKFLOW_RUN_ID")" | |
| if [ "$(jq -r .workflow_id <<<"$workflow_run")" != "$trusted_workflow_id" ] || | |
| [ "$(jq -r .event <<<"$workflow_run")" != "pull_request" ] || | |
| [ "$(jq -r .repository.full_name <<<"$workflow_run")" != "$GH_REPO" ]; then | |
| echo "Workflow run is not a trusted pull_request run from $GH_REPO; skipping." | |
| exit 0 | |
| fi | |
| run_head_sha="$(jq -r .head_sha <<<"$workflow_run")" | |
| run_head_repo="$(jq -r '.head_repository.full_name // empty' <<<"$workflow_run")" | |
| run_head_branch="$(jq -r '.head_branch // empty' <<<"$workflow_run")" | |
| if [ -z "$run_head_repo" ] || | |
| [ -z "$run_head_branch" ] || | |
| [[ ! "$run_head_sha" =~ ^[0-9a-f]{40}$ ]] || | |
| [ "${run_head_repo#*/}" = "$run_head_repo" ] || | |
| [ -z "${run_head_repo%%/*}" ] || | |
| [ -z "${run_head_repo#*/}" ]; then | |
| echo "Workflow run is missing valid head repository, branch, or SHA metadata; skipping." | |
| exit 0 | |
| fi | |
| pull_request_count="$(jq '.pull_requests | length' <<<"$workflow_run")" | |
| if [ "$pull_request_count" -eq 1 ]; then | |
| pr_number="$(jq -r .pull_requests[0].number <<<"$workflow_run")" | |
| elif [ "$pull_request_count" -eq 0 ]; then | |
| run_head_owner="${run_head_repo%%/*}" | |
| matching_prs="$( | |
| gh api --method GET --paginate "repos/$GH_REPO/pulls" \ | |
| -f state=open \ | |
| -f head="$run_head_owner:$run_head_branch" \ | |
| -f per_page=100 | | |
| jq -cs \ | |
| --arg repo "$GH_REPO" \ | |
| --arg head_repo "$run_head_repo" \ | |
| --arg head_branch "$run_head_branch" \ | |
| --arg head_sha "$run_head_sha" \ | |
| 'add | [ | |
| .[] | | |
| select( | |
| .state == "open" and | |
| .base.repo.full_name == $repo and | |
| .head.repo.full_name == $head_repo and | |
| .head.ref == $head_branch and | |
| .head.sha == $head_sha | |
| ) | |
| ]' | |
| )" | |
| if [ "$(jq 'length' <<<"$matching_prs")" -ne 1 ]; then | |
| echo "Workflow run could not be uniquely associated with an open PR; skipping." | |
| exit 0 | |
| fi | |
| pr_number="$(jq -r '.[0].number' <<<"$matching_prs")" | |
| else | |
| echo "Workflow run is associated with multiple PRs; skipping." | |
| exit 0 | |
| fi | |
| if [[ ! "$pr_number" =~ ^[1-9][0-9]*$ ]]; then | |
| echo "Workflow run produced an invalid PR number; skipping." | |
| exit 0 | |
| fi | |
| pr="$(gh api "repos/$GH_REPO/pulls/$pr_number")" | |
| if [ "$(jq -r .base.repo.full_name <<<"$pr")" != "$GH_REPO" ] || | |
| [ "$(jq -r '.head.repo.full_name // empty' <<<"$pr")" != "$run_head_repo" ] || | |
| [ "$(jq -r .head.ref <<<"$pr")" != "$run_head_branch" ] || | |
| [ "$(jq -r .head.sha <<<"$pr")" != "$run_head_sha" ]; then | |
| echo "PR #$pr_number no longer matches the workflow run head; skipping." | |
| exit 0 | |
| fi | |
| if [ "$(jq -r .state <<<"$pr")" != "open" ] || | |
| ! jq -e 'any(.labels[]?; .name == "invalid")' >/dev/null <<<"$pr"; then | |
| echo "PR #$pr_number is not open with the invalid label; skipping." | |
| exit 0 | |
| fi | |
| gh api -X PATCH "repos/$GH_REPO/pulls/$pr_number" -f state=closed | |
| reconcile-invalid-prs: | |
| if: > | |
| github.repository == 'github/copilot-cli' && | |
| (github.event_name == 'schedule' || github.event_name == 'workflow_dispatch') | |
| runs-on: ubuntu-latest | |
| permissions: | |
| pull-requests: write | |
| concurrency: | |
| group: close-invalid-pr-reconciliation | |
| cancel-in-progress: false | |
| steps: | |
| - name: Close open PRs with the invalid label | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| GH_REPO: ${{ github.repository }} | |
| run: | | |
| set -euo pipefail | |
| gh api --paginate "repos/$GH_REPO/pulls?state=open&per_page=100" \ | |
| --jq '.[] | select(any(.labels[]?; .name == "invalid")) | .number' | | |
| while read -r pr_number; do | |
| pr="$(gh api "repos/$GH_REPO/pulls/$pr_number")" | |
| if [ "$(jq -r .state <<<"$pr")" = "open" ] && | |
| jq -e 'any(.labels[]?; .name == "invalid")' >/dev/null <<<"$pr"; then | |
| gh api -X PATCH "repos/$GH_REPO/pulls/$pr_number" -f state=closed | |
| fi | |
| done |