mirror of
https://github.com/Sea-Haven-Industries/.github.git
synced 2026-09-30 07:03:11 +00:00
Add Claude Code review and compliance audit workflows
- Reusable PR review workflow (repos call via thin wrapper) - Weekly compliance audit across all org repos - Rollout script to push wrapper workflow to all repos - Uses GitHub App tokens for cross-repo auth (no PAT rotation needed)
This commit is contained in:
commit
e24004415e
5 changed files with 281 additions and 0 deletions
32
.github/workflows/claude-code-review.yaml
vendored
Normal file
32
.github/workflows/claude-code-review.yaml
vendored
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
name: Claude Code Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_call:
|
||||||
|
secrets:
|
||||||
|
anthropic_api_key:
|
||||||
|
required: true
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
claude-review:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.anthropic_api_key }}
|
||||||
|
review_comments: "true"
|
||||||
|
direct_prompt: |
|
||||||
|
Review this pull request. Focus on:
|
||||||
|
- Code correctness and potential bugs
|
||||||
|
- Security issues (hardcoded secrets, injection, OWASP top 10)
|
||||||
|
- Sea Haven conventions: kebab-case resource names, secrets in AWS Secrets Manager (not env vars), Lambda defaults (Python 3.12+, arm64, explicit 60-day log retention)
|
||||||
|
- README accuracy if changed
|
||||||
|
Post findings as inline review comments. Be concise — flag real issues, skip nitpicks.
|
||||||
106
.github/workflows/compliance-audit.yaml
vendored
Normal file
106
.github/workflows/compliance-audit.yaml
vendored
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
name: Compliance Audit
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 14 * * 1" # Every Monday at 10am ET (14:00 UTC)
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
get-repos:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
outputs:
|
||||||
|
repos: ${{ steps.list.outputs.repos }}
|
||||||
|
steps:
|
||||||
|
- name: Generate GitHub App token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.CLAUDE_CI_APP_ID }}
|
||||||
|
private-key: ${{ secrets.CLAUDE_CI_APP_PRIVATE_KEY }}
|
||||||
|
owner: Sea-Haven-Industries
|
||||||
|
|
||||||
|
- name: List org repos
|
||||||
|
id: list
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
run: |
|
||||||
|
repos=$(gh repo list Sea-Haven-Industries \
|
||||||
|
--no-archived \
|
||||||
|
--json name \
|
||||||
|
--jq '[.[].name] | @json' \
|
||||||
|
--limit 100)
|
||||||
|
echo "repos=$repos" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
audit:
|
||||||
|
needs: get-repos
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
timeout-minutes: 15
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 3
|
||||||
|
matrix:
|
||||||
|
repo: ${{ fromJson(needs.get-repos.outputs.repos) }}
|
||||||
|
steps:
|
||||||
|
- name: Generate GitHub App token
|
||||||
|
id: app-token
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.CLAUDE_CI_APP_ID }}
|
||||||
|
private-key: ${{ secrets.CLAUDE_CI_APP_PRIVATE_KEY }}
|
||||||
|
owner: Sea-Haven-Industries
|
||||||
|
repositories: ${{ matrix.repo }}
|
||||||
|
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
repository: Sea-Haven-Industries/${{ matrix.repo }}
|
||||||
|
token: ${{ steps.app-token.outputs.token }}
|
||||||
|
|
||||||
|
- name: Run compliance audit
|
||||||
|
uses: anthropics/claude-code-action@v1
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
direct_prompt: |
|
||||||
|
Audit this repository for Sea Haven Industries compliance. Check each item and report pass/fail:
|
||||||
|
|
||||||
|
**Naming:**
|
||||||
|
- All resource names in IaC templates use kebab-case (no snake_case or PascalCase)
|
||||||
|
- Stack name matches repo name
|
||||||
|
|
||||||
|
**Secrets:**
|
||||||
|
- No secrets in Lambda environment variables
|
||||||
|
- No secrets in SSM Parameter Store (should be in Secrets Manager)
|
||||||
|
- No hardcoded API keys, tokens, or credentials in source code
|
||||||
|
- Secret names follow `stack-name/secret-name` convention
|
||||||
|
|
||||||
|
**Lambda defaults (if applicable):**
|
||||||
|
- Runtime is Python 3.12+ or Node 22.x
|
||||||
|
- Architecture is arm64
|
||||||
|
- Log retention is explicitly set to 60 days in the IaC template
|
||||||
|
|
||||||
|
**Project hygiene:**
|
||||||
|
- README exists and describes the project architecture
|
||||||
|
- .gitignore exists and covers .env, .aws-sam/, __pycache__
|
||||||
|
- samconfig.toml is gitignored (samconfig.toml.example committed if SAM project)
|
||||||
|
- CloudFormation outputs include function ARNs and URLs
|
||||||
|
|
||||||
|
Output a concise markdown report. Only flag actual violations — don't report items that don't apply (e.g., skip Lambda checks if no Lambdas exist).
|
||||||
|
|
||||||
|
- name: Create issue if violations found
|
||||||
|
if: failure()
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||||
|
run: |
|
||||||
|
existing=$(gh issue list \
|
||||||
|
--repo "Sea-Haven-Industries/${{ matrix.repo }}" \
|
||||||
|
--label "compliance" \
|
||||||
|
--state open \
|
||||||
|
--json number \
|
||||||
|
--jq 'length')
|
||||||
|
if [ "$existing" -eq 0 ]; then
|
||||||
|
gh issue create \
|
||||||
|
--repo "Sea-Haven-Industries/${{ matrix.repo }}" \
|
||||||
|
--title "Compliance audit: violations found" \
|
||||||
|
--body "The weekly compliance audit found violations in this repo. Check the [latest audit run](https://github.com/Sea-Haven-Industries/.github/actions/workflows/compliance-audit.yaml) for details." \
|
||||||
|
--label "compliance"
|
||||||
|
fi
|
||||||
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
.DS_Store
|
||||||
|
.env
|
||||||
57
README.md
Normal file
57
README.md
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# .github
|
||||||
|
|
||||||
|
Organization-level GitHub configuration for Sea Haven Industries.
|
||||||
|
|
||||||
|
## What's in here
|
||||||
|
|
||||||
|
### Reusable Workflows
|
||||||
|
|
||||||
|
**`.github/workflows/claude-code-review.yaml`** — Reusable PR review workflow powered by Claude Code. Individual repos call this via a thin wrapper workflow. Reviews for code correctness, security issues, and Sea Haven conventions (kebab-case, secrets placement, Lambda defaults).
|
||||||
|
|
||||||
|
**`.github/workflows/compliance-audit.yaml`** — Scheduled weekly audit (Mondays 10am ET) that checks all org repos for compliance with Sea Haven conventions. Creates GitHub issues on repos with violations. Can also be triggered manually via `workflow_dispatch`.
|
||||||
|
|
||||||
|
### Scripts
|
||||||
|
|
||||||
|
**`scripts/rollout-review-workflow.sh`** — One-time script to push the thin PR review wrapper workflow to all org repos via the GitHub API. Creates a branch and PR on each repo.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### 1. Create a GitHub App
|
||||||
|
|
||||||
|
1. Go to **Organization Settings > Developer settings > GitHub Apps > New GitHub App**
|
||||||
|
2. Name it `claude-code-ci` (or similar)
|
||||||
|
3. Set Homepage URL to your org URL
|
||||||
|
4. Disable Webhook (uncheck "Active")
|
||||||
|
5. Set these **Repository permissions:**
|
||||||
|
- **Contents:** Read and write
|
||||||
|
- **Issues:** Read and write
|
||||||
|
- **Metadata:** Read-only
|
||||||
|
- **Pull requests:** Read and write
|
||||||
|
6. Set **Where can this app be installed?** to "Only on this account"
|
||||||
|
7. Click **Create GitHub App**
|
||||||
|
8. Note the **App ID** from the app's settings page
|
||||||
|
9. Under **Private keys**, click **Generate a private key** — save the `.pem` file
|
||||||
|
|
||||||
|
### 2. Install the App
|
||||||
|
|
||||||
|
1. From the app's settings page, click **Install App**
|
||||||
|
2. Select `Sea-Haven-Industries`
|
||||||
|
3. Choose **All repositories**
|
||||||
|
|
||||||
|
### 3. Add org-level secrets
|
||||||
|
|
||||||
|
Go to **Organization Settings > Secrets and variables > Actions** and add:
|
||||||
|
|
||||||
|
| Secret | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| `ANTHROPIC_API_KEY` | Your Claude API key |
|
||||||
|
| `CLAUDE_CI_APP_ID` | The App ID from step 1 |
|
||||||
|
| `CLAUDE_CI_APP_PRIVATE_KEY` | The full contents of the `.pem` file from step 1 |
|
||||||
|
|
||||||
|
### 4. Roll out PR reviews to repos
|
||||||
|
|
||||||
|
```bash
|
||||||
|
./scripts/rollout-review-workflow.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a PR on each repo adding the thin wrapper workflow. Review and merge them, then delete the `add-claude-review` branches.
|
||||||
84
scripts/rollout-review-workflow.sh
Executable file
84
scripts/rollout-review-workflow.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ORG="Sea-Haven-Industries"
|
||||||
|
BRANCH="add-claude-review"
|
||||||
|
WORKFLOW_PATH=".github/workflows/claude-review.yaml"
|
||||||
|
COMMIT_MSG="Add Claude Code review workflow"
|
||||||
|
|
||||||
|
WORKFLOW_CONTENT='name: Claude Code Review
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
review:
|
||||||
|
uses: Sea-Haven-Industries/.github/.github/workflows/claude-code-review.yaml@main
|
||||||
|
secrets:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
'
|
||||||
|
|
||||||
|
SKIP_REPOS=(".github")
|
||||||
|
|
||||||
|
should_skip() {
|
||||||
|
local repo="$1"
|
||||||
|
for skip in "${SKIP_REPOS[@]}"; do
|
||||||
|
if [[ "$repo" == "$skip" ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "Fetching non-archived repos from $ORG..."
|
||||||
|
repos=$(gh repo list "$ORG" --no-archived --json name --jq '.[].name' --limit 100)
|
||||||
|
|
||||||
|
for repo in $repos; do
|
||||||
|
if should_skip "$repo"; then
|
||||||
|
echo "SKIP $repo (in skip list)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "--- $repo ---"
|
||||||
|
|
||||||
|
existing=$(gh api "repos/$ORG/$repo/contents/$WORKFLOW_PATH" --jq '.sha' 2>/dev/null || true)
|
||||||
|
if [[ -n "$existing" ]]; then
|
||||||
|
echo "SKIP $repo (workflow already exists)"
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
default_branch=$(gh api "repos/$ORG/$repo" --jq '.default_branch')
|
||||||
|
|
||||||
|
encoded=$(echo -n "$WORKFLOW_CONTENT" | base64)
|
||||||
|
|
||||||
|
gh api "repos/$ORG/$repo/git/refs" \
|
||||||
|
-f "ref=refs/heads/$BRANCH" \
|
||||||
|
-f "sha=$(gh api "repos/$ORG/$repo/git/ref/heads/$default_branch" --jq '.object.sha')" \
|
||||||
|
2>/dev/null || true
|
||||||
|
|
||||||
|
gh api "repos/$ORG/$repo/contents/$WORKFLOW_PATH" \
|
||||||
|
-X PUT \
|
||||||
|
-f "message=$COMMIT_MSG" \
|
||||||
|
-f "content=$encoded" \
|
||||||
|
-f "branch=$BRANCH" \
|
||||||
|
> /dev/null
|
||||||
|
|
||||||
|
pr_url=$(gh pr create \
|
||||||
|
--repo "$ORG/$repo" \
|
||||||
|
--base "$default_branch" \
|
||||||
|
--head "$BRANCH" \
|
||||||
|
--title "$COMMIT_MSG" \
|
||||||
|
--body "Adds a thin workflow that delegates PR reviews to the central reusable workflow in \`Sea-Haven-Industries/.github\`. Requires the \`ANTHROPIC_API_KEY\` org secret to be set." \
|
||||||
|
2>/dev/null || echo "PR already exists")
|
||||||
|
|
||||||
|
echo "DONE $repo → $pr_url"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Rollout complete. Review and merge the PRs, then delete the feature branches."
|
||||||
Loading…
Add table
Reference in a new issue