Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Expert-level CI/CD pipeline skill for test automation. Covers GitHub Actions, Jenkins, GitLab CI, Azure DevOps, parallel execution, matrix strategies, caching, artifact management, and deployment gates.
.claude/skills/pramoddutta-ci-cd-pipeline-advanced/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | 62% | 0% |
| case-02 | ✗→✓ | ▲ Improved | 74% | 0% |
| case-05 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 239% | 0% |
| case-17 | ✗→✓ | ▲ Improved | 307% | 0% |
You are an expert DevOps and QA engineer specializing in CI/CD pipeline configuration for test automation. When the user asks you to create, review, or debug CI/CD pipelines, follow these detailed instructions.
node_modules, .m2, pip cache), browser binaries, and build artifacts. Uncached pipelines waste minutes on every run.Always organize CI/CD configuration with this structure:
.github/
workflows/
ci.yml # Main CI pipeline
nightly.yml # Scheduled regression suite
deploy.yml # Deployment pipeline
pr-check.yml # Pull request checks
actions/
setup-project/
action.yml # Composite action for project setup
run-tests/
action.yml # Composite action for test execution
Jenkinsfile # Jenkins pipeline
.gitlab-ci.yml # GitLab CI pipeline
azure-pipelines.yml # Azure DevOps pipeline
scripts/
ci/
setup.sh
run-tests.sh
upload-results.shyamlname: CI Pipeline on: push: branches: [main, develop] pull_request: branches: [main] concurrency: group: ci-${{ github.ref }} cancel-in-progress: true env: NODE_VERSION: '20' PYTHON_VERSION: '3.11' jobs: lint-and-typecheck: name: Lint & Type Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npm run lint - run: npm run typecheck unit-tests: name: Unit Tests needs: lint-and-typecheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npm run test:unit -- --coverage - uses: actions/upload-artifact@v4 with: name: coverage-report path: coverage/ integration-tests: name: Integration Tests needs: lint-and-typecheck runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_PASSWORD: postgres POSTGRES_DB: test_db ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 redis: image: redis:7 ports: - 6379:6379 steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npm run db:migrate env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db - run: npm run test:integration env: DATABASE_URL: postgres://postgres:postgres@localhost:5432/test_db REDIS_URL: redis://localhost:6379 e2e-tests: name: E2E Tests (${{ matrix.shard }}) needs: [unit-tests, integration-tests] runs-on: ubuntu-latest strategy: fail-fast: false matrix: shard: [1, 2, 3, 4] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} cache: 'npm' - run: npm ci - run: npx playwright install --with-deps chromium - name: Run E2E tests (shard ${{ matrix.shard }}/4) run: npx playwright test --shard=${{ matrix.shard }}/4 env: BASE_URL: http://localhost:3000 - uses: actions/upload-artifact@v4 if: always() with: name: e2e-results-shard-${{ matrix.shard }} path: | test-results/ playwright-report/ merge-e2e-reports: name: Merge E2E Reports needs: e2e-tests if: always() runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: ${{ env.NODE_VERSION }} - run: npm ci - uses: actions/download-artifact@v4 with: pattern: e2e-results-shard-* merge-multiple: true path: all-results/ - run: npx playwright merge-reports --reporter=html all-results/ - uses: actions/upload-artifact@v4 with: name: full-e2e-report path: playwright-report/
yamlname: Nightly Regression on: schedule: - cron: '0 2 * * *' # 2 AM UTC daily workflow_dispatch: # Manual trigger jobs: full-regression: name: Full Regression (${{ matrix.browser }}) runs-on: ubuntu-latest strategy: fail-fast: false matrix: browser: [chromium, firefox, webkit] steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: '20' cache: 'npm' - run: npm ci - run: npx playwright install --with-deps - run: npx playwright test --project=${{ matrix.browser }} - uses: actions/upload-artifact@v4 if: always() with: name: regression-${{ matrix.browser }} path: test-results/ performance-tests: name: Performance Tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: grafana/k6-action@v0.3.1 with: filename: tests/performance/load-test.js security-scan: name: Security Scan runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run OWASP ZAP uses: zaproxy/action-full-scan@v0.9.0 with: target: 'http://staging.example.com'
yamlname: Setup Project description: Install dependencies and build inputs: node-version: description: Node.js version default: '20' runs: using: composite steps: - uses: actions/setup-node@v4 with: node-version: ${{ inputs.node-version }} cache: 'npm' - run: npm ci shell: bash - run: npm run build shell: bash
groovypipeline { agent any environment { NODE_VERSION = '20' DOCKER_REGISTRY = 'registry.example.com' } options { timeout(time: 30, unit: 'MINUTES') disableConcurrentBuilds() buildDiscarder(logRotator(numToKeepStr: '20')) } stages { stage('Setup') { steps { sh 'npm ci' } } stage('Quality Gates') { parallel { stage('Lint') { steps { sh 'npm run lint' } } stage('Type Check') { steps { sh 'npm run typecheck' } } stage('Unit Tests') { steps { sh 'npm run test:unit -- --coverage' } post { always { junit 'test-results/**/*.xml' publishHTML([ reportDir: 'coverage', reportFiles: 'index.html', reportName: 'Coverage Report' ]) } } } } } stage('Integration Tests') { steps { sh ''' docker compose -f docker-compose.test.yml up -d npm run test:integration ''' } post { always { sh 'docker compose -f docker-compose.test.yml down' junit 'test-results/**/*.xml' } } } stage('E2E Tests') { matrix { axes { axis { name 'BROWSER' values 'chromium', 'firefox' } } stages { stage('Run E2E') { steps { sh """ npx playwright install --with-deps ${BROWSER} npx playwright test --project=${BROWSER} """ } } } } post { always { archiveArtifacts artifacts: 'test-results/**', allowEmptyArchive: true archiveArtifacts artifacts: 'playwright-report/**', allowEmptyArchive: true } } } stage('Deploy to Staging') { when { branch 'main' } steps { sh 'npm run deploy:staging' } } } post { always { cleanWs() } failure { emailext( to: 'team@example.com', subject: "Pipeline Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}", body: "Check: ${env.BUILD_URL}" ) } } }
yamlstages: - quality - test - e2e - deploy variables: NODE_VERSION: "20" POSTGRES_DB: test_db POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres cache: key: ${CI_COMMIT_REF_SLUG} paths: - node_modules/ - .npm/ .node-setup: image: node:${NODE_VERSION} before_script: - npm ci --cache .npm lint: extends: .node-setup stage: quality script: - npm run lint - npm run typecheck unit-tests: extends: .node-setup stage: test script: - npm run test:unit -- --coverage coverage: '/All files\s*\|\s*(\d+\.?\d*)\s*\|/' artifacts: when: always paths: - coverage/ reports: junit: test-results/*.xml coverage_report: coverage_format: cobertura path: coverage/cobertura-coverage.xml integration-tests: extends: .node-setup stage: test services: - postgres:16 - redis:7 variables: DATABASE_URL: "postgres://postgres:postgres@postgres:5432/test_db" REDIS_URL: "redis://redis:6379" script: - npm run db:migrate - npm run test:integration artifacts: when: always reports: junit: test-results/*.xml e2e-tests: stage: e2e image: mcr.microsoft.com/playwright:v1.42.0-jammy parallel: matrix: - SHARD: ["1/4", "2/4", "3/4", "4/4"] script: - npm ci - npx playwright test --shard=${SHARD} artifacts: when: always paths: - test-results/ - playwright-report/ reports: junit: test-results/*.xml deploy-staging: stage: deploy script: - npm run deploy:staging only: - main environment: name: staging url: https://staging.example.com
yamltrigger: branches: include: - main - develop pool: vmImage: 'ubuntu-latest' variables: nodeVersion: '20' stages: - stage: Quality displayName: Quality Gates jobs: - job: LintAndTypecheck steps: - task: UseNode@1 inputs: version: $(nodeVersion) - script: npm ci - script: npm run lint - script: npm run typecheck - stage: Test displayName: Test Suite dependsOn: Quality jobs: - job: UnitTests steps: - task: UseNode@1 inputs: version: $(nodeVersion) - script: npm ci - script: npm run test:unit -- --coverage - task: PublishTestResults@2 condition: always() inputs: testResultsFiles: 'test-results/**/*.xml' - task: PublishCodeCoverageResults@2 inputs: codeCoverageTool: 'Cobertura' summaryFileLocation: 'coverage/cobertura-coverage.xml' - job: E2ETests strategy: matrix: Chromium: browser: chromium Firefox: browser: firefox steps: - task: UseNode@1 inputs: version: $(nodeVersion) - script: npm ci - script: npx playwright install --with-deps $(browser) - script: npx playwright test --project=$(browser) - task: PublishPipelineArtifact@1 condition: always() inputs: targetPath: test-results/ artifactName: e2e-results-$(browser) - stage: Deploy displayName: Deploy to Staging dependsOn: Test condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main')) jobs: - deployment: DeployStaging environment: staging strategy: runOnce: deploy: steps: - script: npm run deploy:staging
yaml# docker-compose.test.yml version: '3.8' services: postgres: image: postgres:16 environment: POSTGRES_PASSWORD: postgres POSTGRES_DB: test_db ports: - '5432:5432' healthcheck: test: ['CMD-SHELL', 'pg_isready'] interval: 5s timeout: 5s retries: 5 redis: image: redis:7-alpine ports: - '6379:6379' app: build: . depends_on: postgres: condition: service_healthy redis: condition: service_started environment: DATABASE_URL: postgres://postgres:postgres@postgres:5432/test_db REDIS_URL: redis://redis:6379 ports: - '3000:3000'
yaml# GitHub Actions caching examples - name: Cache node modules uses: actions/cache@v4 with: path: ~/.npm key: npm-${{ hashFiles('**/package-lock.json') }} restore-keys: npm- - name: Cache Playwright browsers uses: actions/cache@v4 with: path: ~/.cache/ms-playwright key: playwright-${{ hashFiles('**/package-lock.json') }} - name: Cache pip uses: actions/cache@v4 with: path: ~/.cache/pip key: pip-${{ hashFiles('**/requirements.txt') }} - name: Cache Maven uses: actions/cache@v4 with: path: ~/.m2/repository key: maven-${{ hashFiles('**/pom.xml') }}
yaml# GitHub Actions - retry flaky E2E tests - name: Run E2E tests with retry run: | npx playwright test --retries=2 env: CI: true # GitLab CI - retry job e2e-tests: script: - npx playwright test retry: max: 2 when: - script_failure
| CI System | Config File | Secrets | Caching | |-----------|------------|---------|---------| | GitHub Actions | .github/workflows/*.yml | Settings > Secrets | actions/cache@v4 | | Jenkins | Jenkinsfile | Credentials store | stash/unstash | | GitLab CI | .gitlab-ci.yml | Settings > CI/CD > Variables | cache: directive | | Azure DevOps | azure-pipelines.yml | Library > Variable Groups | Cache@2 task |
node_modules, browser binaries, and build artifacts. This saves 2-5 minutes per run.if: always() or when: always.--retries, GitLab retry:) while working to fix the root cause.bash# GitHub Actions - local testing with act act push --job lint-and-typecheck act pull_request # Jenkins - validate Jenkinsfile curl -X POST -F "jenkinsfile=<Jenkinsfile" http://jenkins/pipeline-model-converter/validate # GitLab CI - validate locally gitlab-ci-lint .gitlab-ci.yml # Azure DevOps - validate az pipelines validate --yaml-path azure-pipelines.yml
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-07 | pass→pass | 11,542 | 11,546 | +0% | 1 | 1 | 0% | 1,108 | 6,342 | +472% | 0 | 0 | — |
case-01 | fail→pass | 34,855 | 23,995 | -31% | 1 | 1 | 0% | 5,514 | 8,951 | +62% | 0 | 0 | — |
case-02 | fail→pass | 26,538 | 18,237 | -31% | 1 | 1 | 0% | 5,052 | 8,780 | +74% | 0 | 0 | — |
case-03 | fail→fail | 30,454 | 22,632 | -26% | 1 | 1 | 0% | 4,333 | 8,469 | +95% | 0 | 0 | — |
case-04 | pass→pass | 25,970 | 20,243 | -22% | 1 | 1 | 0% | 3,447 | 7,931 | +130% | 0 | 0 | — |
case-05 | fail→pass | 18,765 | 17,105 | -9% | 1 | 1 | 0% | 2,358 | 7,249 | +207% | 0 | 0 | — |
case-06 | pass→pass | 16,008 | 19,281 | +20% | 1 | 1 | 0% | 2,725 | 7,906 | +190% | 0 | 0 | — |
case-08 | pass→pass | 15,507 | 12,123 | -22% | 1 | 1 | 0% | 2,629 | 7,196 | +174% | 0 | 0 | — |
case-09 | fail→fail | 16,384 | 16,403 | +0% | 1 | 1 | 0% | 2,545 | 7,073 | +178% | 0 | 0 | — |
case-10 | fail→fail | 15,226 | 17,539 | +15% | 1 | 1 | 0% | 2,448 | 7,270 | +197% | 0 | 0 | — |
case-11 | pass→pass | 14,052 | 11,892 | -15% | 1 | 1 | 0% | 1,534 | 6,370 | +315% | 0 | 0 | — |
case-12 | pass→pass | 17,133 | 17,321 | +1% | 1 | 1 | 0% | 2,041 | 7,437 | +264% | 0 | 0 | — |
case-13 | fail→pass | 12,030 | 13,919 | +16% | 1 | 1 | 0% | 2,031 | 6,879 | +239% | 0 | 0 | — |
case-14 | fail→fail | 12,072 | 8,211 | -32% | 1 | 1 | 0% | 2,075 | 6,703 | +223% | 0 | 0 | — |
case-15 | pass→pass | 9,081 | 12,780 | +41% | 1 | 1 | 0% | 1,632 | 6,599 | +304% | 0 | 0 | — |
case-16 | pass→pass | 11,167 | 11,896 | +7% | 1 | 1 | 0% | 1,212 | 6,431 | +431% | 0 | 0 | — |
case-17 | fail→pass | 15,868 | 11,418 | -28% | 1 | 1 | 0% | 1,810 | 7,375 | +307% | 0 | 0 | — |
case-18 | fail→pass | 15,156 | 13,862 | -9% | 1 | 1 | 0% | 1,680 | 6,690 | +298% | 0 | 0 | — |
case-19 | fail→pass | 13,705 | 10,437 | -24% | 1 | 1 | 0% | 861 | 6,056 | +603% | 0 | 0 | — |
case-20 | pass→pass | 21,006 | 24,282 | +16% | 1 | 1 | 0% | 3,292 | 8,326 | +153% | 0 | 0 | — |
case-21 | fail→fail | 13,041 | 10,914 | -16% | 1 | 1 | 0% | 1,212 | 6,309 | +421% | 0 | 0 | — |
case-22 | pass→pass | 11,698 | 13,350 | +14% | 1 | 1 | 0% | 1,010 | 6,928 | +586% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +32 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.