DOC_INDEX
THEME
DOCS/Example Cookbook/Control Flow Examples

Control Flow Examples

OrchStep examples for conditionals, switch/case, and task calling


if elif else

If / Elif / Else Chains

# Example: If / Elif / Else Chains
# For multi-branch decisions, use "elif:" to test additional conditions
# in sequence. The first matching branch wins.
#
# Key concepts:
#   - "if:" tests the first condition
#   - "elif:" is a list of additional conditions, tested in order
#   - "else:" runs if nothing above matched
#   - Supports both Go template and JavaScript expressions
#   - Outputs from the matched branch are available via the parent step
#
# Try: orchstep run
# Try: orchstep run --var score=95

name: if-elif-else-demo
desc: "Multi-branch conditional execution"

defaults:
  score: 85

tasks:
  main:
    desc: "Grade a score using if/elif/else"
    steps:
      # Multi-branch grading with Go template expressions
      - name: assign_grade
        if: '{{ gt vars.score 90 }}'
        task: grade_a
        elif:
          - if: '{{ gt vars.score 80 }}'
            task: grade_b
          - if: '{{ gt vars.score 70 }}'
            task: grade_c
          - if: '{{ gt vars.score 60 }}'
            task: grade_d
        else:
          task: grade_f
        outputs:
          final_grade: '{{ steps.result.grade }}'

      # JavaScript expressions in elif chains
      - name: classify_score
        if: 'vars.score === 100'
        task: perfect
        elif:
          - if: 'vars.score >= 90'
            task: excellent
          - if: 'vars.score >= 75'
            task: good
          - if: 'vars.score >= 60'
            task: passing
        else:
          task: needs_improvement

      - name: report
        func: shell
        do: |
          echo "=== Score Report ==="
          echo "Score: {{ vars.score }}"
          echo "Grade: {{ steps.assign_grade.final_grade }}"

  grade_a:
    steps:
      - name: result
        func: shell
        do: echo "Grade A -- Excellent work!"
        outputs:
          grade: "A"

  grade_b:
    steps:
      - name: result
        func: shell
        do: echo "Grade B -- Good job!"
        outputs:
          grade: "B"

  grade_c:
    steps:
      - name: result
        func: shell
        do: echo "Grade C -- Satisfactory"
        outputs:
          grade: "C"

  grade_d:
    steps:
      - name: result
        func: shell
        do: echo "Grade D -- Needs improvement"
        outputs:
          grade: "D"

  grade_f:
    steps:
      - name: result
        func: shell
        do: echo "Grade F -- Did not pass"
        outputs:
          grade: "F"

  perfect:
    steps:
      - name: result
        func: shell
        do: echo "Perfect score!"

  excellent:
    steps:
      - name: result
        func: shell
        do: echo "Excellent!"

  good:
    steps:
      - name: result
        func: shell
        do: echo "Good"

  passing:
    steps:
      - name: result
        func: shell
        do: echo "Passing"

  needs_improvement:
    steps:
      - name: result
        func: shell
        do: echo "Needs improvement"

if else basic

Basic If/Else

# Example: Basic If/Else
# OrchStep supports conditional step execution using if/else.
# Conditions can use Go template expressions or JavaScript expressions.
#
# Key concepts:
#   - "if:" evaluates a condition (Go template or JavaScript)
#   - When true: runs the step's task/func/then
#   - When false: runs the "else:" branch (task or inline steps)
#   - If false with no else: the step is silently skipped
#
# Try: orchstep run
# Try: orchstep run --var environment="staging"
# Try: orchstep run --var deploy_enabled=false

name: if-else-basic-demo
desc: "Conditional execution with if/else"

defaults:
  deploy_enabled: true
  environment: "production"
  threshold: 42

tasks:
  main:
    desc: "Show different if/else patterns"
    steps:
      # Pattern 1: if true -> run task
      - name: check_deploy_flag
        if: '{{ vars.deploy_enabled }}'
        task: do_deploy

      # Pattern 2: if/else with task references
      - name: select_environment
        if: '{{ eq vars.environment "staging" }}'
        task: deploy_staging
        else: deploy_production

      # Pattern 3: if with Go template comparison
      - name: check_threshold
        if: '{{ gt vars.threshold 50 }}'
        task: threshold_high
        else: threshold_low

      # Pattern 4: if with JavaScript expression
      - name: check_range
        if: 'vars.threshold >= 40 && vars.threshold < 50'
        task: in_range

      # Pattern 5: if false with no else (step is skipped)
      - name: dev_only_debug
        if: '{{ eq vars.environment "development" }}'
        task: debug_info

      - name: finish
        func: shell
        do: echo "Workflow completed"

  do_deploy:
    steps:
      - name: run
        func: shell
        do: echo "Deployment is enabled -- proceeding"

  deploy_staging:
    steps:
      - name: run
        func: shell
        do: echo "Deploying to STAGING environment"

  deploy_production:
    steps:
      - name: run
        func: shell
        do: echo "Deploying to PRODUCTION environment"

  threshold_high:
    steps:
      - name: run
        func: shell
        do: echo "Threshold is high (> 50)"

  threshold_low:
    steps:
      - name: run
        func: shell
        do: echo "Threshold is low (<= 50)"

  in_range:
    steps:
      - name: run
        func: shell
        do: echo "Value is in range [40, 50)"

  debug_info:
    steps:
      - name: run
        func: shell
        do: echo "Debug info (only in development)"

