AI Skill Library

CI/CD Deploy Pipeline

Full deploy pipeline: build, test, Docker build+push, SSH deploy, rollback strategy.

ci-cdgithub-actionsdevopsdeployment
# CI/CD Deploy Pipeline

## Full pipeline (GitHub Actions -> VPS)
```yaml
# .github/workflows/deploy.yml
name: Deploy
on:
  push:
    branches: [main]

jobs:
  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 test
      - run: npm run build

  deploy:
    needs: test
    runs-on: ubuntu-latest
    environment: production
    steps:
      - uses: actions/checkout@v4

      - name: Build & push Docker image
        run: |
          echo ${{ secrets.REGISTRY_TOKEN }} | docker login ghcr.io -u ${{ github.actor }} --password-stdin
          docker build -t ghcr.io/${{ github.repository }}:${{ github.sha }} .
          docker push ghcr.io/${{ github.repository }}:${{ github.sha }}

      - name: Deploy to VPS
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: deploy
          key: ${{ secrets.SSH_PRIVATE_KEY }}
          script: |
            cd /opt/app
            export IMAGE=ghcr.io/${{ github.repository }}:${{ github.sha }}
            docker pull $IMAGE
            docker stop api || true
            docker run -d --rm --name api \
              -p 3000:3000 \
              --env-file /opt/app/.env \
              $IMAGE
            echo "Deployed $IMAGE at $(date)"
```

## Secrets to set in GitHub
```
VPS_HOST          # server IP or domain
SSH_PRIVATE_KEY   # private key (ssh-keygen -t ed25519)
REGISTRY_TOKEN    # GitHub PAT with packages:write
```

## Deploy with Docker Compose
```bash
# On server deploy script
cd /opt/app
git pull origin main
echo "IMAGE_TAG=${{ github.sha }}" > .env.deploy
docker compose -f compose.yml -f compose.prod.yml pull
docker compose -f compose.yml -f compose.prod.yml up -d --no-build
docker image prune -f
```

## Blue-Green deployment
```bash
# Run new version on port 3001
docker run -d --name api-green -p 3001:3000 $NEW_IMAGE
# Health check
curl -f http://localhost:3001/health || exit 1
# Switch nginx upstream
sed -i 's/3000/3001/' /etc/nginx/conf.d/api.conf
nginx -s reload
# Stop old version
docker stop api-blue
```

## Rollback
```bash
# Tag images with git SHA, keep last 3
# Rollback = re-run deploy with previous SHA
git log --oneline -5   # get previous SHA
# In GitHub: re-run workflow on previous commit
# Or manually:
docker run -d --name api -p 3000:3000 ghcr.io/org/api:PREVIOUS_SHA
```

## Health check endpoint
```ts
app.get('/health', async (req, res) => {
  try {
    await db.query('SELECT 1')  // check DB
    res.json({ status: 'ok', uptime: process.uptime(), ts: Date.now() })
  } catch {
    res.status(503).json({ status: 'error' })
  }
})
```

## Notification on deploy
```yaml
- name: Notify Slack
  if: always()
  uses: slackapi/slack-github-action@v1
  with:
    payload: '{"text":"Deploy ${{ job.status }}: ${{ github.repository }}@${{ github.sha }}"}'
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}
```

API: /api/skills/ci-cd-deploy-pipeline