Belay is a signal-based workflow control action for GitHub Actions. It evaluates commit messages, pull request titles, and labels against configured signals - tags, regex patterns, and PR labels - and issues a belay order when a match is found. On match, Belay can either report the result via output for the caller to act on, or cancel the workflow run entirely via the GitHub API.
Belay solves a common CI/CD problem: running expensive workflows on commits that don't need them. Instead of duplicating skip logic across every workflow with bash scripts and environment variables, Belay centralizes signal detection into a single reusable action.
| Concept | Description |
|---|---|
| Signal | A condition that triggers a belay order - a tag substring, a regex pattern, or a PR label |
| Belay order | The result of signal evaluation - issued when any configured signal matches |
| On match | The action executed when a belay order is issued - report or cancel |
| Issued | The issued output - "true" if any signal matched, "false" otherwise |
The following diagram illustrates the lifecycle of a GitHub workflow run when evaluated by Belay Action:
graph TD
Event[GitHub Event: Push / PR] --> Job[Job: Signal Evaluation <br/> Belay Action]
Job --> Evaluation{Signal Match Found?}
Evaluation -- "Yes" --> LogMatch[issued='true' <br/> Logs match details]
Evaluation -- "No" --> Continue[issued='false' <br/> Logs execution & proceeds]
LogMatch --> OnMatch{on_match strategy?}
OnMatch -- "cancel" --> Cancel[Terminates run via GitHub API]
OnMatch -- "report" --> Report[Logs result, workflow continues]
style Event fill:#f6d9f5,stroke:#b7a1b6,stroke-width:2px,color:#000
style Evaluation fill:#fff2cc,stroke:#d6b656,stroke-width:2px,color:#000
style LogMatch fill:#ccefff,stroke:#9cb7c4,stroke-width:2px,color:#000
style OnMatch fill:#fff2cc,stroke:#d6b656,stroke-width:2px,color:#000
style Cancel fill:#f9cedc,stroke:#b79ca5,stroke-width:2px,color:#000
style Report fill:#e2f0cb,stroke:#a8b796,stroke-width:2px,color:#000
style Continue fill:#e2f0cb,stroke:#a8b796,stroke-width:2px,color:#000
The default mode. Belay sets the issued output and logs the result. The caller
decides what to do next.
jobs:
check:
name: "Skip Check"
runs-on: ubuntu-latest
outputs:
should_skip: ${{ steps.belay.outputs.issued }}
steps:
- name: "Setup: Checkout repository"
uses: actions/checkout@v6
- name: "Run: Belay"
id: belay
uses: OctalMesh/belay-action@v1
with:
signal_tags: "[ci-skip],[skip-ci]"
signal_labels: "ci-skip,skip-ci"
build:
needs: check
if: needs.check.outputs.should_skip != 'true'
runs-on: ubuntu-latest
steps:
- run: pnpm buildBelay cancels the entire workflow run via the GitHub API when a signal is matched. The run is marked as canceled, not failed.
jobs:
check:
name: "Skip Check"
runs-on: ubuntu-latest
permissions:
actions: write # Required for cancel mode
steps:
- name: "Setup: Checkout repository"
uses: actions/checkout@v6
- name: "Run: Belay"
uses: OctalMesh/belay-action@v1
with:
signal_tags: "[ci-skip],[skip-ci]"
on_match: "cancel"Note
Cancel mode requires actions: write permission on the job or workflow level.
The github_token input defaults to ${{ github.token }} and is passed
automatically - no manual configuration needed in most cases.
Use signal_patterns for more expressive matching. Patterns are evaluated
as standard JavaScript regular expressions.
- uses: OctalMesh/belay-action@v1
with:
signal_patterns: "^chore\\(deps\\),^chore\\(release\\)"
signal_labels: "dependencies,release"
on_match: "cancel"Tip
Escape backslashes in YAML - \( becomes \\( in the input string.
| Input | Required | Default | Description |
|---|---|---|---|
signal_tags |
No | [ci-skip],[skip-ci] |
Comma-separated substrings to match against the commit message and PR title |
signal_patterns |
No | - | Comma-separated regex patterns to match against the commit message and PR title |
signal_labels |
No | ci-skip,skip-ci |
Comma-separated PR label names that trigger a belay order |
on_match |
No | report |
Action to execute on match. Accepted values: report, cancel |
github_token |
No | ${{ github.token }} |
GitHub token used to cancel the workflow run. Required only when on_match is cancel |
| Output | Description |
|---|---|
issued |
"true" if a belay order was issued, "false" otherwise |
Note
The issued output is always set regardless of the on_match value.
In cancel mode it is still available to subsequent steps that run before
the cancellation is processed.
Belay evaluates three independent signal types. A belay order is issued if any signal matches.
Substring matching against the commit message and PR title. Fast and simple - use for conventional skip markers.
signal_tags: "[ci-skip],[skip-ci],[docs-only]"Matches any commit message or PR title that contains at least one of the listed substrings.
Regex matching against the commit message and PR title. Use when tag matching is too broad, or you need anchored, structured patterns.
signal_patterns: "^chore\\(deps\\),^docs:"Each pattern is compiled as a JavaScript RegExp and tested independently.
The first match issues a belay order.
PR label matching. Evaluated only on pull_request events - on push events
the label list is empty and this signal never matches.
signal_labels: "ci-skip,dependencies,release"Tip
Combine all three signal types in a single step. Belay evaluates them in parallel and stops at the first match.
| Mode | Required permission |
|---|---|
report |
contents: read (default) |
cancel |
actions: write |
Permissions can be set at the workflow or job level:
permissions:
contents: read
actions: write
- Feel free to use this project for any purpose, including commercial applications.
- You are permitted to modify, distribute, and include this project in any form, as long as the original copyright notice is retained.
- If you share or publish modified versions, attribution to the original GitHub repository is appreciated.
- This software is provided "as is", without any warranties or guarantees, as detailed in the license terms.
- Feel free to use this project for any purpose, including commercial applications.
- You are permitted to modify, distribute, and include this project in any form, as long as the original copyright notice is retained.
- If you share or publish modified versions, attribution to the original GitHub repository is appreciated.
- This software is provided "as is", without any warranties or guarantees, as detailed in the license terms.