diff --git a/hermes_cli/source_completion.py b/hermes_cli/source_completion.py index 8dbbb0b962..518f892154 100644 --- a/hermes_cli/source_completion.py +++ b/hermes_cli/source_completion.py @@ -60,11 +60,9 @@ def complete_source_checkout( completion_message=completion_message, ) if complete: - from hermes_cli.source_stamp import refresh_bootstrap_receipt, write_source_stamp + from hermes_cli.source_stamp import write_source_stamp - stamp = write_source_stamp(root) - if stamp is not None: - refresh_bootstrap_receipt(root, stamp) + write_source_stamp(root) return complete diff --git a/hermes_cli/source_stamp.py b/hermes_cli/source_stamp.py index c34b6d33db..a62a12f327 100644 --- a/hermes_cli/source_stamp.py +++ b/hermes_cli/source_stamp.py @@ -60,6 +60,9 @@ def write_source_stamp(root: Path) -> dict | None: with suppress(OSError): os.unlink(tmp_name) _reset_version_info_cache() + # Every path that publishes a new checkout identity (completion handoff, the PM + # updater's finish, boot-time adoption) moves the installers' receipt with it. + refresh_bootstrap_receipt(root, stamp) return stamp diff --git a/tests/hermes_cli/test_source_completion_stamp.py b/tests/hermes_cli/test_source_completion_stamp.py index 6556ffdd56..28aa05afe3 100644 --- a/tests/hermes_cli/test_source_completion_stamp.py +++ b/tests/hermes_cli/test_source_completion_stamp.py @@ -7,6 +7,7 @@ import subprocess import sys from hermes_cli.source_completion import complete_source_checkout +from hermes_cli.source_stamp import write_source_stamp def _repo(tmp_path: Path) -> Path: @@ -61,7 +62,9 @@ def _verify_bootstrap_receipt(root: Path) -> subprocess.CompletedProcess: "--repo", str(root)], capture_output=True, text=True, encoding="utf-8") -def test_update_completion_moves_an_installer_receipt_to_the_updated_head(tmp_path, monkeypatch): +def test_publishing_checkout_identity_moves_an_installer_receipt_to_head(tmp_path): + # write_source_stamp is the one seam: the completion handoff, the PM updater's finish and + # boot-time adoption all publish identity through it, and the receipt must follow every one. root = _repo(tmp_path) release = subprocess.run(["git", "rev-parse", "HEAD"], cwd=root, capture_output=True, text=True, check=True).stdout.strip() branch = subprocess.run(["git", "branch", "--show-current"], cwd=root, capture_output=True, text=True, check=True).stdout.strip() @@ -72,17 +75,14 @@ def test_update_completion_moves_an_installer_receipt_to_the_updated_head(tmp_pa subprocess.run(["git", "commit", "-q", "--allow-empty", "-m", "update"], cwd=root, check=True, capture_output=True, env={**os.environ, "GIT_AUTHOR_NAME": "t", "GIT_AUTHOR_EMAIL": "t@example.invalid", "GIT_COMMITTER_NAME": "t", "GIT_COMMITTER_EMAIL": "t@example.invalid"}) - _completion_dependencies(monkeypatch, lambda **_kwargs: True) - - assert complete_source_checkout(root, desktop=False, assume_yes=True) + assert write_source_stamp(root) is not None result = _verify_bootstrap_receipt(root) assert result.returncode == 0, result.stdout + result.stderr -def test_completion_never_invents_an_installer_receipt(tmp_path, monkeypatch): +def test_publishing_checkout_identity_never_invents_an_installer_receipt(tmp_path): # The receipt's presence is what marks a script install; a manual clone stays one. root = _repo(tmp_path) - _completion_dependencies(monkeypatch, lambda **_kwargs: True) - assert complete_source_checkout(root, desktop=False, assume_yes=True) + assert write_source_stamp(root) is not None assert not (root / ".hermes-bootstrap-complete").exists()