Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
144 changes: 20 additions & 124 deletions .github/workflows/agent-shield.yml
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
# AgentShield β€” Agent configuration security validation
# Two-layer approach:
# 1. affaan-m/agentshield β€” deep security scan (102 rules)
# 2. Org-specific structural checks (required files, frontmatter)

# ─────────────────────────────────────────────────────────────────────────────
# 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:
Expand All @@ -16,122 +30,4 @@ permissions:

jobs:
agent-shield:
Comment thread
don-petry marked this conversation as resolved.
Resolved
name: AgentShield
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

# --- Deep security scan via AgentShield CLI ---
# Uses ecc-agentshield (https://github.com/affaan-m/agentshield)
# 102 rules: secrets, permissions, hooks, MCP servers, agent config
- name: AgentShield Security Scan
run: |
npx ecc-agentshield@1.4.0 scan \
--path . \
--min-severity high \
--format terminal

# --- Org-specific structural checks ---
- name: Check required agent files exist
run: |
status=0

if [ ! -f "CLAUDE.md" ]; then
echo "::error::Missing CLAUDE.md"
status=1
fi

if [ ! -f "AGENTS.md" ]; then
echo "::error::Missing AGENTS.md"
status=1
fi

exit $status

- name: Validate cross-references
run: |
status=0

if [ -f "CLAUDE.md" ] && \
! grep -qi 'AGENTS.md' CLAUDE.md; then
echo "::error file=CLAUDE.md::Must reference AGENTS.md"
status=1
fi

if [ -f "AGENTS.md" ] && \
! grep -qi 'petry-projects/\.github' AGENTS.md; then
echo "::error file=AGENTS.md::Must reference org standards"
status=1
fi

exit $status

- 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

- name: Validate module-help.csv consistency
if: hashFiles('src/module-help.csv') != ''
run: |
status=0

if [ -d "src/agents" ]; then
for dir in src/agents/*/; do
[ -d "$dir" ] || continue
skill_name=$(basename "$dir")
if ! awk -F',' -v name="$skill_name" \
'{gsub(/^[ ]+|[ ]+$/, "", $2)}
$2 == name {found=1; exit}
END {exit !found}' src/module-help.csv; then
echo "::error::Agent $skill_name not in module-help.csv"
status=1
fi
done
fi

if [ -d "src/workflows" ]; then
for dir in src/workflows/*/; do
[ -d "$dir" ] || continue
skill_name=$(basename "$dir")
if ! awk -F',' -v name="$skill_name" \
'{gsub(/^[ ]+|[ ]+$/, "", $2)}
$2 == name {found=1; exit}
END {exit !found}' src/module-help.csv; then
echo "::error::Workflow $skill_name not in module-help.csv"
status=1
fi
done
fi

if [ "$status" -eq 0 ]; then
echo "Module-help.csv consistency check passed."
fi
exit $status
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
80 changes: 26 additions & 54 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,60 +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: Determine if auto-merge eligible
id: eligible
run: |
UPDATE_TYPE="${{ steps.metadata.outputs.update-type }}"
DEPENDENCY_TYPE="${{ steps.metadata.outputs.dependency-type }}"
ECOSYSTEM="${{ steps.metadata.outputs.package-ecosystem }}"

# Auto-merge direct dependencies: minor and patch only
# Auto-merge indirect dependencies: patch only (minor/major require review)
if [[ "$DEPENDENCY_TYPE" == "indirect" ]]; then
if [[ "$UPDATE_TYPE" != "version-update:semver-patch" ]]; then
echo "eligible=false" >> "$GITHUB_OUTPUT"
echo "Skipping: indirect dependency updates only auto-merge for patch"
exit 0
fi
else
if [[ "$UPDATE_TYPE" != "version-update:semver-patch" && \
"$UPDATE_TYPE" != "version-update:semver-minor" ]]; then
echo "eligible=false" >> "$GITHUB_OUTPUT"
echo "Skipping: only patch/minor updates are auto-merge eligible"
exit 0
fi
fi

echo "eligible=true" >> "$GITHUB_OUTPUT"
echo "Auto-merge eligible: ecosystem=$ECOSYSTEM update=$UPDATE_TYPE dependency=$DEPENDENCY_TYPE"

- name: Generate app token
if: steps.eligible.outputs.eligible == 'true'
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 and enable auto-merge
if: steps.eligible.outputs.eligible == 'true'
run: |
gh pr review --approve "$PR_URL"
gh pr merge --auto --squash "$PR_URL"
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ steps.app-token.outputs.token }}
uses: petry-projects/.github/.github/workflows/dependabot-automerge-reusable.yml@v1
secrets: inherit
Loading
Loading