name: pr-closer on: schedule: - cron: "0 0 * * *" # daily at midnight workflow_dispatch: concurrency: group: pr-closer cancel-in-progress: true jobs: close-stale-prs: runs-on: ubuntu-latest permissions: pull-requests: write checks: read statuses: read steps: - name: Close stale PRs env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} REPO: ${{ github.repository }} run: | set -o pipefail CUTOFF=$(date -u -d '6 days ago' +%Y-%m-%d) gh pr list -R "$REPO" --state open --search "updated:<$CUTOFF -author:jdx -label:keep-open draft:false" --json number,mergeStateStatus,statusCheckRollup --limit 500 | \ jq -r ' def failed_check: (.statusCheckRollup | length > 0) and ([.statusCheckRollup // [] | .[] | ((.conclusion // .state // "") | ascii_upcase)] | any(. == "FAILURE" or . == "ERROR" or . == "TIMED_OUT" or . == "ACTION_REQUIRED")); .[] | failed_check as $failed | ([.statusCheckRollup // [] | .[] | ((.conclusion // .state // "") | ascii_upcase)] | any(. == "CANCELLED")) as $cancelled | (.mergeStateStatus == "DIRTY") as $conflicted | (.mergeStateStatus == "UNKNOWN") as $unknown | if $failed and $conflicted then [.number, "failing checks and merge conflicts"] elif $failed then [.number, "failing checks"] elif $conflicted then [.number, "merge conflicts"] elif $cancelled then [.number, "cancelled checks", "warn"] elif $unknown then [.number, "unknown merge state", "warn"] else empty end | @tsv ' | \ while IFS=$'\t' read -r pr reason action; do if [ "$action" = "warn" ]; then echo "Skipping PR #$pr ($reason)" continue fi echo "Closing PR #$pr ($reason)" gh pr close "$pr" -R "$REPO" -c "This PR has been inactive for at least 7 days and currently has $reason. Feel free to reopen or create a new PR if you'd like to continue working on this." || echo "Warning: failed to close PR #$pr, skipping" done