diff --git a/tests/conftest.py b/tests/conftest.py
index f6672b8c..9f517dc8 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -25,6 +25,7 @@
from __future__ import annotations
+import importlib.util
import os
import re
import select
@@ -36,6 +37,7 @@
import sys
import time
from argparse import Namespace
+from collections.abc import Mapping, Sequence
from pathlib import Path
import pytest
@@ -65,7 +67,9 @@
def clone_just_pushed(remote: Path, dest: Path,
- branch: str | None = None
+ branch: str | None = None, *, recurse: bool = True,
+ bare: bool = False, config: Sequence[str] = (),
+ env: Mapping[str, str] | None = None
) -> subprocess.CompletedProcess:
"""`git clone --recurse-submodules` of a bare remote a scaffold has JUST
pushed into, READ THROUGH GIT AND NOT THROUGH ITS FILES (#440).
@@ -90,18 +94,73 @@ def clone_just_pushed(remote: Path, dest: Path,
branch into the SOURCE, a plain repository, which is then `remote`.
The two legs it pushed into `--local-remote-dir` are read by the
rewrite only while that directory sits under `remote`'s parent.
+
+ The keywords are for the tests whose subject is the clone itself or the
+ environment it is made in (#544). `recurse=False` is a plain `git clone`,
+ which leaves a submodule's mount empty; `bare` is a bare copy of the
+ remote, with no submodules to read; `config` is more `-c` arguments for
+ the one command (`["-c", "core.autocrlf=true"]`); and `env` is variables
+ laid over this process's own, as `git_under` and `raw` lay them. The
+ output is decoded as UTF-8 whatever the platform's code page: a refusal
+ names a path, and a project's name can be any text.
"""
+ remote = Path(remote)
remotes = remote.parent.resolve()
uri = remotes.as_uri().rstrip("/") + "/"
prefixes = dict.fromkeys([os.path.join(str(remotes), ""),
remotes.as_posix().rstrip("/") + "/"])
rewrites = [arg for prefix in prefixes
for arg in ("-c", f"url.{uri}.insteadOf={prefix}")]
+ if bare:
+ mode = ["--bare"]
+ else:
+ mode = ["--recurse-submodules"] if recurse else []
return subprocess.run(
- ["git", *FILE_PROTOCOL, *rewrites, "clone", "-q", "--no-local",
- *(["-b", branch] if branch else []),
- "--recurse-submodules", str(remote), str(dest)],
- capture_output=True, text=True, check=False)
+ ["git", *FILE_PROTOCOL, *config, *rewrites, "clone", "-q",
+ "--no-local", *(["-b", branch] if branch else []), *mode,
+ str(remote), str(dest)],
+ capture_output=True, encoding="utf-8", errors="replace", check=False,
+ env=None if env is None else {**os.environ, **env})
+
+
+def cloned_just_pushed(remote: Path, dest: Path, branch: str | None = None,
+ **options) -> Path:
+ """`clone_just_pushed`, failing the test where the clone fails; `dest`.
+ `options` are the ones `clone_just_pushed` takes."""
+ cloned = clone_just_pushed(remote, dest, branch, **options)
+ assert cloned.returncode == 0, (f"git clone of {remote} failed:\n"
+ f"{cloned.stderr}{cloned.stdout}")
+ return Path(dest)
+
+
+def load_without_bytecode(path: Path, name: str):
+ """`path` run as a module called `name`, and no bytecode written for it
+ (#544; it is `load()` of `test_bootstrap_spelling.py`, #456, moved here).
+
+ `SourceFileLoader` caches what it compiles in a `__pycache__` beside the
+ source, and these sources are the TEMPLATES: a tree other tests read file
+ by file as text (`test_scaffold_leg_path_collisions.py` does, and a `.pyc`
+ is not UTF-8), and a tree a materializer walks. `.gitignore` hides the
+ stray, so `git status` never says it is there. The flag is process-wide, so
+ it is restored; it also covers the `repo_shape` a root imports as it runs.
+ """
+ spec = importlib.util.spec_from_file_location(name, path)
+ module = importlib.util.module_from_spec(spec)
+ wrote_bytecode = sys.dont_write_bytecode
+ sys.dont_write_bytecode = True
+ try:
+ spec.loader.exec_module(module)
+ finally:
+ sys.dont_write_bytecode = wrote_bytecode
+ return module
+
+
+#: `load_without_bytecode`'s counterpart for a CHILD process (#544). `family.py
+#: siblings` runs the standard's `templates/family-root/scripts/siblings.py`
+#: through `runpy`, and that file imports `bootstrap` from beside it, so
+#: without this in the child's environment the import writes
+#: `__pycache__/bootstrap.*.pyc` into the template tree.
+NO_BYTECODE = {"PYTHONDONTWRITEBYTECODE": "1"}
#: THE DEFAULT IDENTITY `git()` GIVES GIT (#279). A commit needs an author and
@@ -396,8 +455,7 @@ def scaffolded(tmp_path_factory) -> dict:
)
assert result.returncode == 0, result.stderr + result.stdout
clones.mkdir()
- proc = clone_just_pushed(remotes / f"{PROJECT}.git", clones / PROJECT)
- assert proc.returncode == 0, proc.stderr
+ cloned_just_pushed(remotes / f"{PROJECT}.git", clones / PROJECT)
return {
"base": base, "remotes": remotes, "work": work,
"clone": clones / PROJECT, "stdout": result.stdout,
diff --git a/tests/test_adopt_clone_template.py b/tests/test_adopt_clone_template.py
index 02f373fe..0ffc344f 100644
--- a/tests/test_adopt_clone_template.py
+++ b/tests/test_adopt_clone_template.py
@@ -61,8 +61,8 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
- resolve, run_script, write_plan)
+from conftest import (ADOPT, FILE_PROTOCOL, REPO, cloned_just_pushed, git,
+ make_source_repo, resolve, run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
from shape_materialize import SHAPE_REPOSITORY # noqa: E402
@@ -339,10 +339,8 @@ def test_a_clean_recursive_clone_succeeds_and_the_projects_validators_pass(
not contain` the planted commit (exit 128). `bootstrap.py` is the last of
the validators: it places each leg AT its pin, so it fails where a gitlink
and a pin disagree."""
- clone = tmp_path / PROJECT
- git_under(adopted.clean, *FILE_PROTOCOL, "clone", "-q",
- "--recurse-submodules", "-b", BRANCH, str(adopted.source),
- str(clone), cwd=tmp_path)
+ clone = cloned_just_pushed(adopted.source, tmp_path / PROJECT, BRANCH,
+ env=adopted.clean)
for role in ROLES:
assert git_under(adopted.clean, "rev-parse", "HEAD", cwd=clone / role) \
== pushed_tip(adopted.remotes, role)
diff --git a/tests/test_adopt_e2e.py b/tests/test_adopt_e2e.py
index 77f2147d..1403b7de 100644
--- a/tests/test_adopt_e2e.py
+++ b/tests/test_adopt_e2e.py
@@ -18,7 +18,7 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
+from conftest import (ADOPT, REPO, cloned_just_pushed, git, make_source_repo,
resolve, run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
@@ -61,13 +61,8 @@ def adopted(tmp_path_factory) -> dict:
@pytest.fixture
def adopted_clone(adopted, tmp_path):
"""A recursive clone of the source at the split branch."""
- target = tmp_path / PROJECT
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- "-b", "adopt/three-repo-shape", str(adopted["source"]), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
- return target
+ return cloned_just_pushed(adopted["source"], tmp_path / PROJECT,
+ "adopt/three-repo-shape")
# --- what execute refuses --------------------------------------------------
@@ -122,10 +117,8 @@ def test_both_legs_exist_with_more_than_one_commit(adopted):
def test_a_files_history_is_followable_inside_its_leg(adopted, tmp_path):
"""The point of `git filter-repo`: the commits that made a file come with
it. The specification was edited in a second commit; the leg has both."""
- clone = tmp_path / "spec-leg"
- subprocess.run(["git", "clone", "-q",
- str(adopted["remotes"] / f"{PROJECT}-spec.git"),
- str(clone)], check=True)
+ clone = cloned_just_pushed(adopted["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path / "spec-leg")
log = git("log", "--oneline", "--", "specs/001-feature/spec.md",
cwd=clone).stdout.splitlines()
assert len(log) == 2, f"expected two commits for the spec file, got {log}"
@@ -139,10 +132,8 @@ def test_a_files_history_is_followable_inside_its_leg(adopted, tmp_path):
def test_the_legs_keep_the_paths_they_had_in_the_source(adopted, tmp_path):
"""Paths are NOT flattened: `scripts/validate.py` stays there, which is
what makes `../spec/contracts` from `code/` the correct read-across."""
- clone = tmp_path / "code-leg"
- subprocess.run(["git", "clone", "-q",
- str(adopted["remotes"] / f"{PROJECT}-code.git"),
- str(clone)], check=True)
+ clone = cloned_just_pushed(adopted["remotes"] / f"{PROJECT}-code.git",
+ tmp_path / "code-leg")
assert (clone / "src" / "app" / "main.py").is_file()
assert (clone / "tests" / "test_main.py").is_file()
assert not (clone / "specs").exists()
diff --git a/tests/test_adopt_execute_refusals.py b/tests/test_adopt_execute_refusals.py
index 3fd809e7..7c767af6 100644
--- a/tests/test_adopt_execute_refusals.py
+++ b/tests/test_adopt_execute_refusals.py
@@ -1017,6 +1017,13 @@ def a_whole_pin(product: str) -> str:
#: then passes. A `paths:` with no entries is valid as a TYPE and leaves every
#: source path uncovered, which is `plan-uncovered`'s to say, as it was. A
#: `pins:` is a list of mappings, as `plan` writes it since #273.
+#:
+#: A TEXT in the third place is what `check` says instead, because the key
+#: is read as it always was (#544, #435's `source is None` and `legs is None`
+#: arms): `source:` bare or absent is `plan-no-source`, a refusal, exit 2, and
+#: `legs:` bare is the names' own `naming-role-mismatch`, a finding, exit 1.
+#: Neither is `plan-bad-source` or `plan-bad-legs`, which say a value is the
+#: wrong TYPE, and a bare key has no value at all.
GOOD_KEYS = {
"pins-empty": ("pins", "pins: []\n", True),
"pins-bare": ("pins", "pins:\n", True),
@@ -1029,6 +1036,9 @@ def a_whole_pin(product: str) -> str:
"allow-absent": ("allow_empty_legs", "", True),
"allow-both": ("allow_empty_legs", "allow_empty_legs: [spec, code]\n",
True),
+ "source-bare": ("source", "source:\n", "REFUSED plan-no-source"),
+ "source-absent": ("source", "", "REFUSED plan-no-source"),
+ "legs-bare": ("legs", "legs:\n", "FINDING naming-role-mismatch"),
"paths-empty": ("paths", "paths: []\n", False),
"paths-bare": ("paths", "paths:\n", False),
"paths-absent": ("paths", "", False),
@@ -1036,17 +1046,23 @@ def a_whole_pin(product: str) -> str:
@pytest.mark.parametrize("edit", sorted(GOOD_KEYS))
-def test_a_list_an_empty_list_a_bare_key_and_no_key_are_not_findings(
+def test_a_list_an_empty_list_a_bare_key_and_no_key_are_not_plan_bad_findings(
adopter, world, edit):
"""#386 (d): what `plan` writes, and the ways of writing none, are not
findings of this kind; a plan with no entries is `plan-uncovered`, as it
- was, and never `plan-bad-paths`."""
+ was, and never `plan-bad-paths`. A `source:` or `legs:` left bare, or no
+ `source:` at all, is read as it always was too (#544), never as the wrong
+ type."""
key, block, passes = GOOD_KEYS[edit]
text = with_key(world.answered, key, block)
checked = run_check(write_edited(world, text))
assert_no_traceback(checked)
assert "FINDING plan-bad-" not in checked.stderr, checked.stderr
- if passes:
+ if isinstance(passes, str):
+ assert f"{passes}: " in checked.stderr, checked.stderr
+ assert checked.returncode == (2 if passes.startswith("REFUSED")
+ else 1), checked.stderr
+ elif passes:
assert checked.returncode == 0, checked.stderr + checked.stdout
plan, source = open_plan(adopter, world, text)
assert adopter._refuse_what_check_finds(plan, source) is None
diff --git a/tests/test_adopt_gitmodules_values.py b/tests/test_adopt_gitmodules_values.py
index 41aa3734..ca9348bc 100644
--- a/tests/test_adopt_gitmodules_values.py
+++ b/tests/test_adopt_gitmodules_values.py
@@ -27,9 +27,9 @@
import pytest
-from conftest import ADOPT, FILE_PROTOCOL, GITLINK, GITMODULES, as_human, \
- exported_in_a_clone, findings, git, in_the_history, link, \
- make_source_repo, planned, put_gitmodules, refused_before_any_leg, \
+from conftest import ADOPT, GITLINK, GITMODULES, as_human, \
+ cloned_just_pushed, exported_in_a_clone, findings, git, in_the_history, \
+ link, make_source_repo, planned, put_gitmodules, refused_before_any_leg, \
run_script
needs_filter_repo = pytest.mark.skipif(
@@ -454,10 +454,7 @@ def test_a_refused_value_git_never_reads_adopts_and_clones(tmp_path, where):
adopted = execute_in(plan, tmp_path)
assert adopted.returncode == 0, adopted.stderr + adopted.stdout
assert "adoption verified" in adopted.stdout
- clone = tmp_path / "clone"
- cloned = git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- "adopt/three-repo-shape", str(source), str(clone),
- cwd=tmp_path, check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(source, tmp_path / "clone",
+ "adopt/three-repo-shape")
assert (clone / "code" / GITMODULES).read_bytes() \
== with_line("update = bogus").encode()
diff --git a/tests/test_adopt_ignored_shape_files.py b/tests/test_adopt_ignored_shape_files.py
index 034bc21d..5feb03dd 100644
--- a/tests/test_adopt_ignored_shape_files.py
+++ b/tests/test_adopt_ignored_shape_files.py
@@ -41,8 +41,9 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, clear_ambient_pin_sources,
- git, make_source_repo, resolve, run_script, write_plan)
+from conftest import (ADOPT, REPO, clear_ambient_pin_sources,
+ cloned_just_pushed, git, make_source_repo, resolve,
+ run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
from repo_shape import parse_yaml # noqa: E402
@@ -559,9 +560,7 @@ def test_the_split_lands_the_files_the_pin_names(adopted):
@needs_filter_repo
def test_the_assembly_bootstraps_where_it_failed_with_shape_copy_missing(
adopted, tmp_path):
- clone = tmp_path / PROJECT
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b", BRANCH,
- str(adopted.source), str(clone), cwd=tmp_path)
+ clone = cloned_just_pushed(adopted.source, tmp_path / PROJECT, BRANCH)
bootstrapped = run_script(clone / "scripts" / "bootstrap.py", cwd=clone)
output = bootstrapped.stderr + bootstrapped.stdout
assert "shape-copy-missing" not in output, output
@@ -601,13 +600,8 @@ def test_a_checkout_that_writes_crlf_still_bootstraps(adopted, tmp_path, key,
shape copy, its `* text=auto` alone governs it, and it IS checked out
with CRLF, so the setting reached the checkout and the pass means what
it says."""
- clone = tmp_path / PROJECT
- cloned = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- BRANCH, str(adopted.source), str(clone)], cwd=str(tmp_path),
- env=command_scope(key, value), capture_output=True, text=True,
- check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(adopted.source, tmp_path / PROJECT, BRANCH,
+ env=command_scope(key, value))
if adopted.own:
assert b"\r\n" in (clone / README).read_bytes(), \
"this clone did not write CRLF, so it tests nothing"
@@ -752,9 +746,8 @@ def test_a_root_kept_symlink_the_shape_writes_through_adopts(
def test_the_assembly_with_a_root_kept_symlink_bootstraps(
adopted_through_a_link, tmp_path):
assert adopted_through_a_link.result.returncode == 0
- clone = tmp_path / PROJECT
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b", BRANCH,
- str(adopted_through_a_link.source), str(clone), cwd=tmp_path)
+ clone = cloned_just_pushed(adopted_through_a_link.source,
+ tmp_path / PROJECT, BRANCH)
bootstrapped = run_script(clone / "scripts" / "bootstrap.py", cwd=clone)
output = bootstrapped.stderr + bootstrapped.stdout
assert bootstrapped.returncode == 0, output
diff --git a/tests/test_adopt_mount_ignored.py b/tests/test_adopt_mount_ignored.py
index 9d1d93e1..d1dd3e1e 100644
--- a/tests/test_adopt_mount_ignored.py
+++ b/tests/test_adopt_mount_ignored.py
@@ -50,7 +50,8 @@
import pytest
from conftest import (ADOPT, DEFAULT_EDITS, FILE_PROTOCOL, SYNTHETIC_TREE,
- git, make_source_repo, resolve, run_script, write_plan)
+ cloned_just_pushed, git, make_source_repo, resolve,
+ run_script, write_plan)
#: What the end-to-end tests need and the mount tests do not. Spelled here
#: rather than imported from another test module, so that the files do not
@@ -193,9 +194,9 @@ def head(repo: Path) -> str:
return git("rev-parse", "HEAD", cwd=repo).stdout.strip()
-def clone(source: Path, destination: Path, *options: str) -> None:
- git(*FILE_PROTOCOL, "clone", "-q", *options, str(source),
- str(destination), cwd=destination.parent)
+def clone(source: Path, destination: Path) -> None:
+ git(*FILE_PROTOCOL, "clone", "-q", str(source), str(destination),
+ cwd=destination.parent)
def assembly_of(source: Path, tmp_path: Path) -> Path:
@@ -621,8 +622,7 @@ def test_the_assembly_whose_gitignore_hides_its_mounts_bootstraps(adopted,
"""With #167's forced staging of the shape's own files, nothing else
stands between this source and a working assembly."""
assert adopted.result.returncode == 0, adopted.result.stderr
- checkout = tmp_path / PROJECT
- clone(adopted.source, checkout, "--recurse-submodules", "-b", BRANCH)
+ checkout = cloned_just_pushed(adopted.source, tmp_path / PROJECT, BRANCH)
bootstrapped = run_script(checkout / "scripts" / "bootstrap.py",
cwd=checkout)
output = bootstrapped.stderr + bootstrapped.stdout
diff --git a/tests/test_adopt_pins.py b/tests/test_adopt_pins.py
index 77897adc..918ed38b 100644
--- a/tests/test_adopt_pins.py
+++ b/tests/test_adopt_pins.py
@@ -43,10 +43,10 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, SCAFFOLD, SYNTHETIC_TREE,
- WINDOWS_SKIP, as_human, findings, git, make_source_repo,
- planned, refused_before_any_leg, resolve, rmtree,
- run_script)
+from conftest import (ADOPT, REPO, SCAFFOLD, SYNTHETIC_TREE, WINDOWS_SKIP,
+ as_human, cloned_just_pushed, findings, git,
+ make_source_repo, planned, refused_before_any_leg,
+ resolve, rmtree, run_script)
sys.path.insert(0, str(REPO / "scripts"))
import shape_materialize # noqa: E402
@@ -231,11 +231,7 @@ def adopted(request, tmp_path_factory, glass):
"--work-dir", str(base / "work"), env=env)
clone = base / "clone" / PROJECT
if executed.returncode == 0:
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- "-b", BRANCH, str(source), str(clone)],
- capture_output=True, check=False)
- assert proc.returncode == 0, proc.stderr.decode(UTF8, "replace")
+ cloned_just_pushed(source, clone, BRANCH)
return {"base": base, "source": source, "plan": plan, "env": env,
"repository": repository, "commit": commit,
"readable": readable, "planned": planned_run,
@@ -372,11 +368,7 @@ def adopted_as_text(request, tmp_path_factory):
"--work-dir", str(base / "work"), env=env)
clone = base / "clone" / TEXT_PROJECT
if executed.returncode == 0:
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- "-b", BRANCH, str(source), str(clone)],
- capture_output=True, check=False)
- assert proc.returncode == 0, proc.stderr.decode(UTF8, "replace")
+ cloned_just_pushed(source, clone, BRANCH)
return {"product": product, "spelled": spelled, "commit": commit,
"readable": readable, "source": source, "plan": plan, "env": env,
"checked": checked, "executed": executed, "clone": clone}
diff --git a/tests/test_adopt_plan_yaml_quoting.py b/tests/test_adopt_plan_yaml_quoting.py
index 0511c0c0..aaf43d0d 100644
--- a/tests/test_adopt_plan_yaml_quoting.py
+++ b/tests/test_adopt_plan_yaml_quoting.py
@@ -74,7 +74,7 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
+from conftest import (ADOPT, REPO, cloned_just_pushed, git, make_source_repo,
resolve, run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
@@ -2032,11 +2032,7 @@ def test_what_is_still_accepted_adopts_clones_and_bootstraps(tmp_path):
assert result.returncode == 0, result.stderr + result.stdout
assert "adoption verified" in result.stdout
- clone = tmp_path / "clone"
- cloned = git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- SPLIT_BRANCH, str(source), str(clone), cwd=tmp_path,
- check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(source, tmp_path / "clone", SPLIT_BRANCH)
bootstrapped = run_script(clone / "scripts/bootstrap.py", cwd=clone)
assert bootstrapped.returncode == 0, (bootstrapped.stderr
+ bootstrapped.stdout)
diff --git a/tests/test_adopt_relative_submodule_url.py b/tests/test_adopt_relative_submodule_url.py
index e33e5681..df2ee558 100644
--- a/tests/test_adopt_relative_submodule_url.py
+++ b/tests/test_adopt_relative_submodule_url.py
@@ -26,8 +26,8 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, git, make_source_repo, resolve,
- run_script, write_plan)
+from conftest import (ADOPT, FILE_PROTOCOL, clone_just_pushed, git,
+ make_source_repo, resolve, run_script, write_plan)
needs_filter_repo = pytest.mark.skipif(
shutil.which("git-filter-repo") is None,
@@ -626,9 +626,7 @@ def execute(plan: Path, work: Path, remotes: Path):
def clone_the_split(source: Path, clone: Path):
"""`git clone --recurse-submodules` of the branch `execute` pushed the
split to: what a person runs next."""
- return git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- "adopt/three-repo-shape", str(source), str(clone),
- cwd=clone.parent, check=False)
+ return clone_just_pushed(source, clone, "adopt/three-repo-shape")
@needs_filter_repo
diff --git a/tests/test_adopt_root_symlinks.py b/tests/test_adopt_root_symlinks.py
index f3f56ab4..5a098072 100644
--- a/tests/test_adopt_root_symlinks.py
+++ b/tests/test_adopt_root_symlinks.py
@@ -60,8 +60,9 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, clear_ambient_pin_sources,
- git, make_source_repo, run_script, write_plan)
+from conftest import (ADOPT, REPO, clear_ambient_pin_sources,
+ cloned_just_pushed, git, make_source_repo, run_script,
+ write_plan)
sys.path.insert(0, str(REPO / "scripts"))
from shape_materialize import ( # noqa: E402
@@ -1828,9 +1829,7 @@ def split_modes(source: Path) -> dict:
def bootstraps(source: Path, base: Path) -> None:
"""A recursive clone of the split, and its bootstrap, exit 0."""
- clone = base / PROJECT
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b", BRANCH,
- str(source), str(clone), cwd=base)
+ clone = cloned_just_pushed(source, base / PROJECT, BRANCH)
booted = run_script(clone / SCRIPTS / "bootstrap.py", cwd=clone)
assert booted.returncode == 0, booted.stderr + booted.stdout
diff --git a/tests/test_adopt_spec_only.py b/tests/test_adopt_spec_only.py
index 19ec9ae0..a300abfc 100644
--- a/tests/test_adopt_spec_only.py
+++ b/tests/test_adopt_spec_only.py
@@ -16,12 +16,11 @@
from __future__ import annotations
import shutil
-import subprocess
import sys
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
+from conftest import (ADOPT, REPO, cloned_just_pushed, git, make_source_repo,
resolve, run_script, write_plan)
from test_scaffold_ignored_shape_files import excluding, ignored, tracked_under
@@ -96,13 +95,8 @@ def adopted(tmp_path_factory) -> dict:
@pytest.fixture
def adopted_clone(adopted, tmp_path):
- target = tmp_path / PROJECT
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- "-b", "adopt/three-repo-shape", str(adopted["source"]), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
- return target
+ return cloned_just_pushed(adopted["source"], tmp_path / PROJECT,
+ "adopt/three-repo-shape")
# --- the plan records it ----------------------------------------------------
@@ -191,10 +185,8 @@ def test_the_consent_may_be_given_on_execute_itself(tmp_path):
# --- the seeded leg ---------------------------------------------------------
def test_the_seeded_leg_is_one_commit_of_the_template(adopted, tmp_path):
- clone = tmp_path / "code-leg"
- subprocess.run(["git", "clone", "-q",
- str(adopted["remotes"] / f"{PROJECT}-code.git"),
- str(clone)], check=True)
+ clone = cloned_just_pushed(adopted["remotes"] / f"{PROJECT}-code.git",
+ tmp_path / "code-leg")
assert int(git("rev-list", "--count", "HEAD", cwd=clone).stdout) == 1
assert (clone / "README.md").is_file()
assert (clone / "src" / ".gitkeep").is_file()
@@ -243,10 +235,8 @@ def test_the_seeded_leg_keeps_its_template_files_under_the_operators_excludes(
def test_the_spec_leg_still_carries_the_history(adopted, tmp_path):
- clone = tmp_path / "spec-leg"
- subprocess.run(["git", "clone", "-q",
- str(adopted["remotes"] / f"{PROJECT}-spec.git"),
- str(clone)], check=True)
+ clone = cloned_just_pushed(adopted["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path / "spec-leg")
assert int(git("rev-list", "--count", "HEAD", cwd=clone).stdout) > 1
assert (clone / "specs" / "001-render" / "spec.md").is_file()
assert "Source Human" in git("log", "--format=%an", cwd=clone).stdout
diff --git a/tests/test_adopt_submodules.py b/tests/test_adopt_submodules.py
index 5a519fa6..e9d9b2a5 100644
--- a/tests/test_adopt_submodules.py
+++ b/tests/test_adopt_submodules.py
@@ -19,8 +19,9 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
- resolve, run_script, write_plan)
+from conftest import (ADOPT, FILE_PROTOCOL, REPO, clone_just_pushed,
+ cloned_just_pushed, git, make_source_repo, resolve,
+ run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
from repo_shape import load_yaml, parse_yaml # noqa: E402
@@ -85,9 +86,8 @@ def test_adopt_moves_existing_gitmodules_with_its_dependency(tmp_path,
"--work-dir", str(tmp_path / "work"))
assert result.returncode == 0, result.stderr + result.stdout
- clone = tmp_path / "assembly"
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- "adopt/three-repo-shape", str(source), str(clone), cwd=tmp_path)
+ clone = cloned_just_pushed(source, tmp_path / "assembly",
+ "adopt/three-repo-shape")
leg = clone / dependency_leg
assert git("rev-parse", "HEAD:.gitmodules", cwd=leg).stdout.strip() \
== original_modules
@@ -1614,9 +1614,8 @@ def execute_in(plan: Path, base: Path):
def clone_the_split(source: Path, tmp_path: Path):
"""`git clone --recurse-submodules` of the branch `execute` pushed the
split to, into `tmp_path/clone`: what a person runs next."""
- return git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- "adopt/three-repo-shape", str(source), str(tmp_path / "clone"),
- cwd=tmp_path, check=False)
+ return clone_just_pushed(source, tmp_path / "clone",
+ "adopt/three-repo-shape")
def source_state(source: Path) -> list[str]:
diff --git a/tests/test_adopt_verify.py b/tests/test_adopt_verify.py
index 8821ad1a..4969a9ae 100644
--- a/tests/test_adopt_verify.py
+++ b/tests/test_adopt_verify.py
@@ -30,8 +30,8 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, git, make_source_repo,
- resolve, run_script, write_plan)
+from conftest import (ADOPT, FILE_PROTOCOL, REPO, clone_just_pushed, git,
+ make_source_repo, resolve, run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
from shape_materialize import SHAPE_REPOSITORY # noqa: E402
@@ -341,9 +341,7 @@ def test_a_spec_gitlink_not_where_the_plan_spells_the_mount_is_two_findings(
def clone_the_split(source: Path, tmp_path: Path):
"""`git clone --recurse-submodules` of the split branch into
`tmp_path/clone`: what the NEXT lines tell the human to run."""
- return git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules", "-b",
- SPLIT_BRANCH, str(source), str(tmp_path / "clone"),
- cwd=tmp_path, check=False)
+ return clone_just_pushed(source, tmp_path / "clone", SPLIT_BRANCH)
@needs_filter_repo
diff --git a/tests/test_bootstrap_after_bump.py b/tests/test_bootstrap_after_bump.py
index 78063c4c..ee36854c 100644
--- a/tests/test_bootstrap_after_bump.py
+++ b/tests/test_bootstrap_after_bump.py
@@ -56,7 +56,7 @@
import pytest
-from conftest import (FILE_PROTOCOL, ORG, PROJECT, REPO, SCAFFOLD, git,
+from conftest import (ORG, PROJECT, REPO, SCAFFOLD, cloned_just_pushed, git,
run_script)
BUMP = REPO / "scripts" / "bump-leg.py"
@@ -144,9 +144,8 @@ def bootstrapped(tmp_path_factory) -> dict:
"2026-09-02", "--local-remote-dir", str(remotes),
"--work-dir", str(base / "work"))
assert made.returncode == 0, made.stderr + made.stdout
- clone = base / "clone" / PROJECT
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(remotes / f"{PROJECT}.git"), str(clone), cwd=base)
+ clone = cloned_just_pushed(remotes / f"{PROJECT}.git",
+ base / "clone" / PROJECT)
first = bootstrap(clone)
assert first.returncode == 0, first.stderr + first.stdout
assert head_ref(clone / "spec") == MAIN, first.stdout
@@ -172,9 +171,8 @@ def bumped(root: Path, remotes: Path, tmp_path: Path, branch: str,
only that some branch of the remote contains the commit. With `touch`,
the commit also adds a line at the end of that tracked file.
"""
- work = tmp_path / "spec-work"
- git("clone", "-q", str(remotes / f"{PROJECT}-spec.git"), str(work),
- cwd=tmp_path)
+ work = cloned_just_pushed(remotes / f"{PROJECT}-spec.git",
+ tmp_path / "spec-work")
(work / ADDED).write_bytes(b"# a contract the leg gained\n")
git("add", "--", ADDED, cwd=work)
if touch is not None:
@@ -513,9 +511,8 @@ def leg_main_ahead(tmp_path_factory) -> dict:
"2026-09-02", "--local-remote-dir", str(remotes),
"--work-dir", str(base / "work"))
assert made.returncode == 0, made.stderr + made.stdout
- work = base / "spec-work"
- git("clone", "-q", str(remotes / f"{PROJECT}-spec.git"), str(work),
- cwd=base)
+ work = cloned_just_pushed(remotes / f"{PROJECT}-spec.git",
+ base / "spec-work")
(work / MOVED_ON).write_bytes(b"# the leg's main moved on\n")
git("add", "--", MOVED_ON, cwd=work)
git("commit", "-q", "-m", "The leg moves on past the pin", cwd=work)
@@ -528,10 +525,8 @@ def fresh(leg_main_ahead, tmp_path) -> Path:
"""`git clone --recurse-submodules` of the root, as AGENTS.md has a
person run it, never bootstrapped: git made the spec leg's `main` at
the remote's tip and left the leg detached at the pin."""
- clone = tmp_path / PROJECT
- git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(leg_main_ahead["remotes"] / f"{PROJECT}.git"), str(clone),
- cwd=tmp_path)
+ clone = cloned_just_pushed(leg_main_ahead["remotes"] / f"{PROJECT}.git",
+ tmp_path / PROJECT)
leg = clone / "spec"
assert head_ref(leg) is None and rev(leg) == rev(clone, "HEAD:spec")
assert rev(leg, MAIN) == leg_main_ahead["ahead"]
diff --git a/tests/test_bootstrap_spelling.py b/tests/test_bootstrap_spelling.py
index 6cf55d0f..715c05ad 100644
--- a/tests/test_bootstrap_spelling.py
+++ b/tests/test_bootstrap_spelling.py
@@ -49,7 +49,6 @@
from __future__ import annotations
import ast
-import importlib.util
import io
import shutil
import subprocess
@@ -59,7 +58,7 @@
import pytest
-from conftest import REPO, git, run_script
+from conftest import REPO, git, load_without_bytecode, run_script
from test_stdout_encoding import child_environ, run_tool_bytes, shown
sys.path.insert(0, str(REPO / "scripts"))
@@ -660,27 +659,6 @@ def test_a_members_row_and_the_make_target_are_spelled(holder, tmp_path):
# --- the two functions, in repo_shape.py, and the roots that import them -----
-def load(path: Path, name: str):
- """`path` run as a module called `name`, and no bytecode written for it.
-
- `SourceFileLoader` caches what it compiles in a `__pycache__` beside the
- source, and these sources are the TEMPLATES: a tree other tests read file
- by file as text (`test_scaffold_leg_path_collisions.py` does, and a `.pyc`
- is not UTF-8), and a tree a materializer walks. `.gitignore` hides the
- stray, so `git status` never says it is there. The flag is process-wide, so
- it is restored; it also covers the `repo_shape` a root imports as it runs.
- """
- spec = importlib.util.spec_from_file_location(name, path)
- module = importlib.util.module_from_spec(spec)
- wrote_bytecode = sys.dont_write_bytecode
- sys.dont_write_bytecode = True
- try:
- spec.loader.exec_module(module)
- finally:
- sys.dont_write_bytecode = wrote_bytecode
- return module
-
-
def test_loading_a_root_leaves_no_file_where_it_was_loaded_from(tmp_path,
monkeypatch):
"""The tree under `templates/` is the same after both roots are loaded.
@@ -697,9 +675,9 @@ def test_loading_a_root_leaves_no_file_where_it_was_loaded_from(tmp_path,
copy.parent.mkdir(parents=True)
shutil.copyfile(path, copy)
before_copy = set(copy.parent.rglob("*"))
- load(path, f"bootstrap_spelling_probe_{number}")
+ load_without_bytecode(path, f"bootstrap_spelling_probe_{number}")
assert set(templates.rglob("*")) == before, path
- load(copy, f"bootstrap_spelling_probe_copy_{number}")
+ load_without_bytecode(copy, f"bootstrap_spelling_probe_copy_{number}")
assert set(copy.parent.rglob("*")) == before_copy, copy.parent
@@ -708,8 +686,9 @@ def test_loading_a_root_leaves_no_file_where_it_was_loaded_from(tmp_path,
def template(request):
"""Each root's `bootstrap.py`, loaded by path under a name of its own, so
nothing here leaves a module called `bootstrap` importable."""
- return load(request.param, "bootstrap_spelling_"
- f"{request.param.parts[-3].replace('-', '_')}")
+ return load_without_bytecode(
+ request.param,
+ f"bootstrap_spelling_{request.param.parts[-3].replace('-', '_')}")
@pytest.mark.parametrize("text", [
diff --git a/tests/test_bump_leg.py b/tests/test_bump_leg.py
index 966be224..9f489ad0 100644
--- a/tests/test_bump_leg.py
+++ b/tests/test_bump_leg.py
@@ -24,7 +24,7 @@
import pytest
-from conftest import ORG, PROJECT, REPO, git, run_script
+from conftest import ORG, PROJECT, REPO, cloned_just_pushed, git, run_script
#: The trailer helpers and the two lines a lane's run lands, imported from
#: the file that owns those rules rather than retyped — see
#: `tests/test_commit_trailers.py`.
@@ -79,12 +79,9 @@ def bump_leg_module():
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = tmp_path_factory.mktemp("spec-advance") / f"{PROJECT}-spec"
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("spec-advance") / f"{PROJECT}-spec")
(work / "NEW-CONTRACT.md").write_text("# a contract the leg gained\n")
git("add", "--", "NEW-CONTRACT.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_bump_leg_dry_run_tracking.py b/tests/test_bump_leg_dry_run_tracking.py
index 220cf761..196d567f 100644
--- a/tests/test_bump_leg_dry_run_tracking.py
+++ b/tests/test_bump_leg_dry_run_tracking.py
@@ -31,7 +31,7 @@
import pytest
-from conftest import PROJECT, git
+from conftest import PROJECT, cloned_just_pushed, git
from test_bump_leg import branched, bump, reference_both_legs
from test_bump_leg_rollback import assert_nothing_changed, snapshot
from test_commit_trailers import TRAILERS
@@ -54,13 +54,10 @@
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = (tmp_path_factory.mktemp("spec-advance-dry-run-tracking")
- / f"{PROJECT}-spec")
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("spec-advance-dry-run-tracking")
+ / f"{PROJECT}-spec")
(work / "DRY-RUN-TRACKING.md").write_text("# a contract gained\n")
git("add", "--", "DRY-RUN-TRACKING.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_bump_leg_rollback.py b/tests/test_bump_leg_rollback.py
index f46c42f4..94e42b11 100644
--- a/tests/test_bump_leg_rollback.py
+++ b/tests/test_bump_leg_rollback.py
@@ -46,13 +46,12 @@
import io
import os
import stat
-import subprocess
import sys
from pathlib import Path
import pytest
-from conftest import PROJECT, git
+from conftest import PROJECT, cloned_just_pushed, git
# A fixture is used by being named in this module's namespace, which is what
# the import of `bump_leg_module` is for.
from test_bump_leg import ( # noqa: F401
@@ -73,12 +72,9 @@
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = tmp_path_factory.mktemp("spec-advance-rollback") / f"{PROJECT}-spec"
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("spec-advance-rollback") / f"{PROJECT}-spec")
(work / "ROLLBACK-CONTRACT.md").write_text("# a contract the leg gained\n")
git("add", "--", "ROLLBACK-CONTRACT.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_bump_leg_unopenable.py b/tests/test_bump_leg_unopenable.py
index eb49862f..9a3fe37a 100644
--- a/tests/test_bump_leg_unopenable.py
+++ b/tests/test_bump_leg_unopenable.py
@@ -57,13 +57,12 @@
import errno
import os
import stat
-import subprocess
import sys
from pathlib import Path
import pytest
-from conftest import ORG, PROJECT, git
+from conftest import ORG, PROJECT, cloned_just_pushed, git
# A fixture is used by being named in this module's namespace, which is what
# the import of `bump_leg_module` is for.
from test_bump_leg import ( # noqa: F401
@@ -92,13 +91,9 @@
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = (tmp_path_factory.mktemp("spec-advance-unopenable")
- / f"{PROJECT}-spec")
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("spec-advance-unopenable") / f"{PROJECT}-spec")
(work / "UNOPENABLE-CONTRACT.md").write_text("# a contract gained\n")
git("add", "--", "UNOPENABLE-CONTRACT.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_bump_leg_unreadable.py b/tests/test_bump_leg_unreadable.py
index 22773bb2..0a07f891 100644
--- a/tests/test_bump_leg_unreadable.py
+++ b/tests/test_bump_leg_unreadable.py
@@ -39,12 +39,11 @@
from __future__ import annotations
import codecs
-import subprocess
from pathlib import Path
import pytest
-from conftest import ORG, PROJECT, git
+from conftest import ORG, PROJECT, cloned_just_pushed, git
# A fixture is used by being named in this module's namespace, which is what
# the import of `bump_leg_module` is for.
from test_bump_leg import ( # noqa: F401
@@ -74,13 +73,9 @@
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = (tmp_path_factory.mktemp("spec-advance-unreadable")
- / f"{PROJECT}-spec")
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("spec-advance-unreadable") / f"{PROJECT}-spec")
(work / "UNREADABLE-CONTRACT.md").write_text("# a contract gained\n")
git("add", "--", "UNREADABLE-CONTRACT.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_clone_just_pushed.py b/tests/test_clone_just_pushed.py
new file mode 100644
index 00000000..8cd617eb
--- /dev/null
+++ b/tests/test_clone_just_pushed.py
@@ -0,0 +1,102 @@
+# SPDX-License-Identifier: Apache-2.0
+"""The options of `clone_just_pushed` and `cloned_just_pushed` (#544).
+
+#467 added `clone_just_pushed` for two fixtures. #544 moved every other test
+that cloned a just-pushed local remote by its plain path onto it, and those
+tests clone in the ways this file asks of the helper: without the submodules,
+as a bare copy, with `-c` for the one command, with variables of their own,
+and at a branch. Each case is one of those, and each case that makes a clone
+asks the one thing the helper is for of it: the clone's store holds a pack and
+no loose object, which a clone that walked the remote's own files would not
+(`assert_store_read_through_git`). The option itself is asked of git beside
+it, and the last case is the clone that fails.
+"""
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from conftest import (PROJECT, assert_read_through_git,
+ assert_store_read_through_git, clone_just_pushed,
+ cloned_just_pushed, git)
+
+
+@pytest.fixture
+def remote(scaffolded):
+ """The bare remote the session's scaffold pushed the root to."""
+ return scaffolded["remotes"] / f"{PROJECT}.git"
+
+
+def test_a_recursive_clone_holds_the_root_and_both_legs_as_packs(remote,
+ tmp_path):
+ clone = cloned_just_pushed(remote, tmp_path / "clone")
+ assert_read_through_git(clone)
+ assert any((clone / "spec").iterdir()), "the mount was not checked out"
+
+
+def test_without_the_submodules_the_mounts_stay_empty(remote, tmp_path):
+ clone = cloned_just_pushed(remote, tmp_path / "clone", recurse=False)
+ assert_store_read_through_git(clone, "the root")
+ assert not any((clone / "spec").iterdir()), "the mount was checked out"
+ assert not any((clone / "code").iterdir()), "the mount was checked out"
+
+
+def test_a_bare_copy_is_bare_and_was_read_through_git(remote, tmp_path):
+ copy = cloned_just_pushed(remote, tmp_path / "copy.git", bare=True)
+ assert git("rev-parse", "--is-bare-repository",
+ cwd=copy).stdout.strip() == "true"
+ assert_store_read_through_git(copy, "the bare copy")
+
+
+def test_a_branch_is_the_one_checked_out(remote, tmp_path):
+ clone = cloned_just_pushed(remote, tmp_path / "clone", "main")
+ assert_read_through_git(clone)
+ assert git("rev-parse", "--abbrev-ref", "HEAD",
+ cwd=clone).stdout.strip() == "main"
+
+
+def test_a_branch_the_remote_lacks_fails_the_clone(remote, tmp_path):
+ """`-b` reaches git: `main` is the default branch, so the case above
+ passes without it, and a branch no remote has is refused only by git."""
+ asked = clone_just_pushed(remote, tmp_path / "x", "no-such-branch")
+ assert asked.returncode != 0, asked.stderr
+ assert "no-such-branch" in asked.stderr, asked.stderr
+ with pytest.raises(AssertionError, match="no-such-branch"):
+ cloned_just_pushed(remote, tmp_path / "x", "no-such-branch")
+
+
+def test_config_and_env_reach_the_one_command_and_nothing_is_stored(
+ remote, tmp_path):
+ """`clone.defaultRemoteName` is read by `git clone` and is the name its
+ remote is given, so each of the two ways of passing it shows in the clone.
+ The `url..insteadOf` the helper adds is a `-c` too, and must not be
+ stored: the clone's `origin` keeps the plain path it was given."""
+ by_config = cloned_just_pushed(
+ remote, tmp_path / "by-config", recurse=False,
+ config=["-c", "clone.defaultRemoteName=by-config"])
+ assert_store_read_through_git(by_config, "the clone made with `config`")
+ assert git("remote", cwd=by_config).stdout.split() == ["by-config"]
+ # APPENDED to what the environment holds, as `command_scope` does in
+ # `test_adopt_ignored_shape_files.py`: the Windows job sets two entries
+ # of its own there, and `GIT_CONFIG_COUNT=1` would drop them.
+ held = int(os.environ.get("GIT_CONFIG_COUNT") or 0)
+ by_env = cloned_just_pushed(
+ remote, tmp_path / "by-env", recurse=False,
+ env={"GIT_CONFIG_COUNT": str(held + 1),
+ f"GIT_CONFIG_KEY_{held}": "clone.defaultRemoteName",
+ f"GIT_CONFIG_VALUE_{held}": "by-env"})
+ assert_store_read_through_git(by_env, "the clone made with `env`")
+ assert git("remote", cwd=by_env).stdout.split() == ["by-env"]
+ stored = git("config", "--local", "--list", cwd=by_env).stdout
+ assert "insteadof" not in stored.lower(), stored
+ assert git("remote", "get-url", "by-env", cwd=by_env).stdout.strip() \
+ == str(remote)
+
+
+def test_a_clone_that_fails_fails_the_test_and_says_why(tmp_path):
+ missing = tmp_path / "remotes" / "Nothing.git"
+ assert clone_just_pushed(missing, tmp_path / "clone").returncode != 0
+ with pytest.raises(AssertionError, match="git clone of .* failed"):
+ cloned_just_pushed(missing, tmp_path / "clone")
diff --git a/tests/test_doctor_gitlink_repairs.py b/tests/test_doctor_gitlink_repairs.py
index 59c95c02..98ae92b1 100644
--- a/tests/test_doctor_gitlink_repairs.py
+++ b/tests/test_doctor_gitlink_repairs.py
@@ -48,7 +48,7 @@
import pytest
-from conftest import FILE_PROTOCOL, REPO, git, run_script
+from conftest import FILE_PROTOCOL, REPO, cloned_just_pushed, git, run_script
from test_commit_trailers import LANE
from test_shape_doctor import ( # noqa: F401
DOCTOR, DRIFT_TARGET, GIT_IDENTITY, PROJECT, doctor, doctor_module,
@@ -74,11 +74,9 @@ def advance(scaffolded_here, tmp_path_factory, role: str,
"""A new commit on a branch of a leg's remote, as a merged pull request
leaves one, adding `relative` (a path the `placement` row puts in that
leg); its sha."""
- work = tmp_path_factory.mktemp(f"{role}-advance") / f"{PROJECT}-{role}"
- remote = scaffolded_here["base"] / "remotes" / f"{PROJECT}-{role}.git"
- proc = subprocess.run(["git", "clone", "-q", str(remote), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded_here["base"] / "remotes" / f"{PROJECT}-{role}.git",
+ tmp_path_factory.mktemp(f"{role}-advance") / f"{PROJECT}-{role}")
(work / relative).parent.mkdir(parents=True, exist_ok=True)
(work / relative).write_text("# R478\n", encoding="utf-8")
git("add", "--", relative, cwd=work)
diff --git a/tests/test_doctor_legs_members_next.py b/tests/test_doctor_legs_members_next.py
index 54670422..cc2dc543 100644
--- a/tests/test_doctor_legs_members_next.py
+++ b/tests/test_doctor_legs_members_next.py
@@ -47,7 +47,7 @@
import pytest
-from conftest import WINDOWS_SKIP, git, rmtree
+from conftest import WINDOWS_SKIP, cloned_just_pushed, git, rmtree
from test_shape_doctor import ( # noqa: F401
FAMILY_MEMBER, FAMILY_NAME, GIT_IDENTITY, PROJECT, commit_all, doctor,
doctor_module, holder, project, record_gitlink, rows_of,
@@ -508,12 +508,9 @@ def test_a_plain_clones_empty_mounts_are_cured_by_the_pasted_line(
mounts empty. Without `--init` the update prints `not initialized` and
the row stays red; the fixture's own recursive clone sets
`submodule.active`, so every other paste here would pass without it."""
- plain = tmp_path / "Plain"
- cloned = subprocess.run(
- ["git", "clone", "-q", str(scaffolded_here["base"] / "remotes"
- / f"{PROJECT}.git"), str(plain)],
- capture_output=True, text=True, check=False, env=PASTE_ENV)
- assert cloned.returncode == 0, cloned.stderr
+ plain = cloned_just_pushed(
+ scaffolded_here["base"] / "remotes" / f"{PROJECT}.git",
+ tmp_path / "Plain", recurse=False, env=PASTE_ENV)
row = row_of(standard, plain, "legs")
assert row["status"] == "FINDING", row
assert command_of(row).endswith("-- spec code"), row["next"]
diff --git a/tests/test_doctor_unfinished_merge.py b/tests/test_doctor_unfinished_merge.py
index 8150d582..0094ba05 100644
--- a/tests/test_doctor_unfinished_merge.py
+++ b/tests/test_doctor_unfinished_merge.py
@@ -38,7 +38,7 @@
import pytest
-from conftest import FILE_PROTOCOL, git, run_script
+from conftest import FILE_PROTOCOL, cloned_just_pushed, git, run_script
from test_shape_doctor import ( # noqa: F401
FAMILY_NAME, PROJECT, doctor, doctor_module, holder, powershell_tokens,
project, rows_of, scaffolded_here, standard, verdict_line,
@@ -122,9 +122,8 @@ def assert_compliant_with_no_merge_row(standard: Path, root: Path) -> None:
def spec_commits_on_branches(remotes: Path, tmp_path: Path) -> tuple:
"""Two commits on the spec leg's remote, siblings of its pin, each on a
branch of its own, so a merge of two bumps conflicts on the gitlink."""
- work = tmp_path / "spec-work"
- git(*FILE_PROTOCOL, "clone", "-q", str(remotes / f"{PROJECT}-spec.git"),
- str(work), cwd=tmp_path)
+ work = cloned_just_pushed(remotes / f"{PROJECT}-spec.git",
+ tmp_path / "spec-work")
base = git("rev-parse", "HEAD", cwd=work).stdout.strip()
made = []
for side in ("a", "b"):
diff --git a/tests/test_doctor_validator_rows.py b/tests/test_doctor_validator_rows.py
index 07dee634..564ed618 100644
--- a/tests/test_doctor_validator_rows.py
+++ b/tests/test_doctor_validator_rows.py
@@ -40,7 +40,7 @@
import pytest
-from conftest import FILE_PROTOCOL, WINDOWS_SKIP, git, run_script
+from conftest import WINDOWS_SKIP, cloned_just_pushed, git, run_script
from conftest import PTY_SKIP, REPO, run_in_a_terminal
#: The neutral product's stand-in, from the module that scaffolds a declared
#: descendant: a second way of building one would test a different one.
@@ -411,12 +411,8 @@ def descendant_run(standard, tmp_path_factory) -> dict:
"--work-dir", str(base / "work"),
env={"PATH": f"{quiet}{os.pathsep}{os.environ['PATH']}"})
assert made.returncode == 0, made.stderr + made.stdout
- clone = base / "clones" / "MedxGlass"
- cloned = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(base / "remotes" / "MedxGlass.git"), str(clone)],
- capture_output=True, text=True, check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(base / "remotes" / "MedxGlass.git",
+ base / "clones" / "MedxGlass")
assert not (clone.parent / PRODUCT).exists()
bin_dir, calls = logging_gh(base / "doctor-gh")
operators_config = str(base / "operators-gh-config")
diff --git a/tests/test_family.py b/tests/test_family.py
index 610df4c4..ef5ad3b1 100644
--- a/tests/test_family.py
+++ b/tests/test_family.py
@@ -32,7 +32,8 @@
import pytest
-from conftest import FILE_PROTOCOL, REPO, git, rmtree, run_script
+from conftest import (NO_BYTECODE, REPO, cloned_just_pushed, git, rmtree,
+ run_script)
#: The trailer helpers and the two lines a lane's run lands, imported from
#: the file that owns those rules rather than retyped — see
#: `tests/test_commit_trailers.py`.
@@ -1368,12 +1369,8 @@ def test_remove_refuses_a_member_that_is_not_there(holder):
@pytest.fixture(scope="module")
def bootstrapped(family, tmp_path_factory):
"""A fresh recursive clone of the family, bootstrapped."""
- target = tmp_path_factory.mktemp("family-clone") / NAME
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(family["remotes"] / f"{NAME}.git"), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ target = cloned_just_pushed(family["remotes"] / f"{NAME}.git",
+ tmp_path_factory.mktemp("family-clone") / NAME)
result = run_script(target / "scripts" / "bootstrap.py", cwd=target,
env=ALLOW_FILE_PROTOCOL)
return {"root": target, "result": result}
@@ -1400,12 +1397,8 @@ def test_bootstrap_degrades_when_a_member_cannot_be_fetched(family, tmp_path):
"""A missing credential is a DEGRADE, not a failure: without
`protocol.file.allow` git refuses these local submodules exactly as it
would refuse a private one with no token."""
- target = tmp_path / NAME
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q",
- str(family["remotes"] / f"{NAME}.git"), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ target = cloned_just_pushed(family["remotes"] / f"{NAME}.git",
+ tmp_path / NAME, recurse=False)
result = run_script(target / "scripts" / "bootstrap.py", cwd=target)
assert result.returncode == 0, result.stderr + result.stdout
assert "FETCH INCOMPLETE" in result.stderr
@@ -1943,7 +1936,7 @@ def one(name: str, runner) -> dict:
theirs = one("target", lambda holder: siblings(holder))
ours = one("shim", lambda holder: run_script(
FAMILY, "siblings", "--family-root", str(holder),
- env=ALLOW_FILE_PROTOCOL))
+ env={**ALLOW_FILE_PROTOCOL, **NO_BYTECODE}))
assert layout(ours["result"].stdout) == layout(theirs["result"].stdout)
for project in MEMBERS:
diff --git a/tests/test_family_add_excludes.py b/tests/test_family_add_excludes.py
index 80044c29..1dd0f2cc 100644
--- a/tests/test_family_add_excludes.py
+++ b/tests/test_family_add_excludes.py
@@ -43,7 +43,7 @@
import pytest
-from conftest import FILE_PROTOCOL, git, rmtree, run_script
+from conftest import FILE_PROTOCOL, cloned_just_pushed, git, rmtree, run_script
#: The family fixture, its names and the offline environment, imported from
#: the modules that own them rather than rebuilt: a holder made any other way
#: would test a different holder.
@@ -149,10 +149,7 @@ def recursive_clone(root: Path, tmp_path: Path) -> Path:
git_under(env, "init", "-q", "--bare", "-b", "main", str(bare),
cwd=tmp_path)
git_under(env, "push", "-q", str(bare), "HEAD:refs/heads/main", cwd=root)
- clone = tmp_path / "clone" / NAME
- git_under(env, *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(bare), str(clone), cwd=tmp_path)
- return clone
+ return cloned_just_pushed(bare, tmp_path / "clone" / NAME, env=env)
def refused_before_the_mount(result: subprocess.CompletedProcess, root: Path,
diff --git a/tests/test_family_add_member_id.py b/tests/test_family_add_member_id.py
index 5393473d..4409f505 100644
--- a/tests/test_family_add_member_id.py
+++ b/tests/test_family_add_member_id.py
@@ -38,7 +38,7 @@
import pytest
-from conftest import git, run_script
+from conftest import cloned_just_pushed, git, run_script
#: The family fixture, its names and the offline environment, imported from
#: the modules that own them rather than rebuilt: a holder made any other way
#: would test a different holder.
@@ -82,8 +82,8 @@ def renamed_copy(family, tmp_path: Path) -> Path:
name. The fixture's own remotes are not written to."""
remotes = tmp_path / "remotes"
remotes.mkdir()
- git("clone", "-q", "--bare", str(family["remotes"] / f"{PINNED}.git"),
- str(remotes / f"{RENAMED}.git"), cwd=tmp_path)
+ cloned_just_pushed(family["remotes"] / f"{PINNED}.git",
+ remotes / f"{RENAMED}.git", bare=True)
return remotes
diff --git a/tests/test_family_bump_remove_rollback.py b/tests/test_family_bump_remove_rollback.py
index 066591cd..3dcfe47c 100644
--- a/tests/test_family_bump_remove_rollback.py
+++ b/tests/test_family_bump_remove_rollback.py
@@ -61,7 +61,8 @@
import pytest
-from conftest import FILE_PROTOCOL, git, rmtree, run_script
+from conftest import (FILE_PROTOCOL, cloned_just_pushed, git, rmtree,
+ run_script)
#: The family fixture, its names, the offline environment and the holder's
#: byte-for-byte state, imported from the modules that own them rather than
#: rebuilt: a holder made, or compared, any other way would test a
@@ -866,7 +867,7 @@ def test_a_checkout_whose_git_dir_deinit_absorbed_is_put_back(
cwd=holder).stdout.strip()
rmtree(mount)
rmtree(holder / ".git" / "modules" / "members" / MEMBER)
- git(*FILE_PROTOCOL, "clone", "-q", url, str(mount), cwd=tmp_path)
+ cloned_just_pushed(Path(url), mount, recurse=False)
assert (mount / ".git").is_dir(), "premise: an embedded .git directory"
embedded = Path(git("rev-parse", "--absolute-git-dir", cwd=mount
).stdout.strip())
diff --git a/tests/test_family_bump_to_a_tag.py b/tests/test_family_bump_to_a_tag.py
index 0788db97..c02bdbe8 100644
--- a/tests/test_family_bump_to_a_tag.py
+++ b/tests/test_family_bump_to_a_tag.py
@@ -38,6 +38,7 @@
import pytest
+from conftest import cloned_just_pushed
#: The family fixture and its names, from the module that owns them.
from test_family import ORG, family, manifest, validate # noqa: F401
from test_family_add_refusals import holder, offline, refusal # noqa: F401
@@ -144,8 +145,8 @@ def test_a_release_tag_only_the_fetch_brings_is_refused_after_the_fetch(
# A PRIVATE COPY of the member's remote, which the mount is pointed at:
# the fixture's own remote is read here and never written to.
remote = tmp_path / f"{MEMBER}-private.git"
- raw(tmp_path, env, "clone", "-q", "--bare", "--",
- str(family["remotes"] / f"{MEMBER}.git"), str(remote))
+ cloned_just_pushed(family["remotes"] / f"{MEMBER}.git", remote,
+ bare=True, env=env)
work = tmp_path / f"{MEMBER}-release"
raw(tmp_path, env, "clone", "-q", "--", str(remote), str(work))
(work / "RELEASE.md").write_bytes(b"release 2\n")
diff --git a/tests/test_family_dry_run.py b/tests/test_family_dry_run.py
index b8daf51a..b501facd 100644
--- a/tests/test_family_dry_run.py
+++ b/tests/test_family_dry_run.py
@@ -49,7 +49,7 @@
import pytest
-from conftest import git, run_script
+from conftest import cloned_just_pushed, git, run_script
#: The trailer lines a lane's run lands, from the file that owns them.
from test_commit_trailers import TRAILERS, trailers_of
#: The family fixture, its names and the offline environment, imported from
@@ -1046,8 +1046,7 @@ def a_renamed_copy_of_a_member(root: Path) -> Path:
f"submodule.members/{PINNED}.url", cwd=root).stdout.strip()
remotes = root.parent / "renamed-remotes"
remotes.mkdir()
- git("clone", "-q", "--bare", source, str(remotes / f"{RENAMED}.git"),
- cwd=root.parent)
+ cloned_just_pushed(Path(source), remotes / f"{RENAMED}.git", bare=True)
return remotes
diff --git a/tests/test_family_member_name.py b/tests/test_family_member_name.py
index 6263925b..50425ca5 100644
--- a/tests/test_family_member_name.py
+++ b/tests/test_family_member_name.py
@@ -47,7 +47,7 @@
import pytest
-from conftest import REPO, git, run_script
+from conftest import REPO, cloned_just_pushed, git, run_script
from test_family import ( # noqa: F401
FAMILY, ORG, SPARE, family, family_module, manifest, validate,
)
@@ -92,8 +92,8 @@ def remotes_for(family, tmp_path: Path) -> Path:
remotes = tmp_path / "remotes"
remotes.mkdir()
for project in PROJECTS:
- git("clone", "-q", "--bare", str(family["remotes"] / f"{SPARE}.git"),
- str(remotes / f"{project}.git"), cwd=tmp_path)
+ cloned_just_pushed(family["remotes"] / f"{SPARE}.git",
+ remotes / f"{project}.git", bare=True)
return remotes
diff --git a/tests/test_family_member_path.py b/tests/test_family_member_path.py
index f5c2bd2d..8bd2d266 100644
--- a/tests/test_family_member_path.py
+++ b/tests/test_family_member_path.py
@@ -49,7 +49,7 @@
import pytest
-from conftest import git, run_script
+from conftest import cloned_just_pushed, git, run_script
#: The family fixture, its names and the offline environment, imported from
#: the modules that own them rather than rebuilt: a holder made any other way
#: would test a different holder.
@@ -254,8 +254,8 @@ def test_bump_refuses_a_row_naming_a_directory_that_is_really_there(
as a gitlink of its own. The refusal comes before all of that: the clone
stays where it was."""
env = isolated(offline, tmp_path)
- git("clone", "-q", str(family["remotes"] / f"{MEMBER}.git"), GLOB,
- cwd=holder)
+ cloned_just_pushed(family["remotes"] / f"{MEMBER}.git", holder / GLOB,
+ recurse=False)
star = holder / "members" / "*"
assert (star / ".git").exists(), "premise: the literal checkout is there"
tip = raw(star, env, "rev-parse", "HEAD").stdout
diff --git a/tests/test_family_printed_quoting.py b/tests/test_family_printed_quoting.py
index c1af5b64..53a105bc 100644
--- a/tests/test_family_printed_quoting.py
+++ b/tests/test_family_printed_quoting.py
@@ -43,7 +43,7 @@
import pytest
-from conftest import REPO, git, run_script
+from conftest import NO_BYTECODE, REPO, git, run_script
#: The family fixture and its names, imported from the module that owns them.
from test_family import ( # noqa: F401
FAMILY, NAME, ORG, SPARE, family, manifest, validate,
@@ -372,7 +372,7 @@ def test_the_siblings_mv_lines_paste_whole_and_move_the_holder(family,
move the holder to `/InkRouter/InkRouter`."""
holder = copied_holder(family, tmp_path / folder)
result = run_script(FAMILY, "siblings", "--family-root", str(holder),
- "--dry-run")
+ "--dry-run", env=NO_BYTECODE)
assert result.returncode == 0, result.stdout + result.stderr
assert "NOTHING WAS MOVED" in result.stderr
assert "in a POSIX shell (Git Bash" in result.stderr
diff --git a/tests/test_leg_path_yaml_scalars.py b/tests/test_leg_path_yaml_scalars.py
index 110e3993..523cd352 100644
--- a/tests/test_leg_path_yaml_scalars.py
+++ b/tests/test_leg_path_yaml_scalars.py
@@ -47,7 +47,7 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, ORG, REPO, SCAFFOLD, git,
+from conftest import (ADOPT, ORG, REPO, SCAFFOLD, cloned_just_pushed, git,
make_source_repo, resolve, run_script, write_plan)
sys.path.insert(0, str(REPO / "scripts"))
@@ -519,11 +519,8 @@ def test_a_lookalike_scaffolds_clones_and_bootstraps(tmp_path):
"--local-remote-dir", str(tmp_path / REMOTES),
"--work-dir", str(tmp_path / WORK))
assert result.returncode == 0, result.stderr + result.stdout
- clone = tmp_path / "clone"
- cloned = git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(tmp_path / REMOTES / "Northwind.git"), str(clone),
- cwd=tmp_path, check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(tmp_path / REMOTES / "Northwind.git",
+ tmp_path / "clone")
manifest = load_yaml(clone / "project.yaml")
assert [leg["path"] for leg in manifest["legs"]] == [".", spec_path,
code_path]
diff --git a/tests/test_scaffold_e2e.py b/tests/test_scaffold_e2e.py
index 45d7ce17..bbcee142 100644
--- a/tests/test_scaffold_e2e.py
+++ b/tests/test_scaffold_e2e.py
@@ -11,8 +11,9 @@
import re
import subprocess
-from conftest import (FILE_PROTOCOL, ORG, PROJECT, REPO, SCAFFOLD,
- WINDOWS_SKIP, assert_read_through_git, git, run_script)
+from conftest import (ORG, PROJECT, REPO, SCAFFOLD, WINDOWS_SKIP,
+ assert_read_through_git, cloned_just_pushed, git,
+ run_script)
DEGRADE_LINE = "authority is not wallet-carried in this org"
@@ -304,12 +305,8 @@ def test_the_legs_are_submodules_at_the_declared_paths(project):
def test_a_recursive_clone_leaves_the_legs_detached(scaffolded, tmp_path):
"""The submodule tax bootstrap exists to pay, demonstrated."""
- target = tmp_path / "fresh"
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(scaffolded["remotes"] / f"{PROJECT}.git"), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ target = cloned_just_pushed(scaffolded["remotes"] / f"{PROJECT}.git",
+ tmp_path / "fresh")
head = git("rev-parse", "--abbrev-ref", "HEAD", cwd=target / "spec").stdout
assert head.strip() == "HEAD", "expected a detached HEAD before bootstrap"
@@ -903,12 +900,7 @@ def test_a_bad_visibility_is_refused_by_argparse(tmp_path):
def clone_with_autocrlf(source, target):
"""A clone made the way Git for Windows' default makes one."""
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, *AUTOCRLF, "clone", "-q",
- "--recurse-submodules", str(source), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
- return target
+ return cloned_just_pushed(source, target, config=AUTOCRLF)
def test_the_scaffolded_root_says_what_its_bytes_are(project, monkeypatch):
@@ -959,12 +951,8 @@ def test_without_the_attributes_file_the_same_clone_goes_red(scaffolded, tmp_pat
the file above stops. Without this assertion the green test could be green
because `core.autocrlf` did nothing at all.
"""
- plain = tmp_path / "plain"
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(scaffolded["remotes"] / f"{PROJECT}.git"), str(plain)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ plain = cloned_just_pushed(scaffolded["remotes"] / f"{PROJECT}.git",
+ tmp_path / "plain")
git("rm", "-q", "--", ".gitattributes", cwd=plain)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-q", "-m",
"Drop the attributes file", "--", ".gitattributes", cwd=plain)
diff --git a/tests/test_scaffold_ignored_shape_files.py b/tests/test_scaffold_ignored_shape_files.py
index 7e055c28..2da364d2 100644
--- a/tests/test_scaffold_ignored_shape_files.py
+++ b/tests/test_scaffold_ignored_shape_files.py
@@ -34,8 +34,9 @@
import pytest
-from conftest import (FILE_PROTOCOL, ORG, PROJECT, REPO, SCAFFOLD,
- clear_ambient_pin_sources, git, run_script)
+from conftest import (ORG, PROJECT, REPO, SCAFFOLD,
+ clear_ambient_pin_sources, cloned_just_pushed, git,
+ run_script)
sys.path.insert(0, str(REPO / "scripts"))
import shape_materialize # noqa: E402
@@ -193,10 +194,8 @@ def test_the_first_commits_hold_every_file_the_scaffold_wrote(
def test_the_scaffold_bootstraps_under_the_same_excludes(excluded_scaffold,
tmp_path):
env = excluded_scaffold.env
- clone = tmp_path / PROJECT
- git_under(env, *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(excluded_scaffold.remotes / f"{PROJECT}.git"), str(clone),
- cwd=tmp_path)
+ clone = cloned_just_pushed(excluded_scaffold.remotes / f"{PROJECT}.git",
+ tmp_path / PROJECT, env=env)
result = run_script(clone / "scripts" / "bootstrap.py", cwd=clone, env=env)
assert result.returncode == 0, result.stderr + result.stdout
assert "bootstrap ok" in result.stdout
@@ -236,9 +235,8 @@ def test_every_file_the_holder_wrote_is_in_its_first_commit(excluded_family):
def test_the_holder_bootstraps_under_the_same_excludes(excluded_family,
tmp_path):
env = excluded_family.env
- clone = tmp_path / HOLDER
- git_under(env, "clone", "-q", str(excluded_family.bare), str(clone),
- cwd=tmp_path)
+ clone = cloned_just_pushed(excluded_family.bare, tmp_path / HOLDER,
+ recurse=False, env=env)
result = run_script(clone / "scripts" / "bootstrap.py", cwd=clone, env=env)
assert result.returncode == 0, result.stderr + result.stdout
assert "family bootstrap ok" in result.stdout
diff --git a/tests/test_scaffold_leg_paths.py b/tests/test_scaffold_leg_paths.py
index 3db6f97b..6148584f 100644
--- a/tests/test_scaffold_leg_paths.py
+++ b/tests/test_scaffold_leg_paths.py
@@ -28,8 +28,8 @@
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, ORG, REPO, SCAFFOLD, WINDOWS_SKIP,
- git, run_script)
+from conftest import (ADOPT, ORG, REPO, SCAFFOLD, WINDOWS_SKIP,
+ cloned_just_pushed, git, run_script)
sys.path.insert(0, str(REPO / "scripts"))
import shape_materialize # noqa: E402
@@ -270,10 +270,7 @@ def test_canonical_paths_still_scaffold_and_a_clone_survives_the_work_dir(
"a registration still points at the scaffold's work directory")
(tmp_path / WORK).rename(tmp_path / "work.moved")
- clone = tmp_path / "clone"
- cloned = git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(assembly), str(clone), cwd=tmp_path, check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(assembly, tmp_path / "clone")
for path in paths:
assert any((clone / path).iterdir()), f"{path} was not checked out"
diff --git a/tests/test_scaffold_manifest_values.py b/tests/test_scaffold_manifest_values.py
index f36a9bf5..36bbe81f 100644
--- a/tests/test_scaffold_manifest_values.py
+++ b/tests/test_scaffold_manifest_values.py
@@ -53,8 +53,8 @@
import pytest
-from conftest import (FILE_PROTOCOL, ORG, REPO, SCAFFOLD, WINDOWS_SKIP, git,
- run_script)
+from conftest import (ORG, REPO, SCAFFOLD, WINDOWS_SKIP, cloned_just_pushed,
+ git, run_script)
sys.path.insert(0, str(REPO / "scripts"))
import shape_materialize # noqa: E402
@@ -456,11 +456,8 @@ def test_what_is_typed_is_what_the_project_holds_and_bootstrap_agrees(
result = scaffold(tmp_path, f"--tracking-branch={branch}",
f"--id={project_id}", work=True)
assert result.returncode == 0, result.stderr + result.stdout
- clone = tmp_path / "Atlas"
- cloned = git(*FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(tmp_path / REMOTES / "Atlas.git"), str(clone),
- cwd=tmp_path, check=False)
- assert cloned.returncode == 0, cloned.stderr
+ clone = cloned_just_pushed(tmp_path / REMOTES / "Atlas.git",
+ tmp_path / "Atlas")
manifest = load_yaml(clone / "project.yaml")
assert manifest["id"] == project_id
assert manifest["tracking_branch"] == branch
diff --git a/tests/test_scaffold_mount_ignored.py b/tests/test_scaffold_mount_ignored.py
index b3d5a6d1..9b00c496 100644
--- a/tests/test_scaffold_mount_ignored.py
+++ b/tests/test_scaffold_mount_ignored.py
@@ -64,7 +64,7 @@
import pytest
-from conftest import (FILE_PROTOCOL, ORG, SCAFFOLD, git, run_script)
+from conftest import (ORG, SCAFFOLD, cloned_just_pushed, git, run_script)
from test_scaffold_ignored_shape_files import excluding, git_under
PROJECT = "Northwind"
@@ -241,9 +241,7 @@ def clone_and_check(root: Path, env: dict, tmp_path: Path, *mounts: str):
its tracking branch AT the pin, so it fails where a gitlink and a pin
disagree and `validate-pins.py` had not looked at the checkout.
"""
- clone = tmp_path / PROJECT
- git_under(env, *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(root), str(clone), cwd=tmp_path)
+ clone = cloned_just_pushed(root, tmp_path / PROJECT, env=env)
for path in mounts:
assert any((clone / path).iterdir()), f"{path} was not checked out"
for validator, ok in (("validate-manifest.py", "manifest ok"),
diff --git a/tests/test_shape_doctor.py b/tests/test_shape_doctor.py
index 7a420fdf..d2ca4ed2 100644
--- a/tests/test_shape_doctor.py
+++ b/tests/test_shape_doctor.py
@@ -43,7 +43,8 @@
import pytest
from conftest import (FILE_PROTOCOL, REPO, WINDOWS_SKIP,
- assert_read_through_git, clone_just_pushed, git, rmtree,
+ assert_read_through_git, clone_just_pushed,
+ cloned_just_pushed, git, load_without_bytecode, rmtree,
run_script)
from gh_guard import PROBES as GH_PROBES
@@ -476,16 +477,10 @@ def siblings():
scripts = REPO / "templates" / "family-root" / "scripts"
saved_path, saved_module = list(sys.path), sys.modules.get("bootstrap")
try:
- spec = importlib.util.spec_from_file_location(
- "doctor_family_bootstrap", scripts / "bootstrap.py")
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module)
- sys.modules["bootstrap"] = module
- spec = importlib.util.spec_from_file_location(
- "doctor_family_siblings", scripts / "siblings.py")
- loaded = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(loaded)
- yield loaded
+ sys.modules["bootstrap"] = load_without_bytecode(
+ scripts / "bootstrap.py", "doctor_family_bootstrap")
+ yield load_without_bytecode(scripts / "siblings.py",
+ "doctor_family_siblings")
finally:
sys.path[:] = saved_path
if saved_module is None:
@@ -3080,15 +3075,17 @@ def member_beside(holder: dict, tmp_path: Path) -> tuple[Path, Path]:
itself refuses to answer any question about (`fatal: not a git
repository`). It passed only because the row asked nothing of origin
(#146). `git clone` from the url the holder mounts the member from is
- both a real clone and the exact thing `siblings.py` does.
+ both a real clone and the exact thing `siblings.py` does, but for the
+ one flag of #544: the member's remote was just pushed, so the clone is
+ read through git (`cloned_just_pushed`) and not through its files.
"""
base = tmp_path / "fam"
base.mkdir()
root = base / FAMILY_NAME
shutil.copytree(holder["root"], root, symlinks=True)
remote = holder["base"] / "remotes" / f"{FAMILY_MEMBER}.git"
- git("clone", "-q", str(remote), str(base / FAMILY_MEMBER), cwd=base)
- return root, base / FAMILY_MEMBER
+ return root, cloned_just_pushed(remote, base / FAMILY_MEMBER,
+ recurse=False)
def test_a_real_working_clone_beside_the_holder_is_counted(standard, holder,
diff --git a/tests/test_shape_pin_files_not_a_list.py b/tests/test_shape_pin_files_not_a_list.py
index 18459c54..0c931507 100644
--- a/tests/test_shape_pin_files_not_a_list.py
+++ b/tests/test_shape_pin_files_not_a_list.py
@@ -19,10 +19,11 @@
except `files: abc`, which is a finding per character or a refusal that names
the wrong thing.
-The doctor's VERDICT for a root pin `read_pin` refuses is #237's business
-(PR #267 moves it from CANNOT ANSWER, exit 3, to INVALID, exit 1), so the
-doctor's cases assert the ROW and that a verdict was printed at all, never
-which one.
+The doctor's VERDICT for a root pin `read_pin` refuses is INVALID, exit 1
+(#237, moved there by #267 from CANNOT ANSWER, exit 3, which is the failure
+this checkout has when it cannot ask, and not a statement about the pin). The
+doctor's cases assert that exit, that verdict and the ROW, so a doctor that
+answered CANNOT ANSWER for `files: abc` would fail here.
NOTHING HERE REACHES A NETWORK: the scaffold and the holder use
`--local-remote-dir`, and `read_pin` refuses before `--upstream` is opened.
@@ -66,11 +67,6 @@
pytest.param("abc", "'abc'", id="files-abc"),
]
-#: Every verdict the doctor's text report can end on.
-VERDICT = re.compile(r"^(COMPLIANT|DRIFTED|MISPLACED|INVALID|NOT A SHAPE ROOT"
- r"|CANNOT ANSWER)\b")
-
-
def commit_files(root, files_text: str, message: str) -> None:
"""Replace the pin's `files:` rows by `files_text` and commit it.
@@ -146,10 +142,12 @@ def assert_one_shape_pin_row_finding(result, shown: str) -> None:
def assert_shape_currency_row(result, shown: str) -> dict:
- """The doctor printed a report, and its shape-currency row names the
- refusal `read_pin` raised for the pin."""
+ """The doctor printed an INVALID report, exit 1, and its shape-currency
+ row names the refusal `read_pin` raised for the pin."""
no_traceback(result)
- assert result.returncode in (1, 3), result.stdout + result.stderr
+ assert result.returncode == 1, result.stdout + result.stderr
+ verdict = json.loads(result.stdout)["verdict"]
+ assert verdict.startswith("INVALID"), verdict
rows = rows_of(result)
row = rows["shape-currency"]
assert row["status"] == "FINDING", row
@@ -259,9 +257,9 @@ def test_the_doctor_reports_it_in_the_shape_currency_row(project, value,
plain = run_script(DOCTOR, "--root", str(project))
no_traceback(plain)
assert f"`files:` is {shown}, not a list" in plain.stdout, plain.stdout
+ assert plain.returncode == 1, plain.stdout + plain.stderr
last = [line for line in plain.stdout.splitlines() if line.strip()][-1]
- assert VERDICT.match(last), plain.stdout
- assert not last.startswith("COMPLIANT"), last
+ assert last.startswith("INVALID"), plain.stdout
@pytest.mark.parametrize("value, shown", NOT_A_LIST)
diff --git a/tests/test_template_text_placeholders.py b/tests/test_template_text_placeholders.py
index 9a2f7ba2..6327e259 100644
--- a/tests/test_template_text_placeholders.py
+++ b/tests/test_template_text_placeholders.py
@@ -38,13 +38,12 @@
import re
import shutil
-import subprocess
import sys
from pathlib import Path
import pytest
-from conftest import (ADOPT, FILE_PROTOCOL, REPO, SCAFFOLD, git,
+from conftest import (ADOPT, REPO, SCAFFOLD, cloned_just_pushed, git,
make_source_repo, run_script)
from test_family import family_module
@@ -155,15 +154,6 @@ def heads(bare: Path) -> list[str]:
cwd=bare).stdout.split()
-def clone(remote: Path, target: Path, *extra: str) -> Path:
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- *extra, str(remote), str(target)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
- return target
-
-
def assert_the_root_reads_what_it_was_given(manifest: dict, pins: dict,
project_id: str, branch: str,
spec: str, code: str) -> None:
@@ -225,7 +215,8 @@ def test_a_scaffold_reads_back_every_value_it_was_given(tmp_path, project_id,
for name in (PROJECT, f"{PROJECT}-spec", f"{PROJECT}-code"):
assert heads(remotes / f"{name}.git") == [branch], name
- root = clone(remotes / f"{PROJECT}.git", tmp_path / "clone" / PROJECT)
+ root = cloned_just_pushed(remotes / f"{PROJECT}.git",
+ tmp_path / "clone" / PROJECT)
assert_the_clone_works(root, branch, spec, code)
# `bump-leg.py` reads the branch and the leg's path out of the same
@@ -328,7 +319,8 @@ def show(rel: str) -> str:
for leg in ("spec", "code"):
assert heads(remotes / f"{ADOPTED}-{leg}.git") == [branch], leg
- root = clone(source, tmp_path / "clone" / ADOPTED, "-b", ADOPT_BRANCH)
+ root = cloned_just_pushed(source, tmp_path / "clone" / ADOPTED,
+ ADOPT_BRANCH)
assert_the_clone_works(root, branch, spec, code)
diff --git a/tests/test_update_shape.py b/tests/test_update_shape.py
index 7cb6acab..36490f49 100644
--- a/tests/test_update_shape.py
+++ b/tests/test_update_shape.py
@@ -25,7 +25,7 @@
import pytest
-from conftest import (FILE_PROTOCOL, ORG, REPO, WINDOWS_SKIP, git,
+from conftest import (ORG, REPO, WINDOWS_SKIP, cloned_just_pushed, git,
run_script)
#: The trailer helpers and the two lines a lane's run lands, imported from
#: the file that owns those rules rather than retyped — see
@@ -109,12 +109,8 @@ def scaffold_upstream_and_project(tmp_path_factory, subdir: str,
"--work-dir", str(base / "work"), *scaffold_args)
assert result.returncode == 0, result.stderr + result.stdout
- clone = base / "clone" / PROJECT
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(base / "remotes" / f"{PROJECT}.git"), str(clone)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ clone = cloned_just_pushed(base / "remotes" / f"{PROJECT}.git",
+ base / "clone" / PROJECT)
return base, upstream, commit_a, clone
diff --git a/tests/test_update_shape_add.py b/tests/test_update_shape_add.py
index 1aaa97b6..9f3a5053 100644
--- a/tests/test_update_shape_add.py
+++ b/tests/test_update_shape_add.py
@@ -42,7 +42,7 @@
import pytest
-from conftest import FILE_PROTOCOL, ORG, REPO, git, run_script
+from conftest import ORG, REPO, cloned_just_pushed, git, run_script
sys.path.insert(0, str(REPO / "scripts"))
from repo_shape import file_sha256, load_yaml # noqa: E402
@@ -186,12 +186,8 @@ def standard(tmp_path_factory) -> dict:
*SCAFFOLD_ARGS, "--local-remote-dir", str(base / "remotes"),
"--work-dir", str(base / "work"))
assert result.returncode == 0, result.stderr + result.stdout
- clone = base / "clone" / PROJECT
- proc = subprocess.run(
- ["git", *FILE_PROTOCOL, "clone", "-q", "--recurse-submodules",
- str(base / "remotes" / f"{PROJECT}.git"), str(clone)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ clone = cloned_just_pushed(base / "remotes" / f"{PROJECT}.git",
+ base / "clone" / PROJECT)
created = run_script(
upstream / "scripts" / "family.py", "init", "--org", FAMILY,
diff --git a/tests/test_validator_child_encoding.py b/tests/test_validator_child_encoding.py
index 2cc826c6..04a754fd 100644
--- a/tests/test_validator_child_encoding.py
+++ b/tests/test_validator_child_encoding.py
@@ -48,7 +48,7 @@
import pytest
-from conftest import PROJECT, REPO, git
+from conftest import PROJECT, REPO, cloned_just_pushed, git
from test_stdout_encoding import NAME, child_environ, run_tool_bytes
from test_update_shape import (CHANGED, CHANGED_SOURCE, UPSTREAM_FIX_LINE,
commit_upstream_changes,
@@ -131,12 +131,9 @@ def rename_to_a_name_no_western_code_page_holds(project: Path) -> str:
@pytest.fixture(scope="module")
def advanced(scaffolded, tmp_path_factory) -> str:
"""A real new commit on the spec leg's bare remote; returns its sha."""
- work = tmp_path_factory.mktemp("validator-child-spec") / f"{PROJECT}-spec"
- proc = subprocess.run(
- ["git", "clone", "-q",
- str(scaffolded["remotes"] / f"{PROJECT}-spec.git"), str(work)],
- capture_output=True, text=True, check=False)
- assert proc.returncode == 0, proc.stderr
+ work = cloned_just_pushed(
+ scaffolded["remotes"] / f"{PROJECT}-spec.git",
+ tmp_path_factory.mktemp("validator-child-spec") / f"{PROJECT}-spec")
(work / "NEW-CONTRACT.md").write_text("# a contract the leg gained\n")
git("add", "--", "NEW-CONTRACT.md", cwd=work)
git("-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm",
diff --git a/tests/test_windows_paths.py b/tests/test_windows_paths.py
index 088ed3b0..0f5bf35d 100644
--- a/tests/test_windows_paths.py
+++ b/tests/test_windows_paths.py
@@ -34,7 +34,7 @@
import pytest
-from conftest import REPO
+from conftest import REPO, load_without_bytecode
sys.path.insert(0, str(REPO / "scripts"))
from repo_shape import ( # noqa: E402
@@ -80,16 +80,10 @@ def siblings():
scripts = REPO / "templates" / "family-root" / "scripts"
saved_path, saved_module = list(sys.path), sys.modules.get("bootstrap")
try:
- spec = importlib.util.spec_from_file_location(
- "family_root_bootstrap", scripts / "bootstrap.py")
- module = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(module)
- sys.modules["bootstrap"] = module
- spec = importlib.util.spec_from_file_location(
- "family_root_siblings", scripts / "siblings.py")
- loaded = importlib.util.module_from_spec(spec)
- spec.loader.exec_module(loaded)
- yield loaded
+ sys.modules["bootstrap"] = load_without_bytecode(
+ scripts / "bootstrap.py", "family_root_bootstrap")
+ yield load_without_bytecode(scripts / "siblings.py",
+ "family_root_siblings")
finally:
sys.path[:] = saved_path
if saved_module is None: