DOC_INDEX
THEME
DOCS/Example Cookbook/Example Showcase

Example Showcase

Interactive examples with real execution output

Six curated examples that demonstrate OrchStep's core capabilities. Each example shows the YAML source, the actual execution output (captured from orchstep run), the spec test assertions, and the verification results.


Hello World

The simplest OrchStep workflow -- one task, one step.

# Example: Hello World
# The simplest OrchStep workflow -- one task, one step.
# This is the starting point for learning OrchStep.
#
# Try: orchstep run

name: hello-world
desc: "Your first OrchStep workflow"

tasks:
  main:
    desc: "Say hello"
    steps:
      - name: greet
        desc: "Output a greeting using the shell function"
        func: shell
        do: echo 'Hello from OrchStep!'

Variable Precedence

OrchStep resolves variables with 4-level precedence: Runtime > Step > Task > Definition.

# Example: Variable Precedence
# Full demonstration of how OrchStep resolves variables when the same
# name is defined at multiple levels. Higher levels always win:
#
#   Runtime (--var) > Step vars > Task vars > Definition vars
#
# Try: orchstep run

name: variable-precedence-demo
desc: "Shows how variables at different levels override each other"

# Definition variables (lowest priority in-file)
defaults:
  environment: "global-default"
  region: "us-east-1"
  replicas: "1"
  multi_level: "from-definition"
  app_name: "my-app"

tasks:
  show_definition_vars:
    desc: "Display definition-level defaults"
    steps:
      - name: show_globals
        func: shell
        do: |
          echo "environment = {{ vars.environment }}"
          echo "region      = {{ vars.region }}"
          echo "replicas    = {{ vars.replicas }}"

  show_task_override:
    desc: "Task vars override definition vars"
    vars:
      environment: "task-staging"
      replicas: "3"
      task_only: "only-visible-in-this-task"
    steps:
      - name: show_task_vars
        func: shell
        do: |
          echo "environment = {{ vars.environment }}"
          echo "replicas    = {{ vars.replicas }}"
          echo "region      = {{ vars.region }}"
          echo "task_only   = {{ vars.task_only }}"

  show_step_override:
    desc: "Step vars override task vars"
    vars:
      environment: "task-qa"
      replicas: "5"
    steps:
      - name: step_with_overrides
        desc: "This step overrides environment and replicas"
        vars:
          environment: "step-production"
          replicas: "10"
        func: shell
        do: |
          echo "=== Inside step with overrides ==="
          echo "environment = {{ vars.environment }}"
          echo "replicas    = {{ vars.replicas }}"

      - name: step_without_overrides
        desc: "This step inherits from the task level"
        func: shell
        do: |
          echo "=== Inside step without overrides ==="
          echo "environment = {{ vars.environment }}"
          echo "replicas    = {{ vars.replicas }}"

  show_template_vars:
    desc: "Variables can use template expressions"
    vars:
      base_url: "https://api.example.com"
      api_version: "v2"
    steps:
      - name: computed_variable
        desc: "Build a URL from other variables"
        vars:
          full_url: "{{ vars.base_url }}/{{ vars.api_version }}/users"
        func: shell
        do: |
          echo "Base URL:    {{ vars.base_url }}"
          echo "API Version: {{ vars.api_version }}"
          echo "Full URL:    {{ vars.full_url }}"

  main:
    desc: "Run all precedence demonstrations"
    steps:
      - name: demo_definition
        task: show_definition_vars
      - name: demo_task
        task: show_task_override
      - name: demo_step
        task: show_step_override
      - name: demo_template
        task: show_template_vars
      - name: summary
        func: shell
        do: |
          echo "=== Variable Precedence (highest to lowest) ==="
          echo "  1. Runtime (--var key=value)"
          echo "  2. Step vars"
          echo "  3. Task vars"
          echo "  4. Definition vars (defaults:)"

Retry with Exponential Backoff

Resilient error handling with jitter to prevent thundering herd problems.

# Example: Retry with Exponential Backoff and Jitter
# Shows how to prevent "thundering herd" problems by adding
# random jitter to retry delays.
#
# jitter: 0.0  = no randomness (deterministic delays)
# jitter: 0.3  = +/-30% variation around the calculated delay
# jitter: 0.5  = +/-50% variation
# jitter: 1.0  = +/-100% variation (0 to 2x the delay)
#
# Try: orchstep run resilient-deploy
# Try: orchstep run api-with-backoff

name: retry-with-jitter-demo
desc: "Resilient retry with jitter to prevent thundering herd"

