Зачем: тикнуть чекбокс в критериях приёмки нужно при закрытии каждой задачи, а рецепта в доке не было. Наступили на это при закрытии #37: `tea issues edit --description` требует тело целиком строкой, но взять её неоткуда — вывод `tea issues <номер>` обёрнут и разрисован для терминала. Что: раздел «Правка тела issue — только через API» с рабочим рецептом (забрать сырой JSON, поправить body, вернуть через `-X PATCH -d @файл`) и разбором флагов `tea api`. Оттуда же общее правило: CLI удобен, пока команда создаёт объект или меняет его свойство, и мешает, как только надо изменить уже написанный текст. Ссылка из раздела про автозакрытие и запись в «Что проверено и когда». Проверка: рецепт снят живыми запросами при закрытии #37 — так проставлены чекбоксы тикета и пункт в чек-листе карты #4. `make config-test` зелёный. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
182 lines
12 KiB
Markdown
182 lines
12 KiB
Markdown
# Issue tracker: Gitea
|
||
|
||
Задачи этого репозитория живут в Gitea на `git.dementev.space`
|
||
(`ddmitry/clickstream-data-platform`, это remote `origin`). Все операции —
|
||
через CLI [`tea`](https://gitea.com/gitea/tea), официальный клиент Gitea; по
|
||
устройству он близок к `gh` и `glab`. Логин и репозиторий `tea` определяет сам
|
||
по git remote в текущем каталоге.
|
||
|
||
## Перед первым запуском
|
||
|
||
- **Бинарник.** Скачивается с `https://dl.gitea.com/tea/<версия>/` (файл
|
||
`tea-<версия>-linux-amd64` и `.sha256` рядом), кладётся в `~/.local/bin/tea`.
|
||
Проверка: `tea --version`.
|
||
- **Вход.** `tea logins add --name git.dementev.space --url
|
||
https://git.dementev.space`, токен передаётся переменной
|
||
`GITEA_SERVER_TOKEN` (не аргументом командной строки — он попадёт в историю
|
||
оболочки). Логин уже добавлен и назначен по умолчанию, так что `tea` работает
|
||
из любого каталога.
|
||
- **Скоупы токена:** `read:user` (без него `tea` откажется добавлять логин),
|
||
`write:issue`, `write:repository`. Токен выпускается в UI: Settings →
|
||
Applications. Нехватка скоупа выглядит не как «нет прав», а как невнятная
|
||
ошибка или пустой ответ.
|
||
- **Прокси.** Домен `dementev.space` должен быть в `NO_PROXY`, иначе запросы
|
||
уходят в прокси и виснут. В обычной оболочке это делает `proxy-client` из
|
||
`~/dotfiles`. Если переменная не подхватилась, короткий разовый префикс:
|
||
`NO_PROXY='*' tea ...`.
|
||
|
||
## Команды
|
||
|
||
- **Создать issue:** `tea issues create --title "..." --description "..."`.
|
||
Многострочное тело удобнее собрать heredoc'ом в переменную и подставить
|
||
как `--description "$BODY"`.
|
||
- **Прочитать issue:** `tea issues <номер> --comments`.
|
||
- **Список:** `tea issues list --state open --output json --fields
|
||
index,title,labels,assignees`. Фильтры: `--labels`, `--assignee`,
|
||
`--keyword`.
|
||
- **Комментарий:** `tea comments add <номер> -d "..."`.
|
||
- **Метки:** `tea issues edit <номер> --add-labels "..."` / `--remove-labels
|
||
"..."`. Список меток репозитория — `tea labels list`, создать новую —
|
||
`tea labels create --name "..." --color "..."`.
|
||
- **Закрыть:** `tea issues close <номер>`. Комментария при закрытии команда не
|
||
принимает — сначала `tea comments add`, потом `close`.
|
||
- **Взять в работу:** `tea issues edit <номер> --add-assignees ddmitry`.
|
||
Сокращения вида `@me` в `tea` нет, имя пишется целиком.
|
||
- **Чего нет в CLI** — через `tea api <path>`: команда ходит в REST API Gitea
|
||
уже с сохранённым токеном, например
|
||
`tea api repos/ddmitry/clickstream-data-platform/issues/18`.
|
||
|
||
## Правка тела issue — только через API
|
||
|
||
Тикнуть чекбокс в критериях приёмки нужно при закрытии каждой задачи, и `tea`
|
||
для этого не годится. `tea issues edit --description` принимает тело **целиком
|
||
строкой**, а взять эту строку неоткуда: `tea issues <номер>` печатает текст
|
||
обёрнутым и разрисованным для терминала, обратно его не скормишь.
|
||
|
||
Рабочий путь — забрать сырое тело из API, поправить и вернуть:
|
||
|
||
```sh
|
||
# 1. сырой JSON тикета; тело — в поле body
|
||
tea api repos/{owner}/{repo}/issues/<n> > issue.json
|
||
|
||
# 2. правка тела любым удобным способом, например все чекбоксы разом
|
||
python3 -c "
|
||
import json
|
||
d = json.load(open('issue.json'))
|
||
json.dump({'body': d['body'].replace('- [ ]', '- [x]')},
|
||
open('patch.json', 'w'), ensure_ascii=False)
|
||
"
|
||
|
||
# 3. вернуть
|
||
tea api -X PATCH -d @patch.json repos/{owner}/{repo}/issues/<n>
|
||
```
|
||
|
||
Флаги `tea api`: `-X` — метод, `-d` — сырое тело JSON (`@файл` читает из файла,
|
||
`@-` из потока ввода), `-f` и `-F` — отдельные поля строкой и с типом. Флага
|
||
`--input` нет, хотя рука тянется написать именно его.
|
||
|
||
Тот же приём — для чек-листа родительской карты: пункт `- [ ] #NN` тикается
|
||
правкой её тела, автоматически Gitea этого не делает.
|
||
|
||
**Отсюда общее правило.** `tea` удобен там, где команда создаёт объект или
|
||
меняет его свойство: создать issue, добавить комментарий, повесить метку,
|
||
назначить исполнителя, закрыть. Как только нужно **изменить уже написанный
|
||
текст** — тело issue, тело PR, — CLI мешает: он умеет только заменить всё
|
||
целиком, а прочитать это «всё» в пригодном для правки виде не даёт. Такие
|
||
операции идут прямо в API.
|
||
|
||
## Слияние PR не закрывает issue
|
||
|
||
Gitea понимает только английские ключевые слова автозакрытия: `closes`,
|
||
`fixes`, `resolves` (`Closes #13`). Русское «Закрывает #13» в теле PR — обычная
|
||
ссылка, issue останется открытым. Наступали не раз: последний случай — слияние
|
||
PR #17, где issue #13 пришлось закрывать руками.
|
||
|
||
Отсюда простое правило: пиши в теле PR английское `Closes #NN` — тогда
|
||
слияние закроет задачу само, и ручного шага не остаётся. Всё остальное в PR
|
||
по-прежнему на русском: ключевое слово здесь не текст, а команда трекеру.
|
||
|
||
Если ключевого слова не было, порядок после слияния: закрыть задачу
|
||
(`tea issues close <номер>`) и тикнуть её пункт в чек-листе родительского
|
||
issue этапа — Gitea чек-листы сама не обновляет. Чем тикать — выше, в разделе
|
||
про правку тела.
|
||
|
||
## Спека — источник истины
|
||
|
||
- Спецификация фичи — файл в `docs/specs/`, версионируется с кодом.
|
||
- Корневой issue фичи — **тонкий**: ссылка на спеку + чек-лист дочерних issues
|
||
(`- [ ] #NN`). Содержание спеки в issue не дублируется — истина одна, в git.
|
||
- Дочерние issues — полноценные самодостаточные постановки: цель, критерии
|
||
приёмки чекбоксами, границы («что трогать нельзя»), «сначала прочитать»,
|
||
команды проверки.
|
||
- Итоговые резолюции и решения — в спеку или ADR тем же PR; issue — рабочая
|
||
переписка, она не обязана переживать фичу.
|
||
|
||
## Когда скилл говорит «опубликовать в issue tracker»
|
||
|
||
Создать issue в Gitea: `tea issues create ...`.
|
||
|
||
## Когда скилл говорит «достать тикет»
|
||
|
||
`tea issues <номер> --comments`.
|
||
|
||
## PR как поверхность триажа
|
||
|
||
**Нет** — одиночный учебный репозиторий, внешних PR не ждём. (Если включить —
|
||
`/triage` начнёт гонять PR через те же метки и состояния командами
|
||
`tea pulls ...`.)
|
||
|
||
## Wayfinding-операции
|
||
|
||
Используются `/wayfinder`. Карта — один issue, тикеты — дочерние issues.
|
||
|
||
- **Карта**: issue с меткой `wayfinder:map` (Notes / Decisions-so-far / Fog
|
||
в теле).
|
||
- **Дочерний тикет**: вложенных issues в Gitea нет, поэтому связь держится
|
||
двумя ссылками — пункт списка `- [ ] #NN` в теле карты и строка
|
||
`Part of #<карта>` в начале тела тикета. Метки: `wayfinder:<тип>`
|
||
(`research` / `prototype` / `grilling` / `task`).
|
||
- **Блокировки**: нативные зависимости Gitea — единственный источник истины,
|
||
текстовых строк `Blocked by:` в телах тикетов нет. В CLI их команд нет,
|
||
работаем через `tea api` (`{owner}` и `{repo}` подставляются из текущего
|
||
репозитория):
|
||
- добавить блокер: `tea api repos/{owner}/{repo}/issues/<n>/dependencies
|
||
-F index=<блокер> -f owner=ddmitry -f repo=clickstream-data-platform`
|
||
— поля `owner` и `repo` обязательны, без них API отвечает
|
||
«repository does not exist»;
|
||
- кто блокирует тикет: `GET .../issues/<n>/dependencies`;
|
||
- кого блокирует тикет: `GET .../issues/<n>/blocks`;
|
||
- снять блокировку: тот же путь методом `DELETE` с тем же телом.
|
||
|
||
Тикет разблокирован, когда у всех блокеров `state == "closed"`.
|
||
- **Фронтир**: открытые дети карты минус заблокированные и назначенные; первый
|
||
в порядке карты. Блокеры проверяются запросом `dependencies` по каждому
|
||
кандидату.
|
||
- **Взять в работу**: `tea issues edit <n> --add-assignees ddmitry` — первая
|
||
запись за сессию.
|
||
- **Закрыть**: `tea comments add <n> -d "<ответ>"`, затем `tea issues close
|
||
<n>`, затем указатель на контекст (суть + ссылка) в Decisions-so-far карты.
|
||
|
||
## Что проверено и когда
|
||
|
||
2026-08-06: правка тела issue через `tea api -X PATCH -d @файл` снята живыми
|
||
запросами при закрытии #37 — так проставлены чекбоксы самого тикета и пункт
|
||
в чек-листе карты #4. Тогда же проверено, что `tea issues edit --description`
|
||
для этого непригоден, а флага `--input` у `tea api` нет.
|
||
|
||
2026-07-31: Gitea 1.27.0 (`tea api version`), `tea` 0.15.0. Живыми запросами по
|
||
этому репозиторию проверены `tea labels list` и `tea issues list`. Остальной
|
||
набор команд, флагов и приёмы с `tea api` перенесены из доки
|
||
предшественника — там они снимались с `tea <команда> --help` установленного
|
||
бинарника и проверялись живыми запросами 2026-07-29. При обновлении `tea` стоит
|
||
перечитать `--help`: набор флагов между версиями менялся.
|
||
|
||
## Архив
|
||
|
||
- Трекер предшественника — репозиторий
|
||
[`ddmitry/clickstream-ch-kafka-superset-demo`](https://git.dementev.space/ddmitry/clickstream-ch-kafka-superset-demo).
|
||
Задачи оттуда сюда не переносились: платформа v2 начата с чистого трекера.
|
||
- До 2026-07-26 задачи предшественника жили в GitHub Issues
|
||
(`dementev-dev/…`); аккаунт заблокирован, номера воссозданы в Gitea один
|
||
в один.
|