This guide covers everything you need to know to work with the BitVMX CI system: what each workflow does, how to trigger them, and how to customize their behavior.
The CI system is built around three workflow files:
| Workflow | File | Trigger |
|---|---|---|
| CI Template | ci_local_template.yml |
Called by other workflows (not triggered directly) |
| Nightly Build | ci_nightly_build.yml |
Every day at 03:00 UTC / Manual |
| Weekly Build | ci_weekly_build.yml |
Every Thursday at 00:00 UTC / Manual |
The Nightly and Weekly workflows are consumers of the Template — they pass different input combinations to control what gets built and tested.
This is the core workflow that contains all the build, test, and reporting logic. It is designed to be called via workflow_call from other workflows, but it also supports workflow_dispatch for manual one-off runs.
- Disk cleanup — Removes large pre-installed tools (
dotnet, Android SDK, GHC, Boost) to free space before the build. - SSH + Git authentication — Configures SSH agent and Git token auth for accessing private repositories.
- Checkout workspace — Clones
rust-bitvmx-workspaceat the branch extracted from the PR title (format:[branch-name] PR title). Falls back tomainif no branch prefix is found. - Override all submodules branch (optional) — If
submodules_branchis provided, every submodule in the workspace that has that branch available is re-cloned at that branch. - Checkout current repository (optional) — Unless
use_workspace_submodule: true, the submodule being tested is re-cloned at the PR branch. - Override submodules from PR description — Reads the PR body for
Override: <repo>:<branch>lines and re-clones those specific submodules. See PR-level overrides below. - Reorganize workspace — Flattens the workspace structure so all submodules are siblings at the runner root.
- Install Rust toolchain — Installs Rust
1.86.0withllvm-tools-preview. - Install cargo-llvm-cov (if coverage enabled) — Installs the coverage tool.
- Lint with rustfmt — Runs
cargo fmt --all -- --check. Failures are reported but do not fail the build. - Lint with Clippy (optional) — Runs
cargo clippy --all-targets --all-features. Failures are reported but do not fail the build. - Caching — Caches the Rust toolchain, Cargo registry, and the submodule's
target/directory to speed up subsequent runs. - Docker setup (optional) — Builds a Docker image and runs
docker-composeifdocker_requiredistrue. - Build CPU emulator (optional) — Runs
scripts/build-emulator.shinside the submodule. - Build gnova binary (optional) — Builds the
gnovabinary fromrust-bitvmx-gc. - Run tests — Runs
cargo test(orcargo llvm-covif coverage is enabled). Supports optional Cargo features and single-threaded mode. - Run integration tests (optional) — Runs a list of
#[ignore]d tests by name, each with--include-ignored --test-threads=1. - Post coverage comment — On pull requests, posts (or updates) a coverage report comment with line, function, and branch coverage percentages.
- Slack notification — Sends a pass/fail notification to Slack at the end of every run.
| Input | Type | Description |
|---|---|---|
repo |
string | Full org/repo path of the workspace repository (e.g. FairgateLabs/rust-bitvmx-workspace) |
submodule_path |
string | Directory name of the submodule to test (e.g. rust-bitvmx-client) |
cargo_lock_path |
string | Relative path to the Cargo.lock file (used for cache keys) |
target_path |
string | Relative path to the target/ directory (used for caching) |
| Input | Type | Default | Description |
|---|---|---|---|
clippy |
boolean | false |
Run cargo clippy linting |
build_cpu |
boolean | false |
Build the BitVMX-CPU emulator via scripts/build-emulator.sh |
build_gnova |
boolean | false |
Build the gnova binary from rust-bitvmx-gc |
docker_required |
boolean | false |
Build Docker image and run docker-compose |
dockerfile_path |
string | — | Path to the Dockerfile (relative to the submodule root) |
docker_compose_path |
string | — | Path to the docker-compose.yml (relative to the submodule root) |
cargo_features |
string | — | Space or comma-separated Cargo features to enable during tests |
single_thread_tests |
boolean | false |
Run tests with --test-threads=1 |
measure_coverage |
boolean | true |
Measure code coverage with cargo-llvm-cov. Set to false to skip |
coverage_exclude |
string | — | Comma or space-separated regex patterns to exclude paths from coverage (e.g. tests/,benches/) |
use_workspace_submodule |
boolean | false |
Use the submodule as pinned in the workspace instead of checking out the PR branch |
submodules_branch |
string | '' |
Override all workspace submodules to this branch (e.g. dev). Submodules without that branch are left as pinned |
integration_tests |
string | '' |
Space-separated list of #[ignore]d test names to run as integration tests (e.g. test_full test_all) |
| Secret | Description |
|---|---|
REPO_ACCESS_TOKEN |
GitHub token with read access to all involved repositories |
SSH_PRIVATE_KEY |
SSH private key for Git submodule access |
| Secret | Description |
|---|---|
SLACK_WEBHOOK_URL |
Incoming webhook URL for Slack build notifications |
Runs every day at 03:00 UTC (midnight Argentina time). Can also be triggered manually from the Actions tab.
Currently configured for a single matrix entry:
| Submodule | Clippy | CPU Build | gnova Build | Docker | Integration Tests |
|---|---|---|---|---|---|
rust-bitvmx-client |
Yes | Yes | Yes | Yes | test_full, test_all |
- Uses the submodule as pinned in the workspace (
use_workspace_submodule: true). - Overrides all submodules to the
devbranch (submodules_branch: 'dev'). - Runs integration tests (
test_fullandtest_all) which are normally marked#[ignore]. - Sends a Slack notification on completion.
Go to Actions → Nightly Build → Run workflow in the GitHub UI.
Runs every Thursday at 00:00 UTC. Can also be triggered manually.
| Submodule | Clippy | CPU Build | gnova Build | Docker | Integration Tests |
|---|---|---|---|---|---|
rust-bitvmx-client |
Yes | Yes | No | Yes | None |
- Uses the submodule as pinned in the workspace (
use_workspace_submodule: true). - Overrides all submodules to the
devbranch (submodules_branch: 'dev'). - Does not build the gnova binary.
- Does not run integration tests.
- Sends a Slack notification on completion.
Go to Actions → Weekly Build → Run workflow in the GitHub UI.
When opening or updating a Pull Request, you can override the branch used for any workspace submodule by adding Override: lines to the PR body:
Override: rust-bitvmx-protocol:my-feature-branch
Override: rust-bitvmx-cpu:experimental
Rules:
- The format must be exactly
Override: <repo-name>:<branch-name>(case-insensitive keyword). - The repo name must match an existing directory in the workspace.
- If the clone fails, the CI job will fail — double-check branch names before pushing.
- Multiple overrides can be listed, one per line.
The template determines which workspace branch to clone based on the PR title. If the title starts with a branch name in square brackets, that branch is used:
[dev] Fix transaction signing logic
→ Clones rust-bitvmx-workspace at branch dev.
If the PR title has no [...] prefix, the workspace is cloned from main.
When measure_coverage is enabled (default), the CI:
- Runs tests via
cargo llvm-covand produces acoverage-summary.json. - Posts a comment on the PR with a table like:
| Metric | Covered / Total | % |
|---|---|---|
| Lines | 1234/1500 | 82.3% |
| Functions | 320/400 | 80.0% |
| Branches | 210/300 | 70.0% |
The comment is updated (not duplicated) on subsequent pushes to the same PR.
Color legend:
- Green (>=80%) / Yellow (>=60%) / Red (<60%)
To exclude paths from coverage (e.g. test fixtures, benchmarks):
coverage_exclude: 'tests/,benches/,examples/'To disable coverage entirely and run a plain cargo test:
measure_coverage: falseThe template pins Rust to version 1.86.0 with the llvm-tools-preview component. This is managed via dtolnay/rust-toolchain.
Three layers of caching are used to speed up builds:
| Cache | Key |
|---|---|
Rust toolchain (~/.rustup, ~/.cargo/bin) |
OS + rust-toolchain file hash |
Cargo registry (~/.cargo/registry, ~/.cargo/git) |
OS + Cargo.lock hash |
Submodule target/ directory |
OS + submodule name + Cargo.lock hash |
Docker layers (/tmp/.buildx-cache) |
OS + commit SHA |
To add a new submodule to either scheduled workflow, add an entry to the matrix.include list in the respective file:
matrix:
include:
- submodule: rust-bitvmx-client # existing entry
clippy: true
build_cpu: true
...
- submodule: rust-bitvmx-my-new-repo # new entry
clippy: true
build_cpu: false
build_gnova: false
dockerfile_path: tests/docker/Dockerfile
docker_compose_path: tests/docker/docker-compose.ymlThen reference any matrix variables that have been added in the with: block of the uses: call.
The following secrets must be configured in the repository (or organization) for the workflows to function:
| Secret | Where used | Notes |
|---|---|---|
REPO_ACCESS_TOKEN |
All workflows | GitHub PAT or App token with repo read scope |
SSH_PRIVATE_KEY |
All workflows | SSH key registered on GitHub for submodule access |
SLACK_WEBHOOK_URL |
All workflows | Optional — Slack Incoming Webhook URL |