Originally published at claudeguide.io/claude-code-github-actions-cicd
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 in 2026. 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}
---
## Pipeline 3: Test Coverage Gap Detection
yaml
.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:
yaml
.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:
yaml
.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](/claude-api-cost-prompt-caching-break-even).
---
## Security Best Practices
1. **Secrets**: Always use `${{ secrets.ANTHROPIC_API_KEY }}` — never hardcode
2. **Permissions**: Request minimum permissions (`pull-requests: write` only where needed)
3. **Code exposure**: The code you send to Claude goes through Anthropic's API — check your org's data policies
4. **Rate limits**: Add `continue-on-error: true` to Claude steps so API rate limits don't block critical CI jobs
5. **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](/claude-code-docker-setup)), you can use it directly:
yaml
- 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](/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.
---
Top comments (0)