DOC_INDEX
THEME
DOCS/Example Cookbook/Real-World Patterns Examples

Real-World Patterns Examples

OrchStep examples for deployment pipelines, incident response, and release automation


deploy pipeline

Deploy Pipeline

# Example: Deploy Pipeline
# A realistic deployment pipeline that combines multiple OrchStep features:
#   - Variables for configuration
#   - Shell commands for build and deploy
#   - HTTP health checks with retry
#   - Assertions to validate success
#
# This is the kind of workflow that replaces a custom deploy script
# with something readable, retryable, and composable.
#
# Try: orchstep run
# Try: orchstep run --var environment=staging --var version=v2.1.0

name: deploy-pipeline
desc: "Production deployment pipeline with health checks and validation"

defaults:
  app_name: "payments-api"
  version: "v1.0.0"
  environment: "production"
  registry: "registry.example.com"
  replicas: 3
  health_check_url: "https://payments-api.example.com/health"
  rollback_on_failure: true

tasks:
  # --- Step 1: Build ---
  build:
    desc: "Build the container image"
    steps:
      - name: compile
        func: shell
        do: |
          echo "=== Build Phase ==="
          echo "Building {{ vars.app_name }}:{{ vars.version }}"
          echo "Build completed at $(date +%H:%M:%S)"
        outputs:
          image_tag: "{{ vars.registry }}/{{ vars.app_name }}:{{ vars.version }}"

      - name: run_tests
        func: shell
        do: |
          echo "Running unit tests..."
          echo "42 tests passed, 0 failed"
        outputs:
          tests_passed: 42
          tests_failed: 0

      - name: verify_tests
        func: assert
        args:
          conditions:
            - condition: "steps.run_tests.tests_failed === 0"
              desc: "No test failures"
            - condition: "steps.run_tests.tests_passed > 0"
              desc: "At least one test ran"

      - name: push_image
        func: shell
        do: |
          echo "Pushing {{ steps.compile.image_tag }}"
          echo "Image pushed successfully"

  # --- Step 2: Deploy ---
  deploy:
    desc: "Deploy to the target environment"
    steps:
      - name: pre_deploy_check
        func: shell
        do: |
          echo "=== Deploy Phase ==="
          echo "Environment: {{ vars.environment }}"
          echo "Replicas:    {{ vars.replicas }}"

      - name: rolling_deploy
        func: shell
        do: |
          echo "Starting rolling deployment..."
          echo "Scaling {{ vars.app_name }} to {{ vars.replicas }} replicas"
          echo "Deployment ID: deploy-{{ vars.environment }}-$(date +%s)"
          echo "All replicas healthy"
        outputs:
          deploy_id: "deploy-{{ vars.environment }}-001"
          status: "deployed"

  # --- Step 3: Verify ---
  verify:
    desc: "Health check and validation"
    steps:
      # Health check with retry -- real deployments need time to stabilize
      - name: health_check
        desc: "Check service health (retries on failure)"
        func: shell
        do: |
          echo "Checking health at {{ vars.health_check_url }}"
          echo "Status: 200 OK"
          echo "Response: {\"status\":\"healthy\",\"version\":\"{{ vars.version }}\"}"
        outputs:
          http_status: 200
          response: '{"status":"healthy","version":"{{ vars.version }}"}'
        retry:
          max_attempts: 5
          interval: "3s"
          backoff_rate: 2.0

      - name: validate_health
        func: assert
        args:
          conditions:
            - condition: "steps.health_check.http_status === 200"
              desc: "Health endpoint returns 200"
            - condition: '{{ contains "healthy" steps.health_check.response }}'
              desc: "Service reports healthy status"

      - name: smoke_test
        func: shell
        do: |
          echo "Running smoke tests against {{ vars.environment }}..."
          echo "POST /api/payments -- 201 Created"
          echo "GET  /api/payments/1 -- 200 OK"
          echo "All smoke tests passed"

  # --- Orchestrator ---
  main:
    desc: "Run the full deploy pipeline"
    steps:
      - name: run_build
        task: build

      - name: run_deploy
        task: deploy

      - name: run_verify
        task: verify

      - name: report
        func: shell
        do: |
          echo ""
          echo "=========================================="
          echo "  Deployment Complete"
          echo "=========================================="
          echo "  App:         {{ vars.app_name }}"
          echo "  Version:     {{ vars.version }}"
          echo "  Environment: {{ vars.environment }}"
          echo "  Replicas:    {{ vars.replicas }}"
          echo "=========================================="

incident response

Incident Response Automation

# Example: Incident Response Automation
# Automated incident response that checks service health,
# extracts diagnostic data, evaluates conditions, and takes action.
#
# Combines: shell commands, data extraction, conditionals,
# assertions, and structured outputs.
#
# Try: orchstep run
# Try: orchstep run --var service_url="https://my-service.example.com"

