Skip to content

Add workflow to label PRs awaiting workflow approval#8383

Merged
onbuyuka merged 4 commits into
mainfrom
private/magnushar/label-prs-awaiting-approval
Jun 1, 2026
Merged

Add workflow to label PRs awaiting workflow approval#8383
onbuyuka merged 4 commits into
mainfrom
private/magnushar/label-prs-awaiting-approval

Conversation

@Groenbech96
Copy link
Copy Markdown
Contributor

@Groenbech96 Groenbech96 commented Jun 1, 2026

Summary

Adds a scheduled workflow that automatically labels open PRs whose GitHub Actions workflow runs are in the action_required state — i.e. waiting for a maintainer with write access to click Approve and run.

This lets us easily find PRs that need a manual approval click via:

is:pr is:open label:needs-approval

…or:

gh pr list --repo microsoft/BCApps --label needs-approval

How it works

  • Trigger: cron: '*/15 * * * *' + workflow_dispatch (with optional whatIf dry-run input). Mirrors the pattern in CleanUpStalePRChecks.yaml.
  • Reconciliation is idempotent on every run:
    1. Ensures the needs-approval label exists (creates it if missing, color #FBCA04).
    2. Fetches workflow runs with status=action_required from the Actions API.
    3. Resolves each run to a PR number (with a fork-PR fallback via commits/{sha}/pulls since fork-PR workflow runs have an empty pull_requests array).
    4. Computes the diff against PRs currently carrying the label.
    5. Adds the label to new awaiters, removes it from PRs no longer awaiting (approved, or PR closed).
  • Guarded by if: github.repository_owner == 'microsoft' so forks don't run it.
  • Follows existing repo conventions: pwsh, SHA-pinned actions/checkout, composite action under .github/actions/, GitHub step summary.

Files

  • .github/workflows/LabelPRsAwaitingApproval.yaml — workflow (schedule + manual)
  • .github/actions/LabelPRsAwaitingApproval/action.yaml — composite action wrapper
  • .github/actions/LabelPRsAwaitingApproval/action.ps1 — reconciliation logic

Validation

Dry-run against microsoft/BCApps (using -WhatIf) successfully:

Permissions

The workflow requests minimum required scopes:

  • actions: read — list action_required runs
  • contents: read — checkout the composite action
  • issues: write + pull-requests: write — manage the label

Uses github.token — no additional secrets needed.

Caveats / future enhancements

  • Page size is 100 with no pagination. Given BCApps' volume of currently-awaiting runs, this is well within bounds; can be paginated later if needed.
  • Latency is up to 15 minutes. A future enhancement could add workflow_run: types: [completed] for near-instant labelling.

AB#637233

Adds a scheduled workflow (every 15 minutes) and supporting composite
action that reconciles a 'needs-approval' label on open PRs based on
whether they have workflow runs in the 'action_required' state.

This makes it easy to discover and filter PRs that require a maintainer
with write access to approve workflow execution (typically PRs from
first-time contributors or forks), via:

    is:pr is:open label:needs-approval

The reconciliation is idempotent: the label is added when a PR has any
action_required run and removed once those runs are approved (or the
PR is closed).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Groenbech96 Groenbech96 requested a review from a team as a code owner June 1, 2026 08:01
@github-actions github-actions Bot added the Build: Automation Workflows and other setup in .github folder label Jun 1, 2026
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/workflows/LabelPRsAwaitingApproval.yaml
Comment thread .github/actions/LabelPRsAwaitingApproval/action.yaml Outdated
Comment thread .github/workflows/LabelPRsAwaitingApproval.yaml
The previous implementation looked up PRs for each action_required
workflow run via 'repos/{repo}/commits/{sha}/pulls', which returns
any open PR whose branch contains that commit - including PRs whose
older commits had action_required runs that were never approved but
whose newer commits have since been approved.

Result: PRs were falsely flagged as 'needs-approval' for stale runs
on superseded commits. Dry-run against microsoft/BCApps reported 5
PRs of which 3 (#7410, #7843, #8313) had zero action_required runs
on their current head.

Fix: build a {head_sha -> PR number} map from the open PRs API and
only flag a PR when an action_required run matches its *current*
head SHA. This is also simpler (no per-run API call) and correctly
handles fork PRs (head_sha is identical to the PR's head commit
SHA for both pull_request and pull_request_target events).

After fix, dry-run flags exactly the 5 PRs that truly have unrun
workflows on their current head.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Groenbech96
Copy link
Copy Markdown
Contributor Author

Fixed false positives in head SHA matching (86399b2).

Issue found during review: The original logic used commits/{sha}/pulls to map runs to PRs, which returns any open PR whose branch contains that commit. This caused PRs to be flagged for stale action_required runs on older commits that had since been superseded by approved commits.

Example false positive (PR #7410): Had no action_required runs on its current head but was flagged because an older commit in its branch history had an unrun workflow.

Fix: Build a {head_sha -> PR number} map from the open PRs API and only flag a PR when an action_required run's head_sha matches the PR's current head SHA. This is also simpler (no per-run API call required) and works correctly for both pull_request and pull_request_target events (both use the PR head SHA as workflow_run.head_sha).

Verified: Dry-run against microsoft/BCApps now flags exactly the 5 PRs that have action_required runs on their current head: #7699, #8058, #8130, #8165, #8183 — including 3 from community contributors, which is the primary case this label is for.

- Separate stderr capture in Invoke-GhApi to prevent warnings/notices
  from corrupting JSON output (review #1)
- Add --paginate to all gh api calls and switch to NDJSON jq filters
  (one value per line) so multi-page responses parse correctly. Note:
  --slurp cannot be combined with --jq so we cannot use that pattern
  (review #2, #3)
- Add timeout-minutes: 10 to the SyncLabel job (review #4)
- Pass action inputs (label, whatIf) via environment variables instead
  of direct GitHub Actions expression interpolation into the pwsh run
  block, eliminating the script-injection vector (review #5)

Not addressed:
- review #6 claimed actions/checkout v6.0.2 does not exist; verified
  via GitHub API that this SHA is tagged v6.0.2, matching the convention
  used by every other workflow in this repo. Kept as-is for consistency.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/workflows/LabelPRsAwaitingApproval.yaml
- Ensure-Label now distinguishes 404 (label missing) from other errors
  (rate limit / 5xx / network); only the former triggers label creation.
  Other errors propagate instead of being masked as missing-label.
- Map head SHA -> list of PR numbers (instead of a single number) so that
  if two open PRs share a head commit (e.g. same commit pushed to two
  branches with PRs to different bases), both get labelled when the
  workflow runs against that SHA are action_required.

Not addressed:
- "Workflow runs query lacks --paginate": false positive; --paginate is
  already present on line 94 since the previous commit (59afae0).
- "Checkout pinned to non-existent v6.0.2": duplicate of iteration-1
  review #6; already verified via API that the SHA is correctly tagged
  v6.0.2 and matches the convention used throughout this repo.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Groenbech96 Groenbech96 added the Linked Issue is linked to a Azure Boards work item label Jun 1, 2026
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1 Outdated
Comment thread .github/workflows/LabelPRsAwaitingApproval.yaml
Comment thread .github/workflows/LabelPRsAwaitingApproval.yaml
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
Comment thread .github/actions/LabelPRsAwaitingApproval/action.ps1
@github-actions github-actions Bot modified the milestone: Version 29.0 Jun 1, 2026
@JesperSchulz
Copy link
Copy Markdown
Contributor

Looks like a workaround to an annoying issue. @mazhelez, @aholstrup1, is this something we would want, or should we look at the underlying annoyance?

@Groenbech96
Copy link
Copy Markdown
Contributor Author

Looks like a workaround to an annoying issue. @mazhelez, @aholstrup1, is this something we would want, or should we look at the underlying annoyance?

This has been discussed before. We have very few people (MS internal) that has write access to the REPO. So are allowed to run the build. Please either approve this, or relax that requirement. Last time i believe relaxing it was rejected.

@onbuyuka onbuyuka merged commit 3393d18 into main Jun 1, 2026
92 of 98 checks passed
@onbuyuka onbuyuka deleted the private/magnushar/label-prs-awaiting-approval branch June 1, 2026 14:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Build: Automation Workflows and other setup in .github folder Linked Issue is linked to a Azure Boards work item

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants