Elysia Tools
Navigation
CI/CD
CircleCI Configuration Samples
CircleCI configuration examples including workflows, orbs, caching, and advanced deployment strategies
Samples
Entries inside this sample collection
CircleCI Basic Configuration
Simple CircleCI configuration for Node.js application with build, test, and deploy jobs
Difficulty
3/10
Estimated time
15 min
Tags
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
- productionCircleCI Advanced Workflows with Orbs
Advanced CircleCI configuration using orbs, dynamic configuration, and complex deployment strategies
Difficulty
7/10
Estimated time
30 min
Tags
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/node@5.1.0
# Docker orb for Docker operations
docker: circleci/docker@2.4.0
# AWS orb for AWS deployments
aws: circleci/aws@3.0.0
# Slack orb for notifications
slack: circleci/slack@4.10.1
# Helm orb for Kubernetes deployments
helm: circleci/helm@1.4.0
# 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
Complex monorepo configuration with path filtering, dynamic job generation, and conditional workflows
Difficulty
9/10
Estimated time
45 min
Tags
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/node@5.1.0
docker: circleci/docker@2.4.0
aws: circleci/aws@3.0.0
gh: circleci/github@0.2.0
# 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: /.*/Tools
Tools frequently paired with this sample
Related