xtask/tests/python_versions_track_the_workspace.rs
//! **One version, or say which.** facett's `[workspace.package] version` is the
//! single writer for all 71 crates — that is what a workspace version means. The
//! python-facing manifests are outside that mechanism and had drifted:
//!
//! ```text
//! MEASURED 2026-09-05, before this guard:
//! Cargo.toml [workspace.package] version = 0.1.19
//! pkg-facet-wrapped/pyproject.toml version = 0.1.15 (4 behind)
//! facett-py/Cargo.toml version = 0.1.15 (4 behind)
//! facett-py/pyproject.toml version = 0.1.13 (6 behind)
//! ```
//!
//! The last two are the interesting ones: they are the SAME artifact (a maturin
//! wheel built from that Cargo.toml) declaring two different versions of itself.
//! Whichever one a consumer reads, the other is a lie — and a `pip install
//! facett-native==0.1.15` that resolves to bytes stamped 0.1.13 is unfalsifiable
//! from the outside.
//!
//! Drift like this cannot be fixed once. A number in four files with no writer
//! between them will separate again the next time someone bumps three of them, so
//! the fix is this test, not the edit it demanded.
use std::path::{Path, PathBuf};
fn repo_root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().to_path_buf()
}
/// Pull `version = "x.y.z"` out of a TOML file's FIRST occurrence after the given
/// section header. Deliberately dumb: these four files are hand-written manifests,
/// and a full TOML dependency in a guard is more surface than the guard is worth.
fn version_after(path: &Path, section: &str) -> String {
let text = std::fs::read_to_string(path)
.unwrap_or_else(|e| panic!("read {}: {e}", path.display()));
let mut in_section = section.is_empty();
for line in text.lines() {
let t = line.trim();
if t.starts_with('[') {
in_section = t == section;
continue;
}
if in_section && let Some(rest) = t.strip_prefix("version") {
let rest = rest.trim_start();
if let Some(rest) = rest.strip_prefix('=') {
let v = rest.trim().trim_matches('"').trim_matches('\'');
// `version.workspace = true` is the inheriting form, not a number.
if !v.is_empty() && v.chars().next().is_some_and(|c| c.is_ascii_digit()) {
return v.to_string();
}
}
}
}
panic!("no `version = \"…\"` under `{section}` in {}", path.display());
}
/// The one number everything else must equal.
fn workspace_version() -> String {
version_after(&repo_root().join("Cargo.toml"), "[workspace.package]")
}
/// The ONE version both PyPI packages carry: facett-build-thing's `PYPI_VERSION`.
///
/// 0.2.0 (owner decision 2026-10-02) split the PyPI line from the cargo workspace:
/// the two Python packages — `facett` and `facett-demo` — release together on their
/// own number, which the crates need not share. (The same day the owner put the
/// crates on 0.2.0 too, so today the two numbers COINCIDE by decision, not by
/// construction — the next crates bump does not move this one.) So the rule moved: no longer "the
/// wheel equals the workspace", but "both pyprojects equal the recipe's ONE number,
/// and the demo pins the components to that same release line". The recipe is read
/// as CODE (a dependency of this crate), not as a fourth hand-written number.
fn pypi_version() -> String {
facett_build_thing::PYPI_VERSION.to_string()
}
#[test]
fn both_python_packages_ship_the_recipes_pypi_version() {
let want = pypi_version();
for (file, project) in [("pkg-facet-wrapped/pyproject.toml", "facett"), ("py/pyproject.toml", "facett-demo")] {
let got = version_after(&repo_root().join(file), "[project]");
assert_eq!(
got, want,
"{file} ships `{project} {got}` but facett-build-thing's PYPI_VERSION is {want} — \
the two PyPI packages release together, on ONE number"
);
}
}
#[test]
fn the_demo_pins_the_components_to_its_own_release_line() {
// `facett-demo 0.2.0` must install a `facett` its gallery was tested against: the
// same minor line, with the Streamlit extra.
let want = pypi_version();
let text = std::fs::read_to_string(repo_root().join("py/pyproject.toml")).unwrap();
let mut parts = want.split('.');
let (major, minor) = (parts.next().unwrap(), parts.next().unwrap());
let next_minor: u32 = minor.parse::<u32>().unwrap() + 1;
let pin = format!("\"facett[streamlit]>={want},<{major}.{next_minor}\"");
assert!(text.contains(&pin), "py/pyproject.toml must depend on {pin}");
}
#[test]
fn facett_py_agrees_with_itself_and_with_the_workspace() {
let want = workspace_version();
let cargo = version_after(&repo_root().join("facett-py/Cargo.toml"), "[package]");
let py = version_after(&repo_root().join("facett-py/pyproject.toml"), "[project]");
assert_eq!(
cargo, py,
"facett-py declares TWO versions of one artifact: Cargo.toml says {cargo}, \
pyproject.toml says {py}. The maturin wheel is built from the first and \
published under the second."
);
assert_eq!(cargo, want, "facett-py is {cargo}, this workspace is {want}");
}
#[test]
fn the_demo_app_is_a_release_now() {
// Until 0.2.0 `py/` was `facett-demo-python`, an unreleased app with its own 0.1.0,
// and this test held that exclusion. The owner made it a PyPI package
// (`facett-demo`), so it is IN the rule now — `both_python_packages_ship_…` above —
// and its module reads the version from metadata like the components do.
let buildinfo = std::fs::read_to_string(repo_root().join("py/facett_demo/buildinfo.py")).unwrap();
assert_eq!(dunder_version(&repo_root().join("py/facett_demo/buildinfo.py")), None, "a literal version in buildinfo.py");
assert!(buildinfo.contains("_metadata.version(\"facett-demo\")"), "the demo must read its version from its distribution");
}
/// Pull `__version__ = "x.y.z"` out of a python module.
fn dunder_version(path: &Path) -> Option<String> {
let text = std::fs::read_to_string(path).ok()?;
for line in text.lines() {
let t = line.trim();
if let Some(rest) = t.strip_prefix("__version__") {
let rest = rest.trim_start().strip_prefix('=')?.trim();
let v = rest.trim_matches('"').trim_matches('\'');
if v.chars().next().is_some_and(|c| c.is_ascii_digit()) {
return Some(v.to_string());
}
// A non-literal (a call, a name) is the by-construction form — there is
// no second number to drift, which is what this test wants.
return None;
}
}
None
}
#[test]
fn the_shipped_module_does_not_carry_a_hand_written_version() {
// FOUND 2026-09-05, by the guard above missing it: `__init__.py` carried a FOURTH
// copy of the version, `__version__ = "0.1.15"`, while the wheel it ships in says
// 0.1.19. `build_info()["version"]` returns that string and `robot_installed.py`
// records it into the saga tape — so the durable record of every drive was
// stamped with a version the artifact does not have.
//
// Adding it to the three-file equality check would have been the wrong fix: a
// fourth number held in place by a test is still a fourth number, and the next
// bump has four chances to miss one. The right fix is to have NO literal here —
// read it from the installed distribution metadata, whose single writer is
// pyproject.toml. So this test asserts the ABSENCE of a literal rather than its
// value.
let init = repo_root().join("pkg-facet-wrapped/src/facett/__init__.py");
let text = std::fs::read_to_string(&init).unwrap();
assert!(
text.contains("__version__"),
"the package must still expose __version__ — build_info() and the robot read it"
);
assert_eq!(
dunder_version(&init),
None,
"`__init__.py` hard-codes a version literal. That is a copy with no writer, and \
it drifted once already (0.1.15 while the wheel said 0.1.19). Derive it from the \
installed distribution metadata instead — pyproject.toml is the one writer."
);
}
#[test]
fn the_module_reads_its_version_from_distribution_metadata() {
// The positive half. Asserting only the absence of a literal would pass on a file
// that deleted __version__ altogether, or set it to a bare "unknown".
let init = repo_root().join("pkg-facet-wrapped/src/facett/__init__.py");
let text = std::fs::read_to_string(&init).unwrap();
assert!(
text.contains("importlib.metadata") || text.contains("from importlib import metadata"),
"the version must come from the installed distribution's own metadata"
);
assert!(
text.contains("_metadata.version(\"facett\")"),
"it must ask for THIS distribution (`facett`) by name"
);
}