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
6 changes: 6 additions & 0 deletions packages/rust/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,14 @@ repository.workspace = true
# `tests/` is the integration suite -- excluded so test files never ship in the
# published `.crate` (enforced by the `packaging` gate in CI). Inline unit tests
# stay (they are `#[cfg(test)]` modules inside `src/`, not separate files).
# The fragment dirs are repo inputs -- one file per change, assembled into
# nothing and read by no crate consumer. They need naming here because cargo
# ships whatever is not excluded; npm and maturin drop theirs structurally, via
# an allowlist and the wheel layout respectively.
exclude = [
"tests/",
"changelog.d/",
"migrations.d/",
"docs/.vitepress",
"docs/.claude",
"docs/tests",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
**Fixed** — the published `.crate` archive no longer ships `changelog.d/` and
`migrations.d/`. `[package].exclude` named `tests/` and the VitePress tooling
under `docs/` but not the fragment dirs, and cargo ships whatever is not
excluded, so every release carried 60 fragment files no crate consumer reads —
more than half the archive's entries (119 files → 59).

No API, CLI or behavior change; the crate is smaller and its file list now
matches the npm package (an allowlist) and the wheel (maturin's layout), which
already dropped their fragments structurally.
89 changes: 89 additions & 0 deletions packages/rust/tests/crate_contents.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//! What the published `.crate` archive ships.
//!
//! `changelog.d/` and `migrations.d/` are repo *inputs* -- one fragment file
//! per change, assembled by nothing and read by no consumer of the crate.
//! The npm `files` allowlist and maturin's wheel layout keep them out of
//! their artifacts structurally; cargo ships everything not named in
//! `[package].exclude`, so the crate needs the patterns spelled out.
//!
//! Asserted against `cargo package --list` -- the archive's own manifest --
//! rather than against the `exclude` patterns, so a pattern that parses but
//! matches nothing still fails here.

use std::process::Command;

/// Every path `cargo package` would write into the `.crate` archive.
///
/// `--allow-dirty` because the question is what the *working tree* would
/// ship: without it cargo refuses outright the moment a packaged file is
/// uncommitted, so editing `exclude` and re-running would abort before
/// listing anything -- failing for a reason that has nothing to do with the
/// assertions below.
fn packaged_paths() -> Vec<String> {
let output = Command::new(env!("CARGO"))
.args([
"package",
"--manifest-path",
concat!(env!("CARGO_MANIFEST_DIR"), "/Cargo.toml"),
"--list",
"--allow-dirty",
])
.output()
.expect("cargo package --list should run");

assert!(
output.status.success(),
"cargo package --list failed: {}",
String::from_utf8_lossy(&output.stderr)
);

String::from_utf8(output.stdout)
.expect("cargo package --list emits utf-8")
.lines()
.map(str::to_string)
.collect()
}

/// Archive paths under `prefix`.
fn shipped_under(prefix: &str) -> Vec<String> {
packaged_paths()
.into_iter()
.filter(|path| path.starts_with(prefix))
.collect()
}

/// Guards the two assertions below against a vacuous pass: a `--list` that
/// silently returned nothing would otherwise satisfy every "does not ship"
/// check in this file.
#[test]
fn crate_archive_ships_the_library_source() {
let paths = packaged_paths();

assert!(
paths.iter().any(|p| p == "src/lib.rs"),
"expected src/lib.rs in the archive, got {} paths: {paths:?}",
paths.len()
);
}

#[test]
fn crate_archive_omits_changelog_fragments() {
let shipped = shipped_under("changelog.d/");

assert!(
shipped.is_empty(),
"changelog fragments are repo inputs and must not ship; \
add `changelog.d/` to [package].exclude. Shipped: {shipped:?}"
);
}

#[test]
fn crate_archive_omits_migration_fragments() {
let shipped = shipped_under("migrations.d/");

assert!(
shipped.is_empty(),
"migration fragments are repo inputs and must not ship; \
add `migrations.d/` to [package].exclude. Shipped: {shipped:?}"
);
}
Loading