Vibecosystem ci-cd-pipeline
GitHub Actions workflow patterns, matrix builds, caching strategies, deployment pipelines, artifact management, and rollback procedures.
install
source · Clone the upstream repo
git clone https://github.com/vibeeval/vibecosystem
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/vibeeval/vibecosystem "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/ci-cd-pipeline" ~/.claude/skills/vibeeval-vibecosystem-ci-cd-pipeline && rm -rf "$T"
manifest:
skills/ci-cd-pipeline/SKILL.mdsource content
CI/CD Pipeline Patterns
GitHub Actions workflows for consistent, fast, and reliable delivery pipelines.
Workflow Structure
# .github/workflows/ci.yml name: CI on: push: branches: [main, develop] pull_request: branches: [main] workflow_dispatch: # manual trigger concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # cancel outdated runs on same branch 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 test: runs-on: ubuntu-latest needs: lint # runs after lint passes steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm test -- --coverage build: runs-on: ubuntu-latest needs: test steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npm run build
Matrix Builds
Test across multiple versions and operating systems in parallel.
jobs: test: strategy: fail-fast: false # don't cancel others if one fails matrix: node: ['18', '20', '22'] os: [ubuntu-latest, macos-latest, windows-latest] exclude: - os: windows-latest node: '18' # skip this combination runs-on: ${{ matrix.os }} name: Test Node ${{ matrix.node }} on ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ matrix.node }} cache: 'npm' - run: npm ci - run: npm test
# Python matrix example jobs: test: strategy: matrix: python: ['3.10', '3.11', '3.12'] django: ['4.2', '5.0'] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python }} - run: pip install django==${{ matrix.django }} -r requirements-test.txt - run: pytest
Dependency Caching
# Node.js: cache npm or yarn - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' # auto-caches ~/.npm # Manual cache (more control) - uses: actions/cache@v4 id: npm-cache with: path: ~/.npm key: npm-${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} restore-keys: | npm-${{ runner.os }}- - run: npm ci
# Python: cache pip - uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ runner.os }}-${{ hashFiles('**/requirements*.txt') }} restore-keys: | pip-${{ runner.os }}- - run: pip install -r requirements.txt
# Docker layer caching - uses: docker/setup-buildx-action@v3 - uses: actions/cache@v4 with: path: /tmp/.buildx-cache key: buildx-${{ runner.os }}-${{ github.sha }} restore-keys: | buildx-${{ runner.os }}- - uses: docker/build-push-action@v5 with: cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache-new,mode=max # Prevent cache from growing unbounded - run: | rm -rf /tmp/.buildx-cache mv /tmp/.buildx-cache-new /tmp/.buildx-cache
Artifact Upload/Download Between Jobs
jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci && npm run build - uses: actions/upload-artifact@v4 with: name: build-output path: dist/ retention-days: 7 deploy: runs-on: ubuntu-latest needs: build steps: - uses: actions/download-artifact@v4 with: name: build-output path: dist/ - run: ls -la dist/ # verify artifact present - run: ./scripts/deploy.sh
Environment Secrets Management
# Repository secrets: Settings → Secrets and variables → Actions # Environment secrets: require approval before use jobs: deploy-production: environment: production # requires approval + uses env secrets runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Deploy env: API_KEY: ${{ secrets.API_KEY }} DATABASE_URL: ${{ secrets.DATABASE_URL }} run: ./scripts/deploy.sh # Never echo secrets # ✅ GitHub masks them automatically in logs # ❌ Don't: run: echo ${{ secrets.API_KEY }}
Full Deployment Pipeline
# .github/workflows/deploy.yml name: Deploy on: push: branches: [main] 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 && npm run lint 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 && npm test build-and-push: runs-on: ubuntu-latest needs: test outputs: image-tag: ${{ steps.meta.outputs.tags }} steps: - uses: actions/checkout@v4 - uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - uses: docker/metadata-action@v5 id: meta with: images: ghcr.io/${{ github.repository }} tags: | type=sha,prefix=sha- type=raw,value=latest,enable=${{ github.ref == 'refs/heads/main' }} - uses: docker/build-push-action@v5 with: push: true tags: ${{ steps.meta.outputs.tags }} deploy-staging: runs-on: ubuntu-latest needs: build-and-push environment: staging steps: - run: | ssh deploy@staging.example.com \ "docker pull ghcr.io/${{ github.repository }}:latest && \ docker compose up -d" smoke-test: runs-on: ubuntu-latest needs: deploy-staging steps: - run: | sleep 10 curl -f https://staging.example.com/health || exit 1 deploy-production: runs-on: ubuntu-latest needs: smoke-test environment: production # requires manual approval steps: - run: | ssh deploy@prod.example.com \ "docker pull ghcr.io/${{ github.repository }}:latest && \ docker compose up -d"
Conditional Job Execution
jobs: deploy: if: github.ref == 'refs/heads/main' && github.event_name == 'push' runs-on: ubuntu-latest steps: - run: echo "Deploying main branch" notify-failure: if: failure() # only runs if previous job failed needs: [lint, test, build] runs-on: ubuntu-latest steps: - uses: actions/github-script@v7 with: script: | github.rest.issues.createComment({ issue_number: context.issue.number, owner: context.repo.owner, repo: context.repo.repo, body: 'CI failed. Please check the logs.' })
Reusable Workflows and Composite Actions
# .github/workflows/reusable-test.yml name: Reusable Test Workflow on: workflow_call: inputs: node-version: required: false type: string default: '20' secrets: NPM_TOKEN: required: false jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} - run: npm ci && npm test
# Caller workflow jobs: test: uses: ./.github/workflows/reusable-test.yml with: node-version: '22' secrets: NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
# Composite action: .github/actions/setup-app/action.yml name: Setup Application description: Install deps and configure environment runs: using: composite steps: - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci shell: bash - run: npm run db:migrate shell: bash env: DATABASE_URL: ${{ env.DATABASE_URL }}
Deployment Strategies
# Blue-Green: switch traffic between two identical environments - name: Deploy to Blue run: | kubectl set image deployment/app-blue app=my-image:${{ github.sha }} kubectl rollout status deployment/app-blue - name: Run smoke tests on Blue run: curl -f https://blue.example.com/health - name: Switch traffic to Blue run: | kubectl patch service app \ -p '{"spec":{"selector":{"slot":"blue"}}}' # Canary: gradual traffic shift - name: Deploy Canary (10% traffic) run: | kubectl set image deployment/app-canary app=my-image:${{ github.sha }} kubectl patch virtualservice app \ --type merge \ -p '{"spec":{"http":[{"route":[ {"destination":{"host":"app-stable"},"weight":90}, {"destination":{"host":"app-canary"},"weight":10} ]}]}}'
Rollback Procedures
- name: Deploy with rollback on failure id: deploy run: | PREVIOUS_IMAGE=$(kubectl get deployment app -o jsonpath='{.spec.template.spec.containers[0].image}') echo "previous-image=$PREVIOUS_IMAGE" >> $GITHUB_OUTPUT kubectl set image deployment/app app=my-image:${{ github.sha }} kubectl rollout status deployment/app --timeout=5m || { echo "Rollback triggered" kubectl set image deployment/app app=$PREVIOUS_IMAGE kubectl rollout status deployment/app exit 1 }
# Manual rollback command (kubectl) kubectl rollout undo deployment/app kubectl rollout undo deployment/app --to-revision=3 # Docker Compose rollback docker compose pull && docker compose up -d # or pin to previous image tag IMAGE_TAG=sha-abc123 docker compose up -d
Scheduled Jobs and PR Automation
# Scheduled dependency update check on: schedule: - cron: '0 9 * * 1' # every Monday at 09:00 UTC jobs: check-deps: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm outdated || true - run: npx npm-check-updates --errorLevel 2
# Auto-label PR based on files changed on: pull_request: types: [opened, synchronize] jobs: label: runs-on: ubuntu-latest steps: - uses: actions/labeler@v5 with: repo-token: ${{ secrets.GITHUB_TOKEN }} configuration-path: .github/labeler.yml
Status Badges
<!-- README.md badges -->  
Key principle: Fail fast in early stages (lint before test), cache aggressively, require approval gates for production, always have a rollback path.