Cicd pipeline advanced
Skill PramodDutta/qaskills/seed-skills/cicd-pipeline-advanced
QA Skills Directory QA Skills is a curated directory of testing-specific skills for AI coding agents (Claude Code, Cursor, Copilot, etc.).
npx -y skills add PramodDutta/qaskills --skill cicd-pipeline-advancedAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
What its author says it does
Copied from the file, not written here
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.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
19.7 KB, as published. Nobody here has run it
CI/CD Pipeline Advanced Skill
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.
Core Principles
- Fail fast, fix fast -- Run the fastest tests first (lint, unit, type-check), then integration, then E2E. If linting fails, don't waste resources on browser tests.
- Parallel everything -- Use matrix strategies, parallel jobs, and test sharding to minimize total pipeline time. A 60-minute serial pipeline can often run in 15 minutes parallel.
- Cache aggressively -- Cache dependencies (
node_modules,.m2,pip cache), browser binaries, and build artifacts. Uncached pipelines waste minutes on every run. - Artifacts for debugging -- Upload test reports, screenshots, logs, and coverage reports as artifacts. Failed pipelines without artifacts are impossible to debug.
- Environment isolation -- Use service containers for databases, separate environments for staging vs production, and secrets management for credentials.
Project Structure
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.sh
GitHub Actions
Complete CI Pipeline (.github/workflows/ci.yml)
name: 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/
Nightly Regression Suite
name: 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/[email protected]
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/[email protected]
with:
target: 'http://staging.example.com'
Composite Action for Reuse (.github/actions/setup-project/action.yml)
name: 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
Jenkins Pipeline
Complete Jenkinsfile
pipeline {
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: '[email protected]',
subject: "Pipeline Failed: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
body: "Check: ${env.BUILD_URL}"
)
}
}
}
GitLab CI
Complete .gitlab-ci.yml
stages:
- 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
Azure DevOps
Complete azure-pipelines.yml
trigger:
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
Advanced Patterns
Docker Compose for Test Infrastructure
# 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'
Caching Strategies
# 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') }}
Test Retry and Flaky Test Handling
# 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
Quick Reference
| 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 |
Best Practices
- Fail fast with stage dependencies -- Run lint and type-check first. Only proceed to expensive tests if code quality gates pass.
- Use matrix strategies for cross-browser -- Test across Chrome, Firefox, and WebKit in parallel using matrix builds instead of sequential jobs.
- Shard E2E tests -- Split E2E suites into 4-8 shards running in parallel. A 60-minute suite becomes 10 minutes.
- Cache dependencies between runs -- Cache
node_modules, browser binaries, and build artifacts. This saves 2-5 minutes per run. - Upload artifacts on failure -- Always upload test results, screenshots, and reports when tests fail. Use
if: always()orwhen: always. - Use concurrency controls -- Cancel in-progress runs when new commits are pushed to the same branch. Saves resources and provides faster feedback.
- Separate CI from CD -- Keep test pipelines separate from deployment pipelines. Tests run on every push; deployments only on main.
- Service containers for dependencies -- Use Docker service containers for PostgreSQL, Redis, and other dependencies instead of installing them on the runner.
- Environment-specific variable files -- Use environment variables and CI secrets for URLs, credentials, and feature flags. Never hardcode them.
- Merge and publish test reports -- After sharded runs, merge results into a single report. Publish HTML reports as downloadable artifacts.
Anti-Patterns
- Sequential test execution -- Running unit, integration, and E2E tests sequentially in one job when they could run in parallel.
- No caching -- Every pipeline run downloads dependencies from scratch. This wastes 3-5 minutes and bandwidth per run.
- Ignoring artifacts on failure -- Tests fail in CI with no screenshots, reports, or logs uploaded. Debugging requires rerunning locally.
- Hardcoded secrets -- Database passwords, API keys, or tokens in pipeline files. Always use the CI system's secrets management.
- Monolithic pipeline file -- One 500-line YAML file for everything. Split into multiple workflow files or use composite actions/templates.
- No concurrency controls -- Five pipeline runs for the same branch consuming resources simultaneously when only the latest matters.
- Testing against production -- E2E tests pointing at production URLs. Always use staging or ephemeral environments.
- No timeout configuration -- A hung test running for 6 hours consuming a CI runner. Set job and step timeouts.
- Skipping quality gates -- Allowing deployments even when tests fail because "it's just a flaky test." Fix flaky tests, don't skip them.
- Not retrying flaky tests -- Failing the entire pipeline for a known flaky test. Use built-in retry mechanisms (Playwright
--retries, GitLabretry:) while working to fix the root cause.
Run Commands
# 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