feat(workflows): centralize standards via reusable workflows#87
feat(workflows): centralize standards via reusable workflows#87
Conversation
Build org-wide reusable workflows for the four standards that previously required full inline copies in every downstream repo, and migrate the matching standards/workflows/*.yml templates to thin caller stubs that delegate via `uses: petry-projects/.github/.github/workflows/*-reusable.yml@main`. This extends the pattern already proven by feature-ideation and the existing claude-code-reusable workflow to the rest of the standard set: - dependency-audit-reusable.yml (zero per-repo config) - dependabot-automerge-reusable.yml (uses secrets: inherit for APP_*) - dependabot-rebase-reusable.yml (uses secrets: inherit for APP_*) - agent-shield-reusable.yml (inputs for severity/required-files/org-ref) The standards/workflows/claude.yml template was also still the inline 115-line version even though claude-code-reusable.yml has existed for weeks; migrate it to a stub matching the central repo's own claude.yml. Each migrated stub now carries a uniform "SOURCE OF TRUTH" header block telling agents what they may and may not edit. Net effect: ~580 lines removed from standards/workflows, single point of maintenance for the five centralizable workflows. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds four new reusable GitHub Actions workflows (AgentShield, Dependabot auto-merge, Dependabot rebase, Dependency audit) and replaces multiple inline org-standard workflow jobs with thin caller stubs that invoke those reusable workflows; consolidates two Claude jobs into a single reusable invocation. (≤50 words) Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
Centralizes org-standard GitHub Actions workflows by introducing reusable workflows under .github/workflows/ and converting the corresponding standards/workflows/*.yml templates into thin caller stubs that delegate via uses:. This reduces duplication and makes future workflow updates propagate from a single source of truth.
Changes:
- Added 4 new reusable workflows: dependency audit, dependabot automerge, dependabot rebase, and agent-shield.
- Migrated 5 standards workflow templates from inline implementations to thin caller stubs.
- Standardized “SOURCE OF TRUTH” header blocks in the migrated stubs.
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
standards/workflows/dependency-audit.yml |
Converted to thin stub delegating to the new dependency-audit reusable. |
standards/workflows/dependabot-rebase.yml |
Converted to thin stub delegating to the new dependabot-rebase reusable. |
standards/workflows/dependabot-automerge.yml |
Converted to thin stub delegating to the new dependabot-automerge reusable. |
standards/workflows/claude.yml |
Converted to thin stub delegating to existing claude-code-reusable.yml. |
standards/workflows/agent-shield.yml |
Converted to thin stub delegating to the new agent-shield reusable. |
.github/workflows/dependency-audit-reusable.yml |
New reusable implementing multi-ecosystem dependency detection + audits. |
.github/workflows/dependabot-rebase-reusable.yml |
New reusable implementing serialized dependabot update-branch + merge logic. |
.github/workflows/dependabot-automerge-reusable.yml |
New reusable implementing dependabot eligibility + approve/auto-merge flow. |
.github/workflows/agent-shield-reusable.yml |
New reusable implementing AgentShield scan + org structural checks with inputs. |
Comments suppressed due to low confidence (1)
standards/workflows/dependabot-rebase.yml:38
permissions: {}at the workflow level combined with no job-levelpermissionsmeans the called reusable cannot obtain even read scopes for GITHUB_TOKEN (permissions are an intersection for reusable workflows). The reusable usesgh api/gh pr viewand needs at leastcontents: read+pull-requests: read; please set those permissions on this calling job (see standards/workflows/feature-ideation.yml for the same inheritance pattern).
permissions: {}
jobs:
dependabot-rebase:
uses: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main
secrets: inherit
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| env: | ||
| PR_URL: ${{ github.event.pull_request.html_url }} | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| dependabot-automerge: |
There was a problem hiding this comment.
permissions: {} at the workflow level combined with no job-level permissions means the called reusable cannot elevate the GITHUB_TOKEN scopes it needs. The reusable’s job requests at least contents: read and pull-requests: read, but those will be effectively reduced to none, causing dependabot/fetch-metadata / gh API reads to fail. Define the required permissions on this calling job (same pattern used by standards/workflows/feature-ideation.yml).
| dependabot-automerge: | |
| dependabot-automerge: | |
| permissions: | |
| contents: read | |
| pull-requests: read |
| # Copy to .github/workflows/dependency-audit.yml | ||
| # ───────────────────────────────────────────────────────────────────────────── | ||
| # SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependency-audit.yml | ||
| # Standard: petry-projects/.github/standards/ci-standards.md#5-dependency-audit-dependency-auditym |
There was a problem hiding this comment.
The “Standard:” link target looks incorrect (...#5-dependency-audit-dependency-auditym is missing the trailing l, and the section in standards/ci-standards.md is numbered ### 6. Dependency Audit). This breaks the reference in the SOURCE OF TRUTH header; update the URL anchor to match the actual heading.
| # Standard: petry-projects/.github/standards/ci-standards.md#5-dependency-audit-dependency-auditym | |
| # Standard: petry-projects/.github/standards/ci-standards.md#6-dependency-audit |
| # Reusable dependency vulnerability audit — single source of truth for the org. | ||
| # Repo-level dependency-audit.yml files call this to avoid duplicating the | ||
| # multi-ecosystem detection and audit logic. | ||
| # Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#5-dependency-audit-dependency-auditym |
There was a problem hiding this comment.
The comment reference to ci-standards uses an anchor ending in ...dependency-auditym (missing l) and appears to reference section #5-... even though the doc section is ### 6. Dependency Audit. Please fix the Standard URL anchor so it resolves correctly.
| # Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#5-dependency-audit-dependency-auditym | |
| # Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#6-dependency-audit-dependency-audityml |
| # Dependency vulnerability audit — thin caller for the org-level reusable. | ||
| # To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo. | ||
| # Add "dependency-audit / Detect ecosystems" as a required status check | ||
| # in branch protection. |
There was a problem hiding this comment.
This stub instructs users to require the status check dependency-audit / Detect ecosystems, but existing docs/templates in this repo still reference requiring dependency-audit (e.g., standards/dependabot-policy.md and .github/workflows/dependency-audit.yml). Please align the guidance (and/or update the referenced docs) to the actual check context name produced by the reusable call, to avoid misconfigured branch protection.
| permissions: | ||
| contents: read | ||
|
|
There was a problem hiding this comment.
This reusable is a multi-job workflow but sets top-level permissions: contents: read and relies on implicit inheritance for all jobs. Per the repo’s permissions policy, multi-job workflows should reset top-level permissions to {} and scope permissions per job (standards/ci-standards.md#permissions-policy). Update this workflow to declare job-level permissions instead of a shared top-level grant.
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In @.github/workflows/agent-shield-reusable.yml:
- Around line 100-130: The frontmatter check currently uses grep patterns
'^name:' and '^description:' which only match fields at column 0; update the
validation to accept optional leading whitespace by changing those checks to
search for '^[[:space:]]*name:' and '^[[:space:]]*description:' (i.e., modify
the two grep invocations that read the frontmatter variable inside the while
loop), so indented YAML keys like "metadata: \n name: ..." are treated as
present while keeping the same error handling and status logic.
In @.github/workflows/dependabot-rebase-reusable.yml:
- Around line 117-123: The current jq logic for CHECKS_PASS and CHECKS_PENDING
treats an empty statusCheckRollup as passing (vacuous truth); update the gh pr
view/jq pipeline to compute a completed checks count (e.g., COMPLETED_COUNT from
[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") |
.conclusion] | length) and then require COMPLETED_COUNT > 0 in addition to
CHECKS_PASS being true and CHECKS_PENDING == 0 before treating the PR as
merge-ready; adjust the existing CHECKS_PASS and CHECKS_PENDING usage
accordingly so repos with no status checks won’t auto-merge.
In @.github/workflows/dependency-audit-reusable.yml:
- Around line 166-175: The loop currently iterates every Cargo.toml (causing
repeated audits for workspace members); change the find pipeline that feeds the
while loop so it yields each Rust workspace root once (Cargo.toml files
containing a [workspace] table) and also any standalone crate roots that are not
members of a workspace, then iterate those unique roots in the existing while
read loop. In practice update the generator used before "while IFS= read -r dir;
do" to filter results by detecting "[workspace]" in Cargo.toml (e.g. grep/awk on
each Cargo.toml to emit its dirname only if it contains "[workspace]") and
include non-member Cargo.toml dirs once (exclude files under discovered
workspace member paths), so the loop over the block that contains the cargo
generate-lockfile/cargo audit commands runs per workspace root instead of per
Cargo.toml.
- Around line 197-205: The loop currently makes the requirements.txt branch
unreachable when a directory has pyproject.toml; update the logic so both audits
run when both files exist (or choose the authoritative source explicitly).
Concretely, inside the while loop around the pip-audit calls (the block that
checks [ -f "$dir/pyproject.toml" ] and [ -f "$dir/requirements.txt" ]), invoke
pip-audit on pyproject.toml and then separately invoke pip-audit -r
"$dir/requirements.txt" if that file exists, setting status=1 on any failure;
alternatively, replace the if/elif with separate if checks to ensure
requirements.txt is not skipped. Ensure you update the pip-audit invocations
referenced in this loop so both files are audited when present.
In `@standards/workflows/dependency-audit.yml`:
- Around line 16-19: Update the guidance comment that tells users which status
check to add: the reusable workflow job is named detect (lowercase), so change
the suggested required status check string from "dependency-audit / Detect
ecosystems" to "dependency-audit / detect" in the header comment; locate the
comment around the top of standards/workflows/dependency-audit.yml referencing
the status check and replace the incorrect capitalized name with the exact job
name "dependency-audit / detect".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 79c89396-0419-4acc-8b68-c3d208796542
📒 Files selected for processing (9)
.github/workflows/agent-shield-reusable.yml.github/workflows/dependabot-automerge-reusable.yml.github/workflows/dependabot-rebase-reusable.yml.github/workflows/dependency-audit-reusable.ymlstandards/workflows/agent-shield.ymlstandards/workflows/claude.ymlstandards/workflows/dependabot-automerge.ymlstandards/workflows/dependabot-rebase.ymlstandards/workflows/dependency-audit.yml
| - name: Validate SKILL.md frontmatter | ||
| run: | | ||
| status=0 | ||
|
|
||
| while IFS= read -r file; do | ||
| frontmatter=$(awk \ | ||
| '/^---$/{n++; next} n==1{print} n>=2{exit}' \ | ||
| "$file") | ||
|
|
||
| if [ -z "$frontmatter" ]; then | ||
| echo "::error file=$file::Missing YAML frontmatter" | ||
| status=1 | ||
| continue | ||
| fi | ||
|
|
||
| if ! echo "$frontmatter" | grep -q '^name:'; then | ||
| echo "::error file=$file::Missing 'name' field" | ||
| status=1 | ||
| fi | ||
| if ! echo "$frontmatter" | grep -q '^description:'; then | ||
| echo "::error file=$file::Missing 'description' field" | ||
| status=1 | ||
| fi | ||
| done < <(find . -name 'SKILL.md' \ | ||
| -not -path '*/node_modules/*' \ | ||
| -not -path '*/.git/*') | ||
|
|
||
| if [ "$status" -eq 0 ]; then | ||
| echo "All SKILL.md frontmatter validated." | ||
| fi | ||
| exit $status |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Frontmatter validation assumes fields at column 0.
The grep patterns ^name: and ^description: (lines 115, 119) only match fields at the start of a line. Valid YAML frontmatter could have these fields indented (e.g., nested under a parent key):
---
metadata:
name: "My Skill"
description: "Does things"
---This would fail validation despite being valid YAML with the required fields present.
If indented fields should be supported, consider relaxing the patterns:
♻️ Optional: Support indented frontmatter fields
- if ! echo "$frontmatter" | grep -q '^name:'; then
+ if ! echo "$frontmatter" | grep -qE '^\s*name:'; then
echo "::error file=$file::Missing 'name' field"
status=1
fi
- if ! echo "$frontmatter" | grep -q '^description:'; then
+ if ! echo "$frontmatter" | grep -qE '^\s*description:'; then
echo "::error file=$file::Missing 'description' field"
status=1
fiIf the org standard explicitly requires top-level name: and description: fields, the current behavior is correct and this is a non-issue.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/agent-shield-reusable.yml around lines 100 - 130, The
frontmatter check currently uses grep patterns '^name:' and '^description:'
which only match fields at column 0; update the validation to accept optional
leading whitespace by changing those checks to search for '^[[:space:]]*name:'
and '^[[:space:]]*description:' (i.e., modify the two grep invocations that read
the frontmatter variable inside the while loop), so indented YAML keys like
"metadata: \n name: ..." are treated as present while keeping the same error
handling and status logic.
| CHECKS_PASS=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | ||
| --json statusCheckRollup \ | ||
| --jq '[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") | .conclusion] | all(. == "SUCCESS" or . == "NEUTRAL" or . == "SKIPPED")') | ||
|
|
||
| CHECKS_PENDING=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | ||
| --json statusCheckRollup \ | ||
| --jq '[.statusCheckRollup[]? | select(.name != null and .status != "COMPLETED")] | length') |
There was a problem hiding this comment.
Edge case: Empty statusCheckRollup may produce unexpected results.
When statusCheckRollup is empty or contains no items with status == "COMPLETED", the jq expression:
[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") | .conclusion] | all(. == "SUCCESS" or ...)Returns true for an empty array (vacuous truth). Combined with CHECKS_PENDING being 0 for an empty array, a PR with no status checks would be considered merge-ready.
If repos without required status checks should not auto-merge, consider adding an explicit check:
🛡️ Proposed fix to require at least one completed check
+ CHECKS_COUNT=$(gh pr view "$PR_NUMBER" --repo "$REPO" \
+ --json statusCheckRollup \
+ --jq '[.statusCheckRollup[]? | select(.name != null)] | length')
+
+ if [[ "$CHECKS_COUNT" -eq 0 ]]; then
+ echo " Skipping — no status checks configured"
+ continue
+ fi
+
CHECKS_PASS=$(gh pr view "$PR_NUMBER" --repo "$REPO" \🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/dependabot-rebase-reusable.yml around lines 117 - 123, The
current jq logic for CHECKS_PASS and CHECKS_PENDING treats an empty
statusCheckRollup as passing (vacuous truth); update the gh pr view/jq pipeline
to compute a completed checks count (e.g., COMPLETED_COUNT from
[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") |
.conclusion] | length) and then require COMPLETED_COUNT > 0 in addition to
CHECKS_PASS being true and CHECKS_PENDING == 0 before treating the PR as
merge-ready; adjust the existing CHECKS_PASS and CHECKS_PENDING usage
accordingly so repos with no status checks won’t auto-merge.
| # cargo audit operates on Cargo.lock at workspace root | ||
| # For workspaces, a single audit at root covers all crates | ||
| status=0 | ||
| while IFS= read -r dir; do | ||
| echo "::group::cargo audit $dir" | ||
| if ! (cd "$dir" && cargo generate-lockfile 2>/dev/null; cargo audit); then | ||
| status=1 | ||
| fi | ||
| echo "::endgroup::" | ||
| done < <(find . -name 'Cargo.toml' -not -path '*/target/*' -exec dirname {} \; | sort -u) |
There was a problem hiding this comment.
Audit each Rust workspace once, not once per Cargo.toml.
The comment on Lines 166-167 says a single workspace-root audit covers all crates, but the loop still enumerates every Cargo.toml. In a workspace this reruns the same advisory scan for each member and can regenerate the shared lockfile multiple times. Filter the input set down to workspace roots before invoking cargo audit.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/dependency-audit-reusable.yml around lines 166 - 175, The
loop currently iterates every Cargo.toml (causing repeated audits for workspace
members); change the find pipeline that feeds the while loop so it yields each
Rust workspace root once (Cargo.toml files containing a [workspace] table) and
also any standalone crate roots that are not members of a workspace, then
iterate those unique roots in the existing while read loop. In practice update
the generator used before "while IFS= read -r dir; do" to filter results by
detecting "[workspace]" in Cargo.toml (e.g. grep/awk on each Cargo.toml to emit
its dirname only if it contains "[workspace]") and include non-member Cargo.toml
dirs once (exclude files under discovered workspace member paths), so the loop
over the block that contains the cargo generate-lockfile/cargo audit commands
runs per workspace root instead of per Cargo.toml.
| while IFS= read -r dir; do | ||
| echo "::group::pip-audit $dir" | ||
| if [ -f "$dir/pyproject.toml" ]; then | ||
| if ! pip-audit "$dir"; then | ||
| status=1 | ||
| fi | ||
| elif [ -f "$dir/requirements.txt" ]; then | ||
| if ! pip-audit -r "$dir/requirements.txt"; then | ||
| status=1 |
There was a problem hiding this comment.
Don't let pyproject.toml suppress the requirements.txt audit.
Line 199 makes the requirements.txt branch unreachable for any directory that has both files. If that directory's pinned dependency set lives in requirements.txt, this workflow will silently skip auditing it. Prefer auditing the requirements file independently, or only taking the project-path branch when pyproject.toml is the real dependency source.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In @.github/workflows/dependency-audit-reusable.yml around lines 197 - 205, The
loop currently makes the requirements.txt branch unreachable when a directory
has pyproject.toml; update the logic so both audits run when both files exist
(or choose the authoritative source explicitly). Concretely, inside the while
loop around the pip-audit calls (the block that checks [ -f
"$dir/pyproject.toml" ] and [ -f "$dir/requirements.txt" ]), invoke pip-audit on
pyproject.toml and then separately invoke pip-audit -r "$dir/requirements.txt"
if that file exists, setting status=1 on any failure; alternatively, replace the
if/elif with separate if checks to ensure requirements.txt is not skipped.
Ensure you update the pip-audit invocations referenced in this loop so both
files are audited when present.
| # Dependency vulnerability audit — thin caller for the org-level reusable. | ||
| # To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo. | ||
| # Add "dependency-audit / Detect ecosystems" as a required status check | ||
| # in branch protection. |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check the job names defined in the reusable workflow
fd -t f 'dependency-audit-reusable.yml' .github/workflows --exec cat {} \; | grep -E '^\s+[a-z-]+:$' | head -20Repository: petry-projects/.github
Length of output: 277
Correct the status check name in the guidance comment.
The header comment advises adding "dependency-audit / Detect ecosystems" as a required status check. However, the reusable workflow's job is named detect (lowercase), so the actual status check name is dependency-audit / detect. Update line 18-19 to reflect the correct name:
Suggested fix
# Dependency vulnerability audit — thin caller for the org-level reusable.
# To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo.
-# Add "dependency-audit / Detect ecosystems" as a required status check
+# Add "dependency-audit / detect" as a required status check
# in branch protection.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Dependency vulnerability audit — thin caller for the org-level reusable. | |
| # To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo. | |
| # Add "dependency-audit / Detect ecosystems" as a required status check | |
| # in branch protection. | |
| # Dependency vulnerability audit — thin caller for the org-level reusable. | |
| # To adopt: copy this file to .github/workflows/dependency-audit.yml in your repo. | |
| # Add "dependency-audit / detect" as a required status check | |
| # in branch protection. |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@standards/workflows/dependency-audit.yml` around lines 16 - 19, Update the
guidance comment that tells users which status check to add: the reusable
workflow job is named detect (lowercase), so change the suggested required
status check string from "dependency-audit / Detect ecosystems" to
"dependency-audit / detect" in the header comment; locate the comment around the
top of standards/workflows/dependency-audit.yml referencing the status check and
replace the incorrect capitalized name with the exact job name "dependency-audit
/ detect".
Reusable workflows can be granted no more permissions than the calling
workflow has. The dependabot-automerge and dependabot-rebase stubs had
`permissions: {}` at workflow level with no job-level overrides, which
intersected to zero — the reusable's `gh pr ...` calls would fail
because GITHUB_TOKEN had no scopes.
Fix: declare `contents: read` and `pull-requests: read` on the calling
job, matching the scopes the reusable's job already declares. Caught
by Copilot review on #87.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Addressed Copilot review feedback in bc19033: granted CodeRabbit's five suggestions all flag pre-existing logic in the inherited templates (SKILL.md frontmatter regex, vacuous-truth status checks for empty rollups, cargo workspace dedup, pip requirements unreachable branch, status-check name string). Intentionally preserving behavior in this centralization PR — those are good follow-up issues but changing semantics here would expand scope and risk regressions in downstream repos that have been running the existing logic. Will file as separate issues. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@standards/workflows/dependabot-rebase.yml`:
- Around line 1-18: Update the immutable-stub contract text to include the
job-level permissions: stanza in the "MUST NOT change" list so adopters don't
remove it; specifically mention the permissions: stanza alongside the existing
items (trigger event, concurrency group name, the uses: line, and secrets:
inherit) and call out that this is required because the reusable workflow clears
the default workflow token unless explicit permissions are preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 7e779c27-1ddf-4ad7-99fc-d1ef66a848e3
📒 Files selected for processing (2)
standards/workflows/dependabot-automerge.ymlstandards/workflows/dependabot-rebase.yml
CodeRabbit follow-up on #87: now that the dependabot stubs declare a job-level permissions block (required for the reusable's gh API calls), add it to the "MUST NOT change" list so future adopters don't strip it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@standards/workflows/dependabot-rebase.yml`:
- Around line 39-43: The workflow caller sets permissions to read-only which
prevents the reusable workflow (uses:
petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main)
from performing gh api branch updates and merges; change the permissions block
so that at minimum contents: write and pull-requests: write are granted (and
keep secrets: inherit) so the reusable workflow can perform branch updates and
merges when invoked.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f53626c3-923f-4173-8432-a1732b3f6442
📒 Files selected for processing (2)
standards/workflows/dependabot-automerge.ymlstandards/workflows/dependabot-rebase.yml
| permissions: | ||
| contents: read | ||
| pull-requests: read | ||
| steps: | ||
| - name: Check app secrets | ||
| env: | ||
| APP_ID: ${{ secrets.APP_ID }} | ||
| run: | | ||
| if [[ -z "$APP_ID" ]]; then | ||
| echo "::error::APP_ID secret is missing. Set APP_ID and APP_PRIVATE_KEY in org secrets. See standards/dependabot-policy.md" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Generate app token | ||
| id: app-token | ||
| uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3 | ||
| with: | ||
| app-id: ${{ secrets.APP_ID }} | ||
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | ||
|
|
||
| - name: Update and merge Dependabot PRs | ||
| env: | ||
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | ||
| REPO: ${{ github.repository }} | ||
| run: | | ||
| # Find open Dependabot PRs | ||
| PRS=$(gh pr list --repo "$REPO" --author "app/dependabot" \ | ||
| --json number,headRefName \ | ||
| --jq '.[] | "\(.number) \(.headRefName)"') | ||
|
|
||
| if [[ -z "$PRS" ]]; then | ||
| echo "No open Dependabot PRs" | ||
| exit 0 | ||
| fi | ||
|
|
||
| MERGED=false | ||
|
|
||
| while IFS=' ' read -r PR_NUMBER HEAD_REF; do | ||
| BEHIND=$(gh api "repos/$REPO/compare/main...$HEAD_REF" \ | ||
| --jq '.behind_by') | ||
|
|
||
| if [[ "$BEHIND" -gt 0 ]]; then | ||
| echo "PR #$PR_NUMBER ($HEAD_REF) is $BEHIND commit(s) behind — merging main into branch" | ||
| gh api "repos/$REPO/pulls/$PR_NUMBER/update-branch" \ | ||
| -X PUT -f update_method=merge \ | ||
| --silent || echo "Warning: failed to update PR #$PR_NUMBER" | ||
| continue | ||
| fi | ||
|
|
||
| echo "PR #$PR_NUMBER ($HEAD_REF) is up to date — checking if merge-ready" | ||
|
|
||
| # Skip if we already merged one (strict mode means others are now behind) | ||
| if [[ "$MERGED" == "true" ]]; then | ||
| echo " Skipping — already merged a PR this run" | ||
| continue | ||
| fi | ||
|
|
||
| # Check if auto-merge is enabled (set by the automerge workflow) | ||
| AUTO_MERGE=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | ||
| --json autoMergeRequest --jq '.autoMergeRequest != null') | ||
|
|
||
| if [[ "$AUTO_MERGE" != "true" ]]; then | ||
| echo " Skipping — auto-merge not enabled" | ||
| continue | ||
| fi | ||
|
|
||
| # Check if all required checks pass (look at overall rollup) | ||
| CHECKS_PASS=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | ||
| --json statusCheckRollup \ | ||
| --jq '[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") | .conclusion] | all(. == "SUCCESS" or . == "NEUTRAL" or . == "SKIPPED")') | ||
|
|
||
| CHECKS_PENDING=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | ||
| --json statusCheckRollup \ | ||
| --jq '[.statusCheckRollup[]? | select(.name != null and .status != "COMPLETED")] | length') | ||
|
|
||
| if [[ "$CHECKS_PENDING" -gt 0 ]]; then | ||
| echo " Skipping — $CHECKS_PENDING check(s) still pending" | ||
| continue | ||
| fi | ||
|
|
||
| if [[ "$CHECKS_PASS" != "true" ]]; then | ||
| echo " Skipping — some checks failed" | ||
| continue | ||
| fi | ||
|
|
||
| echo " All checks pass — merging PR #$PR_NUMBER" | ||
| if gh api "repos/$REPO/pulls/$PR_NUMBER/merge" \ | ||
| -X PUT -f merge_method=squash \ | ||
| --silent; then | ||
| echo " Merged PR #$PR_NUMBER" | ||
| MERGED=true | ||
| else | ||
| echo " Warning: failed to merge PR #$PR_NUMBER" | ||
| fi | ||
| done <<< "$PRS" | ||
| uses: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main | ||
| secrets: inherit |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify caller permissions are read-only while reusable performs write actions.
rg -n --type=yaml 'contents:\s*read|pull-requests:\s*read' \
standards/workflows/dependabot-rebase.yml \
.github/workflows/dependabot-rebase-reusable.yml
rg -n --type=yaml 'update-branch|/pulls/\$PR_NUMBER/merge|gh pr merge' \
.github/workflows/dependabot-rebase-reusable.ymlRepository: petry-projects/.github
Length of output: 568
Change caller permissions to write scopes; read-only blocks branch updates and merges.
The caller job at lines 40–41 sets read-only scopes (contents: read, pull-requests: read), but the called reusable workflow performs gh api calls to update branches and merge PRs. When a reusable workflow is called with secrets: inherit, it inherits the caller's permissions, making the read-only scopes insufficient and causing the workflow to fail.
Fix
permissions:
- contents: read
- pull-requests: read
+ contents: write
+ pull-requests: write📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| permissions: | |
| contents: read | |
| pull-requests: read | |
| steps: | |
| - name: Check app secrets | |
| env: | |
| APP_ID: ${{ secrets.APP_ID }} | |
| run: | | |
| if [[ -z "$APP_ID" ]]; then | |
| echo "::error::APP_ID secret is missing. Set APP_ID and APP_PRIVATE_KEY in org secrets. See standards/dependabot-policy.md" | |
| exit 1 | |
| fi | |
| - name: Generate app token | |
| id: app-token | |
| uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3 | |
| with: | |
| app-id: ${{ secrets.APP_ID }} | |
| private-key: ${{ secrets.APP_PRIVATE_KEY }} | |
| - name: Update and merge Dependabot PRs | |
| env: | |
| GH_TOKEN: ${{ steps.app-token.outputs.token }} | |
| REPO: ${{ github.repository }} | |
| run: | | |
| # Find open Dependabot PRs | |
| PRS=$(gh pr list --repo "$REPO" --author "app/dependabot" \ | |
| --json number,headRefName \ | |
| --jq '.[] | "\(.number) \(.headRefName)"') | |
| if [[ -z "$PRS" ]]; then | |
| echo "No open Dependabot PRs" | |
| exit 0 | |
| fi | |
| MERGED=false | |
| while IFS=' ' read -r PR_NUMBER HEAD_REF; do | |
| BEHIND=$(gh api "repos/$REPO/compare/main...$HEAD_REF" \ | |
| --jq '.behind_by') | |
| if [[ "$BEHIND" -gt 0 ]]; then | |
| echo "PR #$PR_NUMBER ($HEAD_REF) is $BEHIND commit(s) behind — merging main into branch" | |
| gh api "repos/$REPO/pulls/$PR_NUMBER/update-branch" \ | |
| -X PUT -f update_method=merge \ | |
| --silent || echo "Warning: failed to update PR #$PR_NUMBER" | |
| continue | |
| fi | |
| echo "PR #$PR_NUMBER ($HEAD_REF) is up to date — checking if merge-ready" | |
| # Skip if we already merged one (strict mode means others are now behind) | |
| if [[ "$MERGED" == "true" ]]; then | |
| echo " Skipping — already merged a PR this run" | |
| continue | |
| fi | |
| # Check if auto-merge is enabled (set by the automerge workflow) | |
| AUTO_MERGE=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | |
| --json autoMergeRequest --jq '.autoMergeRequest != null') | |
| if [[ "$AUTO_MERGE" != "true" ]]; then | |
| echo " Skipping — auto-merge not enabled" | |
| continue | |
| fi | |
| # Check if all required checks pass (look at overall rollup) | |
| CHECKS_PASS=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | |
| --json statusCheckRollup \ | |
| --jq '[.statusCheckRollup[]? | select(.name != null and .status == "COMPLETED") | .conclusion] | all(. == "SUCCESS" or . == "NEUTRAL" or . == "SKIPPED")') | |
| CHECKS_PENDING=$(gh pr view "$PR_NUMBER" --repo "$REPO" \ | |
| --json statusCheckRollup \ | |
| --jq '[.statusCheckRollup[]? | select(.name != null and .status != "COMPLETED")] | length') | |
| if [[ "$CHECKS_PENDING" -gt 0 ]]; then | |
| echo " Skipping — $CHECKS_PENDING check(s) still pending" | |
| continue | |
| fi | |
| if [[ "$CHECKS_PASS" != "true" ]]; then | |
| echo " Skipping — some checks failed" | |
| continue | |
| fi | |
| echo " All checks pass — merging PR #$PR_NUMBER" | |
| if gh api "repos/$REPO/pulls/$PR_NUMBER/merge" \ | |
| -X PUT -f merge_method=squash \ | |
| --silent; then | |
| echo " Merged PR #$PR_NUMBER" | |
| MERGED=true | |
| else | |
| echo " Warning: failed to merge PR #$PR_NUMBER" | |
| fi | |
| done <<< "$PRS" | |
| uses: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main | |
| secrets: inherit | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| uses: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main | |
| secrets: inherit |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@standards/workflows/dependabot-rebase.yml` around lines 39 - 43, The workflow
caller sets permissions to read-only which prevents the reusable workflow (uses:
petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml@main)
from performing gh api branch updates and merges; change the permissions block
so that at minimum contents: write and pull-requests: write are granted (and
keep secrets: inherit) so the reusable workflow can perform branch updates and
merges when invoked.
|
@coderabbitai this analysis is incorrect for the dependabot-rebase reusable. The reusable uses a GitHub App token ( Resolving this thread; merging. |
|
Tip For best results, initiate chat on the files or code changes.
My suggestion to add ✏️ Learnings added
🧠 Learnings used |
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#52) Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-authored-by: DJ <dj@Rachels-MacBook-Air.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#149) Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-authored-by: DJ <dj@Rachels-MacBook-Air.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#93) Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…#82) Replace inline copies of standardized workflows with the canonical thin caller stubs from petry-projects/.github/standards/workflows/. Each stub delegates to a versioned reusable workflow at petry-projects/.github/.github/workflows/<name>-reusable.yml@v1, so future updates to the standard propagate automatically and drift is caught by the org-wide compliance audit. See petry-projects/.github#87, #88, #89 for context. Co-authored-by: DJ <dj@Rachels-MacBook-Air.local> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>



Summary
Extends the proven feature-ideation reusable-workflow pattern to the rest of the standardizable workflows. Builds four new reusables in
.github/workflows/and migrates the matchingstandards/workflows/*.ymltemplates from full inline copies to thin caller stubs that delegate viauses: petry-projects/.github/.github/workflows/<name>-reusable.yml@main.Net effect: ~580 lines removed from
standards/workflows; downstream repos that adopt the new stubs go from carrying ~115–220 lines per workflow to ~30 lines.What changed
New reusables in
.github/workflows/:dependency-audit-reusable.yml— zero per-repo config, multi-ecosystem detectiondependabot-automerge-reusable.yml—secrets: inheritforAPP_ID/APP_PRIVATE_KEYdependabot-rebase-reusable.yml— same secrets patternagent-shield-reusable.yml— exposesmin-severity,agentshield-version,required-files,org-standards-refasworkflow_callinputsMigrated to stubs in
standards/workflows/:claude.yml— was still the inline 115-line version even thoughclaude-code-reusable.ymlhas existed since feat: reusable Claude Code workflow with workflows write permission #77dependency-audit.ymldependabot-automerge.ymldependabot-rebase.ymlagent-shield.ymlEach migrated stub now carries a uniform "SOURCE OF TRUTH" header block telling agents what they may and may not edit.
Tier model
Test plan
actionlintclean on all 9 changed/added filesv1(in follow-up PR B), then sweep downstream repos to adopt stubs (follow-up)Follow-ups
ci.yml,codeql.yml,sonarcloud.yml); add tier-model section toci-standards.md; switch all stub@mainrefs to@v1and tag.compliance-audit-and-improvement.ymlto fail when downstream copies of centralized workflows are not thin stubs.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Refactor