name: incident-response
desc: "Automated incident response with diagnostics and remediation"

defaults:
  service_name: "order-service"
  service_url: "https://order-service.example.com"
  alert_threshold_ms: 500
  error_rate_threshold: 5
  escalation_channel: "#incidents"

tasks:
  # --- Gather diagnostics ---
  gather_diagnostics:
    desc: "Collect system health and performance data"
    steps:
      - name: check_health
        desc: "Query the service health endpoint"
        func: shell
        do: |
          echo "Checking {{ vars.service_url }}/health"
          echo "HTTP 200"
          echo '{"status":"degraded","version":"v2.3.1","uptime":"72h","db":"healthy","cache":"timeout"}'
        outputs:
          http_status: 200
          health_data: '{"status":"degraded","version":"v2.3.1","uptime":"72h","db":"healthy","cache":"timeout"}'
          service_status: "degraded"

      - name: check_metrics
        desc: "Pull recent performance metrics"
        func: shell
        do: |
          echo "=== Performance Metrics ==="
          echo "p50_latency_ms: 120"
          echo "p99_latency_ms: 850"
          echo "error_rate_pct: 3.2"
          echo "requests_per_sec: 1500"
        outputs:
          p50_latency: 120
          p99_latency: 850
          error_rate: 3.2
          rps: 1500

      - name: check_resources
        desc: "Check resource utilization"
        func: shell
        do: |
          echo "=== Resource Utilization ==="
          echo "cpu_pct: 78"
          echo "memory_pct: 65"
          echo "disk_pct: 42"
          echo "pod_restarts: 0"
        outputs:
          cpu: 78
          memory: 65
          disk: 42
          restarts: 0

  # --- Evaluate severity ---
  evaluate:
    desc: "Determine incident severity and required actions"
    steps:
      - name: run_diagnostics
        task: gather_diagnostics

      - name: check_critical
        desc: "Check for critical conditions"
        func: assert
        args:
          conditions:
            - condition: "steps.run_diagnostics.http_status === 200"
              desc: "Service is reachable"
            - condition: 'steps.run_diagnostics.service_status !== "down"'
              desc: "Service is not completely down"
        catch:
          - name: critical_alert
            func: shell
            do: |
              echo "CRITICAL: Service is unreachable or down!"
              echo "Escalating to {{ vars.escalation_channel }}"

      # Check if latency exceeds threshold
      - name: latency_check
        if: "steps.run_diagnostics.p99_latency > vars.alert_threshold_ms"
        func: shell
        do: |
          echo "WARNING: p99 latency ({{ steps.run_diagnostics.p99_latency }}ms) exceeds threshold ({{ vars.alert_threshold_ms }}ms)"
        outputs:
          latency_alert: "true"

      # Determine recommended action based on conditions
      - name: recommend_action
        func: shell
        do: |
          echo "=== Incident Assessment ==="
          echo "Service: {{ vars.service_name }}"
          echo "Status:  {{ steps.run_diagnostics.service_status }}"
          echo "p99:     {{ steps.run_diagnostics.p99_latency }}ms"
          echo "Errors:  {{ steps.run_diagnostics.error_rate }}%"
          echo "CPU:     {{ steps.run_diagnostics.cpu }}%"
          echo ""
          echo "Recommended actions:"
          echo "  1. Check cache connectivity (cache: timeout)"
          echo "  2. Monitor p99 latency trend"
          echo "  3. Review recent deployments"
        outputs:
          severity: "warning"
          action: "investigate_cache"

  # --- Remediation ---
  remediate:
    desc: "Execute automated remediation"
    steps:
      - name: assess
        task: evaluate

      - name: restart_cache
        desc: "Restart the cache layer"
        func: shell
        do: |
          echo "Restarting cache connection pool..."
          echo "Cache reconnected successfully"
        outputs:
          cache_status: "healthy"

      - name: verify_fix
        desc: "Verify the remediation worked"
        func: shell
        do: |
          echo "Post-remediation health check:"
          echo "Status: healthy"
          echo "p99 latency: 180ms"
          echo "Cache: connected"
        outputs:
          post_status: "healthy"
          post_latency: 180

      - name: validate_recovery
        func: assert
        args:
          conditions:
            - condition: 'steps.verify_fix.post_status === "healthy"'
              desc: "Service recovered to healthy state"
            - condition: "steps.verify_fix.post_latency < 500"
              desc: "Latency is back within threshold"

  main:
    desc: "Run full incident response workflow"
    steps:
      - name: respond
        task: remediate

      - name: report
        func: shell
        do: |
          echo ""
          echo "=========================================="
          echo "  Incident Response Complete"
          echo "=========================================="
          echo "  Service:     {{ vars.service_name }}"
          echo "  Resolution:  Cache reconnection"
          echo "  Status:      Recovered"
          echo "=========================================="

