fix(6245): bind_repo(moved_from=…) moves the snippet locations recorded in the old repo
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 58s
CI & Build / integration (push) Failing after 1m16s
CI & Build / Python tests (push) Successful in 2m33s
CI & Build / Build & push image (push) Skipped

A repo renamed on its forge kept every snippet location naming the old
repo: the binding and the shape ledger moved, the snippets did not.

- snippets.move_location_repo rewrites, on every live snippet of the
  project, each location whose repo is the old remote in any form (URL,
  normalized key, "owner/Repo", bare "Repo", compared without case) to
  the new normalized key. A snippet the caller can only read is listed
  in read_only and left alone.
- Its loop is shared with move_locations through _rewrite_locations,
  which saves each changed snippet through update_snippet.
- bind_repo calls it after the binding moves and reports snippets_moved
  and snippets_read_only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 16:18:03 -04:00
co-authored by Claude Opus 5.5
parent 787685ac63
commit e89e6a24de
3 changed files with 112 additions and 26 deletions
+32
View File
@@ -100,3 +100,35 @@ async def test_the_same_repo_under_both_names_is_refused(bound):
await svc.move_binding(
bound["owner"], OLD, "git@git.example.com:alice/oldname", bound["pid"],
)
@pytest.mark.integration
async def test_the_snippets_recorded_in_the_old_repo_move_with_it(bound):
from grimoire.services import snippets as snippets_svc
owner, pid = bound["owner"], bound["pid"]
async def snippet(name, *repos):
note = await snippets_svc.create_snippet(
owner, name=name, code=f"def {name}():\n pass\n", language="python",
project_id=pid,
locations=[{"repo": r, "path": "src/app.py", "symbol": name} for r in repos],
)
return int(note.id)
moved_one = await snippet("moved_one", "OldName", "alice/oldname", OLD)
mixed = await snippet("mixed", "git.example.com/alice/oldname", "elsewhere/gadget")
untouched = await snippet("untouched", "Gadget", "git.example.com/alice/oldnamesake")
result = await snippets_svc.move_location_repo(owner, pid, OLD, NEW)
assert sorted(result["moved"]) == sorted([moved_one, mixed])
assert result["read_only"] == []
async def repos(snippet_id):
note = await snippets_svc.get_snippet(owner, snippet_id)
return [loc["repo"] for loc in snippets_svc.snippet_fields(note)["locations"]]
assert await repos(moved_one) == [NEW_KEY, NEW_KEY, NEW_KEY]
assert await repos(mixed) == [NEW_KEY, "elsewhere/gadget"]
assert await repos(untouched) == ["Gadget", "git.example.com/alice/oldnamesake"]