Pipelines
A pipeline is the work that runs when a trigger fires: a push, a pull request, or a manual run. You define it in a .loha.yml file at the root of your repository. The pipeline lives in your repo, versioned alongside your code, reviewed in the same pull requests.
A pipeline is made of jobs. Each job is made of steps. That's the whole shape.
A minimal pipeline
version: 1
on:
push:
branches: [main]
jobs:
test:
steps:
- name: Checkout
uses: loha/git-clone@v1
- name: Test
uses: loha/run-tests@v1
with:
TEST_COMMAND: pnpm test
One trigger, one job, two steps. Push to main and Loha clones the repo and runs your tests.
Jobs
A job is an independent unit of work. It runs in its own isolated environment with its own filesystem, its own environment variables, and its own resource budget.
A pipeline can have 1–20 jobs. By default, every job runs in parallel. To make one job wait for another, use needs::
jobs:
lint:
steps: [...]
test:
steps: [...]
build:
needs: [lint, test]
steps: [...]
Here lint and test run at the same time. build waits for both to succeed before starting. If either fails, build is skipped.
A job that has no needs: is a leaf in the dependency graph and runs immediately.
Steps
A step is the smallest unit of execution. Steps in a job run sequentially and share the same workspace, so files written by one step are visible to the next.
Two kinds of steps:
-
uses:: reference an official task from the catalog. The task is pinned, signed, and version-locked.- name: Build image
uses: loha/buildah-build@v1
with:
IMAGE: registry.example.com/myorg/web:${COMMIT_SHA}Each task defines its own parameters: a
with:key the task doesn't declare is silently ignored, so check the catalog page for the task you're using. -
run:: a shell command in a container of your choice.- name: Install dependencies
run: pnpm install --frozen-lockfile
image: node:20
Every step needs a name.
If a step fails (non-zero exit), the job stops and is marked failed. Subsequent steps in that job don't run; jobs that depend on it via needs: are skipped.
Resource classes
A pipeline declares a default resource class (the compute budget for CPU and memory), which each job can override individually:
| Class | Use case |
|---|---|
micro | Notifications, tiny scripts |
small | Linting, type-checking, lightweight scripts |
medium | Most test suites, small builds |
large | Bigger builds, heavy compilation, monorepo-scale work |
version: '1'
resource_class: large
on:
push:
branches: [main]
jobs:
build:
steps:
- name: Checkout
uses: loha/git-clone@v1
- name: Build
run: make build
notify:
resource_class: micro
needs: [build]
steps:
- name: Announce
run: ./scripts/announce.sh
The top-level resource_class is the default for every job; a job can set its own resource_class to run heavier or lighter than the rest of the pipeline. The classes available to your workspace depend on your plan: every job's class, whether inherited or overridden, must be one your plan allows, and requesting a class outside your plan is rejected with a job-named error when the build is triggered.
What you get back
Every pipeline run produces one build: a record with live logs, status, timing, and any artifacts or container images the jobs produced. You can watch it live in the dashboard, retry it, or cancel it mid-flight.
Where to go next
- Triggers: what causes a pipeline to run
- Builds: what happens when one does
.loha.ymlschema: every field, in order- Catalog: official tasks you can reference with
uses: - Examples: copy-paste templates