Skip to content

ci: delete fork-PR runs still awaiting approval once their PR closes - #2650

Merged
openipc-ai merged 2 commits into
masterfrom
ci-stale-approvals
Oct 21, 2026
Merged

openipc-ai merged 2 commits into
masterfrom
ci-stale-approvals

Conversation

@openipc-ai

@openipc-ai openipc-ai commented Oct 21, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

On #2463 the author asked (comment) why several workflows kept notifying them, a month after the merge, about runs that "lasted 30 days".

Every run a fork PR triggers needs a maintainer's approval. qodo-gate fires on every review and every review comment, so each review round leaves a few extra runs that nobody approves. The run that matters gets approved; on #2463 that was 35520648466, which passed. When the PR merged, GitHub closed the pending pull_request and pull_request_review runs, but it left the pull_request_review_comment ones pending. Six of them stayed until 2026-10-20, when GitHub expired them ("This workflow run required approval but was not approved before it expired", 30d 0h 5m), recorded each as a failure, and sent a notification for each.

Before the cleanup below, 106 runs were awaiting approval, and 82 of them belonged to PRs that were already merged or closed: #2438, #2446, #2465, #2467, #2473, #2474, #2579, #2580, #2628 and #2634. Every one would have produced the same notification.

Change

A new stale-approvals.yml workflow deletes runs that are awaiting approval on a branch no open PR uses. A deleted run never expires, so it never sends a notification.

  • pull_request_target: closed handles the closed PR's branch. Nothing is checked out. The branch name, which the contributor controls, reaches the shell only through env.
  • A weekly schedule handles any run older than 7 days. It catches PRs closed before this workflow existed and any closed event that was missed.
  • workflow_dispatch defaults to a dry run.
  • It refuses any branch that an open PR uses. If two forks both push master, a live PR can never lose its runs.
  • It carries the repository guard.
  • ci-matrix.py lists it as a no-build workflow, with a self-test case.

Evidence

  • I ran a one-off cleanup by hand with the same rule: 82 runs deleted, 0 failed. Only the 24 runs on the open PRs nt98513-diagnostics: add optional stock ABI tools #2588 and xm530: bring up the AltoBeam ATBM6032 USB WiFi chip #2316 are left.
  • I ran the job's script locally against the live repo in dry-run mode:
    PR mode, open branch (xiongmai-atbm60xx-wifi):  deleted=0 kept-for-open-PR=19 failed=0
    PR mode, closed branch (improve-wireguard):     deleted=0 kept-for-open-PR=0  failed=0
    sweep:                                          deleted=0 kept-for-open-PR=24 failed=0
    
  • lint-workflow-shell.py: all run blocks parse clean, and --self-test passes.
  • ci-matrix.py --self-test: ok (107 boards, 157 packages, 76 cases).
  • ci-matrix.py --stdin on this diff: needs-build=false.

Not tested

  • The delete path hasn't run inside Actions. Since the hand cleanup, there's nothing left on a closed PR for it to delete. The first fork PR to close after this merges will exercise it, and its run log names each run it deletes.
  • No hardware is involved. This is CI machinery that only prunes workflow runs.

Approval policy

The fork approval policy was all_external_contributors, which is why every review comment created a run that needed approval. On 2026-10-21 it was switched to first_time_contributors, so only a contributor's first PR now waits for approval. This workflow still matters for those first PRs and for the runs already queued under the old policy.

Every review comment on a fork PR starts a qodo-gate run that needs a
maintainer's approval. Nobody approves the extras. On merge GitHub closes
the pending pull_request runs but leaves the pull_request_review_comment
ones to expire 30 days later, and it reports each expiry to the contributor
as a failed run. #2463's author got six of these a month after the merge.

A deleted run never expires. On pull_request_target closed, this deletes the
runs awaiting approval on the closed PR's branch. A weekly sweep catches
anything that event missed. Neither touches a branch that an open PR uses.
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Prune unapproved fork-PR runs after their PR closes

🐞 Bug fix ⚙️ Configuration changes 🕐 20-40 Minutes

Grey Divider

AI Description

• Delete runs awaiting approval after their PR closes to prevent misleading expiry failures and
 notifications.
• Sweep older runs weekly, protect branches used by open PRs, and default manual runs to dry-run
 mode.
• Exclude the cleanup workflow from firmware builds and add a matrix self-test.
Diagram

graph TD
  Closed["PR closed"] -->|branch runs| Runs["Approval runs"] --> Gate{"Open head?"} -->|no| Prune["Delete or preview"]
  Sweep["Weekly sweep"] -->|older than 7d| Runs
  Manual["Manual dispatch"] -->|dry run default| Runs
  Heads["Open PR heads"] --> Gate
  Gate -->|yes| Keep["Keep run"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Relax fork-run approval policy
  • ➕ Prevents many unapproved runs from accumulating.
  • ➖ Requires a repository-settings decision.
  • ➖ Does not clean up existing runs or all future first-time-contributor runs.
2. Match runs to PR commits
  • ➕ Could identify runs belonging to a specific PR more narrowly.
  • ➖ Force-pushed commits can disappear from the PR's current history.
  • ➖ Requires more complex association logic.

Recommendation: Use the proposed branch-based cleanup with its open-PR safeguard: it handles force-pushed runs and missed closure events without changing approval policy. Maintainers can consider a policy change separately to reduce new run volume.

Files changed (2) +104 / -2

Bug fix (1) +101 / -0
stale-approvals.ymlDelete stale runs awaiting fork-PR approval +101/-0

Delete stale runs awaiting fork-PR approval

• Adds cleanup on PR closure, a weekly sweep for runs older than seven days, and manual dispatch with dry-run enabled by default. It checks all open PR branch names before deleting runs and logs individual outcomes.

.github/workflows/stale-approvals.yml

Other (1) +3 / -2
ci-matrix.pyClassify stale-approvals as a no-build workflow +3/-2

Classify stale-approvals as a no-build workflow

• Adds the new workflow to the no-build allowlist and adds a self-test case confirming it does not trigger firmware builds.

.github/scripts/ci-matrix.py

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Open pull requests can lose pending runs ✓ Resolved
Description
The grep -q check can stop reading while printf is still writing, so pipefail makes a matching
open branch look unmatched. With a sufficiently large list of open PR branches, the loop can then
delete a run it was meant to keep.
Code

.github/workflows/stale-approvals.yml[81]

+            if printf '%s\n' "$open_heads" | grep -qxF -- "$branch"; then
Evidence
The script enables pipefail, captures all paginated open branch names, and pipes that list into an
early-exiting grep -q. A nonzero pipeline status bypasses the keep branch and reaches the DELETE
call.

.github/workflows/stale-approvals.yml[61-64]
.github/workflows/stale-approvals.yml[79-88]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
A successful early `grep -q` match can close the pipe before `printf` finishes. Under `pipefail`, that makes an open PR's branch fail the keep check.
## Fix Focus Areas
- .github/workflows/stale-approvals.yml[61-61]
- .github/workflows/stale-approvals.yml[79-88]
## Recommended Fix
Feed `open_heads` to `grep` through a here-string instead of a pipe, preserving exact, fixed-string, whole-line matching. Add a test with a matching branch near the start of a large open-head list.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can start a comment with 'qodo' or '@qodo' to chat about any finding

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/stale-approvals.yml Outdated
@openipc-ai
openipc-ai merged commit 36033ce into master Oct 21, 2026
43 checks passed
@openipc-ai
openipc-ai deleted the ci-stale-approvals branch October 21, 2026 08:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant