diff --git a/scripts/pipeline.py b/scripts/pipeline.py index 46b271b2..e46ed4f2 100644 --- a/scripts/pipeline.py +++ b/scripts/pipeline.py @@ -92,6 +92,19 @@ class StepResults(dict): # Steps that report without failing the run. NON_BLOCKING = frozenset({"check_buildbot"}) +def _output_dir(typed: str | None) -> str: + """The pack directory as an absolute path. + + Every step runs from the repository root, while the lock and the purge + of stale packs ran here, against the caller's directory: started from + scripts/, the run locked and purged scripts/dist/ and wrote its packs to + dist/. A directory the caller typed is read from where they typed it. + """ + if typed is None: + return str(Path(__file__).resolve().parent.parent / "dist") + return str(Path(typed).resolve()) + + def run(cmd: list[str], label: str) -> tuple[bool, str]: """Run a command, echoing its output as it comes. Returns (success, output). @@ -425,7 +438,8 @@ def main() -> None: "assets already in .cache/large/" ) parser.add_argument( - "--output-dir", default="dist", help="Pack output directory (default: dist/)" + "--output-dir", + help="Pack output directory (default: dist/ at the repository root)", ) parser.add_argument("--target", "-t", help="Hardware target (e.g., switch, rpi4)") parser.add_argument( @@ -455,6 +469,7 @@ def main() -> None: ) args = parser.parse_args() _refuse_unapplied_flags(parser, args) + args.output_dir = _output_dir(args.output_dir) # A second run on the same output directory is refused before any work: # the database rebuild alone takes minutes, and the reader holding the diff --git a/tests/test_audit_regressions.py b/tests/test_audit_regressions.py index c709c523..42e2d6b5 100644 --- a/tests/test_audit_regressions.py +++ b/tests/test_audit_regressions.py @@ -5975,5 +5975,25 @@ class ErrorsGoToStderr(unittest.TestCase): self.assertGreater(seen, 0, script) +class PipelineOutputDirIsTheOneItWrites(unittest.TestCase): + """Started from scripts/, the pipeline locked and purged scripts/dist/ + while every step, run from the repository root, wrote to dist/.""" + + def test_the_default_is_the_root_dist_wherever_the_run_starts(self): + previous = os.getcwd() + self.addCleanup(os.chdir, previous) + os.chdir(ROOT / "scripts") + self.assertEqual(pipeline._output_dir(None), str(ROOT / "dist")) + self.assertEqual(pipeline._output_dir("out"), str(ROOT / "scripts" / "out")) + + def test_main_resolves_it_before_the_lock(self): + source = (ROOT / "scripts" / "pipeline.py").read_text(encoding="utf-8") + body = source[source.index("def main("):] + self.assertLess( + body.index("args.output_dir = _output_dir(args.output_dir)"), + body.index("hold_artifact_lock(args.output_dir)"), + ) + + if __name__ == "__main__": unittest.main()