multi env promotion

Multi-Environment Promotion

# Example: Multi-Environment Promotion
# Promote an application through dev -> staging -> production using
# loops, conditionals, variables, and task calling.
#
# This pattern is common in CI/CD: deploy to each environment in
# sequence, run validation, and only promote if checks pass.
#
# Try: orchstep run
# Try: orchstep run --var skip_prod=true

name: multi-env-promotion
desc: "Promote a release through dev, staging, and production"

defaults:
  app_name: "checkout-service"
  version: "v3.2.0"
  skip_prod: false
  environments:
    - name: "dev"
      replicas: 1
      run_e2e: false
    - name: "staging"
      replicas: 3
      run_e2e: true
    - name: "production"
      replicas: 10
      run_e2e: true

tasks:
  # --- Deploy to a single environment ---
  deploy_to_env:
    desc: "Deploy and validate in one environment"
    vars:
      env_name: ""
      replicas: 1
      run_e2e: false
    steps:
      - name: deploy
        func: shell
        do: |
          echo "--- Deploying {{ vars.app_name }}:{{ vars.version }} to {{ vars.env_name }} ---"
          echo "Scaling to {{ vars.replicas }} replicas"
          echo "Deployment complete"
        outputs:
          status: "deployed"

      - name: basic_check
        func: shell
        do: |
          echo "Health check for {{ vars.env_name }}: OK"
        outputs:
          healthy: "true"

      # Run E2E tests only if the environment requires them
      - name: e2e_tests
        if: "{{ vars.run_e2e }}"
        func: shell
        do: |
          echo "Running E2E tests in {{ vars.env_name }}..."
          echo "12 scenarios passed"
        outputs:
          passed: 12
          failed: 0

      - name: validate
        func: assert
        args:
          condition: 'steps.deploy.status === "deployed"'
          desc: "Deployment to {{ vars.env_name }} must succeed"

  # --- Loop through environments ---
  promote_through_envs:
    desc: "Deploy to each environment in sequence"
    steps:
      # Loop over the environments list
      - name: deploy_dev
        desc: "Deploy to dev"
        task: deploy_to_env
        vars:
          env_name: "dev"
          replicas: 1
          run_e2e: false

      - name: deploy_staging
        desc: "Deploy to staging with E2E tests"
        task: deploy_to_env
        vars:
          env_name: "staging"
          replicas: 3
          run_e2e: true

      # Conditionally skip production
      - name: deploy_prod
        desc: "Deploy to production (skipped if skip_prod=true)"
        if: '{{ not (eq vars.skip_prod true) }}'
        task: deploy_to_env
        vars:
          env_name: "production"
          replicas: 10
          run_e2e: true

  main:
    desc: "Run the full promotion pipeline"
    steps:
      - name: announce
        func: shell
        do: |
          echo "=========================================="
          echo "  Release Promotion: {{ vars.app_name }} {{ vars.version }}"
          echo "=========================================="

      - name: promote
        task: promote_through_envs

      - name: summary
        func: shell
        do: |
          echo ""
          echo "=========================================="
          echo "  Promotion Complete"
          echo "=========================================="
          echo "  {{ vars.app_name }} {{ vars.version }}"
          echo "  dev -> staging -> production"
          echo "=========================================="

release automation

Release Automation

# Example: Release Automation
# Automate the full release lifecycle: version bump, changelog,
# build, tag, and publish -- using shell commands, variables, loops,
# and task composition.
#
# Combines: git operations, shell, variables, step outputs,
# assertions, and task calling.
#
# Try: orchstep run
# Try: orchstep run --var release_type=minor
# Try: orchstep run --var dry_run=true

name: release-automation
desc: "Automated release pipeline with versioning, tagging, and publish"

defaults:
  project_name: "orchstep-sdk"
  release_type: "patch"       # major, minor, or patch
  current_version: "2.3.1"
  dry_run: false
  artifacts:
    - name: "linux-amd64"
      os: "linux"
      arch: "amd64"
    - name: "darwin-arm64"
      os: "darwin"
      arch: "arm64"
    - name: "windows-amd64"
      os: "windows"
      arch: "amd64"

