xtask: reject implicit system python
Browse files- docs/RESEARCH_LEDGER.md +7 -0
- docs/TESTING.md +1 -1
- xtask/src/lib.rs +57 -4
docs/RESEARCH_LEDGER.md
CHANGED
|
@@ -2007,3 +2007,10 @@ RE-863 hook correction: pre-commit/pre-push now require a Nix-store `PYTHON`, pr
|
|
| 2007 |
RE-863 final review: targeted xtask dependency-registry test passes, including all five new source→Rust edges and shared transition composition. Both hooks pass shell syntax checks and reject missing PYTHON before invoking any interpreter.
|
| 2008 |
|
| 2009 |
Batch RE-854–863 boundary: the single `cargo xtask test-full --since cedc6d02` run passed after commit ten through the flake shell and its declared Nix Python3.13.15: 1,688 Python tests, 157 Rust tests (assets 10, gameplay 119, xtask 28), 51 deterministic producers, storage policy, 425 documentation files and whitespace checks. No generated report drift; notes amended into task ten. Original assets unchanged. Remaining commit/push hooks use the explicit Nix-store interpreter.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2007 |
RE-863 final review: targeted xtask dependency-registry test passes, including all five new source→Rust edges and shared transition composition. Both hooks pass shell syntax checks and reject missing PYTHON before invoking any interpreter.
|
| 2008 |
|
| 2009 |
Batch RE-854–863 boundary: the single `cargo xtask test-full --since cedc6d02` run passed after commit ten through the flake shell and its declared Nix Python3.13.15: 1,688 Python tests, 157 Rust tests (assets 10, gameplay 119, xtask 28), 51 deterministic producers, storage policy, 425 documentation files and whitespace checks. No generated report drift; notes amended into task ten. Original assets unchanged. Remaining commit/push hooks use the explicit Nix-store interpreter.
|
| 2010 |
+
|
| 2011 |
+
|
| 2012 |
+
## 2026-10-27 — Block remaining implicit system-Python runner entry points
|
| 2013 |
+
|
| 2014 |
+
Prompt investigation found the ordinary shell resolves python3 to /usr/bin/python3, while xtask still had a PATH fallback and two Rust integration tests invoked that name directly. The Git hooks were already guarded. The runner and real Python-using tests now require the explicit Nix-store interpreter, rejecting missing/system paths without launching them. Direct analysis-script shebangs still require the documented flake shell. No active Python/xcrun process was observed at inspection; that snapshot cannot attribute an already-dispatched installer prompt.
|
| 2015 |
+
|
| 2016 |
+
Validation: all30 xtask tests and the focused storage group (21 Python tests) pass using the flake-declared Nix Python. A real runner invocation with PYTHON unset exits with nix-develop guidance before starting Python. Full suite was not repeated for this focused runner correction.
|
docs/TESTING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
| 2 |
|
| 3 |
## Development toolchain
|
| 4 |
|
| 5 |
-
Run analysis and validation inside `nix develop`. The pinned flake declares Python as a development dependency and sets `PYTHON` to its absolute Nix-store executable, which `cargo xtask`
|
| 6 |
|
| 7 |
The Rust `xtask` workspace package selects and runs the existing Python research tests and Rust crate tests. It has no third-party Rust dependencies. Python remains necessary for the existing analyzers/tests and a syntax-only AST reader; selection, Git inspection, scheduling, determinism checks and command execution live in Rust.
|
| 8 |
|
|
|
|
| 2 |
|
| 3 |
## Development toolchain
|
| 4 |
|
| 5 |
+
Run analysis and validation inside `nix develop`. The pinned flake declares Python as a development dependency and sets `PYTHON` to its absolute Nix-store executable, which `cargo xtask` and its Python-using Rust integration tests require. Without it, they stop before launching an interpreter; there is no PATH/system fallback. Do not use Python from macOS Command Line Tools. For a one-shot run, use `nix develop --command cargo xtask test-group <name>`; the same environment supplies Python for direct analysis scripts and their subprocesses. The shell also supplies Rust, Cargo, Git and pkg-config on macOS and Linux. Directly executing an analysis script outside this shell can still follow its `env python3` shebang, so run those scripts inside the flake too.
|
| 6 |
|
| 7 |
The Rust `xtask` workspace package selects and runs the existing Python research tests and Rust crate tests. It has no third-party Rust dependencies. Python remains necessary for the existing analyzers/tests and a syntax-only AST reader; selection, Git inspection, scheduling, determinism checks and command execution live in Rust.
|
| 8 |
|
xtask/src/lib.rs
CHANGED
|
@@ -7,6 +7,52 @@ use std::process::Command;
|
|
| 7 |
type Result<T> = std::result::Result<T, String>;
|
| 8 |
type Set = BTreeSet<String>;
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
#[derive(Default, Debug)]
|
| 11 |
pub struct Graph {
|
| 12 |
nodes: Set,
|
|
@@ -505,7 +551,7 @@ fn execute(root: &Path, g: &Graph, p: &Plan, python: &str, runner: &mut impl Run
|
|
| 505 |
pub fn run(args: Vec<String>) -> Result<()> {
|
| 506 |
if args.is_empty() || args.iter().any(|a| matches!(a.as_str(), "--help" | "-h")) {
|
| 507 |
println!(
|
| 508 |
-
"cargo xtask test-changed [--since REV] [--path PATH ...] [--dry-run]\ncargo xtask test-group NAME [--dry-run]\ncargo xtask test-full [--since REV] [--dry-run]\ncargo xtask list-groups\nPYTHON
|
| 509 |
);
|
| 510 |
return Ok(());
|
| 511 |
}
|
|
@@ -516,7 +562,7 @@ pub fn run(args: Vec<String>) -> Result<()> {
|
|
| 516 |
)?
|
| 517 |
.trim(),
|
| 518 |
);
|
| 519 |
-
let python =
|
| 520 |
let mode = &args[0];
|
| 521 |
let mut index = 1;
|
| 522 |
let mut group = None;
|
|
@@ -980,7 +1026,7 @@ mod tests {
|
|
| 980 |
let out = capture(
|
| 981 |
&t.0,
|
| 982 |
&Cmd::new(
|
| 983 |
-
"
|
| 984 |
["-I".into(), scanner.to_string_lossy().into_owned()],
|
| 985 |
),
|
| 986 |
)
|
|
@@ -999,7 +1045,14 @@ mod integration_tests {
|
|
| 999 |
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
| 1000 |
let mut g =
|
| 1001 |
Graph::registry(&fs::read_to_string(root.join("xtask/checks.tsv")).unwrap()).unwrap();
|
| 1002 |
-
let output = capture(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1003 |
g.scanner(&output).unwrap();
|
| 1004 |
assert!(g.warnings.is_empty(), "{:?}", g.warnings);
|
| 1005 |
let health = plan(
|
|
|
|
| 7 |
type Result<T> = std::result::Result<T, String>;
|
| 8 |
type Set = BTreeSet<String>;
|
| 9 |
|
| 10 |
+
fn python_program(value: Option<&str>) -> Result<String> {
|
| 11 |
+
let value = value.ok_or("PYTHON is unset; run this command inside nix develop")?;
|
| 12 |
+
let path = Path::new(value);
|
| 13 |
+
if !value.starts_with("/nix/store/")
|
| 14 |
+
|| !value.ends_with("/bin/python3")
|
| 15 |
+
|| path.components().any(|part| {
|
| 16 |
+
matches!(
|
| 17 |
+
part,
|
| 18 |
+
std::path::Component::ParentDir | std::path::Component::CurDir
|
| 19 |
+
)
|
| 20 |
+
})
|
| 21 |
+
{
|
| 22 |
+
return Err(
|
| 23 |
+
"PYTHON must be the flake's absolute Nix-store Python executable; run nix develop"
|
| 24 |
+
.into(),
|
| 25 |
+
);
|
| 26 |
+
}
|
| 27 |
+
Ok(value.into())
|
| 28 |
+
}
|
| 29 |
+
fn python_runtime() -> Result<String> {
|
| 30 |
+
python_program(std::env::var("PYTHON").ok().as_deref())
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
#[cfg(test)]
|
| 34 |
+
mod python_tests {
|
| 35 |
+
use super::*;
|
| 36 |
+
#[test]
|
| 37 |
+
fn missing_system_and_relative_python_are_rejected_without_launching() {
|
| 38 |
+
assert!(python_program(None).unwrap_err().contains("nix develop"));
|
| 39 |
+
for value in [
|
| 40 |
+
"",
|
| 41 |
+
"python3",
|
| 42 |
+
"/usr/bin/python3",
|
| 43 |
+
"/Library/Developer/CommandLineTools/usr/bin/python3",
|
| 44 |
+
"/nix/store/../../usr/bin/python3",
|
| 45 |
+
] {
|
| 46 |
+
assert!(python_program(Some(value)).is_err(), "{value}");
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
#[test]
|
| 50 |
+
fn explicit_nix_dependency_is_accepted() {
|
| 51 |
+
let value = "/nix/store/example-python3/bin/python3";
|
| 52 |
+
assert_eq!(python_program(Some(value)).unwrap(), value);
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
#[derive(Default, Debug)]
|
| 57 |
pub struct Graph {
|
| 58 |
nodes: Set,
|
|
|
|
| 551 |
pub fn run(args: Vec<String>) -> Result<()> {
|
| 552 |
if args.is_empty() || args.iter().any(|a| matches!(a.as_str(), "--help" | "-h")) {
|
| 553 |
println!(
|
| 554 |
+
"cargo xtask test-changed [--since REV] [--path PATH ...] [--dry-run]\ncargo xtask test-group NAME [--dry-run]\ncargo xtask test-full [--since REV] [--dry-run]\ncargo xtask list-groups\nPYTHON must come from nix develop; no system-Python fallback. Run from anywhere inside the checkout. Reports regenerate twice before tests."
|
| 555 |
);
|
| 556 |
return Ok(());
|
| 557 |
}
|
|
|
|
| 562 |
)?
|
| 563 |
.trim(),
|
| 564 |
);
|
| 565 |
+
let python = python_runtime()?;
|
| 566 |
let mode = &args[0];
|
| 567 |
let mut index = 1;
|
| 568 |
let mut group = None;
|
|
|
|
| 1026 |
let out = capture(
|
| 1027 |
&t.0,
|
| 1028 |
&Cmd::new(
|
| 1029 |
+
&python_runtime().expect("run cargo test inside nix develop"),
|
| 1030 |
["-I".into(), scanner.to_string_lossy().into_owned()],
|
| 1031 |
),
|
| 1032 |
)
|
|
|
|
| 1045 |
let root = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
|
| 1046 |
let mut g =
|
| 1047 |
Graph::registry(&fs::read_to_string(root.join("xtask/checks.tsv")).unwrap()).unwrap();
|
| 1048 |
+
let output = capture(
|
| 1049 |
+
root,
|
| 1050 |
+
&Cmd::new(
|
| 1051 |
+
&python_runtime().expect("run cargo test inside nix develop"),
|
| 1052 |
+
["-I", "xtask/python_graph.py"],
|
| 1053 |
+
),
|
| 1054 |
+
)
|
| 1055 |
+
.unwrap();
|
| 1056 |
g.scanner(&output).unwrap();
|
| 1057 |
assert!(g.warnings.is_empty(), "{:?}", g.warnings);
|
| 1058 |
let health = plan(
|