From c07973aa81b382168f0db68d2e1420884515827e Mon Sep 17 00:00:00 2001 From: Bartok Date: Wed, 22 Jul 2026 13:40:44 -0500 Subject: [PATCH] chore(gitignore): ignore installer .install_method stamp (salvage of #54855) (#67364) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * chore(gitignore): ignore installer .install_method stamp Salvage of #54855 by @drissman — rebased onto current main with root-scoped rule and sister-marker comments alongside .update-incomplete. Closes #66189 Root cause: scripts/install.sh writes /.install_method but git did not ignore it, so managed checkouts show ?? .install_method and hermes update may autostash the untracked marker. Fix: add /.install_method to .gitignore (repo-root only). Verification: git check-ignore -v .install_method * test(update): assert .install_method survives update autostash (#66189) Add hermetic regression mirroring the .hermes-bootstrap-complete test: adopt the real .gitignore, drop the installer .install_method stamp, run the exact 'git stash push --include-untracked' the updater uses, and assert the marker is neither swept nor reported dirty. Requested by hermes-sweeper review on #67364. --- .gitignore | 5 +++ tests/hermes_cli/test_update_autostash.py | 50 +++++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/.gitignore b/.gitignore index 5b3c3b1c15720..2948963310456 100644 --- a/.gitignore +++ b/.gitignore @@ -150,6 +150,11 @@ docs/superpowers/* .update-incomplete .update-incomplete.lock +# Installer-written method stamp in the managed checkout root (scripts/install.sh). +# Runtime metadata only — never a code change. Ignore so `git status` stays clean +# and `hermes update`'s untracked autostash does not treat it as a local edit (#66189 / #54855). +/.install_method + # Tool Search live-test harness output — non-deterministic model transcripts, # regenerated by scripts/tool_search_livetest.py. Never an artifact of the repo. scripts/out/ diff --git a/tests/hermes_cli/test_update_autostash.py b/tests/hermes_cli/test_update_autostash.py index be1a5f1acf4c0..5146320482187 100644 --- a/tests/hermes_cli/test_update_autostash.py +++ b/tests/hermes_cli/test_update_autostash.py @@ -901,3 +901,53 @@ def test_bootstrap_marker_not_autostashed_by_update(tmp_path): ["git", "status", "--porcelain"], cwd=tmp_path, capture_output=True, text=True ).stdout assert ".hermes-bootstrap-complete" not in status + + +def test_install_method_marker_not_autostashed_by_update(tmp_path): + """#66189: the installer ``.install_method`` stamp must be git-ignored so + ``hermes update``'s ``git stash push --include-untracked`` does not sweep it + into an autostash on every run. + + ``scripts/install.sh`` writes ``$INSTALL_DIR/.install_method`` as runtime + metadata; it is a sibling of ``.hermes-bootstrap-complete`` / + ``.update-incomplete`` and must be ignored the same way. Behavioral + + hermetic: adopt the project's real ``.gitignore`` (the contract under test), + drop the marker, and confirm the exact stash invocation the updater uses + leaves it untouched. + """ + import shutil + import subprocess + + if shutil.which("git") is None: + pytest.skip("git not available") + + repo_gitignore = Path(hermes_main.__file__).resolve().parents[1] / ".gitignore" + + def git(*args): + return subprocess.run( + ["git", *args], cwd=tmp_path, capture_output=True, text=True, check=True + ) + + git("init", "-q") + git("config", "user.email", "t@example.com") + git("config", "user.name", "t") + (tmp_path / ".gitignore").write_text(repo_gitignore.read_text()) + (tmp_path / "tracked.txt").write_text("x\n") + git("add", "-A") + git("commit", "-qm", "init") + + marker = tmp_path / ".install_method" + marker.write_text("managed\n") + + # Exact flags used by hermes update (hermes_cli/main.py). + git("stash", "push", "--include-untracked", "-m", "hermes-update-autostash") + + assert marker.exists(), ( + ".install_method was swept into the update autostash — it must be listed " + "in .gitignore so `git stash -u` skips it (#66189)." + ) + # It must not even register as a dirty/untracked change. + status = subprocess.run( + ["git", "status", "--porcelain"], cwd=tmp_path, capture_output=True, text=True + ).stdout + assert ".install_method" not in status