DOC_INDEX
THEME
DOCS/Learn OrchStep/Task Files

Task Files

Start with a single-task workflow using top-level steps, then split a growing workflow into auto-discovered task files under tasks/.

A workflow usually starts as one task and grows into many. OrchStep gives you two features to keep that growth tidy:

  1. Top-level steps - when you only have one task, skip the tasks: wrapper entirely.
  2. The tasks/ directory - when you have several, give each its own file and OrchStep discovers them automatically.

Neither needs configuration. This page walks through both with a realistic deploy pipeline, then shows how they compose.


Part 1: the simplest workflow (top-level steps)

When a workflow does exactly one thing, the tasks: main: boilerplate is pure overhead. Write the steps at the top level instead:

# orchstep.yml
name: deploy-web
desc: Deploy the web app to staging

steps:
  - name: build
    func: shell
    do: echo "Building web app"
  - name: upload
    func: shell
    do: echo "Uploading to staging bucket"
  - name: done
    func: shell
    do: echo "Deployed to staging"

Run it with no task name - top-level steps become an implicit task called main, which is the default:

orchstep run
Workflow: deploy-web
Task: main
  Step: build
  $ echo "Building web app"
Building web app
  [ok] build
  Step: upload
  $ echo "Uploading to staging bucket"
Uploading to staging bucket
  [ok] upload
  Step: done
  $ echo "Deployed to staging"
Deployed to staging
  [ok] done
Result: success

Why use it

  • Less ceremony. No tasks:, no naming a task you only ever call one way.
  • Same engine. You still get steps, outputs, branching, loops, defaults:, modules: - everything works, it is just a shorthand for a single main task.
  • A clean upgrade path. When you need a second task, you wrap your steps under tasks: main: and keep going (Part 2).

Tip: top-level steps: and a tasks: section are mutually exclusive. If you write both, OrchStep stops with a clear message telling you to move your steps under tasks: main:. Pick one shape per file.


Part 2: splitting tasks across files (the tasks/ directory)

As a workflow gains stages - build, test, deploy, migrations - a single file gets long and merge-conflict-prone. Move each task into its own file under tasks/. OrchStep scans that directory and turns every file into a callable task. No imports, no registration.

Here is a my-service pipeline split across files. Click through the tree:

orchstep.yml
name: my-service
desc: Build, test, and deploy my-service

# Top-level steps (Part 1) orchestrate the discovered tasks below.
steps:
  - name: build_stage
    task: build
  - name: test_stage
    task: test
  - name: deploy_stage
    task: deploy

A discovered task file is just a task body: a desc: (optional) and a steps: array. The task name comes from the file's path, not from inside the file.

Listing what was discovered

orchstep list-tasks shows every callable task, including the discovered ones and the implicit main:

orchstep list-tasks
build
db-migrate
deploy
main (default)
test

Note db-migrate - the nested tasks/db/migrate.yml flattened its path into a hyphenated name. And main (default) is the top-level-steps task from orchstep.yml.

Running them

Run one discovered task directly by its name:

orchstep run build
Discovered 4 task file(s) from tasks/ directory
Workflow: my-service
Task: build
  Step: compile
  $ echo "go build -o bin/my-service ./cmd/..."
go build -o bin/my-service ./cmd/...
  [ok] compile
Result: success

Or run the whole pipeline (orchstep run -> main), which delegates to each discovered task in turn:

orchstep run
Discovered 4 task file(s) from tasks/ directory
Workflow: my-service
Task: main
  Step: build_stage
Task: build
  Step: compile
  ...
  [ok] build_stage
  Step: test_stage
Task: test
  Step: unit
  ...
  [ok] test_stage
  Step: deploy_stage
Task: deploy
  Step: smoke
  ...
  [ok] deploy_stage
Result: success

Tip: with a directory full of tasks, orchstep menu opens a keyboard-driven picker over all of them - single-keystroke hotkeys for public tasks, fuzzy search by name or description.

The rules (worth knowing up front)

NAMING = PATH

tasks/build.yml becomes build. Nested tasks/db/migrate.yml becomes db-migrate - directory separators are flattened to hyphens.

MAX DEPTH 2

You can nest one folder deep (tasks/db/migrate.yml). Going deeper is rejected so names stay predictable.

UNDERSCORE = HIDDEN

Files and folders starting with _ are skipped - perfect for _helpers.yml or a _drafts/ folder of work in progress.

INLINE WINS

A task defined inline in orchstep.yml takes precedence over a discovered file of the same name, so you can override a shared task locally.

Two more things the engine enforces, so you get a clear error instead of a silent surprise:

  • A file is only a task if it has a steps: field. A stray notes.yml with no steps: is quietly ignored, not loaded as a broken task.
  • Name conflicts fail loudly. If two files resolve to the same task name, OrchStep stops and tells you which two files collided so you can rename one.

Tip (the colon gotcha): a do: value with a bare colon-space - like do: echo "tests: 42 passed" - is read by YAML as a nested mapping and the file is skipped as invalid. Quote the whole value (do: 'echo "tests: 42 passed"') or avoid the colon. This bites everyone once.


How the two compose

You already saw it: the my-service root used top-level steps (Part 1) whose only job is to call the discovered tasks (Part 2) in order. That is the sweet spot for most projects:

  • A short, readable orchstep.yml that reads like a table of contents.
  • Each real unit of work in its own file, owned and reviewed independently.
  • Every file individually runnable (orchstep run test) for fast inner loops.

You are not limited to this shape - the root can be a full tasks: section with a main orchestrator and helpers too. Use top-level steps when the root is purely an orchestrator; use tasks: when the root file itself owns several tasks.

When to reach for which

You have...Use
One task, one fileTop-level steps: - skip the tasks: wrapper
A few tasks, one fileA tasks: section with main + helpers (see Composing Tasks)
Many tasks, growingtasks/ directory - one file per task, auto-discovered
Shared tasks across projectsPromote them to a module

Where to go next

  • Composing Tasks - how task: calls pass parameters and return outputs.
  • Project Layout - full directory layouts for workflows, envs, and modules.
  • Using Modules - share discovered tasks across repositories.
  • Runnable example: serve-task-calls - a pipeline that calls auto-discovered task files (incl. nested tasks/deploy/); on the serve LIVE graph the run descends into each called task.