diff --git a/.github/workflows/close-stale-draft-prs.yml b/.github/workflows/close-stale-draft-prs.yml new file mode 100644 index 000000000..5b0a92b31 --- /dev/null +++ b/.github/workflows/close-stale-draft-prs.yml @@ -0,0 +1,64 @@ +name: Close stale draft pull requests + +on: + schedule: + - cron: "0 0 * * 1" # Every Monday at 00:00 UTC + workflow_dispatch: + +permissions: + pull-requests: write # needed to close PRs and post comments + +jobs: + close-stale-drafts: + runs-on: ubuntu-latest + steps: + - name: Close draft PRs with no activity for more than 6 months + uses: actions/github-script@v7 + with: + script: | + const cutoff = new Date(); + cutoff.setMonth(cutoff.getMonth() - 6); + + // Paginate through all open PRs + const iterator = github.paginate.iterator(github.rest.pulls.list, { + owner: context.repo.owner, + repo: context.repo.repo, + state: "open", + per_page: 100, + }); + + for await (const { data: pulls } of iterator) { + for (const pull of pulls) { + // Guardrail: only process draft PRs + if (!pull.draft) continue; + + const updatedAt = new Date(pull.updated_at); + if (updatedAt >= cutoff) continue; + + console.log( + `Closing stale draft PR #${pull.number}: "${pull.title}" ` + + `(last updated: ${pull.updated_at})` + ); + + // Post an explanatory comment before closing + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: pull.number, + body: + "This draft pull request has been automatically closed " + + "because it has been in draft state with no activity for " + + "more than 6 months. If you would like to continue working " + + "on this, please reopen it and push an update. Maintainers " + + "can also reopen this PR if needed.", + }); + + // Close the PR + await github.rest.pulls.update({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: pull.number, + state: "closed", + }); + } + } diff --git a/documentation/process.md b/documentation/process.md index eaa20d7a7..aa9951a1a 100644 --- a/documentation/process.md +++ b/documentation/process.md @@ -18,6 +18,8 @@ Newly opened PRs are "triaged" at the next ARIA meeting. At triage, it is decide Draft PRs are not triaged at meeting and are considered not read for review. If you want to prevent an approved PR from getting merged, add the `do not merge` label. +> **Automated clean-up:** Draft PRs that have had no activity for more than 6 months are automatically closed by a weekly GitHub Actions workflow. A comment is posted on the PR before it is closed. Authors or maintainers can reopen the PR at any time to continue work. + PR Labels: - `spec:aria`, `spec:accname`, `spec:core-aam`, `spec:html-aam`, `spec:` should be used for all specs which have changes in this PR. - `waiting for implementations`: if a normative PR has been approved but there are no implementations yet, then use this flag to indicate it can be merged when the implementations are completed.