close-stale-prs.yml 2.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. name: close-stale-prs
  2. on:
  3. workflow_dispatch:
  4. inputs:
  5. dryRun:
  6. description: "Log actions without closing PRs"
  7. type: boolean
  8. default: false
  9. schedule:
  10. - cron: "0 6 * * *"
  11. permissions:
  12. contents: read
  13. issues: write
  14. pull-requests: write
  15. jobs:
  16. close-stale-prs:
  17. runs-on: ubuntu-latest
  18. steps:
  19. - name: Close inactive PRs
  20. uses: actions/github-script@v8
  21. with:
  22. github-token: ${{ secrets.GITHUB_TOKEN }}
  23. script: |
  24. const DAYS_INACTIVE = 60
  25. const cutoff = new Date(Date.now() - DAYS_INACTIVE * 24 * 60 * 60 * 1000)
  26. const { owner, repo } = context.repo
  27. const dryRun = context.payload.inputs?.dryRun === "true"
  28. const stalePrs = []
  29. core.info(`Dry run mode: ${dryRun}`)
  30. const prs = await github.paginate(github.rest.pulls.list, {
  31. owner,
  32. repo,
  33. state: "open",
  34. per_page: 100,
  35. sort: "updated",
  36. direction: "asc",
  37. })
  38. for (const pr of prs) {
  39. const lastUpdated = new Date(pr.updated_at)
  40. if (lastUpdated > cutoff) {
  41. core.info(`PR ${pr.number} is fresh`)
  42. continue
  43. }
  44. stalePrs.push(pr)
  45. }
  46. if (!stalePrs.length) {
  47. core.info("No stale pull requests found.")
  48. return
  49. }
  50. for (const pr of stalePrs) {
  51. const issue_number = pr.number
  52. const closeComment = `Closing this pull request because it has had no updates for more than ${DAYS_INACTIVE} days. If you plan to continue working on it, feel free to reopen or open a new PR.`
  53. if (dryRun) {
  54. core.info(`[dry-run] Would close PR #${issue_number} from ${pr.user.login}`)
  55. continue
  56. }
  57. await github.rest.issues.createComment({
  58. owner,
  59. repo,
  60. issue_number,
  61. body: closeComment,
  62. })
  63. await github.rest.pulls.update({
  64. owner,
  65. repo,
  66. pull_number: issue_number,
  67. state: "closed",
  68. })
  69. core.info(`Closed PR #${issue_number} from ${pr.user.login}`)
  70. }