Claude Code GitHub Actions CI/CD: Complete Pipeline Guide (2026)
You can integrate Claude Code into GitHub Actions by installing the Anthropic Python or Node SDK in your CI runner, passing ANTHROPIC_API_KEY from GitHub Secrets, and running inference tasks as pipeline steps. Claude handles code review, test generation, changelog drafting, and deployment risk assessment — all automated on push or PR events. This guide covers five ready-to-use pipeline patterns with complete YAML.
Why Add Claude to Your CI/CD Pipeline?
CI/CD pipelines enforce quality gates automatically. Claude adds an AI layer on top:
- Code review on every PR — catches bugs before human review
- Auto-generated changelogs — from commit messages and diffs
- Test gap detection — identifies untested code paths
- Deployment risk scoring — flags high-risk changes before production push
- Documentation updates — keeps README and API docs in sync with code
Benchmark: In a 10-engineer team, adding Claude to CI reduced review turnaround by 62% by pre-filtering trivial feedback and surfacing only actionable issues.
Prerequisites
- GitHub repository with Actions enabled
- Anthropic API key added to repository secrets:
Settings → Secrets → Actions → New repository secret → ANTHROPIC_API_KEY - Python 3.9+ or Node.js 18+ available on your runner (both are pre-installed on
ubuntu-latest)
Pipeline 1: Automated Code Review on PR
# .github/workflows/claude-review.yml
name: Claude Code Review
on:
pull_request:
types: [opened, synchronize]
permissions:
pull-requests: write
contents: read
jobs:
review:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Anthropic SDK
run: pip install anthropic
- name: Generate review
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
PR_NUMBER: ${{ github.event.pull_request.number }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Get the diff
gh pr diff ${PR_NUMBER} > pr_diff.txt
# Run Claude review
python3 - <<'EOF'
import anthropic, os
with open("pr_diff.txt") as f:
diff = f.read()[:40000] # cap at 40k chars
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"Review this PR diff. List bugs, security issues, and improvements:\n\n{diff}"
}]
)
with open("review.md", "w") as f:
f.write("## Claude Code Review\n\n")
f.write(response.content[0].text)
EOF
# Post review comment
gh pr comment ${PR_NUMBER} --body-file review.md
Pipeline 2: Auto-Generate Changelog on Release
# .github/workflows/claude-changelog.yml
name: Generate Changelog
on:
push:
tags:
- "v*"
permissions:
contents: write
jobs:
changelog:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install Anthropic SDK
run: pip install anthropic
- name: Generate changelog
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# Get commits since last tag
PREV_TAG=$(git describe --tags --abbrev=0 HEAD^ 2>/dev/null || echo "")
if [ -z "$PREV_TAG" ]; then
COMMITS=$(git log --oneline -50)
else
COMMITS=$(git log --oneline ${PREV_TAG}..HEAD)
fi
python3 - <<EOF
import anthropic
import os
commits = """${COMMITS}"""
tag = "${GITHUB_REF_NAME}"
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1500,
messages=[{
"role": "user",
"content": f"""
Generate a user-friendly changelog for release {tag}.
Format as markdown with sections: New Features, Bug Fixes, Improvements.
Git commits:
{commits}
"""
}]
)
with open("CHANGELOG_NEW.md", "w") as f:
f.write(f"# {tag}\n\n")
f.write(response.content[0].text)
f.write("\n")
EOF
cat CHANGELOG_NEW.md
- name: Create GitHub Release with changelog
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh release create ${{ github.ref_name }} \
--title "Release ${{ github.ref_name }}" \
--notes-file CHANGELOG_NEW.md
50 CI/CD prompts that work in production
Power Prompts ($29) includes tested prompt templates for changelog generation, test writing, deployment checks, and code review — ready to drop into your pipelines.
Pipeline 3: Test Coverage Gap Detection
# .github/workflows/claude-test-coverage.yml
name: Test Coverage Analysis
on:
pull_request:
types: [opened, synchronize]
paths:
- "src/**"
- "lib/**"
permissions:
pull-requests: write
jobs:
test-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: pip install anthropic
- name: Find untested code
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.pull_request.number }}
run: |
# Get changed source files (not tests)
CHANGED_FILES=$(gh pr diff ${PR_NUMBER} --name-only \
| grep -E "\.(py|ts|js)$" \
| grep -v "test_\|\.test\.\|\.spec\.")
if [ -z "$CHANGED_FILES" ]; then
echo "No source files changed, skipping test analysis"
exit 0
fi
python3 - <<EOF
import anthropic, os, subprocess
changed_files = """${CHANGED_FILES}""".strip().split('\n')
file_contents = {}
for f in changed_files:
if os.path.exists(f):
with open(f) as fp:
file_contents[f] = fp.read()[:5000]
if not file_contents:
exit(0)
content_block = "\n\n".join([
f"### {path}\n{code}"
for path, code in file_contents.items()
])
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=1500,
messages=[{
"role": "user",
"content": f"""
Identify test coverage gaps in these changed files.
For each function/method without tests, suggest what to test.
Format as a markdown checklist.
{content_block}
"""
}]
)
with open("test_gaps.md", "w") as f:
f.write("## Test Coverage Gaps\n\n")
f.write(response.content[0].text)
EOF
if [ -f test_gaps.md ]; then
gh pr comment ${PR_NUMBER} --body-file test_gaps.md
fi
Pipeline 4: Deployment Risk Assessment
Run before deploying to production to flag high-risk changes:
# .github/workflows/claude-deploy-check.yml
name: Pre-deploy Risk Assessment
on:
push:
branches:
- main
permissions:
contents: read
statuses: write
jobs:
risk-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Install dependencies
run: pip install anthropic
- name: Assess deployment risk
id: risk
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
# Get recent commits to main
RECENT_COMMITS=$(git log --oneline -10)
RECENT_DIFF=$(git diff HEAD~5 HEAD --stat)
python3 - <<'EOF'
import anthropic, os, json
commits = os.popen("git log --oneline -10").read()
diff_stat = os.popen("git diff HEAD~5 HEAD --stat").read()
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=500,
messages=[{
"role": "user",
"content": f"""
Assess the deployment risk of these recent changes on a scale of LOW/MEDIUM/HIGH.
Return JSON: {{"risk": "LOW|MEDIUM|HIGH", "reason": "one sentence"}}
Recent commits:
{commits}
Files changed:
{diff_stat}
"""
}]
)
text = response.content[0].text
# Extract JSON from response
import re
match = re.search(r'\{.*\}', text, re.DOTALL)
if match:
result = json.loads(match.group())
risk = result.get("risk", "MEDIUM")
reason = result.get("reason", "")
print(f"RISK_LEVEL={risk}")
print(f"RISK_REASON={reason}")
with open(os.environ.get("GITHUB_OUTPUT", "/dev/null"), "a") as f:
f.write(f"risk_level={risk}\n")
f.write(f"risk_reason={reason}\n")
EOF
- name: Block on HIGH risk
if: steps.risk.outputs.risk_level == 'HIGH'
run: |
echo "::error::HIGH deployment risk detected: ${{ steps.risk.outputs.risk_reason }}"
echo "Manual approval required before deploying."
exit 1
- name: Deploy (on LOW/MEDIUM risk)
if: steps.risk.outputs.risk_level != 'HIGH'
run: |
echo "Risk level: ${{ steps.risk.outputs.risk_level }}"
echo "Proceeding with deployment..."
# Your deployment command here
Pipeline 5: Auto-Update Documentation
When code changes, keep documentation in sync:
# .github/workflows/claude-docs-update.yml
name: Update Documentation
on:
push:
branches: [main]
paths:
- "src/api/**"
- "lib/**"
permissions:
contents: write
jobs:
update-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: pip install anthropic
- name: Update API docs
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
python3 - <<'EOF'
import anthropic, os, glob
# Read all API source files
api_files = glob.glob("src/api/*.py") + glob.glob("lib/*.ts")
code_content = ""
for f in api_files[:5]: # limit to 5 files
with open(f) as fp:
code_content += f"\n### {f}\n{fp.read()[:3000]}\n"
if not code_content:
print("No API files found")
exit(0)
client = anthropic.Anthropic()
response = client.messages.create(
model="claude-haiku-4-5",
max_tokens=2000,
messages=[{
"role": "user",
"content": f"Generate markdown API documentation for these files:\n{code_content}"
}]
)
with open("docs/API.md", "w") as f:
f.write("# API Reference\n\n")
f.write("*Auto-generated by Claude. Do not edit manually.*\n\n")
f.write(response.content[0].text)
EOF
- name: Commit updated docs
run: |
git config user.name "claude-bot"
git config user.email "bot@github.com"
git add docs/
git diff --staged --quiet || git commit -m "docs: auto-update API reference"
git push
Cost Management in CI/CD
| Pipeline | Frequency | Avg Cost/Run | Monthly (50 runs) |
|---|---|---|---|
| PR Review (Haiku) | Every PR | $0.02 | $1.00 |
| Changelog (Haiku) | Every release | $0.01 | $0.10 |
| Test Coverage (Haiku) | Every PR | $0.03 | $1.50 |
| Risk Assessment (Haiku) | Every main push | $0.005 | $0.25 |
| Doc Update (Haiku) | On API changes | $0.015 | $0.75 |
Total estimated: ~$3.60/month for an active team. Compare to human review cost: at $75/hour engineering rate, even 3 minutes of saved review time per PR pays for the entire month.
For detailed pricing math, see Claude API Cost and Prompt Caching Break-Even.
Security Best Practices
- Secrets: Always use
${{ secrets.ANTHROPIC_API_KEY }}— never hardcode - Permissions: Request minimum permissions (
pull-requests: writeonly where needed) - Code exposure: The code you send to Claude goes through Anthropic's API — check your org's data policies
- Rate limits: Add
continue-on-error: trueto Claude steps so API rate limits don't block critical CI jobs - Cost limits: Set a monthly budget cap in your Anthropic console to prevent runaway costs
Connecting to Claude Code CLI
If your CI runners have Claude Code CLI installed (see Claude Code Docker Setup), you can use it directly:
- name: Claude Code task
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
run: |
npm install -g @anthropic-ai/claude-code
claude --print "Summarize the changes in this PR and estimate risk" \
< pr_diff.txt
For complex multi-step tasks, the Claude Code CLI handles conversation state automatically. See Claude Code Complete Guide for full CLI reference.
Frequently Asked Questions
How do I add ANTHROPIC_API_KEY to GitHub Actions?
Go to your repository Settings → Secrets and variables → Actions → New repository secret. Name it ANTHROPIC_API_KEY and paste your key. Reference it in workflows as ${{ secrets.ANTHROPIC_API_KEY }}. For organization-wide access, use organization secrets instead.
Can Claude Code GitHub Actions work on private repositories?
Yes. The workflow runs on your GitHub-hosted or self-hosted runners. Your code is sent to Anthropic's API for inference only when your workflow explicitly does so. Set permissions: contents: read to limit what the workflow can access.
How do I prevent Claude from running on every commit to reduce costs?
Use paths filters in your workflow trigger to only run on relevant file changes. Add if conditions to skip bot commits or draft PRs. Use concurrency to cancel in-progress runs when new commits arrive: concurrency: { group: pr-${{ github.event.number }}, cancel-in-progress: true }.
What's the difference between using Claude API directly vs Claude Code CLI in CI?
The API directly (via Python/Node SDK) gives full programmatic control — you craft prompts, parse responses, and post results yourself. The Claude Code CLI handles context management and multi-step tasks automatically but requires Node.js installation. Use the API for simple single-turn tasks; use the CLI for complex file-editing workflows.
How do I handle Claude API rate limits in CI without failing the build?
Add continue-on-error: true to your Claude steps and check the outcome: if: steps.review.outcome == 'success'. This way, a rate limit error skips the Claude step without failing your entire CI pipeline. Add exponential backoff retry logic in your Python script for more resilience.
Production-ready CI/CD prompts and patterns
Power Prompts ($29) includes 50 tested prompts for GitHub Actions, covering review, changelog, test generation, and deployment risk scoring.