Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/pr-auto-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ permissions: {}

jobs:
pr-auto-review:
# Skip Dependabot-authored events (#1390, mirrors the #864 guard in
# dev-lead-reusable.yml): a Dependabot `pull_request` run has no access to
# GH_PAT_WORKFLOWS, so the secrets expression below resolves empty and the
# reusable's ready-check checkout fails at startup with
# "Input required and not supplied: token". Dependabot PRs are handled by
# dependabot-automerge.yml and never need the review-agent dispatch, so
# skipping cleanly here removes the false failure without touching the
# channel, trigger event types, or permissions block.
# Two conditions: actor covers Dependabot-triggered events (pull_request,
# workflow_run); PR-author login covers pull_request_review events where the
# actor is the human reviewer, not Dependabot.
if: github.actor != 'dependabot[bot]' && github.event.pull_request.user.login != 'dependabot[bot]'
permissions:
pull-requests: read
checks: read
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/test-dev-lead.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ on:
- '.github/workflows/auto-rebase-retry.yml'
- '.github/workflows/pr-review.yml'
- '.github/workflows/pr-review-trigger.yml'
- '.github/workflows/pr-auto-review.yml'
- '.github/workflows/spec-drift.yml'
- 'tests/dev-lead/**'
- 'prompts/dev-lead/**'
Expand All @@ -39,6 +40,7 @@ on:
- '.github/workflows/auto-rebase-retry.yml'
- '.github/workflows/pr-review.yml'
- '.github/workflows/pr-review-trigger.yml'
- '.github/workflows/pr-auto-review.yml'
- '.github/workflows/spec-drift.yml'
workflow_dispatch: {}

Expand Down Expand Up @@ -171,3 +173,16 @@ jobs:
run: python3 -m pip install --quiet pyyaml
- name: Validate spec-drift.yml post-merge advisory contract
run: python3 tests/dev-lead/integration/test_spec_drift_workflow.py

pr-auto-review-stub:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Install PyYAML
run: python3 -m pip install --quiet pyyaml
- name: Validate pr-auto-review.yml skips Dependabot and pins @pr-auto-review/v1-next
run: python3 tests/dev-lead/integration/test_pr_auto_review_stub.py
4 changes: 2 additions & 2 deletions scripts/seed-repo-template.sh
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ readonly -a WORKFLOW_MANIFEST=(
"ci|inline|-|-"
"copilot-setup-steps|inline|-|-"
"dependabot-automerge|caller|petry-projects/.github|dependabot-automerge/stable"
"dependabot-rebase|caller|petry-projects/.github|dependabot-rebase/stable"
"dependabot-rebase|caller|petry-projects/.github|dependabot-rebase/v2-stable"
"dependency-audit|caller|petry-projects/.github|dependency-audit/stable"
"dev-lead|caller|petry-projects/.github-private|dev-lead/stable"
"pr-review-mention|caller|petry-projects/.github|pr-review-mention/v2-next"
"pr-review-mention|caller|petry-projects/.github|pr-review-mention/v2-stable"
"sonarcloud|inline|-|-"
)

Expand Down
105 changes: 105 additions & 0 deletions tests/dev-lead/integration/test_pr_auto_review_stub.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#!/usr/bin/env python3
"""Validate that the pr-auto-review.yml caller stub skips Dependabot-triggered events.

A Dependabot `pull_request` run has no access to org/repo secrets, so the caller's
`secrets.GH_PAT_DON_PETRY || secrets.GH_PAT_WORKFLOWS` resolves to an empty string and
the reusable's ready-check tooling checkout fails at startup with
"Input required and not supplied: token" (the 20% failure the Fleet Monitor flagged in
#1390). The fix mirrors the documented #864 precedent in dev-lead-reusable.yml: guard the
calling job with `if: github.actor != 'dependabot[bot]'` so it skips cleanly before the
reusable's checkout. Dependabot PRs are handled by dependabot-automerge.yml and never need
the review-agent dispatch.

Regression guard: dropping the Dependabot guard (or repinning off the @pr-auto-review/v1-next
channel) reintroduces the failure class.
"""
from __future__ import annotations

import sys

