GitHub Actions workflows have a steep learning curve — YAML syntax, job dependencies, secret management, Blue-Green deployments. Claude Code eliminates that curve.
With a CLAUDE.md that defines your project's CI/CD rules, Claude Code generates project-specific workflows in seconds — not generic templates, but pipelines that match your actual constraints.
Step 1: Define CI/CD Rules in CLAUDE.md
# CI/CD Rules
## Branch Strategy
- Direct push to main is prohibited. Always use PRs
- All PRs require CI to pass (ci-required status check)
- Branch flow: feature/* → develop → main
## Pipeline Stages (strict order)
1. lint — ESLint + Prettier check
2. test — Jest (minimum 80% line coverage required)
3. build — tsc + vite build
4. deploy — main merge only, Blue-Green to production
## Secret Management
- All API keys stored in GitHub Secrets (no hardcoding)
- Environment variable names: SCREAMING_SNAKE_CASE
- .env.example is public-safe; .env must be in .gitignore
## Deploy Requirements
- Health check endpoint: GET /health → 200
- Rollback: auto-revert to previous version on deploy failure
- Notification: Slack alert on deploy success/failure
Step 2: Generate the CI Workflow
Prompt:
Generate a GitHub Actions CI workflow for a Node.js app (npm workspace, TypeScript)
following CLAUDE.md rules.
Requirements:
- Node.js 20.x
- Stages: lint → test (with coverage report) → build
- Trigger: PR open and push
- Cache npm dependencies
- Post test results to GitHub Actions Summary
- Fail if line coverage drops below 80%
Generated .github/workflows/ci.yml:
name: CI
on:
pull_request:
types: [opened, synchronize, reopened]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm run lint
- run: npm run format:check
test:
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- name: Run tests with coverage
run: npm test -- --coverage --coverageReporters=json-summary
- name: Coverage gate (80%)
run: |
COVERAGE=$(node -e "
const s = require('./coverage/coverage-summary.json');
console.log(s.total.lines.pct);
")
echo "Line coverage: ${COVERAGE}%"
node -e "if (${COVERAGE} < 80) { console.error('Coverage below 80%'); process.exit(1); }"
echo "## Test Coverage: ${COVERAGE}%" >> $GITHUB_STEP_SUMMARY
build:
needs: test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20"
cache: "npm"
- run: npm ci
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
Step 3: Auto-Post Coverage to PRs
Prompt:
Add a job that posts test coverage diff as a PR comment on every PR open/update.
- Compare against base branch coverage
- Show a warning icon if coverage decreased
- Update the existing comment instead of creating a new one
- Use secrets.GITHUB_TOKEN
Generated job:
coverage-comment:
needs: test
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Get base branch coverage
run: |
git checkout ${{ github.base_ref }}
npm ci --silent
npm test -- --coverage --coverageReporters=json-summary 2>/dev/null || true
BASE=$(node -e "
try {
const s = require('./coverage/coverage-summary.json');
console.log(s.total.lines.pct);
} catch { console.log('0'); }
")
echo "BASE_COVERAGE=${BASE}" >> $GITHUB_ENV
- name: Get PR coverage
run: |
git checkout ${{ github.head_ref }}
npm ci --silent
npm test -- --coverage --coverageReporters=json-summary
PR=$(node -e "
const s = require('./coverage/coverage-summary.json');
console.log(s.total.lines.pct);
")
echo "PR_COVERAGE=${PR}" >> $GITHUB_ENV
- name: Post coverage comment
uses: actions/github-script@v7
with:
script: |
const base = parseFloat(process.env.BASE_COVERAGE) || 0;
const pr = parseFloat(process.env.PR_COVERAGE) || 0;
const diff = (pr - base).toFixed(1);
const icon = pr < base ? '⚠️' : '✅';
const body = `## ${icon} Test Coverage Report
| | Coverage |
|---|---|
| Base (${context.payload.pull_request.base.ref}) | ${base}% |
| This PR | ${pr}% |
| Diff | ${diff > 0 ? '+' : ''}${diff}% |`;
const comments = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const existing = comments.data.find(c => c.body.includes('Test Coverage Report'));
if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body,
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body,
});
}
Step 4: Production Deploy with Blue-Green
Prompt:
Generate a GitHub Actions deploy workflow triggered on main branch merge.
Use Blue-Green deployment with Docker containers.
- Health check new (Green) environment before switching traffic
- Auto-rollback if health check fails after 10 retries
- Send Slack notification on success or failure
Generated .github/workflows/deploy.yml:
name: Deploy to Production
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
environment: production
steps:
- uses: actions/checkout@v4
- name: Build Docker image
run: |
docker build -t myapp:${{ github.sha }} .
echo "${{ secrets.REGISTRY_TOKEN }}" | docker login ghcr.io -u ${{ github.actor }} --password-stdin
docker push ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
- name: Deploy to Green environment
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
echo "$DEPLOY_KEY" > /tmp/deploy_key && chmod 600 /tmp/deploy_key
ssh -i /tmp/deploy_key -o StrictHostKeyChecking=no ubuntu@${DEPLOY_HOST} "
docker pull ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
docker stop myapp-green 2>/dev/null || true
docker rm myapp-green 2>/dev/null || true
docker run -d --name myapp-green \
-p 3001:3000 \
-e NODE_ENV=production \
ghcr.io/${{ github.repository }}/myapp:${{ github.sha }}
"
- name: Health check Green
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
run: |
for i in {1..10}; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" http://${DEPLOY_HOST}:3001/health)
if [ "$STATUS" = "200" ]; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i: status=$STATUS, retrying in 5s..."
sleep 5
done
echo "Health check failed after 10 attempts"
exit 1
- name: Switch traffic to Green
if: success()
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
ssh -i /tmp/deploy_key ubuntu@${DEPLOY_HOST} "
sed -i 's/3000/3001/' /etc/nginx/conf.d/app.conf
nginx -s reload
docker stop myapp-blue 2>/dev/null || true
docker rename myapp-green myapp-blue
"
- name: Rollback on failure
if: failure()
env:
DEPLOY_HOST: ${{ secrets.DEPLOY_HOST }}
DEPLOY_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: |
ssh -i /tmp/deploy_key ubuntu@${DEPLOY_HOST} "
docker stop myapp-green 2>/dev/null || true
docker rm myapp-green 2>/dev/null || true
echo 'Rolled back to Blue (previous version)'
"
- name: Notify Slack
if: always()
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
STATUS="${{ job.status }}"
COLOR=$([ "$STATUS" = "success" ] && echo "good" || echo "danger")
ICON=$([ "$STATUS" = "success" ] && echo ":rocket:" || echo ":x:")
curl -s -X POST "$SLACK_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"attachments\": [{
\"color\": \"${COLOR}\",
\"text\": \"${ICON} Deploy ${STATUS}: \`${{ github.sha }}\` by ${{ github.actor }}\"
}]
}"
Why CLAUDE.md First?
The key insight: Claude Code generates project-specific workflows when it has context.
Without CLAUDE.md:
- Generic Node.js template
- No coverage gate
- No rollback logic
- No Slack notifications
With CLAUDE.md rules defined:
- Coverage threshold from your rules (80%)
- Branch strategy enforced in workflow triggers
- Deploy pattern matches your infrastructure
The CLAUDE.md investment pays off every time you add a new workflow — the rules transfer automatically.
Cost vs Manual Setup
| Task | Manual Time | With Claude Code |
|---|---|---|
| CI workflow (lint+test+build) | 2-3 hours | 5 minutes |
| PR coverage comments | 1-2 hours | 3 minutes |
| Blue-Green deploy workflow | 4-8 hours | 10 minutes |
| Total | ~13 hours | ~20 minutes |
Code Review Pack (¥980) includes /code-review for CI/CD configuration review. 👉 https://prompt-works.jp
Myouga (@myougatheaxo) — Security-focused Claude Code engineer.
Top comments (0)