Lesson 42: Lab — CI Pipeline with Claude Code
Day 42 of 50 · ~20 min hands-on · Phase 5: Advanced Patterns
The Mission
Build a working GitHub Actions CI/CD pipeline that uses Claude Code to:
- Automatically review pull requests when they're created
- Analyze code quality (security, style, efficiency)
- Leave comments with suggestions
- Optionally auto-fix simple issues (formatting, linting)
All hands-off. No human in the loop. Claude Code runs on your CI infrastructure and does the work.
What You'll Practice
- Headless mode (Lesson 36): Claude Code running without interactive prompts
- Permissions (Lesson 17): Setting what Claude can and cannot do
- Git workflows (Lesson 13): Creating commits and PRs
- Hooks (Lesson 27): Triggering Claude Code on events
- Security (Lesson 39): Reviewing what headless Claude is allowed to do
Setup
Prerequisites
- A GitHub repository (public or private)
- GitHub Actions enabled
- Claude Code installed locally
- A working
.envwith ANTHROPIC_API_KEY
Files You'll Create
.github/workflows/claude-review.yml— GitHub Actions workflow.claude-code/review-config.md— Claude Code configurationscripts/claude-review.sh— Helper script
Step 1: Configure Claude Code Permissions
Create .claude-code/review-config.md:
# Claude Code PR Review Configuration
## What Claude Can Read
- All `.js`, `.ts`, `.py`, `.go` files in `src/` and `tests/`
- `package.json`, `pyproject.toml`, `go.mod` (dependencies)
- `README.md` (context)
- NOT: `.env`, secrets, credentials, private keys
## What Claude Can Edit
- Nothing in production code initially (review-only mode)
- Can create comments on GitHub PRs
- Can log findings to review-report.md
## What Claude Can Execute
- `git` commands (read-only: diff, log, show)
- Analysis tools: `npm test`, `python -m pytest` (read-only)
- NOT: `npm publish`, `git push`, any destructive commands
## Review Criteria
1. Security issues (eval, SQL injection, unsafe parsing)
2. Performance problems (N+1 queries, memory leaks)
3. Code style (consistency with project standards)
4. Test coverage (are critical functions tested?)
5. Documentation (docstrings, comments where needed)
## Output Format
Create a GitHub comment with:
- Summary: [N] findings across [M] files
- Issues: [list by severity]
- Suggestions: [actionable fixes]
Save this in your repo. Claude Code will read it before analyzing PRs.
Step 2: Create the GitHub Actions Workflow
Create .github/workflows/claude-review.yml:
name: Claude Code PR Review
on:
pull_request:
types: [opened, synchronize]
paths:
- 'src/**'
- 'tests/**'
- '*.json'
- '*.ts'
- '*.js'
jobs:
review:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0 # Full history for context
- name: Set up Claude Code
uses: anthropics/claude-code-action@v2
with:
api-key: ${{ secrets.ANTHROPIC_API_KEY }}
- name: Run PR Review
run: |
# Get the PR diff
DIFF=$(git diff origin/main...HEAD)
# Run Claude Code in headless mode
claude --headless \
--task "review-pr" \
--context "$(cat .claude-code/review-config.md)" \
--input "$DIFF" \
--output ./review-report.txt
- name: Comment on PR
uses: actions/github-script@v7
if: always()
with:
script: |
const fs = require('fs');
const reviewReport = fs.readFileSync('./review-report.txt', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '## Claude Code Review\n\n' + reviewReport
});
- name: Fail if critical issues
run: |
if grep -q "CRITICAL" ./review-report.txt; then
echo "Critical issues found. Please fix before merging."
exit 1
fi
This workflow:
- Triggers on every PR to
src/or test files - Runs Claude Code in headless mode
- Posts a comment with findings
- Fails the check if critical issues are found
Step 3: Create a Helper Script
Create scripts/claude-review.sh:
#!/bin/bash
# Helper script to run Claude Code review locally (for testing)
set -e
# Get the current branch
BRANCH=$(git rev-parse --abbrev-ref HEAD)
MAIN_BRANCH=${1:-main}
if [ "$BRANCH" = "$MAIN_BRANCH" ]; then
echo "Error: Run this on a feature branch, not main"
exit 1
fi
# Get the diff
DIFF=$(git diff "$MAIN_BRANCH"...HEAD)
# Run Claude Code review
echo "Running Claude Code PR review..."
claude --headless \
--task "review-pr" \
--context "$(cat .claude-code/review-config.md)" \
--input "$DIFF" \
--print > /tmp/review-preview.txt
echo "Review preview (no changes made):"
cat /tmp/review-preview.txt
echo ""
echo "To run the review (will post to GitHub):"
echo " git push origin $BRANCH"
echo " # GitHub Actions will run the review and comment on the PR"
Make it executable: chmod +x scripts/claude-review.sh
Step 4: Set Up the GitHub Secret
- Generate a new ANTHROPIC_API_KEY at https://console.anthropic.com
- Go to your GitHub repo → Settings → Secrets and variables → Actions
- Create new secret:
ANTHROPIC_API_KEYwith your API key
Step 5: Test the Workflow
Local Test:
# Create a feature branch
git checkout -b feature/test-review
# Make a change (intentionally bad code)
cat > src/test.js << 'EOF'
// Bad practice: using eval
const userCode = req.body.code;
eval(userCode); // SECURITY ISSUE
// Bad practice: no error handling
const data = JSON.parse(input);
EOF
git add src/test.js
git commit -m "Test: intentionally bad code"
# Run the local review script
./scripts/claude-review.sh
# You should see Claude identify:
# - SECURITY: eval() is dangerous
# - ERROR HANDLING: no try/catch
# - SUGGESTION: validate input first
Real Test:
# Push your feature branch
git push origin feature/test-review
# Create a PR on GitHub
# GitHub Actions will run automatically
# Claude Code will review and comment on the PR
Reflect
After running the workflow, think about these questions:
-
Did Claude Code find real issues? Were the suggestions helpful or false positives?
-
How long did it take? Was it faster than manual review? How does it scale to larger PRs?
-
What permissions matter most? Which restrictions protect you vs. slow you down?
-
Would you trust this in production? What additional safeguards would you add?
-
What's missing? What else should Claude Code check for in your codebase?
Bonus Challenge
Once you have the basic workflow working:
Challenge 1: Auto-Fix Simple Issues
Extend Claude Code to automatically fix formatting and linting errors:
claude --headless \
--task "review-and-fix-simple-issues" \
--context "Fix only: formatting, unused imports, variable naming"
Then create a new commit:
git add -A
git commit -m "refactor: auto-format with Claude Code"
git push
Challenge 2: Performance Analysis
Add a performance analysis step:
claude --headless \
--task "analyze-performance" \
--context "Look for: N+1 queries, memory leaks, inefficient loops"
Challenge 3: Custom Rules
Create a .claude-code/custom-rules.md with your team's specific rules:
# Custom Linting Rules for [Project]
## Required patterns:
- All API endpoints must have error handling
- All async functions must have try/catch
- Database queries must use prepared statements
## Forbidden patterns:
- eval() or Function() constructor
- global variables outside of singletons
- console.log in production code
Then have Claude Code check against these rules automatically.
Challenge 4: Multi-Language Support
Extend the workflow to handle Python, Go, or other languages:
paths:
- 'src/**/*.py' # Python files
- 'src/**/*.go' # Go files
Claude Code should automatically detect the language and use appropriate analysis.
Key Takeaways
- Headless mode lets you integrate Claude Code into CI/CD pipelines
- Permissions are critical — specify exactly what Claude can do
- GitHub Actions is a natural home for automated code review
- Preview mode (
--print) is your safety net before running headless - Iterative improvement — start simple, add features gradually