try:
import yaml
except ImportError:
yaml = None

WORKFLOW = ".github/workflows/pr-auto-review.yml"
REUSABLE = "petry-projects/.github/.github/workflows/pr-auto-review-reusable.yml"
EXPECTED_REF = "@pr-auto-review/v1-next"
DEPENDABOT_ACTOR = "dependabot[bot]"


def main() -> int:
if yaml is None:
print("FAIL: PyYAML is required (pip install pyyaml)", file=sys.stderr)
return 2

try:
with open(WORKFLOW, encoding="utf-8") as fh:
doc = yaml.safe_load(fh)
except FileNotFoundError:
print(f"FAIL: {WORKFLOW} not found")
return 1
Comment thread
qodo-code-review[bot] marked this conversation as resolved.
except yaml.YAMLError as exc:
print(f"FAIL: {WORKFLOW} contains invalid YAML: {exc}", file=sys.stderr)
return 1

if not isinstance(doc, dict):
print(f"FAIL: {WORKFLOW} did not parse as a YAML mapping")
return 1

jobs = doc.get("jobs") or {}
if not isinstance(jobs, dict):
print(f"FAIL: {WORKFLOW} 'jobs' key is not a mapping")
return 1

caller_jobs = [
job
for job in jobs.values()
if isinstance(job, dict) and isinstance(job.get("uses"), str) and REUSABLE in job["uses"]
]

if not caller_jobs:
print(f"FAIL: {WORKFLOW} does not call {REUSABLE}")
return 1

failures = []
for job in caller_jobs:
ref = job["uses"]
if not ref.endswith(EXPECTED_REF):
failures.append(
f" Wrong ref: {ref!r}\n"
f" Expected: '{REUSABLE}{EXPECTED_REF}'\n"
" Reason: first-party reusable must stay on the @pr-auto-review/v1-next\n"
" moving channel (AGENTS.md §Release channel tags)."
)

expected_guard = (
f"github.actor != '{DEPENDABOT_ACTOR}' && "
f"github.event.pull_request.user.login != '{DEPENDABOT_ACTOR}'"
)
guard = job.get("if")
if not isinstance(guard, str) or guard.strip() != expected_guard:
failures.append(
f" Missing Dependabot guard: job 'if' is {guard!r}\n"
f" Expected exactly:\n"
f" if: {expected_guard}\n"
" Reason: actor-only guard is bypassed on re-runs; PR-author check\n"
" also skips pull_request_review events on Dependabot PRs.\n"
" Dependabot runs lack GH_PAT_WORKFLOWS, so the reusable's\n"
" checkout fails with 'Input required and not supplied: token' (#1390, #864)."
Comment thread
coderabbitai[bot] marked this conversation as resolved.
)
Comment thread
qodo-code-review[bot] marked this conversation as resolved.

if failures:
print(f"FAIL: {WORKFLOW} caller stub is misconfigured:")
for msg in failures:
print(msg)
return 1

print(
f"PASS: {WORKFLOW} pins {REUSABLE}{EXPECTED_REF} and skips {DEPENDABOT_ACTOR}"
)
return 0


if __name__ == "__main__":
sys.exit(main())
6 changes: 3 additions & 3 deletions tests/test_seed_repo_template.bats
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ INLINE_STUBS=(ci copilot-setup-steps sonarcloud)
# Published channel a caller stub is repinned to, read from the script's
# WORKFLOW_MANIFEST row ("name|kind|host|channel"). Keeps this test in lockstep
# with the shipped repin target rather than hard-coding a channel that goes stale
# when a stub advances its major line — e.g. pr-review-mention rides
# `pr-review-mention/v2-next` during its v2 migration (documented in the shipped
# caller stub as a centrally-advanced tag that must not be repointed).
# when a stub advances its major line — e.g. dependabot-rebase promoted from
# `dependabot-rebase/stable` to `dependabot-rebase/v2-stable` (centrally-advanced
# tags that must not be repointed by consumers).
_manifest_channel() {
sed -nE "s/^[[:space:]]*\"$1\|caller\|[^|]+\|([^\"]+)\".*/\1/p" "$SEED"
}
Expand Down
Loading