switch case

Switch / Case

# Example: Switch / Case
# For matching a single value against multiple options, use switch/case.
# This is cleaner than long if/elif chains when comparing one variable.
#
# Key concepts:
#   - "switch: value:" the expression to match against
#   - "cases:" a list of when/then pairs
#   - "when:" can be a single value or a list of values
#   - "default:" runs if no case matches
#   - Each case's "then:" contains a list of steps to execute
#
# Try: orchstep run
# Try: orchstep run --var environment="production"
# Try: orchstep run --var user_role="viewer"

name: switch-case-demo
desc: "Pattern matching with switch/case"

defaults:
  environment: "staging"
  user_role: "admin"

tasks:
  main:
    desc: "Configure based on environment and role"
    steps:
      # Match environment to set configuration
      - name: env_config
        switch:
          value: '{{ vars.environment }}'
          cases:
            - when: production
              then:
                - name: prod_settings
                  func: shell
                  do: |
                    echo "Environment: Production"
                    echo "LOG_LEVEL=error"
                    echo "REPLICAS=5"
                  outputs:
                    log_level: "error"
                    replicas: "5"

            - when: staging
              then:
                - name: staging_settings
                  func: shell
                  do: |
                    echo "Environment: Staging"
                    echo "LOG_LEVEL=warn"
                    echo "REPLICAS=2"
                  outputs:
                    log_level: "warn"
                    replicas: "2"

            # "when" can match multiple values
            - when: [development, dev]
              then:
                - name: dev_settings
                  func: shell
                  do: |
                    echo "Environment: Development"
                    echo "LOG_LEVEL=debug"
                    echo "REPLICAS=1"
                  outputs:
                    log_level: "debug"
                    replicas: "1"

          default:
            - name: unknown_env
              func: shell
              do: echo "Unknown environment -- using defaults"
              outputs:
                log_level: "info"
                replicas: "1"

      # Match user role to determine permissions
      - name: permissions
        switch:
          value: '{{ vars.user_role }}'
          cases:
            - when: admin
              then:
                - name: admin_perms
                  func: shell
                  do: 'echo "Permissions: full access (create, read, update, delete)"'
                  outputs:
                    access_level: "full"

            - when: [editor, moderator]
              then:
                - name: editor_perms
                  func: shell
                  do: 'echo "Permissions: edit access (create, read, update)"'
                  outputs:
                    access_level: "edit"

            - when: viewer
              then:
                - name: viewer_perms
                  func: shell
                  do: 'echo "Permissions: read-only"'
                  outputs:
                    access_level: "read"

          default:
            - name: guest_perms
              func: shell
              do: 'echo "Permissions: no access"'
              outputs:
                access_level: "none"

      - name: summary
        func: shell
        do: |
          echo "=== Configuration Summary ==="
          echo "Environment: {{ vars.environment }}"
          echo "User Role:   {{ vars.user_role }}"

task calling

Task Calling and File Discovery

# Example: Task Calling and File Discovery
# OrchStep automatically discovers task files from a tasks/ directory.
# This lets you organize large workflows into separate files.
#
# Key concepts:
#   - Tasks defined in tasks/<name>.yml are auto-discovered
#   - Nested directories work: tasks/deploy/staging.yml -> task "deploy-staging"
#   - The main orchstep.yml can call discovered tasks by name
#   - Inline tasks and file-based tasks can be mixed freely
#
# Directory structure for this example:
#   orchstep.yml          <-- this file
#   tasks/
#     build.yml           <-- auto-discovered as "build"
#     test.yml            <-- auto-discovered as "test"
#     deploy/
#       staging.yml       <-- auto-discovered as "deploy-staging"
#       production.yml    <-- auto-discovered as "deploy-production"
#
# Try: orchstep run

name: task-calling-demo
desc: "Orchestrate auto-discovered task files"

tasks:
  # Inline task: call out to file-based tasks in sequence
  main:
    desc: "CI/CD pipeline using auto-discovered tasks"
    steps:
      - name: run_build
        desc: "Execute the build task (from tasks/build.yml)"
        task: build

      - name: run_tests
        desc: "Execute the test task (from tasks/test.yml)"
        task: test

      - name: deploy_staging
        desc: "Deploy to staging (from tasks/deploy/staging.yml)"
        task: deploy-staging

      - name: deploy_production
        desc: "Deploy to production (from tasks/deploy/production.yml)"
        task: deploy-production

      - name: done
        func: shell
        do: echo "Pipeline complete -- all tasks executed."

  # You can also define tasks inline alongside discovered ones
  notify:
    desc: "Send notification (inline task)"
    steps:
      - name: send
        func: shell
        do: echo "Sending deployment notification..."