diff --git a/CHANGELOG.md b/CHANGELOG.md index fa92a92..618af0c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Changelog +## 14 +- `install.sh`: no longer fails with `mv: cannot stat '.dpcli'` when `.dpcli` is recorded as a submodule without a `.gitmodules` entry (now registered and checked out) or when a vendored copy is tracked but deleted from disk (restored from the index first). + ## 13 - Process description (`workflow.md`) trimmed by ~110 tokens more: duplicated and decorative phrases removed, no rule changed. diff --git a/install.sh b/install.sh index d7578e8..15a1342 100755 --- a/install.sh +++ b/install.sh @@ -41,7 +41,19 @@ ask LANG_COMMITS "Language of commit messages" en if git config -f .gitmodules --get submodule..dpcli.path >/dev/null 2>&1; then git submodule update --init .dpcli say "submodule .dpcli present" +elif git ls-files -s .dpcli | grep -q '^160000'; then + # recorded as a submodule but missing from .gitmodules (or never initialized): register it and check it out + git config -f .gitmodules submodule..dpcli.path .dpcli + git config -f .gitmodules submodule..dpcli.url "$REPO" + git add .gitmodules + if ! git submodule update --init .dpcli 2>/dev/null; then + git rm -q --cached .dpcli; rmdir .dpcli 2>/dev/null || true # an empty uninitialized dir only + git submodule add -q --force "$REPO" .dpcli + fi + say "submodule .dpcli registered from $REPO" elif [ -n "$(git ls-files .dpcli | head -1)" ]; then + # vendored copy; deleted from disk — restore it from the index first so own agents can be carried over + [ -d .dpcli ] || git checkout -q -- .dpcli backup="$(git rev-parse --git-dir)/dpcli-vendored-$(date +%Y%m%d%H%M%S)" mv .dpcli "$backup" git rm -r -q --cached .dpcli diff --git a/lib/dpcli/__init__.py b/lib/dpcli/__init__.py index 6cfd6af..d3d2ef5 100644 --- a/lib/dpcli/__init__.py +++ b/lib/dpcli/__init__.py @@ -6,4 +6,4 @@ else in the current copy. Exit codes: 0 ok, 1 error/check failed, 2 arguments. """ -VERSION = 13 # bump on every change: older copies re-exec from the main copy (reexec.maybe_reexec) +VERSION = 14 # bump on every change: older copies re-exec from the main copy (reexec.maybe_reexec) diff --git a/tests/test_install.py b/tests/test_install.py index 68faebb..6c48a0b 100644 --- a/tests/test_install.py +++ b/tests/test_install.py @@ -66,6 +66,33 @@ def test_vendored_copy_migrates_own_agent_and_rerun_is_idempotent(self): again = self.install("--lang", "en", "--commit-lang", "en") self.assertIn("nothing changed", again) + def test_vendored_copy_deleted_from_disk(self): + shutil.rmtree(self.p.root / ".dpcli") # tracked in git, gone from the working tree + out = self.install("--lang", "en", "--commit-lang", "ru") + self.assertIn("replaced by the submodule", out) + self.assertIn("160000", git("ls-files", "-s", ".dpcli", cwd=self.p.root)) + + def test_gitlink_without_gitmodules(self): + self.drop_vendored() + r = self.p.root + sha = git("rev-parse", "HEAD", cwd=self.p.tmp / "src") + git("update-index", "--add", "--cacheinfo", f"160000,{sha},.dpcli", cwd=r) # a gitlink, no .gitmodules + git("commit", "-q", "-m", "broken submodule", cwd=r) + out = self.install("--lang", "en", "--commit-lang", "en") + self.assertIn("registered", out) + self.assertTrue((r / ".dpcli" / "dpcli").is_file()) + self.assertIn(".dpcli", (r / ".gitmodules").read_text()) + + def test_gitlink_to_unknown_commit_is_re_added(self): + self.drop_vendored() + r = self.p.root + git("update-index", "--add", "--cacheinfo", f"160000,{'1' * 40},.dpcli", cwd=r) # commit the hub doesn't have + git("commit", "-q", "-m", "stale submodule", cwd=r) + out = self.install("--lang", "en", "--commit-lang", "en") + self.assertIn("registered", out) + self.assertTrue((r / ".dpcli" / "dpcli").is_file()) + self.assertNotIn("1" * 40, git("ls-files", "-s", ".dpcli", cwd=r)) + if __name__ == "__main__": unittest.main() diff --git a/tools/budgets.json b/tools/budgets.json index 58b914e..882d74f 100644 --- a/tools/budgets.json +++ b/tools/budgets.json @@ -5,7 +5,7 @@ "duplicate_windows": 0, "risky": { "rm -rf": 0, - "force": 21, + "force": 22, "reset --hard": 0, "shell=True": 0, "push": 4