Skip to content

Instantly share code, notes, and snippets.

@AXDOOMER
Last active June 17, 2026 21:23
Show Gist options
  • Select an option

  • Save AXDOOMER/38f14e70c4dd5986119d158cd80bb641 to your computer and use it in GitHub Desktop.

Select an option

Save AXDOOMER/38f14e70c4dd5986119d158cd80bb641 to your computer and use it in GitHub Desktop.
Complex enterprise CI/CD pipeline
name: Complex CI CD Pipeline
on:
push:
branches:
- main
- develop
- 'release/**'
- 'hotfix/**'
- 'feature/**'
tags:
- 'v*.*.*'
pull_request:
branches:
- main
- develop
- 'release/**'
workflow_dispatch:
inputs:
deploy_target:
description: 'Override deployment target'
required: false
default: 'auto'
type: choice
options:
- auto
- none
- dev
- staging
- production
skip_tests:
description: 'Skip test execution'
required: false
default: false
type: boolean
force_deploy:
description: 'Force deployment even if change scope is small'
required: false
default: false
type: boolean
env:
NODE_VERSION: '20'
PYTHON_VERSION: '3.12'
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
DEFAULT_REGION: us-east-1
concurrency:
group: ${{ github.workflow }}-${{ github.ref_name }}
cancel-in-progress: ${{ !startsWith(github.ref, 'refs/tags/') }}
jobs:
context:
name: Resolve Context
runs-on: ubuntu-latest
outputs:
branch_type: ${{ steps.resolve.outputs.branch_type }}
deploy_env: ${{ steps.resolve.outputs.deploy_env }}
should_deploy: ${{ steps.resolve.outputs.should_deploy }}
should_run_e2e: ${{ steps.resolve.outputs.should_run_e2e }}
should_publish_image: ${{ steps.resolve.outputs.should_publish_image }}
release_channel: ${{ steps.resolve.outputs.release_channel }}
risk_level: ${{ steps.resolve.outputs.risk_level }}
steps:
- name: Resolve branch and event context
id: resolve
shell: bash
run: |
BRANCH_TYPE="unknown"
DEPLOY_ENV="none"
SHOULD_DEPLOY="false"
SHOULD_RUN_E2E="false"
SHOULD_PUBLISH_IMAGE="false"
RELEASE_CHANNEL="snapshot"
RISK_LEVEL="low"
REF="${GITHUB_REF}"
EVENT="${GITHUB_EVENT_NAME}"
INPUT_DEPLOY_TARGET="${{ github.event.inputs.deploy_target || 'auto' }}"
INPUT_FORCE_DEPLOY="${{ github.event.inputs.force_deploy || 'false' }}"
if [[ "$REF" == refs/heads/main ]]; then
BRANCH_TYPE="main"
DEPLOY_ENV="production"
SHOULD_DEPLOY="true"
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RELEASE_CHANNEL="stable"
RISK_LEVEL="critical"
else
if [[ "$REF" == refs/heads/develop ]]; then
BRANCH_TYPE="develop"
DEPLOY_ENV="dev"
SHOULD_DEPLOY="true"
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RELEASE_CHANNEL="beta"
RISK_LEVEL="medium"
else
if [[ "$REF" == refs/heads/release/* ]]; then
BRANCH_TYPE="release"
DEPLOY_ENV="staging"
SHOULD_DEPLOY="true"
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RELEASE_CHANNEL="rc"
RISK_LEVEL="high"
else
if [[ "$REF" == refs/heads/hotfix/* ]]; then
BRANCH_TYPE="hotfix"
DEPLOY_ENV="staging"
SHOULD_DEPLOY="true"
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RELEASE_CHANNEL="hotfix"
RISK_LEVEL="critical"
else
if [[ "$REF" == refs/tags/v* ]]; then
BRANCH_TYPE="tag"
DEPLOY_ENV="production"
SHOULD_DEPLOY="true"
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RELEASE_CHANNEL="stable"
RISK_LEVEL="critical"
else
if [[ "$REF" == refs/heads/feature/* ]]; then
BRANCH_TYPE="feature"
DEPLOY_ENV="dev"
SHOULD_DEPLOY="false"
SHOULD_RUN_E2E="false"
SHOULD_PUBLISH_IMAGE="false"
RELEASE_CHANNEL="snapshot"
RISK_LEVEL="low"
else
BRANCH_TYPE="other"
DEPLOY_ENV="none"
SHOULD_DEPLOY="false"
SHOULD_RUN_E2E="false"
SHOULD_PUBLISH_IMAGE="false"
RELEASE_CHANNEL="snapshot"
RISK_LEVEL="low"
fi
fi
fi
fi
fi
fi
if [[ "$EVENT" == "pull_request" ]]; then
SHOULD_DEPLOY="false"
SHOULD_PUBLISH_IMAGE="false"
if [[ "$BRANCH_TYPE" == "main" || "$BRANCH_TYPE" == "release" || "$BRANCH_TYPE" == "hotfix" ]]; then
SHOULD_RUN_E2E="true"
RISK_LEVEL="high"
else
SHOULD_RUN_E2E="false"
if [[ "$BRANCH_TYPE" == "develop" ]]; then
RISK_LEVEL="medium"
else
RISK_LEVEL="low"
fi
fi
else
if [[ "$EVENT" == "workflow_dispatch" ]]; then
if [[ "$INPUT_DEPLOY_TARGET" != "auto" ]]; then
if [[ "$INPUT_DEPLOY_TARGET" == "none" ]]; then
SHOULD_DEPLOY="false"
DEPLOY_ENV="none"
else
DEPLOY_ENV="$INPUT_DEPLOY_TARGET"
SHOULD_DEPLOY="true"
if [[ "$INPUT_DEPLOY_TARGET" == "production" ]]; then
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RISK_LEVEL="critical"
else
if [[ "$INPUT_DEPLOY_TARGET" == "staging" ]]; then
SHOULD_RUN_E2E="true"
SHOULD_PUBLISH_IMAGE="true"
RISK_LEVEL="high"
else
SHOULD_RUN_E2E="false"
SHOULD_PUBLISH_IMAGE="true"
RISK_LEVEL="medium"
fi
fi
fi
else
if [[ "$INPUT_FORCE_DEPLOY" == "true" && "$BRANCH_TYPE" == "feature" ]]; then
SHOULD_DEPLOY="true"
DEPLOY_ENV="dev"
SHOULD_PUBLISH_IMAGE="true"
RISK_LEVEL="medium"
fi
fi
fi
fi
echo "branch_type=$BRANCH_TYPE" >> "$GITHUB_OUTPUT"
echo "deploy_env=$DEPLOY_ENV" >> "$GITHUB_OUTPUT"
echo "should_deploy=$SHOULD_DEPLOY" >> "$GITHUB_OUTPUT"
echo "should_run_e2e=$SHOULD_RUN_E2E" >> "$GITHUB_OUTPUT"
echo "should_publish_image=$SHOULD_PUBLISH_IMAGE" >> "$GITHUB_OUTPUT"
echo "release_channel=$RELEASE_CHANNEL" >> "$GITHUB_OUTPUT"
echo "risk_level=$RISK_LEVEL" >> "$GITHUB_OUTPUT"
changes:
name: Detect Change Scope
runs-on: ubuntu-latest
outputs:
app_changed: ${{ steps.filter.outputs.app }}
infra_changed: ${{ steps.filter.outputs.infra }}
docs_changed: ${{ steps.filter.outputs.docs }}
tests_changed: ${{ steps.filter.outputs.tests }}
steps:
- uses: actions/checkout@v4
- name: Detect changed files
id: filter
uses: dorny/paths-filter@v3
with:
filters: |
app:
- 'src/**'
- 'package.json'
- 'package-lock.json'
- 'pyproject.toml'
- 'requirements*.txt'
- 'Dockerfile'
infra:
- '.github/workflows/**'
- 'infra/**'
- 'helm/**'
- 'terraform/**'
- 'k8s/**'
docs:
- '**/*.md'
- 'docs/**'
tests:
- 'tests/**'
- 'cypress/**'
- 'playwright/**'
lint_and_unit:
name: Lint And Unit Tests
runs-on: ubuntu-latest
needs:
- context
- changes
if: |
needs.changes.outputs.app_changed == 'true' ||
needs.changes.outputs.infra_changed == 'true' ||
github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
suite: [node, python]
steps:
- uses: actions/checkout@v4
- name: Setup Node
if: matrix.suite == 'node'
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: npm
- name: Setup Python
if: matrix.suite == 'python'
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install Node dependencies
if: matrix.suite == 'node'
run: npm ci
- name: Install Python dependencies
if: matrix.suite == 'python'
run: |
python -m pip install --upgrade pip
if [[ -f requirements.txt ]]; then
pip install -r requirements.txt
else
echo "No requirements.txt found, skipping Python dependency install"
fi
- name: Run Node lint and tests
if: matrix.suite == 'node' && github.event.inputs.skip_tests != 'true'
run: |
npm run lint
npm test -- --ci
- name: Run Python lint and tests
if: matrix.suite == 'python' && github.event.inputs.skip_tests != 'true'
run: |
if command -v ruff >/dev/null 2>&1; then
ruff check .
else
echo "ruff not installed, skipping lint"
fi
if command -v pytest >/dev/null 2>&1; then
pytest -q
else
echo "pytest not installed, skipping tests"
fi
- name: Skip tests explicitly
if: github.event.inputs.skip_tests == 'true'
run: echo "Tests skipped by workflow_dispatch input"
security_scan:
name: Security And Compliance
runs-on: ubuntu-latest
needs:
- context
- changes
- lint_and_unit
if: |
always() &&
(needs.lint_and_unit.result == 'success' || needs.lint_and_unit.result == 'skipped')
steps:
- uses: actions/checkout@v4
- name: Run dependency audit for high-risk branches
if: |
needs.context.outputs.risk_level == 'critical' ||
needs.context.outputs.risk_level == 'high'
run: |
if [[ -f package.json ]]; then
npm audit --audit-level=high || true
else
echo "No package.json found"
fi
- name: Run lightweight audit for low-risk branches
if: |
needs.context.outputs.risk_level != 'critical' &&
needs.context.outputs.risk_level != 'high'
run: |
if [[ -f package.json ]]; then
npm audit --omit=dev || true
else
echo "No package.json found"
fi
- name: Infra policy checks
if: needs.changes.outputs.infra_changed == 'true'
run: |
echo "Run terraform validate / kubeconform / conftest here"
build:
name: Build Artifacts
runs-on: ubuntu-latest
needs:
- context
- changes
- lint_and_unit
- security_scan
if: |
needs.lint_and_unit.result == 'success' &&
needs.security_scan.result == 'success'
outputs:
image_tag: ${{ steps.meta.outputs.image_tag }}
steps:
- uses: actions/checkout@v4
- name: Compute image tag
id: meta
run: |
if [[ "${GITHUB_REF}" == refs/tags/v* ]]; then
TAG="${GITHUB_REF#refs/tags/}"
else
if [[ "${{ needs.context.outputs.release_channel }}" == "stable" ]]; then
TAG="stable-${GITHUB_SHA::7}"
else
if [[ "${{ needs.context.outputs.release_channel }}" == "rc" ]]; then
TAG="rc-${GITHUB_SHA::7}"
else
if [[ "${{ needs.context.outputs.release_channel }}" == "beta" ]]; then
TAG="beta-${GITHUB_SHA::7}"
else
TAG="snapshot-${GITHUB_SHA::7}"
fi
fi
fi
fi
echo "image_tag=$TAG" >> "$GITHUB_OUTPUT"
- name: Setup Docker Buildx
if: needs.context.outputs.should_publish_image == 'true'
uses: docker/setup-buildx-action@v3
- name: Login to GHCR
if: needs.context.outputs.should_publish_image == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build image only
if: |
needs.context.outputs.should_publish_image != 'true' &&
needs.changes.outputs.app_changed == 'true'
run: |
docker build -t local/test-image:${{ steps.meta.outputs.image_tag }} .
- name: Build and push image
if: needs.context.outputs.should_publish_image == 'true'
uses: docker/build-push-action@v6
with:
context: .
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.meta.outputs.image_tag }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.context.outputs.release_channel }}
integration_tests:
name: Integration Tests
runs-on: ubuntu-latest
needs:
- context
- changes
- build
if: |
needs.build.result == 'success' &&
(
needs.context.outputs.should_run_e2e == 'true' ||
needs.changes.outputs.tests_changed == 'true'
)
steps:
- uses: actions/checkout@v4
- name: Run staging-grade integration tests
if: |
needs.context.outputs.deploy_env == 'staging' ||
needs.context.outputs.deploy_env == 'production'
run: |
echo "Running full integration suite"
echo "Could invoke Playwright/Cypress/API smoke tests here"
- name: Run lightweight integration tests
if: |
needs.context.outputs.deploy_env != 'staging' &&
needs.context.outputs.deploy_env != 'production'
run: |
echo "Running lightweight integration suite"
deploy:
name: Deploy
runs-on: ubuntu-latest
needs:
- context
- changes
- build
- integration_tests
if: |
always() &&
needs.context.outputs.should_deploy == 'true' &&
needs.build.result == 'success' &&
(
needs.integration_tests.result == 'success' ||
needs.integration_tests.result == 'skipped'
)
environment:
name: ${{ needs.context.outputs.deploy_env }}
steps:
- uses: actions/checkout@v4
- name: Select deployment strategy
id: strategy
run: |
STRATEGY="rolling"
if [[ "${{ needs.context.outputs.deploy_env }}" == "production" ]]; then
if [[ "${{ needs.context.outputs.risk_level }}" == "critical" ]]; then
STRATEGY="blue-green"
else
STRATEGY="rolling"
fi
else
if [[ "${{ needs.context.outputs.deploy_env }}" == "staging" ]]; then
if [[ "${{ needs.changes.outputs.infra_changed }}" == "true" ]]; then
STRATEGY="canary"
else
STRATEGY="rolling"
fi
else
if [[ "${{ needs.context.outputs.deploy_env }}" == "dev" ]]; then
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
STRATEGY="recreate"
else
STRATEGY="rolling"
fi
else
STRATEGY="none"
fi
fi
fi
echo "strategy=$STRATEGY" >> "$GITHUB_OUTPUT"
- name: Block unsafe production deploys
if: |
needs.context.outputs.deploy_env == 'production' &&
github.event_name == 'push' &&
startsWith(github.ref, 'refs/heads/feature/')
run: |
echo "Feature branches cannot deploy to production"
exit 1
- name: Deploy to dev
if: needs.context.outputs.deploy_env == 'dev'
run: |
echo "Deploying to dev with strategy ${{ steps.strategy.outputs.strategy }}"
echo "helm upgrade --install app-dev ./helm/app --set image.tag=${{ needs.build.outputs.image_tag }}"
- name: Deploy to staging
if: needs.context.outputs.deploy_env == 'staging'
run: |
echo "Deploying to staging with strategy ${{ steps.strategy.outputs.strategy }}"
echo "terraform apply -var environment=staging"
- name: Deploy to production
if: needs.context.outputs.deploy_env == 'production'
run: |
echo "Deploying to production with strategy ${{ steps.strategy.outputs.strategy }}"
echo "kubectl set image deployment/app app=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ needs.build.outputs.image_tag }}"
post_deploy_validation:
name: Post Deploy Validation
runs-on: ubuntu-latest
needs:
- context
- deploy
if: needs.deploy.result == 'success'
steps:
- name: Production validation
if: needs.context.outputs.deploy_env == 'production'
run: |
echo "Running production smoke tests"
echo "Checking SLO dashboards and synthetic probes"
- name: Non-production validation
if: needs.context.outputs.deploy_env != 'production'
run: |
echo "Running non-production smoke tests"
rollback:
name: Conditional Rollback
runs-on: ubuntu-latest
needs:
- context
- deploy
- post_deploy_validation
if: |
always() &&
needs.deploy.result == 'success' &&
needs.post_deploy_validation.result == 'failure' &&
needs.context.outputs.deploy_env != 'dev'
steps:
- name: Roll back staging or production
run: |
if [[ "${{ needs.context.outputs.deploy_env }}" == "production" ]]; then
echo "Rolling back production deployment"
else
if [[ "${{ needs.context.outputs.deploy_env }}" == "staging" ]]; then
echo "Rolling back staging deployment"
else
echo "No rollback required"
fi
fi
notify:
name: Notify
runs-on: ubuntu-latest
needs:
- context
- lint_and_unit
- security_scan
- build
- integration_tests
- deploy
- post_deploy_validation
- rollback
if: always()
steps:
- name: Summarize pipeline result
run: |
echo "Branch type: ${{ needs.context.outputs.branch_type }}"
echo "Deploy env: ${{ needs.context.outputs.deploy_env }}"
echo "Lint/unit: ${{ needs.lint_and_unit.result }}"
echo "Security: ${{ needs.security_scan.result }}"
echo "Build: ${{ needs.build.result }}"
echo "Integration: ${{ needs.integration_tests.result }}"
echo "Deploy: ${{ needs.deploy.result }}"
echo "Validation: ${{ needs.post_deploy_validation.result }}"
echo "Rollback: ${{ needs.rollback.result }}"
- name: Notify Slack on production failure
if: |
needs.context.outputs.deploy_env == 'production' &&
(
needs.deploy.result == 'failure' ||
needs.post_deploy_validation.result == 'failure' ||
needs.rollback.result == 'failure'
)
run: |
echo "Send critical Slack/PageDuty alert"
- name: Notify team on staging issues
if: |
needs.context.outputs.deploy_env == 'staging' &&
(
needs.deploy.result == 'failure' ||
needs.post_deploy_validation.result == 'failure'
)
run: |
echo "Send staging alert"
- name: Notify success
if: |
needs.build.result == 'success' &&
(
needs.deploy.result == 'success' ||
needs.deploy.result == 'skipped'
)
run: |
echo "Send success notification"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment