Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

_No changes yet — entries land here as PRs merge into `dev`, then move to a per-RC file under `changelogs/pre-releases/` when the next RC is cut._
### Added

- **`cargo-binstall` support** ([#27](https://github.com/kbrdn1/gwm-cli/issues/27)). `[package.metadata.binstall]` in `Cargo.toml` lets `cargo binstall gwm` pull the prebuilt archive (`gwm-v{version}-{target}.tar.gz`, `.zip` on Windows) straight from the GitHub Release — no Rust toolchain or libgit2 compile at install time. Pinned against artefact-naming drift by `tests/binstall_metadata_tests.rs`.

## Past releases

Expand Down
19 changes: 19 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,21 @@ readme = "README.md"
keywords = ["git", "worktree", "tui", "cli", "ratatui"]
categories = ["command-line-utilities", "development-tools"]

# cargo-binstall (#27): pull the prebuilt binary from the GitHub
# Release instead of compiling git2/vendored-libgit2 from source.
# Mirrors the release workflows' artefact naming — `gwm-v{version}-
# {target}.tar.gz` (`.zip` on windows) — and the in-archive layout
# `gwm-v{version}-{target}/{gwm|gwm.exe}`. `tests/binstall_metadata_tests.rs`
# pins this block against drift.
[package.metadata.binstall]
pkg-url = "{ repo }/releases/download/v{ version }/gwm-v{ version }-{ target }.tar.gz"
pkg-fmt = "tgz"
bin-dir = "gwm-v{ version }-{ target }/{ bin }"

[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-url = "{ repo }/releases/download/v{ version }/gwm-v{ version }-{ target }.zip"
pkg-fmt = "zip"

[lib]
name = "gwm"
path = "src/lib.rs"
Expand Down Expand Up @@ -62,6 +77,10 @@ libc = "0.2"
assert_cmd = "2"
criterion = "0.8"
predicates = "3"
# Parse our own Cargo.toml in tests/binstall_metadata_tests.rs (#27) so
# the `[package.metadata.binstall]` contract is asserted structurally,
# not by string-matching. Same `toml` major as the runtime dependency.
toml = "1.1"

[[bench]]
name = "commit_graph"
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ Rust CLI + ratatui TUI to manage git worktrees across projects. Native `libgit2`
| Channel | Command |
|:---------------|:---------------------------------------------------------------------|
| Cargo (source) | `cargo install --path .` |
| cargo-binstall | `cargo binstall gwm` |
| Homebrew (macOS) | `brew tap kbrdn1/tap && brew install gwm` |
| Nix flake | `nix profile install github:kbrdn1/gwm-cli` |
| Prebuilt | <https://github.com/kbrdn1/gwm-cli/releases> (Linux / macOS / Windows) |

`cargo binstall gwm` grabs the prebuilt binary from the matching GitHub Release instead of compiling `git2`/vendored-libgit2 from source — no Rust toolchain needed at install time.

Full install matrix and verification steps: [`docs/getting-started/install.md`](docs/1.getting-started/1.install.md).

## the 30-second tour
Expand Down
10 changes: 10 additions & 0 deletions docs/1.getting-started/1.install.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,16 @@ cargo install --path .

The binary lands in `~/.cargo/bin/gwm`. Requires a recent stable Rust toolchain (1.80+).

## via cargo-binstall

```bash
cargo binstall gwm
```

[`cargo-binstall`](https://github.com/cargo-bins/cargo-binstall) reads the `[package.metadata.binstall]` block in `gwm`'s `Cargo.toml`, downloads the prebuilt archive matching your host triple from the GitHub Release, extracts it, and drops the binary in `~/.cargo/bin/`. No Rust toolchain or `git2`/libgit2 C compile is needed at install time — much faster than `cargo install` on first run.

The metadata points at the same artefacts the release workflow publishes (`gwm-v{version}-{target}.tar.gz`, or `.zip` on Windows), so any tagged release is binstall-able. `tests/binstall_metadata_tests.rs` pins the block against drift.

## via Homebrew (macOS)

```bash
Expand Down
2 changes: 2 additions & 0 deletions skills/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ cargo install --path . # → ~/.cargo/bin/gwm
gwm --version
```

No Rust toolchain at hand? `cargo binstall gwm` pulls the prebuilt binary from the matching GitHub Release (via `[package.metadata.binstall]`) and drops it in `~/.cargo/bin/` without compiling `git2`/vendored-libgit2 from source — much faster on first install.

Prebuilt releases (Linux x86_64/aarch64, macOS Intel/Apple Silicon, Windows): https://github.com/kbrdn1/gwm-cli/releases. A Homebrew formula ships under `packaging/homebrew/` and a Nix `flake.nix` is at the repo root.

## Default conventions
Expand Down
77 changes: 77 additions & 0 deletions tests/binstall_metadata_tests.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Pin the `[package.metadata.binstall]` contract (issue #27).
//!
//! `cargo binstall gwm` reads this block to locate the prebuilt archive
//! on the GitHub Release instead of compiling from source. The block
//! must stay in lock-step with the release workflows' artefact naming
//! (`gwm-v{version}-{target}.{tar.gz|zip}`) and the in-archive layout
//! (`gwm-v{version}-{target}/{gwm|gwm.exe}`). A drift here ships a
//! `binstall` that 404s or extracts the wrong path, so the contract is
//! asserted structurally against the parsed manifest.

use std::path::{Path, PathBuf};

fn manifest() -> toml::Value {
let path: PathBuf = Path::new(env!("CARGO_MANIFEST_DIR")).join("Cargo.toml");
let raw = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
toml::from_str(&raw).expect("Cargo.toml is valid TOML")
}

fn binstall() -> toml::Value {
manifest()
.get("package")
.and_then(|p| p.get("metadata"))
.and_then(|m| m.get("binstall"))
.cloned()
.expect("[package.metadata.binstall] block is present")
}

#[test]
fn binstall_pkg_url_points_at_the_release_tarball() {
let b = binstall();
let pkg_url = b.get("pkg-url").and_then(|v| v.as_str()).expect("pkg-url is a string");
assert_eq!(
pkg_url, "{ repo }/releases/download/v{ version }/gwm-v{ version }-{ target }.tar.gz",
"pkg-url must match the release workflow's tarball naming"
);
}

#[test]
fn binstall_pkg_fmt_is_tgz() {
let b = binstall();
let fmt = b.get("pkg-fmt").and_then(|v| v.as_str()).expect("pkg-fmt is a string");
assert_eq!(fmt, "tgz", "the non-windows artefacts are gzipped tarballs");
}

#[test]
fn binstall_bin_dir_matches_in_archive_layout() {
let b = binstall();
let bin_dir = b.get("bin-dir").and_then(|v| v.as_str()).expect("bin-dir is a string");
// The packaging step stages the binary under
// `gwm-v{version}-{target}/` inside the archive; `{ bin }` resolves
// to `gwm` (or `gwm.exe` on windows).
assert_eq!(bin_dir, "gwm-v{ version }-{ target }/{ bin }");
}

#[test]
fn binstall_windows_override_uses_zip_artefact() {
let b = binstall();
let win = b
.get("overrides")
.and_then(|o| o.get("x86_64-pc-windows-msvc"))
.expect("windows MSVC override is present");

let fmt = win
.get("pkg-fmt")
.and_then(|v| v.as_str())
.expect("windows pkg-fmt is a string");
assert_eq!(fmt, "zip", "the windows artefact is a .zip, not a tarball");

let pkg_url = win
.get("pkg-url")
.and_then(|v| v.as_str())
.expect("windows pkg-url is a string");
assert!(
pkg_url.ends_with("gwm-v{ version }-{ target }.zip"),
"windows pkg-url must target the .zip artefact, got: {pkg_url}"
);
}
Loading