Airlock
Guides

Custom Steps

Write reusable steps and produce artifacts from workflow steps.

Workflow steps can reference reusable step definitions using the uses keyword. You can also write inline steps with run that produce artifacts.

The uses Format

The uses field references a step definition from a Git repository:

owner/repo/path@ref

For example:

- name: lint
  uses: airlock-hq/airlock/defaults/lint@main

This fetches the step definition from the defaults/lint directory of the airlock-hq/airlock repo at the main ref.

Writing a Custom Step

A custom step is a directory in a Git repository containing a step.yml file and any scripts it needs:

my-org/my-steps/
  custom-lint/
    step.yml
    run.sh

The step.yml defines what the step does:

name: Custom Lint
run: bash run.sh
shell: bash

Reference it in your workflow:

- name: lint
  uses: my-org/my-steps/custom-lint@main

Environment Variables

Steps have access to environment variables like AIRLOCK_WORKTREE, AIRLOCK_BRANCH, and AIRLOCK_FROZEN that provide context about the current run.

Producing Artifacts

Custom steps can produce artifacts — content, comments, and patches — using the airlock artifact commands.

Example: Custom Review Step

Here's a complete custom step that runs a linter and turns each finding into a review comment:

# my-org/my-steps/review/step.yml
name: Custom Review
shell: bash
run: |
  set -euo pipefail

  # Run a custom linter that outputs JSON
  ./bin/lint --json > results.json

  # Loop through each finding and produce a comment artifact
  COUNT=$(airlock exec json 'findings | length' < results.json)
  for i in $(seq 0 $((COUNT - 1))); do
    FILE=$(airlock exec json "findings[$i].file" < results.json)
    LINE=$(airlock exec json "findings[$i].line" < results.json)
    MSG=$(airlock exec json "findings[$i].message" < results.json)
    SEV=$(airlock exec json "findings[$i].severity" < results.json)

    airlock artifact comment \
      --file "$FILE" \
      --line "$LINE" \
      --message "$MSG" \
      --severity "$SEV"
  done

Steps run in the worktree directory ($AIRLOCK_WORKTREE), so file paths in artifact commands should be relative to the repo root.