Trill

Defining Workflows

A workflow is a YAML file that defines jobs and their steps.

Workflow Name

Workflows can have an optional name field for display purposes. The name appears in the TUI title bar, the dashboard run list, and the web UI:

name: Deploy Pipeline
jobs:
  build:
    steps:
      - name: compile
        run: cargo build --release

When name is omitted, the filename is used as the display name (e.g. deploy.yaml). For workflows submitted without a filename, a truncated run ID is shown instead.

Jobs

Jobs are the unit of parallel execution. They’re defined as a map where the key is the job name:

jobs:
  build:
    steps:
      - name: compile
        run: cargo build --release

  test:
    steps:
      - name: run_tests
        run: cargo test

Jobs with no dependencies run in parallel. Trill schedules as many concurrent jobs as the dependency graph allows.

Dependencies

Use depends_on to declare that a job needs another job to finish first:

jobs:
  setup:
    steps:
      - name: install
        run: npm install

  build:
    depends_on: [setup]
    steps:
      - name: compile
        run: npm run build

  test:
    depends_on: [setup]
    steps:
      - name: run
        run: npm test

  deploy:
    depends_on: [build, test]
    steps:
      - name: ship
        run: ./deploy.sh

Trill validates the dependency graph at startup. It rejects:

Environment Variables

Set environment variables at the job level (available to all steps) or the step level:

jobs:
  build:
    env:
      NODE_ENV: production
    steps:
      - name: compile
        run: npm run build
        env:
          MINIFY: "true"

Step-level variables override job-level ones with the same name.

Timeouts

Jobs and steps can have a timeout that cancels execution if it takes too long:

jobs:
  build:
    timeout: 30m
    steps:
      - name: checkout
        run: git clone $REPO_URL .
        timeout: 5m
      - name: compile
        run: cargo build --release

Timeout values use human-readable duration format: 5s, 30m, 1h30m, 2h. When a timeout expires, the job or step is marked as timed out and subsequent steps are skipped.

Job Outputs

Jobs can expose outputs that other jobs consume. Outputs reference step outputs using step_name.key format:

jobs:
  build:
    outputs:
      tag: "docker_build.tag"
      sha: "docker_build.sha"
    steps:
      - name: docker_build
        run: |
          TAG="v$(date +%s)"
          SHA="abc123"
          echo "{\"tag\": \"$TAG\", \"sha\": \"$SHA\"}" > "$STEP_OUTPUT_FILE"

  deploy:
    depends_on: [build]
    steps:
      - name: push
        run: echo "Deploying {{ jobs.build.outputs.tag }}"

The $STEP_OUTPUT_FILE environment variable points to a temporary file where steps write JSON key-value pairs. Trill reads this file after each step and makes the values available to subsequent steps and jobs.

See Defining Steps for more on step outputs and Advanced Concepts for the expression engine.

Conditionals

Jobs and steps support if conditions that control whether they execute:

jobs:
  deploy:
    depends_on: [test]
    if: "not local"
    steps:
      - name: push
        run: ./deploy.sh

  local_verify:
    depends_on: [build]
    if: "local"
    steps:
      - name: check
        run: echo "Local verification passed"

The local variable is true when running on a local machine (as opposed to a CI server). You can use not to negate conditions.

Conditions can also reference job statuses and outputs:

jobs:
  deploy:
    depends_on: [test]
    if: "jobs.test.status == 'success'"
    steps:
      - name: push
        run: ./deploy.sh

See Advanced Concepts for the full list of available context variables.

Failure Handling

When a step fails, subsequent steps in the same job are skipped and the job is marked as failed. Jobs that depend on a failed job are also skipped.

Steps can be marked allow_failure: true to continue even if they exit with a non-zero code:

steps:
  - name: lint
    run: cargo clippy
    allow_failure: true
  - name: build
    run: cargo build

To run a job even when a dependency fails, use status functions in the if condition:

jobs:
  build:
    steps:
      - name: compile
        run: cargo build

  cleanup:
    depends_on: [build]
    if: "always()"
    steps:
      - name: teardown
        run: docker-compose down

  notify:
    depends_on: [build]
    if: "failure()"
    steps:
      - name: alert
        run: curl -X POST https://slack.example.com/webhook

Available status functions: always(), failure(), success(), cancelled(). See Advanced Concepts for details.

Cancelling siblings on failure

By default, when a job fails, its running siblings continue executing. This can waste compute — if build-linux fails, build-mac keeps running even though the downstream deploy (which needs both) will never execute.

Set cancel_on_failure: true on a job to cancel all non-dependent running and pending siblings when it fails:

jobs:
  build-linux:
    cancel_on_failure: true
    steps:
      - name: build
        run: ./build.sh linux

  build-mac:
    cancel_on_failure: true
    steps:
      - name: build
        run: ./build.sh mac

  deploy:
    depends_on: [build-linux, build-mac]
    steps:
      - name: ship
        run: ./deploy.sh

If build-linux fails, build-mac is cancelled. deploy is skipped (normal dependency failure propagation). Siblings whose if: condition uses one of the status functions — always(), failure(), success(), cancelled() — are exempt from cancellation and continue running. That’s what keeps cleanup and notify jobs alive when the main work fails.

CLI Flags

These flags apply to trill run:

FlagEnv varEffect
--quietSuppress output except summary and exit code
--dry-runShow execution plan without running
--debugStep through each step interactively
--debug --jsonDebug mode with JSON protocol
--localForce local mode (overrides CI env auto-detection)
--server <URL>TRILL_SERVERSubmit workflow to trill.build instead of running locally
--project <slug>TRILL_PROJECTProject slug or ID when submitting remotely (requires --server)
--token <token>TRILL_TOKENUser token for remote submission (requires --server)
--followStream remote run events to console until completion (requires --server)
--dashboardOpen the dashboard TUI for the submitted run (requires --server and --token)

Exit codes: 0 = success (or submission success for remote fire-and-forget), 1 = workflow failed, 2 = validation error.

Other Commands

Beyond trill run, a few subcommands help you work with runs on the hosted service. All of them read TRILL_SERVER and TRILL_TOKEN from the environment, so you only need to set them once per shell.

trill dashboard

A terminal UI for browsing and inspecting runs on a server. Shows live progress, lets you drill into job and step output, and submits approvals interactively.

trill dashboard --server https://app.trill.build --token trl_<token>

Filter to a single project with --project <slug>.

trill projects

List the projects your token has access to. Useful when you don’t remember the slug to pass to --project:

trill projects --server https://app.trill.build --token trl_<token>

trill signal / trill approve

See Signals and Approval Steps. Both commands work against local files (no --server) or against a hosted service (with --server and a user token).

Next Steps