tasks:
  # --- Calculate new version ---
  bump_version:
    desc: "Calculate the next version based on release type"
    steps:
      - name: calculate
        func: shell
        do: |
          echo "Current version: {{ vars.current_version }}"
          echo "Release type:    {{ vars.release_type }}"
          echo "New version:     2.3.2"
        outputs:
          new_version: "2.3.2"
          tag: "v2.3.2"

      - name: validate_version
        func: assert
        args:
          conditions:
            - condition: 'steps.calculate.new_version !== "{{ vars.current_version }}"'
              desc: "New version must differ from current"
            - condition: 'matches("^\\d+\\.\\d+\\.\\d+$", steps.calculate.new_version)'
              desc: "Version must follow semver format"

  # --- Generate changelog ---
  changelog:
    desc: "Generate changelog from recent commits"
    steps:
      - name: collect_changes
        func: shell
        do: |
          echo "=== Changelog ==="
          echo ""
          echo "## What's Changed"
          echo "- fix: resolve timeout in payment processor"
          echo "- feat: add webhook retry with exponential backoff"
          echo "- docs: update API reference for v2 endpoints"
          echo "- chore: upgrade Go to 1.22"
          echo ""
          echo "3 contributors, 12 commits since v{{ vars.current_version }}"
        outputs:
          entry_count: 4
          changelog: "fix: timeout, feat: webhook retry, docs: API ref, chore: Go 1.22"

      - name: verify_changelog
        func: assert
        args:
          condition: "steps.collect_changes.entry_count > 0"
          desc: "Changelog must have at least one entry"

  # --- Build artifacts ---
  build_artifacts:
    desc: "Build release artifacts for all platforms"
    steps:
      - name: build_linux
        func: shell
        do: |
          echo "Building {{ vars.project_name }} for linux/amd64..."
          echo "Binary: dist/{{ vars.project_name }}-linux-amd64"
        outputs:
          artifact: "dist/{{ vars.project_name }}-linux-amd64"

      - name: build_darwin
        func: shell
        do: |
          echo "Building {{ vars.project_name }} for darwin/arm64..."
          echo "Binary: dist/{{ vars.project_name }}-darwin-arm64"
        outputs:
          artifact: "dist/{{ vars.project_name }}-darwin-arm64"

      - name: build_windows
        func: shell
        do: |
          echo "Building {{ vars.project_name }} for windows/amd64..."
          echo "Binary: dist/{{ vars.project_name }}-windows-amd64.exe"
        outputs:
          artifact: "dist/{{ vars.project_name }}-windows-amd64.exe"

      - name: verify_builds
        func: assert
        args:
          conditions:
            - condition: '{{ contains "linux" steps.build_linux.artifact }}'
              desc: "Linux artifact built"
            - condition: '{{ contains "darwin" steps.build_darwin.artifact }}'
              desc: "Darwin artifact built"
            - condition: '{{ contains "windows" steps.build_windows.artifact }}'
              desc: "Windows artifact built"

  # --- Tag and publish ---
  publish:
    desc: "Create git tag and publish release"
    steps:
      - name: create_tag
        func: shell
        do: |
          echo "Creating git tag {{ vars.tag }}"
          echo "Tag created successfully"
        outputs:
          tag: "{{ vars.tag }}"

      - name: push_tag
        if: '{{ not (eq vars.dry_run true) }}'
        func: shell
        do: |
          echo "Pushing tag {{ steps.create_tag.tag }} to origin"
          echo "Tag pushed"

      - name: create_release
        if: '{{ not (eq vars.dry_run true) }}'
        func: shell
        do: |
          echo "Creating GitHub release for {{ steps.create_tag.tag }}"
          echo "Uploading 3 artifacts..."
          echo "Release published"
        outputs:
          release_url: "https://github.com/example/{{ vars.project_name }}/releases/tag/{{ steps.create_tag.tag }}"

      - name: dry_run_notice
        if: "{{ vars.dry_run }}"
        func: shell
        do: |
          echo "DRY RUN: Skipped push and release creation"
          echo "Would have published {{ steps.create_tag.tag }}"

  # --- Full pipeline ---
  main:
    desc: "Run the complete release automation"
    steps:
      - name: announce
        func: shell
        do: |
          echo "=========================================="
          echo "  Release: {{ vars.project_name }}"
          echo "  Type:    {{ vars.release_type }}"
          echo "  Dry run: {{ vars.dry_run }}"
          echo "=========================================="

      - name: version
        task: bump_version

      - name: changes
        task: changelog

      - name: build
        task: build_artifacts

      - name: release
        task: publish
        vars:
          tag: "v2.3.2"

      - name: report
        func: shell
        do: |
          echo ""
          echo "=========================================="
          echo "  Release Complete"
          echo "=========================================="
          echo "  Project:  {{ vars.project_name }}"
          echo "  Version:  {{ vars.current_version }} -> v2.3.2"
          echo "  Artifacts: 3 platforms"
          echo "  Changelog: 4 entries"
          echo "=========================================="