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: 2 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 40 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "netsuke"
name = "netsuke-build"
version = "0.1.0"
edition = "2024"
include = [
Expand All @@ -20,6 +20,13 @@ repository = "https://github.com/leynos/netsuke"
keywords = ["build", "ninja", "jinja", "yaml", "automation"]
categories = ["command-line-utilities", "development-tools::build-utils"]

[lib]
name = "netsuke"

[[bin]]
name = "netsuke"
path = "src/main.rs"

[package.metadata.ortho_config]
root_type = "netsuke::cli::CliConfig"
locales = [
Expand Down Expand Up @@ -63,6 +70,38 @@ locales = [
[package.metadata.kani.flags]
default-unwind = "6"

# `cargo binstall` derives release asset names from the Cargo package name,
# which is `netsuke-build`. Every release asset is named after the `netsuke`
# binary instead, so the defaults would never match and `cargo binstall
# netsuke-build` would fall back to a source build on the pinned nightly.
#
# Release assets are unarchived binaries (`pkg-fmt = "bin"`) whose names are
# built by `.github/workflows/release.yml` and
# `.github/actions/upload-release-assets`: the workflow artefact name, then
# `__`, then the staging directory from `.github/release-staging.toml`
# (`{bin_name}_{platform}_{arch}`), then `-`, then the staged file name.
# `tests/binstall_metadata_tests.rs` holds these overrides to that contract.
[package.metadata.binstall]
pkg-fmt = "bin"

[package.metadata.binstall.overrides.x86_64-unknown-linux-gnu]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-linux-amd64__netsuke_linux_x86_64-netsuke"

[package.metadata.binstall.overrides.aarch64-unknown-linux-gnu]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-linux-arm64__netsuke_linux_aarch64-netsuke"

[package.metadata.binstall.overrides.x86_64-apple-darwin]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-macos-x86_64__netsuke_macos_x86_64-netsuke"

[package.metadata.binstall.overrides.aarch64-apple-darwin]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-macos-arm64__netsuke_macos_aarch64-netsuke"

[package.metadata.binstall.overrides.x86_64-pc-windows-msvc]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-windows-amd64__netsuke_windows_x86_64-netsuke.exe"

[package.metadata.binstall.overrides.aarch64-pc-windows-msvc]
pkg-url = "{ repo }/releases/download/v{ version }/netsuke-windows-arm64__netsuke_windows_aarch64-netsuke.exe"

[features]
default = []
legacy-digests = ["sha1", "md5"]
Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ requirement below.
<!-- tested-example: readme-binstall-install -->

```sh
cargo binstall netsuke
cargo binstall netsuke-build
Comment thread
coderabbitai[bot] marked this conversation as resolved.
```

Building from the registry instead runs outside a repository checkout, so
Expand All @@ -60,7 +60,7 @@ supply both explicitly:

```sh
rustup toolchain install nightly-2026-06-25
RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke
RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke-build
```

Pre-built installers are available from the
Expand Down
26 changes: 11 additions & 15 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,8 +145,6 @@ fn emit_rerun_directives() {
println!("cargo:rerun-if-changed=src/cli/parser.rs");
println!("cargo:rerun-if-changed=src/cli/parsing.rs");
println!("cargo:rerun-if-env-changed=CARGO_PKG_VERSION");
println!("cargo:rerun-if-env-changed=CARGO_PKG_NAME");
println!("cargo:rerun-if-env-changed=CARGO_BIN_NAME");
println!("cargo:rerun-if-env-changed=CARGO_PKG_DESCRIPTION");
println!("cargo:rerun-if-env-changed=CARGO_PKG_AUTHORS");
println!("cargo:rerun-if-env-changed=SOURCE_DATE_EPOCH");
Expand All @@ -167,34 +165,32 @@ fn emit_rerun_directives() {

#[expect(
clippy::disallowed_methods,
reason = "CARGO_BIN_NAME, CARGO_PKG_NAME, CARGO_PKG_VERSION and OUT_DIR are Cargo's own build-script inputs; they describe the crate being compiled and Cargo provides them only through the environment"
reason = "CARGO_PKG_VERSION and OUT_DIR are Cargo's own build-script inputs; they describe the crate being compiled and Cargo provides them only through the environment"
)]
fn generate_man_page(out_dir: &Path) -> Result<(), Box<dyn std::error::Error>> {
let cmd = cli::Cli::command();
let name = cmd
.get_bin_name()
.unwrap_or_else(|| cmd.get_name())
.to_owned();
let cargo_bin = env::var("CARGO_BIN_NAME")
.or_else(|_| env::var("CARGO_PKG_NAME"))
.unwrap_or_else(|_| name.clone());
if name != cargo_bin {
return Err(format!(
"CLI name {name} differs from Cargo bin/package name {cargo_bin}; packaging expects {cargo_bin}.1"
)
.into());
}
let version = env::var("CARGO_PKG_VERSION").map_err(
|_| "CARGO_PKG_VERSION must be set by Cargo; cannot render manual page without it.",
)?;
let man = Man::new(cmd)
.section("1")
.source(format!("{cargo_bin} {version}"))
.source(format!("{name} {version}"))
.date(manual_date());
let mut buf = Vec::new();
man.render(&mut buf)?;
let page_name = format!("{cargo_bin}.1");
write_man_page(&buf, out_dir, &page_name)?;
let page_name = format!("{name}.1");
let destination = write_man_page(&buf, out_dir, &page_name)?;
// Publish the destination so the crate's tests can assert the manual page
// contract (name, location, and `.TH` source) without re-deriving where the
// build script chose to write it.
println!(
"cargo:rustc-env=NETSUKE_GENERATED_MAN_PAGE={}",
destination.display()
);
if let Some(extra_dir) = env::var_os("OUT_DIR") {
let extra_dir_path = PathBuf::from(extra_dir);
if let Err(err) = write_man_page(&buf, &extra_dir_path, &page_name) {
Expand Down
12 changes: 6 additions & 6 deletions docs/adr-006-adopt-polonius-nightly-toolchain.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,14 @@ remains correct.

- Publishing to crates.io remains possible, but the packaged source excludes
`rust-toolchain.toml` and `.cargo/config.toml` (and Cargo would not apply
them to a registry build anyway), so a bare `cargo install netsuke` of a
Polonius-dependent release fails borrow checking on the user's default
them to a registry build anyway), so a bare `cargo install netsuke-build` of
a Polonius-dependent release fails borrow checking on the user's default
toolchain. Registry installs must select the pinned nightly and pass the flag
explicitly
(`RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke`); the
README and users' guide document this command and a contract test pins it.
Source installs from a checkout are unaffected because the pinned toolchain
and workspace configuration apply there.
(`RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke-build`);
the README and users' guide document this command and a contract test pins
it. Source installs from a checkout are unaffected because the pinned
toolchain and workspace configuration apply there.
- Release packaging builds from the pinned nightly. Binary artefacts are
unaffected: the borrow checker changes what compiles, not what is generated.
- Dependabot-style toolchain drift is impossible; moving the pin is a
Expand Down
100 changes: 100 additions & 0 deletions docs/adr-007-publish-as-netsuke-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
# Architecture decision record (ADR): Publish the crates.io package as `netsuke-build`

## Status

Accepted.

## Date

2026-08-05.

## Context and problem statement

Netsuke ships a single application crate whose Cargo package, library target,
and binary target were all named `netsuke`. The `netsuke` name is already
taken on crates.io by an unrelated package, so the registry cannot accept a
release under it.

Renaming the package is the only way to publish, but the name is load-bearing
in several places that have nothing to do with the registry:

- the command users type, and the `Usage:` line clap renders for it;
- the manual page, which packaging installs as `netsuke.1` and users read with
`man netsuke`;
- the Debian, RPM, macOS, and Windows package names, and the release assets
they are built from;
- the library target that the integration tests, behavioural tests, and build
script all import as `netsuke`.

Cargo lets the package name and the target names diverge, but nothing enforces
the divergence: the build script previously derived the manual page name from
`CARGO_BIN_NAME`/`CARGO_PKG_NAME` and rejected a mismatch with the
command-line interface (CLI) name, which would have renamed the manual page to
follow the package.

## Decision

Publish as `netsuke-build`, and keep every user-facing name as `netsuke`.

- Set `package.name = "netsuke-build"` in `Cargo.toml`, with `[lib] name =
"netsuke"` and `[[bin]] name = "netsuke"`.
- Derive the manual page name and its `.TH` source from the CLI name that
`clap` reports, not from Cargo's package or binary environment variables.
`build.rs` no longer reads `CARGO_PKG_NAME` or `CARGO_BIN_NAME`, and no
longer fails the build when they differ from the CLI name; that check
enforced exactly the coupling this decision removes.
- Keep `.github/release-staging.toml`, the `linux-packages`, `windows-package`,
and `macos-package` steps, and the release help tooling driven by the
`bin-name` Cargo metadata field, which resolves to `netsuke`.
- Add `[package.metadata.binstall]` overrides so `cargo binstall
netsuke-build` resolves the release assets, which are named after the binary.
Without them `cargo binstall` would look for `netsuke-build`-prefixed assets,
fail to find any, and fall back to a source build that needs the pinned
nightly and the Polonius flag — the very fallback the documented command
exists to avoid.
- Update the crates.io installation guidance in the README, the users' guide,
and the quickstart to install `netsuke-build`.

## Rationale

- **The registry name is an implementation detail.** Users invoke `netsuke`,
read `man netsuke`, and install a `netsuke` operating-system package. Only
the two `cargo install` and `cargo binstall` commands mention the package
name, and both are documented and pinned by contract tests.
- **Renaming the targets would be far more invasive.** The library target name
is the crate path every test, the build script, and the
`[package.metadata.ortho_config]` `root_type` setting use; renaming it would
churn the whole tree to work around a registry collision.
- **`netsuke-build` reads as a description, not a substitute.** It names what
the package is — the Netsuke build system — so a reader who finds it on
crates.io is not left guessing whether it is the same project.

## Consequences

- The package name and target names diverge permanently. Anything deriving a
user-facing name from Cargo package metadata is a defect; derive from the
CLI name or from the `bin-name` metadata field instead.
- `tests/man_page_contract_tests.rs` pins the manual page's name, staging
location, and `.TH` source against the CLI name, and asserts the package
name never reaches the title. `tests/binstall_metadata_tests.rs` pins the
`binstall` overrides to `.github/release-staging.toml` and to the release
workflow's target matrix.
- The `binstall` overrides encode release asset names. Changing
`staging_dir_template`, `bin_name`, or the workflow artefact names without
updating the overrides breaks `cargo binstall`; the contract test fails
first for the parts it can derive.
- Documentation and contract tests refer to `netsuke-build` only for registry
installation. Everywhere else — prose, examples, help output, packaging —
the project remains Netsuke.
- Should the `netsuke` name become available on crates.io, this decision can be
reversed by changing `package.name` alone, because nothing else derives from
it.

## References

- [ADR-006](adr-006-adopt-polonius-nightly-toolchain.md): the pinned-nightly
policy that makes the `cargo binstall` path worth preserving.
- [Repository layout](repository-layout.md): the package-versus-target naming
rule.
- [Developer guide](developers-guide.md): the day-to-day naming guidance and
the contract tests that enforce it.
3 changes: 3 additions & 0 deletions docs/contents.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ operator, user, and contributor references are easier to find.
`command_available`.
- [adr-006-adopt-polonius-nightly-toolchain.md](adr-006-adopt-polonius-nightly-toolchain.md):
Pinned-nightly Polonius borrow-checker adoption decision record.
- [adr-007-publish-as-netsuke-build.md](adr-007-publish-as-netsuke-build.md):
crates.io package rename decision record, and the package-versus-target
naming rule it establishes.

## User and operator guides

Expand Down
44 changes: 44 additions & 0 deletions docs/developers-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,44 @@ they are per-invocation arguments tagged `#[serde(skip)]` on
would silently change the artefact destination — a footgun the design avoids by
construction.

## Package and target naming

The crates.io package is `netsuke-build`; the library target, the binary
target, and the command are all `netsuke`. The names diverge because `netsuke`
is taken on crates.io. [ADR-007](adr-007-publish-as-netsuke-build.md) records
the decision and [repository layout](repository-layout.md) states the rule.

The practical consequence is that **no user-facing name may be derived from
Cargo package metadata**. Derive from the command-line interface (CLI) name, or
from the `bin-name` field that
`leynos/shared-actions/.github/actions/export-cargo-metadata` reads out of
`[[bin]]`:

- `build.rs` names the manual page `<CLI name>.1` and stamps its `.TH` source
as `<CLI name> <version>`, taking the name from `Cli::command()`. It reads
neither `CARGO_PKG_NAME` nor `CARGO_BIN_NAME`. The build script publishes the
path it wrote through `cargo:rustc-env=NETSUKE_GENERATED_MAN_PAGE`, and
`tests/man_page_contract_tests.rs` asserts the file is `netsuke.1`, is staged
under `target/generated-man/<target>/<profile>/`, and carries a title that
never mentions `netsuke-build`.
- Release packaging takes `bin-name` from the `metadata` job in
`.github/workflows/release.yml`, so `.github/release-staging.toml`, the
Debian and RPM payloads, the Windows Installer product, and the macOS
installer package all stay named `netsuke`.
- `[package.metadata.binstall]` in `Cargo.toml` overrides `cargo binstall`'s
default asset resolution, which would otherwise look for `netsuke-build`
assets and fall back to a source build on the pinned nightly. The overrides
spell out one unarchived (`pkg-fmt = "bin"`) asset per released target;
`tests/binstall_metadata_tests.rs` rebuilds the expected names from
`.github/release-staging.toml` and checks the target set against the release
workflow matrix.

Only the two registry installation commands name `netsuke-build`, and
`tests/documentation_examples_tests.rs` pins both. When adding a release
target, a packaging format, or an artefact name, update the `binstall`
overrides and the artefact-name table in `tests/binstall_metadata_tests.rs`
alongside the workflow.

## Toolchain and borrow checker

Netsuke builds on the dated nightly toolchain pinned in `rust-toolchain.toml`
Expand Down Expand Up @@ -1171,6 +1209,12 @@ stale `ninja_env/` paths. It also asserts that every catalogue named by the
locale registry ships in the package, so adding a locale cannot silently omit
its `messages.ftl` from a release.

`tests/man_page_contract_tests.rs` and `tests/binstall_metadata_tests.rs` guard
the package-versus-target naming split described in
[package and target naming](#package-and-target-naming). The first asserts the
manual page `build.rs` generates, the second holds the `cargo binstall`
overrides to the release staging configuration and workflow matrix.

### Temporary executable test helpers

The low-level executable-stub primitive is owned by
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ exit status 0
make nixie
exit status 0

cargo test -p netsuke manifest::expand
cargo test -p netsuke-build manifest::expand
29 passed; 0 failed

make check-fmt
Expand Down
2 changes: 1 addition & 1 deletion docs/execplans/3-14-3-lower-target-and-action-deps.md
Original file line number Diff line number Diff line change
Expand Up @@ -698,7 +698,7 @@ Expected: the new parameterized cases pass; existing IR cases continue to pass.
After Stage D (cycle detection):

```sh
cargo test -p netsuke ir::cycle \
cargo test -p netsuke-build ir::cycle \
2>&1 \
| tee /tmp/stage-d-netsuke-3-14-3-lower-target-and-action-deps.out
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,7 @@ will consume. No user-visible behaviour change. Pure scaffolding.

6. **Stage A acceptance**:

- `cargo test -p netsuke graph_view::tests` passes.
- `cargo test -p netsuke-build graph_view::tests` passes.
- The proptest covers at least 256 cases with shrinking and reports no
failures over 60 seconds.
- No public behaviour change visible to existing tests.
Expand Down
6 changes: 3 additions & 3 deletions docs/polonius.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,10 @@ flag or avoid compiling the crate:
coverage inherits the flag from the job environment.
- **Registry installs**: the crates.io package excludes
`rust-toolchain.toml` and `.cargo/config.toml`, and registry builds run
outside the checkout, so `cargo install netsuke` must select the pinned
outside the checkout, so `cargo install netsuke-build` must select the pinned
nightly and pass the flag explicitly
(`RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke`). The
README and users' guide document the command and
(`RUSTFLAGS=-Zpolonius=next cargo +nightly-2026-06-25 install netsuke-build`).
The README and users' guide document the command and
`tests/documentation_examples_tests.rs` pins it.
- **cargo-mutants** (scheduled, informational) runs through the shared
`mutation-cargo.yml` workflow, which controls its own environment; if those
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ minutes.
Before beginning, ensure the following are available:

- **Netsuke** installed — install a prebuilt release binary with
`cargo binstall netsuke` where
`cargo binstall netsuke-build` where
[`cargo binstall`](https://github.com/cargo-bins/cargo-binstall) is
available, install from source inside a repository checkout with
`cargo install --path .` (which puts `netsuke` on `PATH` for the commands
Expand Down
Loading
Loading