Repository navigation
Development
How to build, test and release the CLI. The repo is deliberately small: five Rust source files, two GitHub Actions workflows, and no build script.
A stable Rust toolchain with rustfmt and clippy. Nothing else — TLS is
rustls, so there is no OpenSSL or system library to install, and there is no
code generation step.
| Path | What is in it |
|---|---|
src/main.rs |
Entry point, the command match arms, query-string helpers, and the unit tests |
src/cli.rs |
The whole clap definition — every command, subcommand and flag |
src/client.rs |
HTTP client: auth headers, timeout, and the GET/POST/PUT/PATCH/DELETE helpers |
src/config.rs |
Environment-variable loading and credential selection |
src/output.rs |
JSON pretty-printing and the two table renderers |
Cargo.toml |
Crate metadata and the release profile |
.github/workflows/ci.yml |
Format, clippy, build, test on every push and PR to main
|
.github/workflows/release.yml |
Cross-platform binaries on v* tags |
The dependency set is intentionally short: clap, reqwest, tokio, serde,
serde_json, anyhow, comfy-table, plus libc on Unix only (for the
SIGPIPE reset).
git clone https://github.com/reportmate/reportmate-cli
cd reportmate-cli
cargo build
Run against a live API by exporting the variables from Configuration and
using cargo run. Note the -- separating cargo's arguments from the CLI's.
cargo run -- devices --limit 5
CI runs these four steps in order and treats any clippy warning as an error, so run them locally before pushing.
cargo fmt --all -- --check
cargo clippy --all-targets -- -D warnings
cargo build --locked
cargo test --locked
--locked means Cargo.lock must already be up to date; a dependency bump that
is not committed will fail CI rather than silently resolving.
The test module in src/main.rs covers urlencode and query_string only —
five tests, all pure functions. Nothing that performs a request is tested: there
is no mock HTTP server, no fixture responses, no snapshot tests of the table
renderers, and no end-to-end test against a running API.
If you are adding a command, the practical consequence is that CI will not catch a wrong path, a wrong method, or a broken table. Verify against a real or local API by hand.
- Add a variant to
Commandinsrc/cli.rs, with doc comments —clapturns those into the help text. - Add the matching arm in
main.rsand call the relevantClienthelper. - If it needs a table, add a renderer to
output.rsand branch onargs.output; otherwise calloutput::print_json. - Update the Commands page.
Field names in clap's derive become kebab-case flags, so include_archived
becomes --include-archived and the CleanupUsage variant becomes
cleanup-usage.
The version string comes from RM_VERSION at compile time when set, and falls
back to the Cargo package version otherwise. The release workflow sets it from
the tag with the leading v stripped, so tag names carry the real version and
Cargo.toml's version field is only a development placeholder.
Cutting a release is pushing a v* tag. The workflow then builds four targets
in parallel, packages each binary as a .tar.gz, and attaches the archives to
the GitHub release with softprops/action-gh-release.
git tag v2026.09.03.1400
git push origin v2026.09.03.1400
The matrix has fail-fast: false, so one target failing still ships the others —
check that all four archives are attached before announcing a release.
There is no signing, notarization, checksum generation, or package-manager publication in the workflow, and the crate is not published to crates.io.
Worth knowing before you file a bug that is really a design gap:
- No config file or profiles; the source comments flag this as future work.
- Responses are untyped
serde_json::Value. The stated intent is to generate a typed client from the API's OpenAPI spec later. - No retry, backoff, or rate-limit handling.
- The 120-second timeout is hard-coded.
- No Intel macOS release target.
AGPL-3.0-or-later — reportmate.app