tasks:
  # -- Basic jitter for distributed retries --
  resilient-deploy:
    desc: "Deploy with jitter to avoid retry storms"
    steps:
      - name: deploy_to_cluster
        func: shell
        do: |
          echo "Deploying application to cluster..."
          # In real usage: kubectl apply -f deployment.yml
          echo "Deployment successful"
        retry:
          max_attempts: 5
          interval: 2s
          jitter: 0.3     # +/-30% variation: delays between 1.4s and 2.6s

  # -- Exponential backoff with jitter --
  api-with-backoff:
    desc: "API calls with exponential backoff and jitter"
    steps:
      - name: call_rate_limited_api
        func: shell
        do: |
          echo "Calling rate-limited API..."
          # In real usage: curl https://api.example.com/data
          echo "Response received"
        retry:
          max_attempts: 5
          interval: 100ms
          backoff_rate: 2.0    # Double the delay each time
          jitter: 0.3          # +/-30% jitter on each backoff delay
          # Approximate delays:
          # Attempt 1->2: ~100ms  (70ms  - 130ms)
          # Attempt 2->3: ~200ms  (140ms - 260ms)
          # Attempt 3->4: ~400ms  (280ms - 520ms)
          # Attempt 4->5: ~800ms  (560ms - 1040ms)

  # -- Jitter with max delay cap --
  capped-backoff:
    desc: "Backoff with jitter capped at a maximum delay"
    steps:
      - name: sync_to_remote
        func: shell
        do: |
          echo "Syncing data to remote storage..."
          echo "Sync complete"
        retry:
          max_attempts: 5
          interval: 100ms
          backoff_rate: 3.0     # Triple each time (aggressive backoff)
          max_delay: 500ms      # Never wait more than 500ms
          jitter: 0.5           # +/-50% on the capped delay

  # -- Multiple steps with independent jitter --
  parallel-service-calls:
    desc: "Each step retries with its own jitter pattern"
    steps:
      - name: call_auth_service
        func: shell
        do: echo "Authenticating with auth service..."
        retry:
          max_attempts: 3
          interval: 100ms
          jitter: 0.3
        on_error: warn

      - name: call_data_service
        func: shell
        do: echo "Fetching data from data service..."
        retry:
          max_attempts: 3
          interval: 200ms
          jitter: 0.5
        on_error: warn

      - name: aggregate_results
        func: shell
        do: |
          echo "Aggregating service responses..."
          echo "Auth status: {{ steps.call_auth_service.status }}"
          echo "Data status: {{ steps.call_data_service.status }}"

HTTP API Call

Built-in HTTP function with response parsing and variable-driven URLs.

# Example: Basic HTTP GET Requests
# Shows how to make GET requests with the built-in http function.
#
# The http function supports two syntaxes:
#   - Short form:  do: "GET https://api.example.com/data"
#   - Args form:   args: { url: "...", method: GET }
#
# Response data is available via:
#   result.status_code   - HTTP status code (200, 404, etc.)
#   result.body          - Raw response body as string
#   result.data_object   - Auto-parsed JSON response (if JSON)
#   result.headers       - Response headers map
#   result.url           - Requested URL
#   result.method        - HTTP method used
#
# Try: orchstep run fetch-api-data

name: basic-get-request-demo
desc: "HTTP GET requests with variable URLs and response parsing"

defaults:
  api_base: "https://httpbin.org"

tasks:
  # -- Simple GET request --
  fetch-api-data:
    desc: "Fetch data from a REST API"
    steps:
      - name: get_users
        func: http
        do: "GET {{ vars.api_base }}/get"
        outputs:
          status: "{{ result.status_code }}"
          url: "{{ result.url }}"
          method: "{{ result.method }}"

      - name: show_result
        func: shell
        do: |
          echo "Status: {{ steps.get_users.status }}"
          echo "URL: {{ steps.get_users.url }}"
          echo "Method: {{ steps.get_users.method }}"

  # -- Parse JSON response --
  parse-json-response:
    desc: "Access fields from a JSON response body"
    steps:
      - name: get_json_data
        func: http
        do: "GET {{ vars.api_base }}/json"
        outputs:
          author: "{{ result.data_object.slideshow.author }}"
          title: "{{ result.data_object.slideshow.title }}"

      - name: display_parsed_data
        func: shell
        do: |
          echo "Author: {{ steps.get_json_data.author }}"
          echo "Title: {{ steps.get_json_data.title }}"

Parallel Build

Build frontend and backend concurrently, then deploy with merged outputs.

# Example: Parallel Step Execution
# Demonstrates: Running multiple steps concurrently using the parallel: block
#
# Key concepts:
#   - Steps inside parallel: run simultaneously
#   - Each parallel step can produce outputs
#   - Outputs merge back for subsequent sequential steps
#   - If any parallel step fails, the block fails
#
# Try: orchstep run build-and-deploy

name: parallel-build-demo
desc: "Build frontend and backend in parallel, then deploy"

defaults:
  version: "2.0.0"
  environment: staging

tasks:
  build-and-deploy:
    desc: "Parallel build followed by sequential deploy"
    steps:
      - name: build_all
        desc: "Build both services in parallel"
        parallel:
          - name: build_frontend
            func: shell
            do: |
              echo "Building frontend v{{ vars.version }}..."
              echo "Compiling TypeScript..."
              echo "Bundling assets..."
              echo "FRONTEND_ARTIFACT=frontend-{{ vars.version }}.js"
            outputs:
              artifact: '{{ result.output | regexFind "FRONTEND_ARTIFACT=(.+)" }}'

          - name: build_backend
            func: shell
            do: |
              echo "Building backend v{{ vars.version }}..."
              echo "Compiling Go..."
              echo "Running unit tests..."
              echo "BACKEND_ARTIFACT=backend-{{ vars.version }}"
            outputs:
              artifact: '{{ result.output | regexFind "BACKEND_ARTIFACT=(.+)" }}'

      - name: deploy
        desc: "Deploy both artifacts"
        func: shell
        do: |
          echo "Deploying to {{ vars.environment }}..."
          echo "Frontend: {{ steps.build_frontend.artifact }}"
          echo "Backend: {{ steps.build_backend.artifact }}"
          echo "Deploy complete!"

Deploy Pipeline

A realistic production pipeline: build, test, assert, deploy, health-check, and smoke-test.

# 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
#
# 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:
      - 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 "=========================================="