* refactor(tools): extract shared module for gh spawnSync logic * feat(tools): add repo parameter to 5 GitHub tools * test(tools): add tunnel tool tests * test(tools): add repo parameter test cases for 5 tools * docs(handoff): scaffold handoff and ADR for PR * docs(handoff): set PR number * docs(project-map): update after PR#65 structural changes --------- Co-authored-by: opencode-agent <agent@opencode.local>
104 lines
No EOL
7.4 KiB
Markdown
104 lines
No EOL
7.4 KiB
Markdown
---
|
||
pr: 65
|
||
title: refactor(tools): add repo parameter + shared module + tunnel tests
|
||
---
|
||
|
||
## Что сделано
|
||
|
||
- **`.opencode/tools/_shared.ts`** — новый shared module: `parseRepo(repo?)`
|
||
(возвращает `["--repo", repo]` или `[]` для auto-detect), `runGh(args, repo?,
|
||
opts?)` (spawnSync с prepended `--repo` при явном repo + `cwd` из opts),
|
||
`formatResult(r, toolName)` (canonical `⚠️ <tool> failed (exit K): ...` на
|
||
ошибке, `stdout.trim()` на успехе). Устраняет ~50 строк дублирования
|
||
spawnSync-логики в 5 GitHub tools.
|
||
- **`tests/_ts_loader.mjs`** — расширение для поддержки relative imports
|
||
(`./_shared`): `stripTs` стриппает `import { ... } from "./..."` + `export`
|
||
на top-level declarations + type annotations в function params; новая
|
||
`inlineShared()` инлайнит код shared-модуля в `new Function` sandbox (с
|
||
авто-добавлением `.ts` расширения к specifier). Без этого loader падал на
|
||
ENOENT — local imports не резолвились.
|
||
- **5 GitHub tools отрефакторены** на `runGh`/`formatResult` + добавлен
|
||
опциональный `repo?: string` параметр (backward-compatible):
|
||
- `create-issue.ts` — `runGh` + `formatResult(r, "gh issue create")`
|
||
- `create-pr.ts` — `runGh` + `formatResult(r, "gh pr create")`
|
||
- `post-review.ts` — `runGh` (error-ветка оставлена кастомной с PR номером)
|
||
- `post-docs-review.ts` — `runGh` (аналогично)
|
||
- `merge-pr.ts` — `runGh` (аналогично)
|
||
При `repo` omitted → `parseRepo` возвращает `[]`, gh auto-detect'ит из
|
||
`context.worktree` (cwd) — поведение идентично pre-refactor (ADR-025 / PR#61).
|
||
- **Tunnel tests созданы** (issue #64 проблема 3 — единственный tool без
|
||
тестов):
|
||
- `tests/test_tunnel_tool.py` — 6 pytest-тестов: start без токена (exit 1),
|
||
start с токеном (`started (PID: N)`), start с `TUNNEL_DOMAIN` (`domain:
|
||
...`), stop при живом процессе (`stopped` + PID-файл удалён), stale PID-файл
|
||
(чистка + start), toggle (повторный start → stop). Изоляция: копия
|
||
`tunnel.sh` в tmp с переписанными PID/LOG путями + fake `cloudflared`
|
||
(sleep 30s) на PATH.
|
||
- `tests/test_tunnel_tool.ts` — 4 TS-теста: spawnSync с `bash` + путём к
|
||
скрипту, success → trimmed stdout, failure → `⚠️ tunnel failed (exit K)`,
|
||
cwd из context.
|
||
- **5 test-файлов обновлены** кейсами для `repo?: string` (явный / auto-detect /
|
||
invalid):
|
||
- `test_create_issue_tool.py/.ts` — +3 кейса (explicit, omitted, invalid)
|
||
- `test_create_pr_tool.py/.ts` — +3 кейса
|
||
- `test_post_review_tool.py/.ts` — +3 кейса
|
||
- `test_post_docs_review_tool.py/.ts` — +3 кейса
|
||
- `test_merge_pr_tool.py/.ts` — созданы с нуля (8 .py + 5 .ts: базовые
|
||
кейсы + repo; merge-pr не имел тестов ранее)
|
||
- **ADR-027** + этот handoff.
|
||
|
||
## Почему
|
||
|
||
Research (subagent explore, PR#63) выявил 3 проблемы в GitHub tools инфраструктуре:
|
||
|
||
1. **Дублирование spawnSync логики** — каждый из 5 GitHub tools дублировал
|
||
`spawnSync("gh", [...], { encoding, cwd })` + `if (r.status !== 0) return
|
||
"⚠️ ... failed (exit ...): ..."` — ~10 строк × 5 файлов = ~50 строк
|
||
дублирования. Shared module устраняет дублирование, единый canonical
|
||
error-формат.
|
||
2. **Нет `repo` параметра** — tools работали только через auto-detect (`gh`
|
||
определяет owner/repo из `git remote` в `context.worktree`). Ограничение:
|
||
из не-git-директории `gh` не может определить remote → fall back на
|
||
хардкод (баг issue #60, исправлен в PR#61). Явный `repo?: string` даёт
|
||
гибкость для pipeline оркестрации без потери auto-detect по умолчанию
|
||
(backward-compatible). Связано с ADR-025 (PR#61 убрал хардкод, этот PR
|
||
делает `repo` параметром — явный путь вместо хардкода).
|
||
3. **Tunnel без тестов** — `tunnel` единственный из 10 tools без парных
|
||
`.py` + `.ts` тестов. Toggle-логика (PID-файл, kill -0, stale cleanup)
|
||
не покрыта → регрессии проходят незамеченными. 6 .py + 4 .ts тестов
|
||
покрывают все ветви `tunnel.sh`.
|
||
|
||
## Pending
|
||
|
||
- После merge: на хосте `git pull` + рестарт opencode-контейнера чтобы
|
||
подхватились обновлённые tools (auto-discovered через @opencode-ai/plugin).
|
||
- Зависимости: supersedes partial PR#61 (ADR-025) — убрал хардкод `--repo`,
|
||
этот PR параметризует; related PR#63 (ADR-026) — tool-usage policy
|
||
описывает 10 tools, этот PR улучшает их реализацию.
|
||
|
||
## Watch out
|
||
|
||
- **`_ts_loader.mjs` extension** — `inlineShared()` добавляет `.ts` к
|
||
relative import specifier если его нет (`./_shared` → `./_shared.ts`).
|
||
Это необходимо потому что TS import specifiers опускают расширение, а
|
||
`readFileSync` требует полный путь. Если будущие shared-модули используют
|
||
другие расширения (`.tsx`, `.mts`) — потребуется расширить логику.
|
||
- **`formatResult` только для create-issue/create-pr** — post-review,
|
||
post-docs-review, merge-pr сохраняют кастомный error с PR номером
|
||
(`⚠️ ... failed for PR #N (exit K): ...`) для диагностики. `formatResult`
|
||
дал бы `⚠️ ... failed (exit K): ...` без PR — потеря информации. Тесты
|
||
`test_gh_failure_returns_error` проверяют substring `"post-review failed"`
|
||
/ `"exit 1"` — оба варианта проходят.
|
||
- **Backward-compat критична** — существующие `test_spawnsync_args` в
|
||
post-review/post-docs-review ассертят `"--repo" not in args`. При `repo`
|
||
omitted `parseRepo` возвращает `[]` → `--repo` не добавляется → тесты
|
||
проходят (51→62 на 4 tools). `test_repo_omitted_no_repo_flag` явно
|
||
закрепляет это поведение.
|
||
- **Tunnel test isolation** — `tunnel.sh` хардкодит `/tmp/tunnel.pid`. Тесты
|
||
копируют скрипт в `tmp_path` и переписывают PID/LOG пути через `replace()`.
|
||
Если в `tunnel.sh` изменится формат строк `PID_FILE="..."` — паттерн
|
||
replace сломается, тесты упадут (раннее обнаружение).
|
||
- **`commit.ts` НЕ в списке 5** — это `git`, не `gh` tool (нет `--repo`
|
||
флага). Оставлен как есть.
|
||
- **`.opencode/package-lock.json` untracked** — не относится к этому PR,
|
||
оставлен вне коммитов. |