fix(sdlc-rename): cutover сверяет HEAD с новым репозиторием — Gitea не перенаправляет SSH
После переименования в Gitea старый адрес по SSH отвечает «Cannot find repository» (T6.2 подтвердилось): preflight сверяет main по адресу aep/sdlc-hub, derive_remote принимает уже сменённый origin; README — сменить origin сразу после переименования. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ddc3fd6a10
commit
eb86563e2a
@ -371,10 +371,12 @@ make web
|
||||
Одноразовое переключение живой установки (хаб назывался `jira-hub`) делает скрипт `scripts/cutover.sh` (обёртка над
|
||||
`scripts/cutover.py`, stdlib). Выполняет владелец; без `--apply` скрипт только проверяет и печатает шаги.
|
||||
|
||||
1. Слить PR переименования; переименовать репозиторий в Gitea `aep/jira-hub` -> `aep/sdlc-hub`.
|
||||
1. Слить PR переименования; переименовать репозиторий в Gitea `aep/jira-hub` -> `aep/sdlc-hub`. Gitea не
|
||||
перенаправляет git по SSH со старого имени: сразу сменить адрес `origin` (общий для checkout и всех worktree) —
|
||||
`git -C ~/projects/jira-bridge/jira-hub remote set-url origin ssh://git@git.aepif.ru:2222/aep/sdlc-hub.git`.
|
||||
2. Закрыть или не трогать сессии Claude с MCP хаба (после переключения -- `/mcp` reconnect).
|
||||
3. `git -C ~/projects/jira-bridge/jira-hub pull --ff-only`, сразу `bash ~/projects/jira-bridge/jira-hub/scripts/cutover.sh`
|
||||
-- сухой прогон, проверить вывод (шаг 1 скрипта: чистый checkout на `main` = `origin/main`, доступен `aep/sdlc-hub`,
|
||||
-- сухой прогон, проверить вывод (шаг 1 скрипта: чистый checkout на `main` = `main` в `aep/sdlc-hub`, доступен `aep/sdlc-hub`,
|
||||
нет целевых каталогов, есть `uv`, `make`, `claude`, `launchctl`, `node`, `npm`; неотслеживаемая старая сборка
|
||||
`src/jira_hub/` не мешает, на шаге 4 она удаляется; адрес нового `origin` берётся из прежнего с заменой имени
|
||||
репозитория, протокол (ssh/https) сохраняется). Скрипт также предупреждает об открытых сессиях Claude со старым
|
||||
|
||||
@ -46,9 +46,9 @@
|
||||
launchd, двух чужих репозиториев и user scope Claude; руками их легко сделать не в том порядке (I-2 wiki-module).
|
||||
Скрипт одноразовый, после переключения остаётся в истории как образец; тесты — на сухой прогон и проверки шага 1
|
||||
(во временном `HOME`), без launchd.
|
||||
6. **Переименование репозитория в Gitea — руками владельца** в интерфейсе (Settings → Repository name). Gitea оставляет
|
||||
перенаправление со старого адреса; `tea` и `origin` после этого работают по новому адресу. Скрипт проверяет, что
|
||||
переименование сделано, и меняет `origin`.
|
||||
6. **Переименование репозитория в Gitea — руками владельца** в интерфейсе (Settings → Repository name). Перенаправления
|
||||
по SSH Gitea не даёт (проверено 24.10.2026: «Cannot find repository: aep/jira-hub»), поэтому владелец сразу меняет
|
||||
`origin` на новый адрес; скрипт сверяет `HEAD` с `main` нового репозитория и принимает уже сменённый `origin`.
|
||||
7. **Checkout переезжает в `jira-bridge/sdlc-hub`**, workspace `jira-bridge` остаётся: от его пути зависят каталог
|
||||
памяти Claude, workspace `CLAUDE.md`, путь общей папки реле (`…/utm/rshb/jira-bridge`) и настройки агентов на
|
||||
виртуалке. Переименование workspace — отдельно, если понадобится.
|
||||
|
||||
@ -78,7 +78,9 @@ NEW_REPO = "/aep/sdlc-hub"
|
||||
|
||||
|
||||
def derive_remote(origin: str) -> str:
|
||||
"""Адрес нового репозитория из адреса origin: тот же протокол и суффикс, имя другое."""
|
||||
"""Адрес нового репозитория из адреса origin: тот же протокол и суффикс, имя другое; уже новый — как есть."""
|
||||
if NEW_REPO in origin:
|
||||
return origin
|
||||
if OLD_REPO not in origin:
|
||||
raise ValueError("origin %s не содержит %s" % (origin, OLD_REPO))
|
||||
return origin.replace(OLD_REPO, NEW_REPO)
|
||||
@ -125,17 +127,18 @@ def preflight(cfg: Config) -> List[str]:
|
||||
if br != "main":
|
||||
problems.append("checkout %s не на ветке main (сейчас: %s)" % (old, br or "?"))
|
||||
head = _git(old, "rev-parse", "HEAD").stdout.strip()
|
||||
# ls-remote вместо fetch: проверка остаётся строго чтением (fetch пишет FETCH_HEAD)
|
||||
lr = _git(old, "ls-remote", "origin", "refs/heads/main")
|
||||
remote_head = lr.stdout.split()[0] if lr.returncode == 0 and lr.stdout.split() else ""
|
||||
if not remote_head:
|
||||
problems.append("не удалось получить origin/main из %s" % old)
|
||||
elif remote_head != head:
|
||||
problems.append("HEAD не совпадает с origin/main: сделайте git pull --ff-only в %s" % old)
|
||||
if not remote:
|
||||
remote, prob = target_remote(cfg)
|
||||
if prob:
|
||||
problems.append(prob)
|
||||
# сверка с новым адресом: после переименования Gitea не перенаправляет git по SSH, старый origin недоступен;
|
||||
# ls-remote вместо fetch: проверка остаётся строго чтением (fetch пишет FETCH_HEAD)
|
||||
if remote:
|
||||
lr = _git(old, "ls-remote", remote, "refs/heads/main")
|
||||
remote_head = lr.stdout.split()[0] if lr.returncode == 0 and lr.stdout.split() else ""
|
||||
if remote_head and remote_head != head:
|
||||
problems.append("HEAD не совпадает с main в %s: сделайте git pull --ff-only в %s (origin — %s)"
|
||||
% (remote, old, remote))
|
||||
if remote:
|
||||
lr = subprocess.run(["git", "ls-remote", remote], capture_output=True, text=True, env=_git_env(),
|
||||
stdin=subprocess.DEVNULL)
|
||||
|
||||
@ -258,6 +258,7 @@ def test_preflight_tracked_or_other_untracked_is_dirty(env):
|
||||
@pytest.mark.parametrize("origin,expected", [
|
||||
("ssh://git@git.aepif.ru:2222/aep/jira-hub.git", "ssh://git@git.aepif.ru:2222/aep/sdlc-hub.git"),
|
||||
("https://git.aepif.ru/aep/jira-hub.git", "https://git.aepif.ru/aep/sdlc-hub.git"),
|
||||
("ssh://git@git.aepif.ru:2222/aep/sdlc-hub.git", "ssh://git@git.aepif.ru:2222/aep/sdlc-hub.git"), # origin уже сменён
|
||||
])
|
||||
def test_derive_remote(origin, expected):
|
||||
assert cutover.derive_remote(origin) == expected
|
||||
@ -367,3 +368,17 @@ def test_step6_rewrites_consumers_with_backups(env):
|
||||
assert "sdlc-jira" in (proj / ".claude" / "settings.local.json").read_text()
|
||||
assert any("каталог jira-hub" in ln for ln in left)
|
||||
assert not (proj / "CLAUDE.md.pre-sdlc").exists()
|
||||
|
||||
|
||||
def test_preflight_checks_head_against_new_remote(env):
|
||||
"""Gitea не перенаправляет git по SSH после переименования: старый origin недоступен, сверка — с новым адресом."""
|
||||
git("remote", "set-url", "origin", str(env["tmp"] / "gone.git"), cwd=env["old"])
|
||||
problems = cutover.preflight(cutover.Config.from_env())
|
||||
assert not any("origin/main" in s or "HEAD не совпадает" in s for s in problems), problems
|
||||
other = env["tmp"] / "other"
|
||||
git("clone", str(env["bare"]), str(other), cwd=env["tmp"])
|
||||
(other / "b.txt").write_text("y")
|
||||
git("add", ".", cwd=other)
|
||||
git("commit", "-m", "ahead", cwd=other)
|
||||
git("push", "origin", "main", cwd=other)
|
||||
assert any("HEAD не совпадает" in s for s in cutover.preflight(cutover.Config.from_env()))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user