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
43 changes: 25 additions & 18 deletions .github/workflows/agent-shield.yml
Original file line number Diff line number Diff line change
@@ -1,26 +1,33 @@
# Agent Shield — thin caller that delegates to the org-level reusable workflow.
# All logic is maintained centrally in agent-shield-reusable.yml.
# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#required-workflows
name: Agent Shield
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/agent-shield.yml
# Standard: petry-projects/.github/standards/agent-standards.md
# Reusable: petry-projects/.github/.github/workflows/agent-shield-reusable.yml
#
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. The AgentShield CLI scan and the
# org-specific structural checks live in the reusable workflow above.
# • You MAY change: the `with:` inputs (min-severity, agentshield-version,
# required-files, org-standards-ref) — only if your repo genuinely needs
# a different policy.
# • You MUST NOT change: trigger events, the `uses:` line, or the job name
# (used as a required status check).
# • If you need different behaviour beyond the inputs, open a PR against
# the reusable in the central repo.
# ─────────────────────────────────────────────────────────────────────────────
#
# AgentShield — thin caller for the org-level reusable.
# To adopt: copy this file to .github/workflows/agent-shield.yml in your repo.
name: AgentShield

on:
push:
branches: [main]
pull_request:
branches: [main]
types: [opened, reopened, synchronize]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, edited, labeled]

permissions: {}
permissions:
contents: read

jobs:
agent-shield:
uses: petry-projects/.github/.github/workflows/agent-shield-reusable.yml@main
secrets: inherit
permissions:
contents: read
pull-requests: read
issues: read
uses: petry-projects/.github/.github/workflows/agent-shield-reusable.yml@v1
24 changes: 21 additions & 3 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/claude.yml
# Standard: petry-projects/.github/standards/ci-standards.md#4-claude-code-claudeyml
# Reusable: petry-projects/.github/.github/workflows/claude-code-reusable.yml
#
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. All Claude Code logic, the prompt,
# allowedTools, and trigger gating live in the reusable workflow above.
# • You MAY change: nothing in this file in normal use. Adopt verbatim.
# • You MUST NOT change: trigger events, job permissions, the `uses:` line,
# or `secrets: inherit`. These are required for the reusable to work.
# • If you need different behaviour, open a PR against the reusable in the
# central repo. The change will propagate everywhere on next run.
# ─────────────────────────────────────────────────────────────────────────────
#
# Claude Code — thin caller that delegates to the org-level reusable workflow.
# All logic and prompts are maintained centrally in claude-code-reusable.yml.
# Standard: https://github.com/petry-projects/.github/blob/main/standards/ci-standards.md#4-claude-code-claudeyml
# To adopt: copy this file to .github/workflows/claude.yml in your repo.
# Required org/repo secret: CLAUDE_CODE_OAUTH_TOKEN
# Optional org/repo secret: GH_PAT_WORKFLOWS (PAT with `workflow` scope —
# required if Claude needs to push changes to .github/workflows/*.yml)

name: Claude Code

on:
Expand All @@ -18,7 +36,7 @@ permissions: {}

jobs:
claude-code:
uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@main
uses: petry-projects/.github/.github/workflows/claude-code-reusable.yml@v1
secrets: inherit
permissions:
contents: write
Expand Down
81 changes: 26 additions & 55 deletions .github/workflows/dependabot-automerge.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,26 @@
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependabot-automerge.yml
# Standard: petry-projects/.github/standards/dependabot-policy.md
# Reusable: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml
#
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. All eligibility logic and the GitHub
# App token dance live in the reusable workflow above.
# • You MAY change: nothing in this file in normal use. Adopt verbatim.
# • You MUST NOT change: trigger event (must be `pull_request_target`),
# the `uses:` line, `secrets: inherit`, or the job-level `permissions:`
# block — reusable workflows can be granted no more permissions than the
# calling job has, so removing the stanza breaks the reusable's gh API
# calls.
# • If you need different behaviour, open a PR against the reusable in the
# central repo.
# ─────────────────────────────────────────────────────────────────────────────
#
# Dependabot auto-merge — thin caller for the org-level reusable.
# To adopt: copy this file to .github/workflows/dependabot-automerge.yml in your repo.
# Required org/repo secrets (inherited):
# APP_ID — GitHub App ID with contents:write and pull-requests:write
# APP_PRIVATE_KEY — GitHub App private key
name: Dependabot auto-merge

on:
Expand All @@ -8,61 +31,9 @@ on:
permissions: {}

