🎯 Recommended Samples
Balanced sample collections from various categories for you to explore
CircleCI Configuration Samples
CircleCI configuration examples including workflows, orbs, caching, and advanced deployment strategies
💻 CircleCI Basic Configuration yaml
🟢 simple
⭐⭐
Simple CircleCI configuration for Node.js application with build, test, and deploy jobs
⏱️ 15 min
🏷️ circleci, nodejs, workflow, testing
Prerequisites:
CircleCI basics, YAML syntax, CI/CD concepts
# .circleci/config.yml - Basic CircleCI Configuration
version: 2.1
# Define reusable executor
executors:
node-executor:
docker:
- image: cimg/node:18.17
- image: cimg/postgres:15
environment:
POSTGRES_USER: test_user
POSTGRES_PASSWORD: test_password
POSTGRES_DB: test_db
working_directory: ~/project
resource_class: medium
# Define job templates
commands:
setup-node:
description: "Setup Node.js environment"
parameters:
cache-key:
type: string
default: "node-v1-{{ checksum "package-lock.json" }}"
steps:
- restore_cache:
keys:
- << parameters.cache-key >>
- node-v1-
- run:
name: Install dependencies
command: npm ci
- save_cache:
key: << parameters.cache-key >>
paths:
- ~/.npm
- node_modules
run-tests:
description: "Run test suite"
parameters:
coverage:
type: boolean
default: true
steps:
- run:
name: Run tests
command: |
<<# parameters.coverage >>
npm run test:coverage
<< parameters.coverage >>
npm run test
- store_test_results:
path: test-results
<<# parameters.coverage >>
- store_artifacts:
path: coverage
destination: coverage-report
- store_artifacts:
path: coverage/lcov.info
destination: coverage-lcov
<</ parameters.coverage >>
# Job definitions
jobs:
# Build job
build:
executor: node-executor
steps:
- checkout
- setup-node
- run:
name: Build application
command: npm run build
- persist_to_workspace:
root: ~/project
paths:
- .
- store_artifacts:
path: dist
destination: build-artifacts
# Lint job
lint:
executor: node-executor
steps:
- checkout
- setup-node
- run:
name: Run linter
command: npm run lint
- store_artifacts:
path: lint-results.json
destination: lint-report
# Unit tests
test:unit:
executor: node-executor
steps:
- checkout
- setup-node
- run-tests:
coverage: true
# Integration tests
test:integration:
executor: node-executor
environment:
DATABASE_URL: postgresql://test_user:test_password@localhost:5432/test_db
NODE_ENV: test
steps:
- checkout
- setup-node
- run:
name: Wait for database
command: dockerize -wait tcp://localhost:5432 -timeout 1m
- run:
name: Run integration tests
command: npm run test:integration
- store_test_results:
path: integration-test-results
# E2E tests
test:e2e:
executor:
name: node-executor
resource_class: large
steps:
- checkout
- setup-node
- run:
name: Build for E2E tests
command: npm run build
- run:
name: Install Playwright
command: |
npx playwright install --with-deps
npx playwright install-deps
- run:
name: Run E2E tests
command: npx playwright test
- store_artifacts:
path: playwright-report
destination: playwright-report
- store_test_results:
path: test-results
# Security scan
security:
executor: node-executor
steps:
- checkout
- setup-node
- run:
name: Security audit
command: |
npm audit --audit-level=high
npm audit --json > security-audit.json
- store_artifacts:
path: security-audit.json
destination: security-report
# Deploy to staging
deploy:staging:
executor: node-executor
environment:
name: staging
url: https://staging.example.com
steps:
- attach_workspace:
at: ~/project
- run:
name: Deploy to staging
command: |
echo "Deploying to staging environment..."
# Add your deployment commands here
# Example: scp -r dist/* user@staging-server:/var/www/html/
- run:
name: Health check
command: |
sleep 30
curl -f https://staging.example.com/health || exit 1
# Deploy to production
deploy:production:
executor: node-executor
environment:
name: production
url: https://example.com
steps:
- attach_workspace:
at: ~/project
- run:
name: Deploy to production
command: |
echo "Deploying to production environment..."
# Add your production deployment commands here
# Example:
# aws s3 sync dist/ s3://production-bucket/ --delete
# aws cloudfront create-invalidation --distribution-id $CLOUDFRONT_ID --paths "/*"
- run:
name: Production health check
command: |
sleep 60
curl -f https://example.com/health || exit 1
# Define workflows
workflows:
version: 2
# Main workflow
build-and-deploy:
jobs:
- build
- lint:
requires:
- build
- test:unit:
requires:
- build
- test:integration:
requires:
- build
- test:e2e:
requires:
- build
- security:
requires:
- build
- hold-for-approval:
type: approval
requires:
- lint
- test:unit
- test:integration
- test:e2e
- security
filters:
branches:
only:
- main
- deploy:staging:
requires:
- hold-for-approval
filters:
branches:
only:
- main
- deploy:production:
requires:
- deploy:staging
filters:
branches:
only:
- main
# Nightly workflow
nightly:
triggers:
- schedule:
cron: "0 2 * * *" # Run at 2 AM UTC
filters:
branches:
only:
- main
jobs:
- build
- test:unit:
requires:
- build
- test:integration:
requires:
- build
- security:
requires:
- build
# Feature branch workflow
feature-branch:
jobs:
- build
- lint:
requires:
- build
- test:unit:
requires:
- build
- test:integration:
requires:
- build
filters:
branches:
ignore:
- main
- staging
- production
💻 CircleCI Advanced Workflows with Orbs yaml
🟡 intermediate
⭐⭐⭐⭐
Advanced CircleCI configuration using orbs, dynamic configuration, and complex deployment strategies
⏱️ 30 min
🏷️ circleci, orbs, kubernetes, blue-green, deployment
Prerequisites:
CircleCI advanced, Docker, Kubernetes, Helm, AWS EKS
# .circleci/config.yml - Advanced Configuration with Orbs
version: 2.1
# Import official orbs
orbs:
# Node.js orb for Node.js specific commands
node: circleci/[email protected]
# Docker orb for Docker operations
docker: circleci/[email protected]
# AWS orb for AWS deployments
aws: circleci/[email protected]
# Slack orb for notifications
slack: circleci/[email protected]
# Helm orb for Kubernetes deployments
helm: circleci/[email protected]
# Custom commands
commands:
setup-environment:
description: "Setup environment variables and configuration"
parameters:
environment:
type: string
default: "development"
steps:
- run:
name: Setup environment
command: |
echo "Setting up environment: << parameters.environment >>"
echo "NODE_ENV=<< parameters.environment >>" >> $BASH_ENV
echo "BUILD_NUMBER=$CIRCLE_BUILD_NUM" >> $BASH_ENV
echo "BRANCH_NAME=$CIRCLE_BRANCH" >> $BASH_ENV
echo "COMMIT_SHA=$CIRCLE_SHA1" >> $BASH_ENV
echo "CI=true" >> $BASH_ENV
deploy-notification:
description: "Send deployment notification"
parameters:
environment:
type: string
status:
type: string
default: "started"
steps:
- slack/notify:
event: always
template: basic_success_1
channel: '#deployments'
custom: |
{
"text": "🚀 Deployment << parameters.status >> for << parameters.environment >>",
"attachments": [
{
"color": "<< parameters.status >>" == "completed" ? "good" : "warning",
"fields": [
{
"title": "Branch",
"value": "$CIRCLE_BRANCH",
"short": true
},
{
"title": "Commit",
"value": "$CIRCLE_SHA1",
"short": true
},
{
"title": "Build URL",
"value": "<$CIRCLE_BUILD_URL|View Build>",
"short": false
}
]
}
]
}
# Custom executors
executors:
node-executor:
docker:
- image: cimg/node:18.17
working_directory: ~/project
resource_class: medium
docker-executor:
machine:
image: ubuntu-2204:2023.04.2
resource_class: medium
large-executor:
docker:
- image: cimg/node:18.17
resource_class: large
# Job definitions
jobs:
# Build job with caching and optimization
build:
executor: node-executor
steps:
- checkout
- setup-environment:
environment: production
- node/install-packages:
cache-version: v1
pkg-manager: npm
- run:
name: Build application
command: |
npm run build
npm run analyze
- persist_to_workspace:
root: ~/project
paths:
- dist/
- node_modules/
- package.json
- package-lock.json
- store_artifacts:
path: dist/
destination: build-artifacts
- store_artifacts:
path: bundle-analysis.html
destination: bundle-analysis
# Multi-environment testing
test-matrix:
parameters:
node-version:
type: string
test-type:
type: enum
enum: ["unit", "integration", "e2e"]
executor:
name: node-executor
docker:
- image: cimg/node:<< parameters.node-version >>
steps:
- checkout
- setup-environment:
environment: test
- node/install-packages:
cache-version: "v1-<< parameters.node-version >>"
- when:
condition:
equal: [ unit, << parameters.test-type >> ]
steps:
- run:
name: Run unit tests
command: npm run test:unit
- store_test_results:
path: test-results
- store_artifacts:
path: coverage/
destination: coverage-report
- when:
condition:
equal: [ integration, << parameters.test-type >> ]
steps:
- run:
name: Run integration tests
command: npm run test:integration
- store_test_results:
path: integration-test-results
- when:
condition:
equal: [ e2e, << parameters.test-type >> ]
steps:
- run:
name: Install Playwright
command: |
npx playwright install --with-deps
npx playwright install-deps
- run:
name: Build application
command: npm run build
- run:
name: Run E2E tests
command: npx playwright test
- store_artifacts:
path: playwright-report
destination: playwright-report
# Docker build and push
docker-build:
executor: docker-executor
parameters:
image-name:
type: string
tag:
type: string
default: "$CIRCLE_SHA1"
steps:
- checkout
- setup-environment
- docker/check:
docker-username: DOCKER_USERNAME
docker-password: DOCKER_PASSWORD
- docker/build:
image: << parameters.image-name >>
tag: << parameters.tag >>
path: .
docker-context: .
extra-build-args: |
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
--build-arg VCS_REF=$CIRCLE_SHA1
--build-arg VERSION=<< parameters.tag >>
- docker/push:
image: << parameters.image-name >>
tag: << parameters.tag >>
# Security scanning
security-scan:
executor: node-executor
parallelism: 2
steps:
- checkout
- setup-environment
- node/install-packages
- run:
name: Security audit
command: |
npm audit --audit-level=high --json > npm-audit.json || true
npm audit --audit-level=high
- run:
name: SAST scan
command: |
npm install -g semgrep
semgrep --config=auto --json --output=semgrep-report.json .
- store_artifacts:
path: npm-audit.json
destination: npm-security-audit
- store_artifacts:
path: semgrep-report.json
destination: sast-report
# Kubernetes deployment
deploy:k8s:
executor: docker-executor
parameters:
environment:
type: string
namespace:
type: string
helm-chart:
type: string
default: "./helm/app"
steps:
- checkout
- setup-environment:
environment: << parameters.environment >>
- aws-eks/update-kubeconfig:
aws-region: AWS_REGION
cluster-name: EKS_CLUSTER_NAME
install-kubectl: true
- helm/install-helm-client:
version: v3.12.0
- run:
name: Deploy with Helm
command: |
helm upgrade --install app-<< parameters.namespace >> \
<< parameters.helm-chart >> \
--namespace << parameters.namespace >> \
--create-namespace \
--set image.tag=$CIRCLE_SHA1 \
--set environment=<< parameters.environment >> \
--set ingress.hosts[0].host=<< parameters.namespace >>.example.com \
--wait --timeout=10m
- run:
name: Health check
command: |
sleep 60
kubectl wait --for=condition=ready pod -l app=app-<< parameters.namespace >> -n << parameters.namespace >> --timeout=300s
# Blue-green deployment
deploy:blue-green:
executor: docker-executor
parameters:
environment:
type: string
namespace:
type: string
steps:
- checkout
- setup-environment:
environment: << parameters.environment >>
- aws-eks/update-kubeconfig:
aws-region: AWS_REGION
cluster-name: EKS_CLUSTER_NAME
install-kubectl: true
- run:
name: Blue-green deployment
command: |
# Determine current active color
CURRENT_COLOR=$(kubectl get service app-<< parameters.namespace >> -n << parameters.namespace >> -o jsonpath='{.spec.selector.color}' || echo "blue")
NEW_COLOR="blue"
if [ "$CURRENT_COLOR" = "blue" ]; then
NEW_COLOR="green"
fi
echo "Current color: $CURRENT_COLOR"
echo "New color: $NEW_COLOR"
# Deploy new version with new color
helm upgrade --install app-<< parameters.namespace >>-$NEW_COLOR ./helm/app \
--namespace << parameters.namespace >> \
--set image.tag=$CIRCLE_SHA1 \
--set color=$NEW_COLOR \
--set environment=<< parameters.environment >>
# Wait for new deployment to be ready
kubectl rollout status deployment/app-<< parameters.namespace >>-$NEW_COLOR -n << parameters.namespace >> --timeout=600s
# Health check
sleep 30
kubectl port-forward -n << parameters.namespace >> service/app-<< parameters.namespace >>-$NEW_COLOR 8080:80 &
PORT_FORWARD_PID=$!
sleep 10
# Perform health check
if curl -f http://localhost:8080/health; then
echo "Health check passed"
kill $PORT_FORWARD_PID
# Switch traffic to new version
kubectl patch service app-<< parameters.namespace >> -n << parameters.namespace >> -p '{"spec":{"selector":{"color":"'$NEW_COLOR'"}}}'
echo "Traffic switched to $NEW_COLOR version"
# Wait and verify
sleep 30
kubectl port-forward -n << parameters.namespace >> service/app-<< parameters.namespace >> 8080:80 &
PORT_FORWARD_PID=$!
sleep 10
if curl -f http://localhost:8080/health; then
echo "Deployment successful"
kill $PORT_FORWARD_PID
# Clean up old version
helm uninstall app-<< parameters.namespace >>-$CURRENT_COLOR -n << parameters.namespace >> || true
else
echo "Health check failed after switch, rolling back"
kill $PORT_FORWARD_PID
kubectl patch service app-<< parameters.namespace >> -n << parameters.namespace >> -p '{"spec":{"selector":{"color":"'$CURRENT_COLOR'"}}}'
helm uninstall app-<< parameters.namespace >>-$NEW_COLOR -n << parameters.namespace >>
exit 1
fi
else
echo "Health check failed, rolling back"
kill $PORT_FORWARD_PID
helm uninstall app-<< parameters.namespace >>-$NEW_COLOR -n << parameters.namespace >>
exit 1
fi
# Performance testing
performance-test:
executor: large-executor
parameters:
environment:
type: string
default: "staging"
steps:
- checkout
- setup-environment:
environment: << parameters.environment >>
- node/install-packages
- run:
name: Install Artillery
command: npm install -g artillery
- run:
name: Run performance test
command: |
artillery run tests/performance/load-test.yml --output performance-results.json
artillery report performance-results.json --output performance-report.html
- store_artifacts:
path: performance-results.json
destination: performance-results
- store_artifacts:
path: performance-report.html
destination: performance-report
# Workflows
workflows:
version: 2
# Main development workflow
build-and-deploy:
jobs:
# Build application
- build
# Parallel test matrix
- test-matrix:
matrix:
parameters:
node-version: ["16", "18", "20"]
test-type: ["unit"]
requires:
- build
- test-matrix:
matrix:
parameters:
node-version: ["18"]
test-type: ["integration", "e2e"]
requires:
- build
# Security scanning
- security-scan:
requires:
- build
# Docker build
- docker-build:
matrix:
parameters:
image-name: ["$DOCKER_REGISTRY/app", "$DOCKER_REGISTRY/nginx"]
requires:
- build
- test-matrix
- security-scan
# Performance testing for staging
- performance-test:
environment: staging
requires:
- docker-build
filters:
branches:
only:
- main
# Deployment approval and deployment
- hold-for-staging-approval:
type: approval
requires:
- docker-build
- security-scan
filters:
branches:
only:
- main
- deploy:k8s:
name: deploy-staging
environment: staging
namespace: staging
requires:
- hold-for-staging-approval
- staging-health-check:
executor: node-executor
steps:
- run:
name: Staging health check
command: |
sleep 120
curl -f https://staging.example.com/health || exit 1
requires:
- deploy-staging
- hold-for-production-approval:
type: approval
requires:
- deploy-staging
- staging-health-check
- performance-test
filters:
branches:
only:
- main
- deploy:blue-green:
name: deploy-production
environment: production
namespace: production
requires:
- hold-for-production-approval
- production-health-check:
executor: node-executor
steps:
- run:
name: Production health check
command: |
sleep 300
curl -f https://example.com/health || exit 1
requires:
- deploy-production
# Release workflow
release:
jobs:
- build
- docker-build:
matrix:
parameters:
image-name: ["$DOCKER_REGISTRY/app"]
tag: ["$CIRCLE_TAG"]
requires:
- build
filters:
branches:
ignore: /.*/
tags:
only: /^v[0-9]+(\.[0-9]+)*$/
# Scheduled maintenance workflow
scheduled-maintenance:
triggers:
- schedule:
cron: "0 3 * * 6" # Every Saturday at 3 AM UTC
filters:
branches:
only:
- main
jobs:
- build
- security-scan:
requires:
- build
- performance-test:
environment: production
requires:
- build
- slack/notify:
channel: '#maintenance'
event: pass
template: basic_success_1
custom: |
{
"text": "🔧 Scheduled maintenance completed",
"attachments": [
{
"color": "good",
"fields": [
{
"title": "Build Number",
"value": "$CIRCLE_BUILD_NUM",
"short": true
},
{
"title": "Results",
"value": "Security scan and performance tests passed",
"short": true
}
]
}
]
}
# Emergency rollback workflow
emergency-rollback:
jobs:
- hold-for-rollback-approval:
type: approval
filters:
branches:
only:
- main
- deploy:k8s:
name: rollback-production
environment: production
namespace: production
helm-chart: "./helm/app"
requires:
- hold-for-rollback-approval
steps:
- checkout
- setup-environment:
environment: production
- aws-eks/update-kubeconfig:
aws-region: AWS_REGION
cluster-name: EKS_CLUSTER_NAME
install-kubectl: true
- helm/install-helm-client:
version: v3.12.0
- run:
name: Rollback to previous version
command: |
helm rollback app-production -n production --wait --timeout=300s
kubectl rollout status deployment/app-production -n production --timeout=300s
- run:
name: Verify rollback
command: |
sleep 60
kubectl port-forward -n production service/app-production 8080:80 &
PORT_FORWARD_PID=$!
sleep 10
curl -f http://localhost:8080/health || exit 1
kill $PORT_FORWARD_PID
echo "Rollback verified successfully"
💻 CircleCI Monorepo Workflows yaml
🔴 complex
⭐⭐⭐⭐⭐
Complex monorepo configuration with path filtering, dynamic job generation, and conditional workflows
⏱️ 45 min
🏷️ circleci, monorepo, dynamic, path filtering, conditional
Prerequisites:
Advanced CircleCI, Monorepo architecture, Docker, Kubernetes, Git workflows
# .circleci/config.yml - Monorepo Configuration with Dynamic Workflows
version: 2.1
# Import orbs
orbs:
node: circleci/[email protected]
docker: circleci/[email protected]
aws: circleci/[email protected]
gh: circleci/[email protected]
# Global parameters and commands
parameters:
app_name:
type: string
default: "my-monorepo-app"
docker_registry:
type: string
default: "docker.io/myorg"
commands:
detect-changed-packages:
description: "Detect which packages have changed"
parameters:
base-branch:
type: string
default: "main"
steps:
- run:
name: Detect changed packages
command: |
# Get list of changed files
if [ "$CIRCLE_BRANCH" = "main" ]; then
# For main branch, compare with previous commit
BASE_COMMIT=$(git rev-parse HEAD^1)
else
# For feature branches, compare with main
BASE_COMMIT=$(git merge-base origin/<< parameters.base-branch >> HEAD)
fi
echo "Base commit: $BASE_COMMIT"
# Get changed files
CHANGED_FILES=$(git diff --name-only $BASE_COMMIT HEAD)
echo "Changed files:"
echo "$CHANGED_FILES"
# Detect affected packages
AFFECTED_PACKAGES=""
for file in $CHANGED_FILES; do
if [[ $file == packages/* ]]; then
PACKAGE=$(echo $file | cut -d'/' -f1-2)
if [[ $AFFECTED_PACKAGES != *$PACKAGE* ]]; then
AFFECTED_PACKAGES="$AFFECTED_PACKAGES $PACKAGE"
fi
fi
done
echo "Affected packages: $AFFECTED_PACKAGES"
# Generate matrix parameters
MATRIX_CONFIG="{"
FIRST=true
for package in $AFFECTED_PACKAGES; do
if [ "$FIRST" = true ]; then
FIRST=false
else
MATRIX_CONFIG="$MATRIX_CONFIG,"
fi
PACKAGE_NAME=$(basename $package)
MATRIX_CONFIG="$MATRIX_CONFIG"$PACKAGE_NAME":{"path":"$package","name":"$PACKAGE_NAME"}"
done
MATRIX_CONFIG="$MATRIX_CONFIG}"
echo "Matrix config: $MATRIX_CONFIG"
# Save to file for persistence
echo "$MATRIX_CONFIG" > /tmp/matrix-config.json
# Create environment file
echo "export AFFECTED_PACKAGES='$AFFECTED_PACKAGES'" >> $BASH_ENV
echo "export MATRIX_CONFIG_FILE='/tmp/matrix-config.json'" >> $BASH_ENV
build-package:
description: "Build a specific package"
parameters:
package-path:
type: string
package-name:
type: string
node-version:
type: string
default: "18"
steps:
- checkout
- run:
name: Build << parameters.package-name >>
command: |
cd << parameters.package-path >>
echo "Building package: << parameters.package-name >>"
echo "Node version: << parameters.node-version >>"
echo "Package path: << parameters.package-path >>"
# Use nvm to switch Node versions if needed
if command -v nvm &> /dev/null; then
nvm use << parameters.node-version >> || nvm install << parameters.node-version >>
fi
npm ci
npm run build
echo "Build completed for << parameters.package-name >>"
- persist_to_workspace:
root: ~/project
paths:
- << parameters.package-path >>/dist
- << parameters.package-path >>/build
- << parameters.package-path >>/lib
test-package:
description: "Test a specific package"
parameters:
package-path:
type: string
package-name:
type: string
test-type:
type: enum
enum: ["unit", "integration", "e2e"]
steps:
- attach_workspace:
at: ~/project
- run:
name: Run << parameters.test-type >> tests for << parameters.package-name >>
command: |
cd << parameters.package-path >>
echo "Running << parameters.test-type >> tests for << parameters.package-name >>"
case "<< parameters.test-type >>" in
"unit")
npm run test:unit || npm test
;;
"integration")
npm run test:integration
;;
"e2e")
npm run build
npm run test:e2e
;;
esac
- store_test_results:
path: << parameters.package-path >>/test-results
- when:
condition:
equal: [ unit, << parameters.test-type >> ]
steps:
- store_artifacts:
path: << parameters.package-path >>/coverage
destination: << parameters.package-name >>-coverage
# Executors
executors:
node-executor:
docker:
- image: cimg/node:18.17
working_directory: ~/project
resource_class: medium
large-executor:
docker:
- image: cimg/node:18.17
working_directory: ~/project
resource_class: large
docker-executor:
machine:
image: ubuntu-2204:2023.04.2
resource_class: medium
# Job templates
jobs:
# Setup and detection job
setup-and-detect:
executor: node-executor
steps:
- checkout
- detect-changed-packages
- persist_to_workspace:
root: ~/project
paths:
- /tmp/matrix-config.json
# Dynamic build job
build-packages:
executor: node-executor
parameters:
package-path:
type: string
package-name:
type: string
node-version:
type: string
default: "18"
steps:
- build-package:
package-path: << parameters.package-path >>
package-name: << parameters.package-name >>
node-version: << parameters.node-version >>
# Dynamic test job
test-packages:
executor: node-executor
parameters:
package-path:
type: string
package-name:
type: string
test-type:
type: enum
enum: ["unit", "integration", "e2e"]
steps:
- test-package:
package-path: << parameters.package-path >>
package-name: << parameters.package-name >>
test-type: << parameters.test-type >>
# Build Docker images for packages
build-docker-images:
executor: docker-executor
parameters:
package-name:
type: string
dockerfile-path:
type: string
default: "Dockerfile"
steps:
- checkout
- docker/check:
docker-username: DOCKER_USERNAME
docker-password: DOCKER_PASSWORD
- run:
name: Build Docker image for << parameters.package-name >>
command: |
echo "Building Docker image for << parameters.package-name >>"
# Check if Dockerfile exists
DOCKERFILE_PATH="<< parameters.package-name >>/<< parameters.dockerfile-path >>"
if [ ! -f "$DOCKERFILE_PATH" ]; then
echo "Dockerfile not found at $DOCKERFILE_PATH, skipping"
exit 0
fi
# Build and push image
IMAGE_NAME="<< parameters.docker_registry >>/<< parameters.package-name >>"
IMAGE_TAG="$CIRCLE_SHA1"
docker build \
-t "$IMAGE_NAME:$IMAGE_TAG" \
-t "$IMAGE_NAME:latest" \
-f "$DOCKERFILE_PATH" \
"<< parameters.package-name >>/"
echo "Image built: $IMAGE_NAME:$IMAGE_TAG"
# Push to registry
docker push "$IMAGE_NAME:$IMAGE_TAG"
docker push "$IMAGE_NAME:latest"
echo "Image pushed: $IMAGE_NAME:$IMAGE_TAG"
# Deploy specific package to Kubernetes
deploy-package:
executor: docker-executor
parameters:
package-name:
type: string
environment:
type: string
default: "staging"
namespace:
type: string
default: "staging"
steps:
- checkout
- aws-eks/update-kubeconfig:
aws-region: AWS_REGION
cluster-name: EKS_CLUSTER_NAME
install-kubectl: true
- run:
name: Deploy << parameters.package-name >> to << parameters.environment >>
command: |
echo "Deploying << parameters.package-name >> to << parameters.environment >>"
# Check if Helm chart exists
HELM_CHART_PATH="<< parameters.package-name >>/helm"
if [ ! -d "$HELM_CHART_PATH" ]; then
echo "Helm chart not found at $HELM_CHART_PATH, using generic deployment"
# Generic deployment
cat > << parameters.package-name >>-deployment.yaml << EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: << parameters.package-name >>
namespace: << parameters.namespace >>
spec:
replicas: 2
selector:
matchLabels:
app: << parameters.package-name >>
template:
metadata:
labels:
app: << parameters.package-name >>
spec:
containers:
- name: << parameters.package-name >>
image: << parameters.docker_registry >>/<< parameters.package-name >>:$CIRCLE_SHA1
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "<< parameters.environment >>"
EOF
kubectl apply -f << parameters.package-name >>-deployment.yaml
kubectl rollout status deployment/<< parameters.package-name >> -n << parameters.namespace >> --timeout=300s
else
# Deploy with Helm
helm upgrade --install << parameters.package-name >> \
"$HELM_CHART_PATH" \
--namespace << parameters.namespace >> \
--create-namespace \
--set image.tag=$CIRCLE_SHA1 \
--set environment=<< parameters.environment >> \
--set service.name=<< parameters.package-name >> \
--wait --timeout=600s
fi
echo "Deployment completed for << parameters.package-name >>"
# Integration test across packages
integration-test-cross-package:
executor: large-executor
steps:
- checkout
- run:
name: Setup test environment
command: |
echo "Setting up cross-package integration test environment"
# Install dependencies
npm ci
# Start all services
docker-compose -f docker-compose.test.yml up -d
# Wait for services to be ready
sleep 60
# Check service health
for service in frontend backend api-gateway; do
echo "Checking $service health..."
timeout 60 bash -c 'until curl -f http://localhost:$(docker port $service 3000 | cut -d: -f2)/health; do sleep 2; done'
done
- run:
name: Run cross-package integration tests
command: |
echo "Running cross-package integration tests"
# Run integration tests that span multiple packages
npm run test:integration:cross-package
echo "Cross-package integration tests completed"
- store_test_results:
path: cross-package-test-results
- store_artifacts:
path: integration-test-logs
destination: integration-logs
- run:
name: Cleanup test environment
command: |
docker-compose -f docker-compose.test.yml down -v
echo "Test environment cleaned up"
# Performance test specific package
performance-test-package:
executor: large-executor
parameters:
package-name:
type: string
environment:
type: string
default: "staging"
steps:
- checkout
- run:
name: Install Artillery
command: npm install -g artillery
- run:
name: Performance test for << parameters.package-name >>
command: |
echo "Running performance test for << parameters.package-name >>"
# Determine target URL based on environment
case "<< parameters.environment >>" in
"staging")
TARGET_URL="https://staging.example.com/<< parameters.package-name >>"
;;
"production")
TARGET_URL="https://example.com/<< parameters.package-name >>"
;;
*)
TARGET_URL="http://localhost:3000"
;;
esac
echo "Target URL: $TARGET_URL"
# Create performance test config
cat > performance-test.yml << EOF
config:
target: '$TARGET_URL'
phases:
- duration: 60
arrivalRate: 10
- duration: 120
arrivalRate: 50
- duration: 60
arrivalRate: 100
payloads:
path: "payloads.csv"
fields:
- "endpoint"
processor: "function (params) { return params.target + params.endpoint; }"
scenarios:
- name: "Load test for << parameters.package-name >>"
weight: 100
flow:
- get:
url: "/health"
- think: 1
- get:
url: "/api/data"
EOF
# Run performance test
artillery run performance-test.yml --output << parameters.package-name >>-performance-results.json
# Generate report
artillery report << parameters.package-name >>-performance-results.json --output << parameters.package-name >>-performance-report.html
- store_artifacts:
path: << parameters.package-name >>-performance-results.json
destination: << parameters.package-name >>-performance-results
- store_artifacts:
path: << parameters.package-name >>-performance-report.html
destination: << parameters.package-name >>-performance-report
# Workflows
workflows:
version: 2
# Main monorepo workflow
monorepo-pipeline:
jobs:
# Setup and detect changes
- setup-and-detect
# Dynamic build jobs based on changed packages
- build-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
node-version: ["18"]
requires:
- setup-and-detect
# Dynamic test jobs
- test-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
test-type: ["unit"]
requires:
- build-packages
- test-packages:
matrix:
parameters:
package-path: ["packages/backend", "packages/api-gateway"]
package-name: ["backend", "api-gateway"]
test-type: ["integration"]
requires:
- build-packages
- test-packages:
matrix:
parameters:
package-path: ["packages/frontend"]
package-name: ["frontend"]
test-type: ["e2e"]
requires:
- build-packages
# Build Docker images
- build-docker-images:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
requires:
- test-packages
filters:
branches:
only:
- main
- develop
- /^release\/.*/
# Cross-package integration tests
- integration-test-cross-package:
requires:
- build-packages
filters:
branches:
only:
- main
- develop
# Deploy to staging
- deploy-package:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
environment: ["staging"]
namespace: ["staging"]
requires:
- build-docker-images
filters:
branches:
only:
- main
- develop
# Performance tests for staging
- performance-test-package:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
environment: ["staging"]
requires:
- deploy-package
filters:
branches:
only:
- main
# Hold for production approval
- hold-for-production-approval:
type: approval
requires:
- integration-test-cross-package
- deploy-package
- performance-test-package
filters:
branches:
only:
- main
# Deploy to production
- deploy-package:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
environment: ["production"]
namespace: ["production"]
requires:
- hold-for-production-approval
filters:
branches:
only:
- main
# Package-specific workflows
package-workflows:
jobs:
# Frontend specific workflow
- build-packages:
name: build-frontend-specific
package-path: "packages/frontend"
package-name: "frontend"
node-version: "18"
filters:
branches:
only:
- /frontend\/.*/
- test-packages:
name: test-frontend-specific
package-path: "packages/frontend"
package-name: "frontend"
test-type: "e2e"
requires:
- build-frontend-specific
filters:
branches:
only:
- /frontend\/.*/
# Backend specific workflow
- build-packages:
name: build-backend-specific
package-path: "packages/backend"
package-name: "backend"
node-version: "18"
filters:
branches:
only:
- /backend\/.*/
- test-packages:
name: test-backend-specific
package-path: "packages/backend"
package-name: "backend"
test-type: "integration"
requires:
- build-backend-specific
filters:
branches:
only:
- /backend\/.*/
# Nightly monorepo workflow
nightly-monorepo:
triggers:
- schedule:
cron: "0 2 * * *" # 2 AM UTC
filters:
branches:
only:
- main
jobs:
- setup-and-detect
- build-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
requires:
- setup-and-detect
- test-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
test-type: ["unit"]
requires:
- build-packages
- integration-test-cross-package:
requires:
- build-packages
# Release workflow
release-packages:
jobs:
- setup-and-detect
- build-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
requires:
- setup-and-detect
filters:
tags:
only: /^v[0-9]+(\.[0-9]+)*$/
branches:
ignore: /.*/
- test-packages:
matrix:
parameters:
package-path: ["packages/frontend", "packages/backend", "packages/api-gateway", "packages/shared"]
package-name: ["frontend", "backend", "api-gateway", "shared"]
test-type: ["unit"]
requires:
- build-packages
filters:
tags:
only: /^v[0-9]+(\.[0-9]+)*$/
branches:
ignore: /.*/
- build-docker-images:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
dockerfile-path: ["Dockerfile"]
requires:
- test-packages
filters:
tags:
only: /^v[0-9]+(\.[0-9]+)*$/
branches:
ignore: /.*/
- deploy-package:
matrix:
parameters:
package-name: ["frontend", "backend", "api-gateway"]
environment: ["production"]
namespace: ["production"]
requires:
- build-docker-images
filters:
tags:
only: /^v[0-9]+(\.[0-9]+)*$/
branches:
ignore: /.*/