fix(tools): create-pr omits head field, Forgejo POST /pulls returns 422 #9

Closed
opened 2026-08-07 09:51:29 +03:00 by slaid098 · 0 comments
Owner

Контекст

Tool create-pr (create-pr.ts) вызывает runGh(["pr", "create", "--title", title, "--body", body], repo, {cwd}) БЕЗ флагов --head и --base. В GitHub-режиме gh pr create автоопределяет head из текущей ветки и base из дефолтной ветки репо.

В Forgejo-режиме callForgejoGh в _shared.ts:126-144 извлекает --head/--base из argv через args.indexOf("--head") — если флаги отсутствуют, head и base остаются undefined и НЕ попадают в POST body (...(head ? {head} : {})). Forgejo API POST /repos/{repo}/pulls требует поле head (название ветки источника) — без него возвращает HTTP 422 {"message":"[Head]: Required"}.

Воспроизведение: попытка создать PR через create-pr tool на ветке refactor/tools/forgejo-first-dispatch → Forgejo API POST /repos/slaid098/opencode-config/pulls → HTTP 422: {"message":"[Head]: Required"}.

Обнаружено при работе над issue #5 (PR для cut GitHub fallback) — на шаге создания PR упал с 422.

Задача

  1. В _shared.ts:callForgejoGh для pr create — если --head отсутствует в argv, определить head из текущей git-ветки (git rev-parse --abbrev-ref HEAD через spawnSync с opts.cwd), а base — из дефолтной ветки репо (Forgejo GET /repos/{repo} → default_branch, или хардкод main с fallback).
  2. Передать head и base в POST body всегда (не опционально).
  3. Тест: create-pr на feature-ветке без --head/--base → PR создаётся, head=текущая ветка, base=default branch.

Контракты

  • callForgejoGh имеет доступ к opts.cwd (передаётся из context.worktree)
  • spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {cwd}) → текущая ветка
  • Forgejo GET /repos/{repo} → {default_branch: "main", ...}
  • POST body: {title, body, head, base} (все поля обязательны для Forgejo, кроме body которое может быть пустым)

Инварианты

  • Внешний контракт create-pr НЕ меняется (параметры title/body/issue_number/repo те же)
  • --head/--base если переданы — имеют приоритет над автоопределением
  • GitHub-режим (gh binary) не затрагивается (только Forgejo dispatch в callForgejoGh)

Граничные случаи

  • Текущая ветка = (HEAD detached at SHA) → head не определяется → понятная ошибка "cannot determine head branch (detached HEAD)"
  • default_branch endpoint недоступен → fallback на "main"
  • Head == base (попытка создать PR из main в main) → Forgejo вернёт 422, это OK (пользовательская ошибка)

Влияние на связанные компоненты

  • _shared.ts:callForgejoGh (pr create ветка, ~L126-144) — основная правка
  • create-pr.ts — без изменений (контракт сохранён)
  • merge-pr.ts, post-review.ts, create-issue.ts — без изменений (не используют pr create)
  • Issue #5 (этот PR) — НЕ блокируется, workaround: создать PR через Forgejo веб-UI или raw API

Вне scope

  • gh pr merge dispatch (работает, merge-pr не передаёт head/base)
  • gh pr comment dispatch (работает)
  • gh issue create dispatch (работает, labels баг в issue #4)
  • Skills/agents правки — отдельный PR

Критерии приемки

  • create-pr({title, body, issue_number: N}) на feature-ветке (без --head) → PR создаётся на Forgejo
  • head в POST body = текущая git-ветка, base = default_branch репо
  • --head/--base если переданы — переопределяют автоопределение
  • Тест на detached HEAD возвращает понятную ошибку, не падает
## Контекст Tool `create-pr` (`create-pr.ts`) вызывает `runGh(["pr", "create", "--title", title, "--body", body], repo, {cwd})` БЕЗ флагов `--head` и `--base`. В GitHub-режиме `gh pr create` автоопределяет head из текущей ветки и base из дефолтной ветки репо. В Forgejo-режиме `callForgejoGh` в `_shared.ts:126-144` извлекает `--head`/`--base` из argv через `args.indexOf("--head")` — если флаги отсутствуют, `head` и `base` остаются `undefined` и НЕ попадают в POST body (`...(head ? {head} : {})`). Forgejo API `POST /repos/{repo}/pulls` требует поле `head` (название ветки источника) — без него возвращает HTTP 422 `{"message":"[Head]: Required"}`. Воспроизведение: попытка создать PR через `create-pr` tool на ветке `refactor/tools/forgejo-first-dispatch` → `Forgejo API POST /repos/slaid098/opencode-config/pulls → HTTP 422: {"message":"[Head]: Required"}`. Обнаружено при работе над issue #5 (PR для cut GitHub fallback) — на шаге создания PR упал с 422. ## Задача 1. В `_shared.ts:callForgejoGh` для `pr create` — если `--head` отсутствует в argv, определить head из текущей git-ветки (`git rev-parse --abbrev-ref HEAD` через `spawnSync` с `opts.cwd`), а base — из дефолтной ветки репо (Forgejo `GET /repos/{repo}` → `default_branch`, или хардкод `main` с fallback). 2. Передать `head` и `base` в POST body всегда (не опционально). 3. Тест: `create-pr` на feature-ветке без `--head`/`--base` → PR создаётся, head=текущая ветка, base=default branch. ## Контракты - `callForgejoGh` имеет доступ к `opts.cwd` (передаётся из `context.worktree`) - `spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"], {cwd})` → текущая ветка - Forgejo `GET /repos/{repo}` → `{default_branch: "main", ...}` - POST body: `{title, body, head, base}` (все поля обязательны для Forgejo, кроме body которое может быть пустым) ## Инварианты - Внешний контракт `create-pr` НЕ меняется (параметры title/body/issue_number/repo те же) - `--head`/`--base` если переданы — имеют приоритет над автоопределением - GitHub-режим (`gh` binary) не затрагивается (только Forgejo dispatch в `callForgejoGh`) ## Граничные случаи - Текущая ветка = `(HEAD detached at SHA)` → head не определяется → понятная ошибка "cannot determine head branch (detached HEAD)" - `default_branch` endpoint недоступен → fallback на `"main"` - Head == base (попытка создать PR из main в main) → Forgejo вернёт 422, это OK (пользовательская ошибка) ## Влияние на связанные компоненты - `_shared.ts:callForgejoGh` (pr create ветка, ~L126-144) — основная правка - `create-pr.ts` — без изменений (контракт сохранён) - `merge-pr.ts`, `post-review.ts`, `create-issue.ts` — без изменений (не используют `pr create`) - Issue #5 (этот PR) — НЕ блокируется, workaround: создать PR через Forgejo веб-UI или raw API ## Вне scope - `gh pr merge` dispatch (работает, `merge-pr` не передаёт head/base) - `gh pr comment` dispatch (работает) - `gh issue create` dispatch (работает, labels баг в issue #4) - Skills/agents правки — отдельный PR ## Критерии приемки - `create-pr({title, body, issue_number: N})` на feature-ветке (без `--head`) → PR создаётся на Forgejo - `head` в POST body = текущая git-ветка, `base` = `default_branch` репо - `--head`/`--base` если переданы — переопределяют автоопределение - Тест на detached HEAD возвращает понятную ошибку, не падает
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/opencode-config#9
No description provided.