* fix(pipeline-status): handle git insteadOf userinfo in parse_remote_url * test(pipeline-status): add tests for URL with userinfo * docs(handoff): set PR number 55 in handoff + ADR-023 --------- Co-authored-by: opencode-agent <agent@slaid098.dev>
39 lines
No EOL
3.6 KiB
Markdown
39 lines
No EOL
3.6 KiB
Markdown
# ADR-023: parse_remote_url strips git insteadOf userinfo from HTTPS URLs
|
||
|
||
## Статус
|
||
Accepted (2026-07-24)
|
||
|
||
## Контекст
|
||
PR#53 (ADR-022) добавил `git config --global url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf https://github.com/` в `setup-memory.sh` для non-interactive HTTPS auth в Docker. После этого `git remote get-url origin` возвращает rewritten URL с встроенным userinfo:
|
||
|
||
```
|
||
https://x-access-token:github_pat_...@github.com/slaid098/opencode-config.git
|
||
```
|
||
|
||
`parse_remote_url` (в `pipeline-status.py` и `spec-status.py`) использовал regex `https?://([^/]+)/([^/]+)/(.+?)(?:\.git)?$` — группа `[^/]+` жадно матчит всё между `://` и первым `/`, включая `user:password@` (символ `@` не исключён). В результате `host` = `x-access-token:github_pat_...@github.com` вместо `github.com`.
|
||
|
||
Это ломало `get_memory_file_path()` → путь `repos/x-access-token:...@github.com/slaid098/opencode-config.md` (не существует) → `check_memory` возвращал NOT_DONE для каждого PR после PR#53. `get_repo_full_name()` случайно не ломался (возвращает `org/repo`, host отбрасывается), но фикс применён в обоих файлах для консистентности.
|
||
|
||
CI на PR#53 не поймал regression — тесты `test_parse_remote_url_*` покрывали только plain URLs без userinfo.
|
||
|
||
## Решение
|
||
Изменить HTTPS-regex в `parse_remote_url` (оба файла) на:
|
||
|
||
```python
|
||
re.match(r"https?://(?:[^/@]*@)?([^/]+)/([^/]+)/(.+?)(?:\.git)?$", url)
|
||
```
|
||
|
||
`(?:[^/@]*@)?` — опциональная non-capturing группа, пропускающая `user:password@` userinfo перед host:
|
||
- Для plain `https://github.com/...` группа не матчится (нет `@`) → host = `github.com`.
|
||
- Для `https://user:token@github.com/...` группа матчит `user:token@` и отбрасывается → host = `github.com`.
|
||
- `[^/@]` внутри userinfo не матчит ни `/` ни `@`, поэтому корректно останавливается на первом `@` (поддерживает `user:pass`, `user`, `token@` формы).
|
||
|
||
7 новых тестов: 4 в `test_pipeline_status.py` (parse + get_memory_file_path), 3 в `test_spec_status.py` (parse + get_repo_full_name).
|
||
|
||
## Альтернативы
|
||
1. **Использовать `git config --get remote.origin.url`** вместо `git remote get-url origin` — bypasses insteadOf, возвращает raw URL. Отклонено: coupling с git internals, ломается если кто-то задаст remote URL с token напрямую (не через insteadOf).
|
||
2. **Strip userinfo через `url.split("@")[-1]`** — хрупкий: ломается если path содержит `@` (legitimate в GitHub URLs для определённых refs/paths).
|
||
3. **Рефакторинг `parse_remote_url` в shared module** — оба файла дублируют функцию. Отклонено для этого PR: риск для ADR-010 cwd-aware `_resolve_repo_root` logic, выходит за рамки bugfix.
|
||
4. **Не чинить, отключить insteadOf** — отклонено: insteadOf нужен для non-interactive memory clone (PR#53/ADR-022), regression в pipeline_status — меньшее из зол.
|
||
|
||
Regex fix (выбран) — минимальный, root-cause, без env coupling, покрывает оба caller'а. |