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 or a local directory.
Remote Steps
Reference a step from a Git repository with owner/repo/path@ref:
- name: lint
uses: airlock-hq/airlock/defaults/lint@mainThis fetches the step definition from the defaults/lint directory of the airlock-hq/airlock repo at the main ref.
Local Steps
Reference a step from your repository with ./path (relative to the repo root):
- name: custom-lint
uses: ./my-steps/lintThis resolves the step definition from the my-steps/lint directory in your repository. Airlock looks for step.yml, action.yml, or stage.yaml in the referenced directory. Path traversal outside the repository is blocked.
Local steps are useful for developing and testing custom steps before publishing them to a Git repository.
Writing a Custom Step
A custom step is a directory containing a step.yml file and any scripts it needs:
custom-lint/
step.yml
run.shThe step.yml defines what the step does:
name: Custom Lint
run: bash run.sh
shell: bashReference it in your workflow — either from a local directory or a Git repository:
# Local step (during development)
- name: lint
uses: ./custom-lint
# Remote step (after publishing)
- name: lint
uses: my-org/my-steps/custom-lint@mainEnvironment 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"
doneSteps run in the worktree directory ($AIRLOCK_WORKTREE), so file paths in artifact commands should be relative to
the repo root.
Related
- Artifacts — The three artifact types in detail
- Custom Workflows — Assemble custom steps into workflows
- Default Steps — Built-in steps you can use or extend