Development
Setup and workflow for working on Bindrune. The rules the code follows are in Style.
Workspace
Work inside the Nix devshell (nix develop, or direnv allow). The repository
is a cargo workspace with:
- a crate that wraps BlueZ and the other system services, and knows nothing about GTK;
- the Relm4 application;
- a crate that renders the screenshots;
xtask, which holds the CI and packaging scripts.
Building and checking
cargo run -p bindrune-gtk # run from the checkout
cargo test --workspace
cargo clippy --workspace --all-targets
cargo xtask check-install-data # validate the installed desktop, metainfo and schema files
nix flake check # everything CI checks, plus the VM tests
nix fmt
cargo xtask --help # the CI and packaging tasks
mdbook serve docs # preview the documentation site
End-to-end tests
The VM tests run the real application against a real BlueZ. Scenarios that need another device start a second virtual machine with its own BlueZ to play it, and both machines share one emulated radio. The tests operate the window the way a user does and check both what it shows and what BlueZ reports on each side. They need KVM.
cargo xtask list-vm-scenarios # the scenarios, one per file in vm-tests/scenarios
cargo xtask vm-tests # run all of them
cargo xtask vm-tests pairing low-energy # run some of them
cargo xtask vm-tests low-energy --repeat 5 # run one several times to catch flaky steps
nix run .#checks.x86_64-linux.vm-pairing.driverInteractive # step through by hand
Each attempt keeps its screenshots and log in target/vm-tests/<scenario>/attempt-<n>/,
also when it fails, and a failing attempt prints the end of its log. When a check is
new, break the behaviour it covers on purpose and run it once more, to see it fail.
The radios are emulated, so these tests say nothing about real hardware: quirks of particular phones, signal strength, audio and input timing still need a manual check.
Packaging
cargo xtask builds the release packages into dist/, the same way CI does.
The Debian package has to be built on Debian, because its dependencies are read
from Debian’s libraries and a binary from the devshell links against the Nix store.
To build it locally, run the steps of its pipeline in a Debian container. Give the
container its own volume for target/, so the build does not mix with the artifacts
of the devshell.
CI
Woodpecker CI builds the unit tests, lints and formatting check on pull requests and
on pushes to main, and checks pull request commits against Conventional Commits.
Pipelines run on a self-hosted agent, so they queue while that machine is off.
The VM tests run on Forgejo Actions, which provides KVM, on pushes to main, and can
be started manually on any branch. They do not run on pull requests, because those
jobs run in privileged containers.
CI scripts live in xtask, and pipelines call them through flake apps, which add the
tools each task needs.
Releases
Releases are automated with release-plz in git-only mode, so versions come from tags and nothing goes to crates.io.
- Every push to
mainopens or updates a release pull request with the next version and the generated changelog entry. - Merging that pull request creates the tag and the Codeberg release.
- The release event builds the packages and attaches them to the release with their checksums.
The Flatpak builds on Forgejo Actions, because flatpak-builder needs privileged
containers, and can be started manually without a release. Other packaging runs on
Woodpecker, where pull requests that touch it run everything except the attachment.
Test it that way: a manual Woodpecker run also updates the release pull request.
Which commits count towards a release is set in the release-plz configuration. The secrets each pipeline needs are named in its pipeline file.
To preview the next version and changelog, run release-plz update in a fresh clone
and discard the result. Do this outside your working checkout if it has the tags
inherited from Overskride, because they can change the result.
Screenshots
The screenshots are rendered from the real application, not drawn by hand. The showcase serves a fake BlueZ with fixture devices on an isolated D-Bus and captures the application in a headless compositor. A run is reproducible, so rebuilding the flake’s screenshot package compares two renders byte for byte.
A pairing scenario drives the real window, so a layout change can make it time out. Adjust the scenario when that happens.
After the checks pass on main, CI renders the screenshots in the Nix sandbox and
force-pushes them as the single commit of the orphan screenshots branch, which the
other documents embed.