Provide CI/CD best practices and pipeline templates for GitHub Actions and GitLab CI, recommending configurations based on project type (frontend, backend, fullstack, library, monorepo, mobile). Trigger when users ask about setting up CI/CD, automating builds, improving pipelines, or mention keywords like GitHub Actions, GitLab CI, pipeline templates, or deployment automation.
Install with the open skills CLI (global, non-interactive — available in every Claude Code session):
npx skills add zebbern/claude-code-guide --skill "pipeline-blueprint" -g -a claude-code -yOr manually — clone and copy the skill directory (SKILL.md + companion files):
git clone --depth 1 https://github.com/zebbern/claude-code-guide /tmp/claude-code-guide && cp -r /tmp/claude-code-guide/skills/pipeline-blueprint ~/.claude/skills/pipeline-blueprintThis skill is a directory: SKILL.md is the entry point; the files below ship with it.
---
name: pipeline-blueprint
description: "Provide CI/CD best practices and pipeline templates for GitHub Actions and GitLab CI, recommending configurations based on project type (frontend, backend, fullstack, library, monorepo, mobile). Trigger when users ask about setting up CI/CD, automating builds, improving pipelines, or mention keywords like GitHub Actions, GitLab CI, pipeline templates, or deployment automation."
license: MIT
---
# CI/CD Configuration Best Practices
This skill provides CI/CD pipeline templates and best practices for **GitHub Actions** and **GitLab CI**. It recommends configurations based on project type, helping teams quickly set up reliable, secure, and efficient pipelines.
## How to Use
When a user describes their project (language, framework, deployment target), recommend the most appropriate pipeline template below. Adapt stages, caching strategies, and deployment steps to match their stack.
---
## General Best Practices
### Pipeline Design Principles
1. **Fail fast**: Run linting and unit tests before expensive integration or E2E tests.
2. **Cache aggressively**: Cache dependency directories (`node_modules`, `.pip_cache`, `.m2`, `.gradle`) to speed up builds.
3. **Pin versions**: Pin CI runner images, tool versions, and action versions to SHA or exact tags — never use `latest`.
4. **Least privilege**: Use minimal permissions for tokens and credentials. Prefer OIDC over long-lived secrets where supported.
5. **Parallelize**: Split test suites across parallel jobs. Use matrix builds for multi-version testing.
6. **Immutable artifacts**: Build once, promote the same artifact through staging → production.
7. **Branch protection**: Require CI to pass before merging. Use status checks on the default branch.
### Security Checklist
- Never hardcode secrets in pipeline files; use the platform's secret management (GitHub Secrets / GitLab CI Variables).
- Audit third-party actions/images before use. Prefer official or verified sources.
- Enable dependency scanning (Dependabot, GitLab Dependency Scanning) and SAST where possible.
- Restrict who can trigger production deployments.
- Rotate secrets on a regular cadence.
---
## Project Type Templates
### 1. Frontend (React / Vue / Angular / Static Sites)
**Key stages**: Install → Lint → Test → Build → Deploy
#### GitHub Actions
```yaml
name: Frontend CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-22.04
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 test -- --coverage
- run: npm run build
- uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
deploy:
needs: ci
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-22.04
environment: production
steps:
- uses: actions/download-artifact@v4
with:
name: dist
path: dist/
# Replace with your deployment step (e.g., S3 sync, Cloudflare Pages, Vercel)
- name: Deploy
run: echo "Add your deployment command here"
```
#### GitLab CI
```yaml
stages:
- install
- lint
- test
- build
- deploy
default:
image: node:20-slim
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
install:
stage: install
script:
- npm ci
lint:
stage: lint
script:
- npm run lint
test:
stage: test
script:
- npm test -- --coverage
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
build:
stage: build
script:
- npm run build
artifacts:
paths:
- dist/
deploy:
stage: deploy
script:
- echo "Add your deployment command here"
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: on_success
```
---
### 2. Backend (Node.js / Python / Go / Java)
**Key stages**: Install → Lint → Test → Build → Docker Build → Deploy
#### GitHub Actions (Python example)
```yaml
name: Backend CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
packages: write
jobs:
ci:
runs-on: ubuntu-22.04
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: ruff check .
- run: pytest --cov --cov-report=xml
env:
DATABASE_URL: postgresql://test:test@localhost:5432/testdb
docker:
needs: ci
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
```
#### GitLab CI (Python example)
```yaml
stages:
- test
- build
- deploy
variables:
PIP_CACHE_DIR: "$CI_PROJECT_DIR/.pip_cache"
default:
image: python:3.12-slim
test:
stage: test
services:
- postgres:16
variables:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_DB: testdb
DATABASE_URL: postgresql://test:test@postgres:5432/testdb
cache:
key: pip
paths:
- .pip_cache/
script:
- pip install -r requirements.txt
- ruff check .
- pytest --cov --cov-report=xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
build-image:
stage: build
image: docker:24
services:
- docker:24-dind
variables:
DOCKER_TLS_CERTDIR: "/certs"
script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
rules:
- if: $CI_COMMIT_BRANCH == "main"
deploy:
stage: deploy
script:
- echo "Add your deployment command here"
environment:
name: production
rules:
- if: $CI_COMMIT_BRANCH == "main"
when: manual
```
---
### 3. Library / Package (npm / PyPI / Maven)
**Key stages**: Lint → Test (matrix) → Build → Publish
#### GitHub Actions (npm library example)
```yaml
name: Library CI/CD
on:
push:
branches: [main]
tags: ["v*"]
pull_request:
branches: [main]
permissions:
contents: read
id-token: write
jobs:
test:
runs-on: ubuntu-22.04
strategy:
matrix:
node-version: [18, 20, 22]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
publish:
needs: test
if: startsWith(github.ref, 'refs/tags/v')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org
cache: npm
- run: npm ci
- run: npm run build
- run: npm publish --provenance --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
```
#### GitLab CI (PyPI library example)
```yaml
stages:
- test
- publish
test:
stage: test
image: python:${PYTHON_VERSION}-slim
parallel:
matrix:
- PYTHON_VERSION: ["3.10", "3.11", "3.12"]
script:
- pip install -e ".[dev]"
- ruff check .
- pytest
publish:
stage: publish
image: python:3.12-slim
script:
- pip install build twine
- python -m build
- twine upload dist/*
variables:
TWINE_USERNAME: __token__
TWINE_PASSWORD: $PYPI_TOKEN
rules:
- if: $CI_COMMIT_TAG =~ /^v/
```
---
### 4. Fullstack (Frontend + Backend monorepo)
**Key stages**: Detect changes → Run affected pipelines → Integration test → Deploy
#### GitHub Actions
```yaml
name: Fullstack CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
changes:
runs-on: ubuntu-22.04
outputs:
frontend: ${{ steps.filter.outputs.frontend }}
backend: ${{ steps.filter.outputs.backend }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v3
id: filter
with:
filters: |
frontend:
- 'frontend/**'
backend:
- 'backend/**'
frontend:
needs: changes
if: needs.changes.outputs.frontend == 'true'
runs-on: ubuntu-22.04
defaults:
run:
working-directory: frontend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
cache-dependency-path: frontend/package-lock.json
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
backend:
needs: changes
if: needs.changes.outputs.backend == 'true'
runs-on: ubuntu-22.04
defaults:
run:
working-directory: backend
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: pip
cache-dependency-path: backend/requirements.txt
- run: pip install -r requirements.txt
- run: ruff check .
- run: pytest
e2e:
needs: [frontend, backend]
if: always() && !cancelled() && !contains(needs.*.result, 'failure')
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Run E2E tests
run: echo "Add E2E test command (e.g., Playwright, Cypress)"
```
---
### 5. Monorepo (Turborepo / Nx / Lerna)
#### GitHub Actions (Turborepo example)
```yaml
name: Monorepo CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
ci:
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npx turbo run lint test build --filter='...[HEAD^]'
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
```
---
### 6. Mobile (React Native / Flutter)
#### GitHub Actions (React Native / Android example)
```yaml
name: Mobile CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-22.04
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 test
android-build:
needs: test
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17
cache: gradle
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- name: Build Android
working-directory: android
run: ./gradlew assembleRelease
- uses: actions/upload-artifact@v4
with:
name: android-apk
path: android/app/build/outputs/apk/release/*.apk
```
---
## Advanced Patterns
### Reusable Workflows (GitHub Actions)
Extract common CI logic into reusable workflows to reduce duplication across repositories:
```yaml
# .github/workflows/reusable-node-ci.yml
name: Reusable Node CI
on:
workflow_call:
inputs:
node-version:
type: string
default: "20"
working-directory:
type: string
default: "."
jobs:
ci:
runs-on: ubuntu-22.04
defaults:
run:
working-directory: ${{ inputs.working-directory }}
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
```
### GitLab CI Include Templates
```yaml
# Use shared templates to standardize pipelines across projects
include:
- template: Security/SAST.gitlab-ci.yml
- template: Security/Dependency-Scanning.gitlab-ci.yml
- project: 'my-org/ci-templates'
ref: main
file: '/templates/node-ci.yml'
```
### Environment Protection Rules
- **GitHub**: Use environment protection rules with required reviewers for production deployments.
- **GitLab**: Use `when: manual` with `allow_failure: false` for gated deployments.
### Caching Strategy Summary
| Ecosystem | Cache Key | Cache Path |
|------------|------------------------|-----------------------------|
| Node (npm) | `package-lock.json` | `~/.npm` or `node_modules/` |
| Python | `requirements.txt` | `~/.cache/pip` |
| Go | `go.sum` | `~/go/pkg/mod` |
| Java | `build.gradle` / `pom.xml` | `~/.gradle/caches` / `~/.m2` |
| Rust | `Cargo.lock` | `~/.cargo` and `target/` |
---
## Decision Guide: Which Template to Use
| Your project looks like… | Recommended template |
|-------------------------------------------------|----------------------|
| Single-page app, static site, or SSR frontend | Frontend |
| REST API, microservice, or server app | Backend |
| npm/PyPI/Maven package for others to install | Library |
| Frontend + backend in one repo | Fullstack |
| Multiple packages managed by Turborepo/Nx/Lerna | Monorepo |
| React Native or Flutter app | Mobile |
Use when completing tasks, implementing major features, or before merging to verify work meets requirements
Use when starting feature work that needs isolation from current workspace or before executing implementation plans - ensures an isolated workspace exists via native tools or git worktree fallback
Operate the Antigravity CLI (agy): plugins, auth, sandbox.