skills/deployment/ci-cd-pipeline/SKILL.md
Use this skill when the user says 'CI/CD', 'GitHub Actions', 'pipeline', 'continuous integration', 'continuous deployment', 'ci-cd-pipeline', 'automate deploys', or needs to set up automated build, test, and deployment pipelines. Do NOT use for one-time manual deployments.
npx skillsauth add cwinvestments/memstack memstack-deployment-ci-cd-pipelineInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Detect project type and generate a complete CI/CD pipeline with lint, test, build, deploy stages, rollback strategy, and environment management.
When this skill activates, output:
🔄 CI/CD Pipeline — Designing your pipeline...
| Context | Status | |---------|--------| | User says "CI/CD", "pipeline", "GitHub Actions" | ACTIVE | | User wants automated testing and deployment | ACTIVE | | User mentions branch strategy, deploy gates, or rollbacks | ACTIVE | | User wants Docker setup (CI/CD is secondary) | DORMANT — see docker-setup | | User wants server provisioning (not pipeline) | DORMANT — see hetzner-setup | | User wants to deploy to a specific platform only | DORMANT — see railway-deploy or netlify-deploy |
Ask the user for:
Auto-detect from project files and recommend CI/CD platform:
| Project Signal | Platform Recommendation | Reason |
|---------------|------------------------|--------|
| .github/ exists | GitHub Actions | Already on GitHub, native integration |
| vercel.json or Next.js | Vercel (auto-deploy) + GitHub Actions (CI) | Vercel handles deploy, GHA handles testing |
| railway.json | Railway (auto-deploy) + GitHub Actions (CI) | Railway handles deploy, GHA handles testing |
| netlify.toml | Netlify (auto-deploy) + GitHub Actions (CI) | Netlify handles deploy, GHA handles testing |
| Dockerfile present | GitHub Actions → Docker registry → deploy | Full control pipeline |
| Monorepo (packages/) | GitHub Actions with matrix/path filters | Need per-package CI |
| Self-hosted server | GitHub Actions → SSH deploy | Push-based deployment |
Default recommendation: GitHub Actions — free for public repos, 2,000 min/month for private.
Define the stage sequence:
┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
│ LINT │──→│ TEST │──→│ BUILD │──→│ DEPLOY │──→│ VERIFY │
│ │ │ │ │ │ │ │ │ │
│ ESLint │ │ Unit │ │ Compile │ │ Push to │ │ Health │
│ Prettier│ │ Integ. │ │ Bundle │ │ target │ │ Smoke │
│ Types │ │ E2E │ │ Docker │ │ env │ │ Rollback│
└─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
│ │ │ │ │
Fail fast Gate: block Artifacts Env-specific Auto-rollback
< 1 min if < 80% cached secrets on failure
Stage details:
| Stage | Trigger | Failure Action | Duration Target | |-------|---------|---------------|-----------------| | Lint | Every push, every PR | Block merge | < 1 min | | Test | Every push, every PR | Block merge | < 5 min | | Build | PR merge to main/develop | Block deploy | < 3 min | | Deploy | Build passes on target branch | Alert + rollback | < 2 min | | Verify | After deploy completes | Auto-rollback | < 1 min |
Recommended: Simplified GitFlow
main (production)
├── develop (staging)
│ ├── feature/add-auth
│ ├── feature/dashboard
│ └── fix/login-bug
└── hotfix/critical-fix (→ main + develop)
| Branch | Deploys To | CI Runs | Auto-Deploy? |
|--------|-----------|---------|-------------|
| feature/* | — | Lint + Test | No |
| develop | Staging | Lint + Test + Build + Deploy | Yes |
| main | Production | Lint + Test + Build + Deploy | Yes (or manual gate) |
| hotfix/* | — | Lint + Test | No (merge to main to deploy) |
Branch protection rules:
main:
- Require PR review (1+ approvals)
- Require status checks (lint, test, build)
- No force push
- No direct push
develop:
- Require status checks (lint, test)
- Allow direct push (for solo devs)
- No force push
── ENVIRONMENT VARIABLES ──────────────────
Three environments with escalating secrets:
LOCAL (.env.local — never committed):
DATABASE_URL=postgresql://localhost:5432/app_dev
API_KEY=dev_test_key
NODE_ENV=development
STAGING (GitHub Secrets / platform env):
DATABASE_URL=[staging DB connection string]
API_KEY=[staging API key]
NODE_ENV=staging
PRODUCTION (GitHub Secrets / platform env):
DATABASE_URL=[production DB connection string]
API_KEY=[production API key]
NODE_ENV=production
Secret management rules:
GITHUB_TOKEN for GitHub operations (auto-provided)GitHub Environments setup:
Repository → Settings → Environments:
staging:
Secrets: DATABASE_URL, API_KEY
Deployment branches: develop
production:
Secrets: DATABASE_URL, API_KEY
Deployment branches: main
Required reviewers: [team member] (optional gate)
GitHub Actions — Node.js project:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
name: 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 typecheck # If TypeScript
test:
name: Test
runs-on: ubuntu-latest
needs: lint
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm test -- --coverage
- uses: actions/upload-artifact@v4
if: always()
with:
name: coverage-report
path: coverage/
build:
name: Build
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push'
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: build-output
path: dist/
deploy-staging:
name: Deploy to Staging
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/develop'
environment: staging
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
# Platform-specific deploy step here
# Option A: Railway
# - uses: bervProject/railway-deploy@main
# with:
# railway_token: ${{ secrets.RAILWAY_TOKEN }}
# Option B: SSH deploy to VPS
# - uses: appleboy/ssh-action@v1
# with:
# host: ${{ secrets.SERVER_HOST }}
# username: deploy
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# port: 2222
# script: |
# cd /opt/app && git pull && npm ci --production && pm2 restart all
deploy-production:
name: Deploy to Production
runs-on: ubuntu-latest
needs: build
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: build-output
path: dist/
# Same deploy pattern as staging but with production env
verify:
name: Verify Deployment
runs-on: ubuntu-latest
needs: [deploy-staging, deploy-production]
if: always() && (needs.deploy-staging.result == 'success' || needs.deploy-production.result == 'success')
steps:
- name: Health check
run: |
URL="${{ github.ref == 'refs/heads/main' && secrets.PRODUCTION_URL || secrets.STAGING_URL }}"
for i in 1 2 3 4 5; do
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "$URL/health")
if [ "$STATUS" = "200" ]; then
echo "Health check passed"
exit 0
fi
echo "Attempt $i: HTTP $STATUS — retrying in 10s..."
sleep 10
done
echo "Health check failed after 5 attempts"
exit 1
GitHub Actions — Python project:
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install ruff mypy
- run: ruff check .
- run: mypy .
test:
runs-on: ubuntu-latest
needs: lint
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
ports: ['5432:5432']
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- run: pip install -r requirements.txt
- run: pytest --cov --cov-report=xml
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
build-and-deploy:
runs-on: ubuntu-latest
needs: test
if: github.event_name == 'push'
steps:
- uses: actions/checkout@v4
# Docker build + push or direct deploy
Automated rollback on failed health check:
rollback:
name: Rollback on Failure
runs-on: ubuntu-latest
needs: verify
if: failure()
steps:
- name: Rollback deployment
run: |
echo "Deployment verification failed — rolling back"
# Option A: Revert to previous Docker image
# docker pull $REGISTRY/app:previous && docker tag $REGISTRY/app:previous $REGISTRY/app:latest
# Option B: Railway rollback
# railway rollback
# Option C: Git revert + redeploy
# git revert HEAD --no-edit && git push
- name: Notify team
run: |
curl -X POST "${{ secrets.SLACK_WEBHOOK }}" \
-H 'Content-Type: application/json' \
-d '{"text":"🔴 Deployment rolled back — health check failed on ${{ github.ref }}"}'
Rollback strategies by deployment target:
| Target | Rollback Method | Speed | Data Safety |
|--------|----------------|-------|------------|
| Vercel | Instant rollback in dashboard or CLI | Instant | Safe — immutable deploys |
| Railway | railway rollback or dashboard | Instant | Safe — previous deploy preserved |
| Netlify | Deploy previous build in dashboard | Instant | Safe — immutable deploys |
| Docker | Tag previous image as latest, restart | Seconds | Safe — images preserved |
| VPS/PM2 | git revert + pm2 restart | Minutes | Check DB migrations first |
| Kubernetes | kubectl rollout undo | Seconds | Check DB migrations first |
Database migration caution:
up + down)down migration before deploying upSlack notification on deploy:
notify:
name: Notify
runs-on: ubuntu-latest
needs: [deploy-staging, deploy-production, verify]
if: always()
steps:
- name: Send Slack notification
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
if [ "${{ needs.verify.result }}" = "success" ]; then
EMOJI="✅"
STATUS="succeeded"
else
EMOJI="🔴"
STATUS="failed"
fi
curl -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "{
\"text\": \"${EMOJI} Deploy ${STATUS}\",
\"blocks\": [
{
\"type\": \"section\",
\"text\": {
\"type\": \"mrkdwn\",
\"text\": \"${EMOJI} *Deploy ${STATUS}*\nBranch: \`${{ github.ref_name }}\`\nCommit: \`${{ github.sha }}\`\nBy: ${{ github.actor }}\"
}
}
]
}"
Alternative notification channels:
Present the complete CI/CD configuration:
━━━ CI/CD PIPELINE ━━━━━━━━━━━━━━━━━━━━━━━
Project: [name]
Platform: GitHub Actions
Deploy target: [platform]
Branch strategy: [strategy]
── PIPELINE STAGES ────────────────────────
Lint → Test → Build → Deploy → Verify
[stage diagram with timing]
── BRANCH STRATEGY ────────────────────────
[branch → environment mapping]
[protection rules]
── ENVIRONMENT VARIABLES ──────────────────
[per-environment secret setup]
── PIPELINE CONFIG ────────────────────────
[complete .github/workflows/ci-cd.yml]
── ROLLBACK STRATEGY ──────────────────────
[per-target rollback method]
[database migration caution]
── NOTIFICATIONS ──────────────────────────
[Slack/Discord/email setup]
── SETUP CHECKLIST ────────────────────────
[ ] Create .github/workflows/ directory
[ ] Add pipeline YAML file
[ ] Configure GitHub Environments (staging, production)
[ ] Add secrets to each environment
[ ] Set branch protection rules
[ ] Configure notification webhook
[ ] Test pipeline with a feature branch PR
[ ] Verify staging deploy on develop merge
[ ] Verify production deploy on main merge
tools
Use when the user says 'save diary', 'log session', 'wrapping up', or at end of a productive session.
tools
Use when the user says 'submit to marketplace', 'publish my skill', 'share this skill', 'list on marketplace', 'submit plugin', 'publish to community', or needs to submit a skill or plugin to a community marketplace via PR. Do NOT use for building skills or writing plugin code.
development
Use when the user says 'write browser tests', 'test this page', 'playwright test', 'e2e test', 'end to end test', 'browser test', 'test the UI', or needs Playwright-based browser testing for a web application. Do NOT use for unit tests, API tests, or non-browser testing.
development
Use when the user says 'teach me', 'explain as you go', 'mentor mode', 'walk me through', 'help me learn', 'explain why', 'learning mode', or wants real-time plain language narration of decisions and tradeoffs while building. Do NOT use for code review or debugging.