jobs:
dependabot:
runs-on: ubuntu-latest
dependabot-automerge:
permissions:
contents: read
pull-requests: read
if: github.event.pull_request.user.login == 'dependabot[bot]'
steps:
- name: Dependabot metadata
id: metadata
uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v2
with:
github-token: '${{ secrets.GITHUB_TOKEN }}'
skip-commit-verification: true

- name: Generate app token
if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.dependency-type == 'indirect'
id: app-token
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}

- name: Approve, resolve threads, and auto-merge
if: steps.metadata.outputs.update-type == 'version-update:semver-patch' || steps.metadata.outputs.update-type == 'version-update:semver-minor' || steps.metadata.outputs.dependency-type == 'indirect'
env:
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
REPO_OWNER: ${{ github.repository_owner }}
REPO_NAME: ${{ github.event.repository.name }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
run: |
# Approve the PR
gh pr review --approve "$PR_URL"

# Resolve all review threads (from Copilot, CodeRabbit, etc.)
THREADS=$(gh api graphql -f query='
query($owner: String!, $repo: String!, $pr: Int!) {
repository(owner: $owner, name: $repo) {
pullRequest(number: $pr) {
reviewThreads(first: 100) {
nodes { id isResolved }
}
}
}
}' -f owner="$REPO_OWNER" -f repo="$REPO_NAME" -F pr="$PR_NUMBER" \
--jq '.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false) | .id')

for THREAD_ID in $THREADS; do
gh api graphql -f query='
mutation($id: ID!) {
resolveReviewThread(input: {threadId: $id}) {
thread { isResolved }
}
}' -f id="$THREAD_ID" --silent
done

# Queue auto-merge (waits for all checks to pass)
gh pr merge --squash --auto "$PR_URL"
uses: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml@v1
secrets: inherit
131 changes: 23 additions & 108 deletions .github/workflows/dependabot-rebase.yml
Original file line number Diff line number Diff line change
@@ -1,29 +1,26 @@
# Dependabot update and merge workflow
# Copy to .github/workflows/dependabot-rebase.yml
# ─────────────────────────────────────────────────────────────────────────────
# SOURCE OF TRUTH: petry-projects/.github/standards/workflows/dependabot-rebase.yml
# Standard: petry-projects/.github/standards/dependabot-policy.md
# Reusable: petry-projects/.github/.github/workflows/dependabot-rebase-reusable.yml
#
# Requires repository secrets:
# AGENTS — READ BEFORE EDITING:
# • This file is a THIN CALLER STUB. All rebase/merge serialization logic
# lives in the reusable workflow above.
# • You MAY change: nothing in this file in normal use. Adopt verbatim.
# • You MUST NOT change: trigger event, the concurrency group name,
# the `uses:` line, `secrets: inherit`, or the job-level `permissions:`
# block — reusable workflows can be granted no more permissions than the
# calling job has, so removing the stanza breaks the reusable's gh API
# calls.
# • If you need different behaviour, open a PR against the reusable in the
# central repo.
# ─────────────────────────────────────────────────────────────────────────────
#
# Dependabot update-and-merge — thin caller for the org-level reusable.
# To adopt: copy this file to .github/workflows/dependabot-rebase.yml in your repo.
# Required org/repo secrets (inherited):
# APP_ID — GitHub App ID with contents:write and pull-requests:write
# APP_PRIVATE_KEY — GitHub App private key
#
# Problem: when branch protection requires branches to be up-to-date
# (strict status checks), merging one Dependabot PR makes the others fall
# behind. Dependabot does not auto-rebase PRs that are merely behind — it
# only rebases on its next scheduled run or when there are merge conflicts.
# Additionally, GitHub auto-merge (--auto) may not trigger when rulesets
# cause mergeable_state to report "blocked" even though all requirements
# are actually met.
#
# Solution: after every push to main (typically a merged PR), this workflow:
# 1. Updates behind Dependabot PRs using the merge method (not rebase)
# 2. Merges any Dependabot PR that is up-to-date, approved, and passing CI
#
# Using the app token for merges ensures the resulting push to main triggers
# this workflow again, creating a self-sustaining chain that serializes
# Dependabot PR merges one at a time.
#
# Note: the merge commit from update-branch is authored by GitHub, not
# Dependabot, so the dependabot-automerge workflow must use
# skip-commit-verification: true in the dependabot/fetch-metadata step.
name: Dependabot update and merge

on:
Expand All @@ -38,91 +35,9 @@ concurrency:
permissions: {}

jobs:
update-and-merge:
runs-on: ubuntu-latest
dependabot-rebase:
permissions:
contents: read
pull-requests: read
steps:
- 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@v1
secrets: inherit
Loading
Loading