mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-06 10:59:59 +00:00
170 lines
6.3 KiB
Python
170 lines
6.3 KiB
Python
"""Contract of ``make worktree-down``.
|
|
|
|
Two promises are pinned here because both were broken. A teardown must release
|
|
the branch even when the agent sandbox pins the metadata dir as a read-only bind
|
|
mount: ``git worktree remove`` then dies with EBUSY after it has already deleted
|
|
the checkout, which used to abort the whole target with rc 255 even though the
|
|
branch was in fact free. And a status probe that fails must never be read as
|
|
"clean": on a broken ``.git`` link ``git status`` exits non-zero with empty
|
|
output, which used to satisfy the guard, tear down the compose stack, and only
|
|
then die at ``git worktree remove`` with an unexplained rc 128.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import stat
|
|
import subprocess
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from utils.cache.files import PROJECT_ROOT
|
|
|
|
WORKTREE_SCRIPTS = PROJECT_ROOT / "scripts" / "system" / "worktree"
|
|
GIT_IDENTITY = {
|
|
"GIT_AUTHOR_NAME": "test",
|
|
"GIT_AUTHOR_EMAIL": "test@example.invalid",
|
|
"GIT_COMMITTER_NAME": "test",
|
|
"GIT_COMMITTER_EMAIL": "test@example.invalid",
|
|
}
|
|
GIT_REPO_SCOPE = (
|
|
"GIT_DIR",
|
|
"GIT_WORK_TREE",
|
|
"GIT_INDEX_FILE",
|
|
"GIT_COMMON_DIR",
|
|
"GIT_PREFIX",
|
|
"GIT_OBJECT_DIRECTORY",
|
|
"GIT_ALTERNATE_OBJECT_DIRECTORIES",
|
|
)
|
|
NEEDS_UNPRIVILEGED = "clearing the write bit does not restrain root"
|
|
|
|
|
|
class WorktreeDownFixture:
|
|
"""A throwaway repo with the worktree scripts copied in and branch ``feat``."""
|
|
|
|
def __init__(self, tmp: str) -> None:
|
|
self.root = Path(tmp)
|
|
self.repo = self.root / "repo"
|
|
self.base = self.root / "worktrees"
|
|
self.checkout = self.base / "feat"
|
|
inherited = {k: v for k, v in os.environ.items() if k not in GIT_REPO_SCOPE}
|
|
self.env = {**inherited, **GIT_IDENTITY}
|
|
|
|
scripts = self.repo / "scripts" / "system" / "worktree"
|
|
scripts.parent.mkdir(parents=True)
|
|
shutil.copytree(WORKTREE_SCRIPTS, scripts)
|
|
self.script = scripts / "down.sh"
|
|
|
|
self.git("init", "-b", "main")
|
|
(self.repo / "Makefile").write_text("compose-down:\n\t@true\n")
|
|
self.git("add", "-A")
|
|
self.git("commit", "-m", "init")
|
|
self.git("branch", "feat")
|
|
self.base.mkdir()
|
|
self.git("worktree", "add", str(self.checkout), "feat")
|
|
|
|
def git(self, *args: str) -> subprocess.CompletedProcess:
|
|
return subprocess.run(
|
|
["git", *args],
|
|
cwd=self.repo,
|
|
env=self.env,
|
|
capture_output=True,
|
|
text=True,
|
|
check=True,
|
|
)
|
|
|
|
def meta_dir(self) -> Path:
|
|
return self.repo / ".git" / "worktrees" / "feat"
|
|
|
|
def down(self, force: str = "false") -> subprocess.CompletedProcess:
|
|
return subprocess.run(
|
|
["bash", str(self.script), "feat", str(self.base), force],
|
|
env=self.env,
|
|
capture_output=True,
|
|
text=True,
|
|
check=False,
|
|
)
|
|
|
|
def registered(self) -> bool:
|
|
listing = self.git("worktree", "list", "--porcelain").stdout
|
|
return str(self.checkout) in listing
|
|
|
|
|
|
class TestWorktreeDown(unittest.TestCase):
|
|
def test_clean_worktree_is_released(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
result = fixture.down()
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertFalse(fixture.checkout.exists())
|
|
self.assertFalse(fixture.registered())
|
|
|
|
def test_uncommitted_changes_block_the_removal(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
(fixture.checkout / "scratch.txt").write_text("work in progress\n")
|
|
result = fixture.down()
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("uncommitted changes", result.stderr)
|
|
self.assertTrue(fixture.checkout.exists())
|
|
|
|
def test_force_drops_uncommitted_changes(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
(fixture.checkout / "scratch.txt").write_text("work in progress\n")
|
|
result = fixture.down(force="true")
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertFalse(fixture.checkout.exists())
|
|
|
|
def test_unreadable_status_refuses_instead_of_deleting(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
(fixture.checkout / ".git").write_text("gitdir: /nonexistent\n")
|
|
result = fixture.down()
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("cannot read the git status", result.stderr)
|
|
self.assertTrue(fixture.checkout.exists())
|
|
|
|
@unittest.skipIf(os.geteuid() == 0, NEEDS_UNPRIVILEGED)
|
|
def test_pinned_metadata_dir_still_releases_the_branch(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
meta_parent = fixture.meta_dir().parent
|
|
mode = meta_parent.stat().st_mode
|
|
meta_parent.chmod(mode & ~stat.S_IWUSR)
|
|
try:
|
|
result = fixture.down()
|
|
finally:
|
|
meta_parent.chmod(mode)
|
|
self.assertEqual(result.returncode, 0, result.stderr)
|
|
self.assertFalse(fixture.checkout.exists())
|
|
self.assertFalse(fixture.registered())
|
|
|
|
@unittest.skipIf(os.geteuid() == 0, NEEDS_UNPRIVILEGED)
|
|
def test_undeletable_pointers_fail_loudly(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
meta = fixture.meta_dir()
|
|
mode = meta.stat().st_mode
|
|
meta.chmod(mode & ~stat.S_IWUSR)
|
|
try:
|
|
result = fixture.down()
|
|
finally:
|
|
meta.chmod(mode)
|
|
self.assertNotEqual(result.returncode, 0)
|
|
self.assertIn("still registers branch", result.stderr)
|
|
|
|
def test_missing_worktree_is_an_error(self) -> None:
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
fixture = WorktreeDownFixture(tmp)
|
|
shutil.rmtree(fixture.checkout)
|
|
result = fixture.down()
|
|
self.assertEqual(result.returncode, 1)
|
|
self.assertIn("no worktree at", result.stderr)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|