In the docs

Workflow files

The format of .lorx/actions/*.yml, every key a job takes, and what each does.

For agents: this page as Markdown

A workflow is a YAML file in .lorx/actions/ in your repository, such as .lorx/actions/ci.yml. The file's name is the workflow's name. You can have as many workflows as you like: each plans its own jobs, and needs names jobs in the same file.

Each top-level key is a job. The shortest job is a name and a command:

lint: npm run lint
test: npm test

The longer form is a map:

test:
  needs: [lint]
  paths: ["src/**", "package.json"]
  steps:
    - npm ci
    - name: unit tests
      run: npm test
  timeout: 20m

On a push, Lorx runs the jobs whose paths the push touched (every job without paths) on the branches they allow. Jobs start in levels: a job waits for everything in its needs to pass, and if one fails, the jobs that need it don't run.

Job keys

Key Takes What it does
run a command The job's one step. Use run or steps, not both.
steps a list Each item is a command, or name: plus run:. Steps run in order in the same directory, and the job stops at the first that fails.
needs job names Jobs in the same file that must pass first.
paths globs Run on a push only when a changed file matches one of them. Globs are relative to the repository root.
branches globs Run on pushes to matching branches only.
matrix axes and values Run the job once per combination: matrix: { os: [linux, macos], node: [20, 22] } makes four instances. Each gets its values as environment variables named MATRIX_<AXIS>, such as MATRIX_OS.
env names and values Environment variables for every step.
image an image name Run the steps in this container image, with docker or podman, instead of on the machine itself.
cache directories Directories kept between runs on the same machine: container volumes for jobs with an image, shared by every job of the repository with the same image.
cache_inputs commands Commands whose output is part of each step's cache key, for what the files don't pin, such as clang --version. See The step cache.
runner a label Only a runner with this label takes the job, such as runner: gpu. See Runners.
timeout a duration 30 or 30m is minutes, 2h hours, 90s seconds, up to a day. The job is stopped when it runs over.
if a condition Run only when it holds. See below.
on a schedule on: schedule 0 3 * * * (five cron fields) makes a scheduled job, which never runs on pushes. See the note below.
secrets names The secrets the job is given, as environment variables. See Secrets.
artifacts paths Not supported yet: a warning says the files aren't kept.

A key Lorx doesn't know is a warning, not an error: the rest of the workflow still runs. A GitHub Actions file (with top-level on: and jobs:) is an error that says so: Lorx's format is the one on this page.

A command with : in it needs the whole value quoted, as YAML requires: lint: 'echo "lint: 0 warnings"', or a block with lint: | and the command on the next line. bit ci check shows both for the line in question.

Conditions

if compares what set the run off. It knows two names:

  • branch: the branch pushed to (empty for a schedule);
  • event: push, schedule or manual.

Values are quoted, with 'single' or "double" quotes, and comparisons combine with &&, ||, ! and parentheses:

deploy:
  needs: [test]
  if: branch == 'main' && event != 'schedule'
  run: ./deploy.sh

Every step's environment

Variable Value
CI, LORX true
LORX_JOB The job instance, such as test (os=linux)
LORX_WORKFLOW The workflow's name
MATRIX_<AXIS> Each matrix value

Your own env comes last, and wins.

The step cache

A step that passed before with the same inputs is skipped, on your machine and on runners. Its inputs are:

  • its command, and the steps before it in its job;
  • its environment, matrix values included;
  • every file in the checkout, by content;
  • for a job with an image, the image by digest, so a moved latest is a new image;
  • for a job in the machine's own shell, the versions of the well-known tools its commands call, asked of each tool in the checkout: cargo and rustc (so a rust-toolchain.toml counts), node, bun, go, python3, git, clang and more. A new compiler on the runner runs the step again;
  • the output of the job's cache_inputs.

A skipped step says what it matched on: skipped, it passed before with the same inputs (cargo 1.98.0, rustc 1.98.0). bit run --no-cache runs every step anyway.

Secrets

secrets: [DEPLOY_TOKEN] gives the job's steps $DEPLOY_TOKEN, and only the secrets it names.

  • On a runner, they come from the repository's secrets, then its owner's account's: a repository's secret overrides an account's of the same name. They are encrypted at rest and sent only to the runner that holds the job.
  • An account's secret reaches only the repositories you list for it. A new one reaches none until you give it some. That way, someone who can write to one of your repositories can't get at a secret meant for another.
  • Only code on a branch or tag is given secrets. A run you start by hand at any other commit (an imported pull request's, say) runs without them, and its jobs that need them fail saying why.
  • With bit run, they come from your environment, then from .lorx/secrets.env in the checkout. See Run jobs on your machine.
  • Every value is replaced with *** in the job's log, along with its base64 and URL-encoded forms. Values under 4 characters aren't masked, so keep secrets longer than that.
  • A job with secrets is never cached: its steps run every time, and a container job mounts no shared cache volume.
  • A job that names a secret nobody set fails before its first step, naming it.

Scheduled jobs

A job with on: schedule … is left out of push runs. Lorx does not fire schedules yet, so today a scheduled job runs only when you name it: bit run nightly.

Check a workflow

bit ci check

This prints every problem with its line and column, the way compilers do, and shows the plan a push would make. It exits with status 1 when there are errors. See Run CI on your machine.