Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
10 changes: 10 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
open-pull-requests-limit: 10
labels:
- "security"
- "dependencies"
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions: {}

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
lint:
name: Lint
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Lint Markdown
uses: DavidAnson/markdownlint-cli2-action@5b7c9f74fec47e6b15667b2cc23c63dff11e449e # v9.0.0
with:
globs: '**/*.md'

- name: Lint YAML
run: |
pip install --quiet yamllint
yamllint -c <(cat <<'YAMLLINTRC'
extends: default
rules:
line-length:
max: 200
truthy:
check-keys: false
document-start: disable
comments:
min-spaces-from-content: 1
YAMLLINTRC
) .github/workflows/ standards/workflows/ standards/dependabot/

- name: Lint GitHub Actions
run: |
# Install actionlint
bash <(curl -s https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-bash.sh)
Comment thread
don-petry marked this conversation as resolved.
Outdated
# Lint all workflow files (our own and templates)
./actionlint .github/workflows/*.yml || true
# Template workflows may use placeholder expressions — lint with relaxed mode
./actionlint -ignore 'context' standards/workflows/*.yml || true
Comment thread
don-petry marked this conversation as resolved.
Outdated
Comment thread
don-petry marked this conversation as resolved.

shellcheck:
name: ShellCheck
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- name: Run ShellCheck
run: |
shellcheck --severity=warning scripts/*.sh
Comment thread
don-petry marked this conversation as resolved.
Outdated

agent-security:
name: Agent Security Scan
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2

- uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6.3.0
with:
node-version: '20'
Comment thread
don-petry marked this conversation as resolved.

- name: Run AgentShield
run: |
npx ecc-agentshield scan --path . --format json --min-severity high
Comment thread
don-petry marked this conversation as resolved.
37 changes: 37 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
name: Claude Code

on:
pull_request:
branches: [main]
types: [opened, reopened, synchronize]
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]

permissions: {}

jobs:
claude:
if: >-
(github.event_name == 'pull_request' &&
github.event.pull_request.head.repo.full_name == github.repository) ||
(github.event_name == 'issue_comment' && github.event.issue.pull_request &&
contains(github.event.comment.body, '@claude') &&
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) ||
(github.event_name == 'pull_request_review_comment' &&
contains(github.event.comment.body, '@claude') &&
contains(fromJson('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association))
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: read
id-token: write
pull-requests: write
issues: write
steps:
- name: Run Claude Code
if: github.event_name != 'pull_request' || github.event.pull_request.user.login != 'dependabot[bot]'
uses: anthropics/claude-code-action@1eddb334cfa79fdb21ecbe2180ca1a016e8e7d47 # v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
78 changes: 78 additions & 0 deletions .github/workflows/dependabot-automerge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Dependabot auto-merge workflow
# Copy to .github/workflows/dependabot-automerge.yml
#
# Requires repository secrets:
# APP_ID — GitHub App ID with contents:write and pull-requests:write
# APP_PRIVATE_KEY — GitHub App private key
#
# Auto-approves and enables auto-merge for Dependabot PRs that are:
# - GitHub Actions updates (patch or minor version bumps)
# - Security updates for any ecosystem (patch or minor)
# - Indirect (transitive) dependency updates
# Major version updates are always left for human review.
# Uses --auto so the merge waits for all required CI checks to pass.
#
# Safety model: application ecosystems use open-pull-requests-limit: 0 in
# dependabot.yml, so the only app-ecosystem PRs Dependabot can create are
# security updates. This workflow adds defense-in-depth by also checking
# the package ecosystem.
name: Dependabot auto-merge

on:
pull_request_target:
branches:
- main

permissions: {}

jobs:
dependabot:
runs-on: ubuntu-latest
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 }}"

- name: Determine if auto-merge eligible
id: eligible
run: |
UPDATE_TYPE="${{ steps.metadata.outputs.update-type }}"
DEP_TYPE="${{ steps.metadata.outputs.dependency-type }}"
ECOSYSTEM="${{ steps.metadata.outputs.package-ecosystem }}"

# Must be patch, minor, or indirect
if [[ "$UPDATE_TYPE" != "version-update:semver-patch" && \
"$UPDATE_TYPE" != "version-update:semver-minor" && \
"$DEP_TYPE" != "indirect" ]]; then
echo "eligible=false" >> "$GITHUB_OUTPUT"
echo "Skipping: major update requires human review"
exit 0
fi

# GitHub Actions version updates are always eligible
# App ecosystem PRs can only exist as security updates (limit: 0)
echo "eligible=true" >> "$GITHUB_OUTPUT"
echo "Auto-merge eligible: ecosystem=$ECOSYSTEM update=$UPDATE_TYPE"
Comment thread
don-petry marked this conversation as resolved.

- 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 }}
Loading
Loading