docs: подробная инструкция по сборке и автообновлению из Gitea Packages
This commit is contained in:
216
README.md
216
README.md
@@ -1,49 +1,205 @@
|
|||||||
# Ratatoskr-go
|
# Ratatoskr-go
|
||||||
|
|
||||||
Ratatoskr на Go — **оркестратор в единый статический бинарь** + субагенты через
|
Ratatoskr на Go — **оркестратор в единый статический бинарь** + субагенты через
|
||||||
[opencode](https://opencode.ai) как внешний процесс. Переезд с Python-версии.
|
[opencode](https://opencode.ai) как внешний процесс. Переезд с Python-версии:
|
||||||
|
все подсистемы (диалог, воркер, автообновление) собраны в один исполняемый
|
||||||
## Почему Go
|
файл без runtime-зависимостей (`CGO_ENABLED=0`), что упрощает доставку и деплой.
|
||||||
|
|
||||||
Агентный конвейер — это клей (HTTP/JSON к LLM, SQLite, Gitea API, Telegram), а не
|
|
||||||
горячие вычисления. Цель — **один статический бинарь без runtime-зависимостей**
|
|
||||||
(CGO_ENABLED=0), который решает проблему доставки/деплоя. Go: горизонт
|
|
||||||
HTTP/JSON + goroutine-параллелизм research/feedback/notify + SQLite без CGO
|
|
||||||
(`modernc.org/sqlite`).
|
|
||||||
|
|
||||||
## Структура
|
## Структура
|
||||||
|
|
||||||
```
|
```
|
||||||
cmd/ratatoskr/ # точка входа, сборка бинаря
|
cmd/ratatoskr/ # точка входа, сборка бинаря (App.New + Run)
|
||||||
internal/
|
internal/
|
||||||
app/ # компоновка (composition root), конфиг, DI
|
app/ # компоновка (composition root), config, DI, packageOwner
|
||||||
dialog/ # state-machine диалога: collect→confirm→created
|
config/ # загрузка YAML+env: ε подстановка ${VAR:-default}, defaults, validate (классы C1–C4)
|
||||||
decide/ # аналитик: промпт + разбор JSON-решения (opencode agent)
|
chat/ # мультиканальный Router; telegram — long-poll канал
|
||||||
research/ # исследователь: FACTS о репозитории
|
core/ # state-machine задач: /start /cancel /skip /retry N /status N /continue N
|
||||||
feedback/ # feedback: транскрипт→proposals (opencode agent)
|
analyst/ # аналитик: промпт + разбор JSON-решения (opencode agent, классы A1–A4)
|
||||||
opencode/ # обёртка запуска opencode-процесса, парсинг вердикта
|
worker/ # polling-планировщик + dev/reviewer-конвейер (классы W*, E*, R*)
|
||||||
gitea/ # Gitea API-клиент: issues, labels, PR, webhook
|
agents/ # встроенные агенты opencode (analyst.md, dev.md, reviewer.md) через go:embed
|
||||||
webhook/ # digest-ответы и статус в Telegram
|
opencode/ # обёртка запуска opencode-процесса, парсинг вердикта (классы O1–O4)
|
||||||
issue/ # issue: формирование тела задачи, task-tag
|
storage/ # SQLite (modernc.org/sqlite, без CGO): tasks, traces, task_history (S1–S5)
|
||||||
|
update/ # автообновление из Gitea Packages (классы U1–U6)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Сборка
|
## Сборка
|
||||||
|
|
||||||
|
Требуется Go (на хосте без `go` в PATH — экспорт вручную):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
go build -o bin/ratatoskr ./cmd/ratatoskr # обычная
|
export PATH=/opt/data/.local/go/bin:$PATH # локальный Go-тулчейн
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build \
|
go version # go1.25.0 linux/amd64
|
||||||
-o bin/ratatoskr-linux-amd64 ./cmd/ratatoskr # статический бинарь
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Обычная сборка (текущая платформа)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /opt/data/src/ratatoskr-go
|
||||||
|
make build # go build -ldflags="-s -w" -o ratatoskr ./cmd/ratatoskr/
|
||||||
|
./ratatoskr -version
|
||||||
|
```
|
||||||
|
|
||||||
|
### Кросс-сборка под linux/amd64 и windows/amd64 (только эти две платформы)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make cross
|
||||||
|
# → ratatoskr-linux-amd64
|
||||||
|
# → ratatoskr-windows-amd64.exe (Windows-машина Камиля)
|
||||||
|
```
|
||||||
|
|
||||||
|
`Makefile` вшивает в бинарь версию (ldflag):
|
||||||
|
|
||||||
|
```make
|
||||||
|
GIT_SHA := $(shell git rev-parse --short HEAD || echo dev)
|
||||||
|
GOFLAGS ?= -ldflags="-s -w -X main.version=commit-$(GIT_SHA) -X main.updateToken=$(UPDATE_TOKEN)"
|
||||||
|
```
|
||||||
|
|
||||||
|
- `main.version` = `commit-<sha7>` — та же версия, под которой бинарь публикуется в Gitea Packages.
|
||||||
|
- `main.updateToken` = токен read:package для автообновления (см. ниже). Если не задан
|
||||||
|
(`UPDATE_TOKEN ?= ""`) — пустая встройка, тогда обновление берёт `update.token` из конфига.
|
||||||
|
|
||||||
|
**Как на самом деле публикуется рабочий бинарь:** локальный `go build` токен НЕ вшивает
|
||||||
|
(значение секрета есть только у CI). Запушь `main` — CI (см. ниже) сам соберёт оба бинаря
|
||||||
|
со вшитым `TC_UPDATE_TOKEN` и загрузит их в Gitea Packages. Так соблюдён least-privilege:
|
||||||
|
read-токен не светится на хосте разработки.
|
||||||
|
|
||||||
|
### Проверки перед сборкой
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make test # go test ./... -v -count=1 -timeout 120s
|
||||||
|
make vet # go vet ./...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Прогон
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make run # build + ./ratatoskr -config config.yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI (.gitea/workflows/ci.yaml)
|
||||||
|
|
||||||
|
- **Job `test`**: checkout → setup-go → `go test ./...` → `go vet ./...`.
|
||||||
|
- **Job `build-and-package`** (matrix linux/amd64 + windows/amd64, без артефактов):
|
||||||
|
собирает бинарь со вшитым `-X main.updateToken=${secrets.TC_UPDATE_TOKEN}` и
|
||||||
|
**публикует в Gitea Packages** — только на `main`, сразу в версию `commit-<sha7>/`
|
||||||
|
(псевдо-версия `latest` НЕ используется — см. автообновление). Плюс companion-файлы
|
||||||
|
`<file>.version` и `<file>.sha256`.
|
||||||
|
|
||||||
|
Секреты/vars: `TC_GITEA_TOKEN` (write:packages), `TC_UPDATE_TOKEN` (read:package),
|
||||||
|
`GIT_MAIN_URL`.
|
||||||
|
|
||||||
|
## Конфигурация (config.yaml)
|
||||||
|
|
||||||
|
Минимальный рабочий конфиг (порядок полей произвольный) и блок автообновления
|
||||||
|
(подробнее в `config.yaml.example`):
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
telegram:
|
||||||
|
token: "..." # TG_TOKEN
|
||||||
|
chat_id: "..." # TG_CHAT_ID
|
||||||
|
opencode:
|
||||||
|
bin: "opencode"
|
||||||
|
config_dir: "./agents" # каталог, куда распаковываются встроенные агенты
|
||||||
|
paths:
|
||||||
|
db: "./ratatoskr.db" # дефолт; резолвится от каталога бинаря (не от cwd)
|
||||||
|
worktree: "./worktrees" # то же правило «всё рядом с .exe»
|
||||||
|
update:
|
||||||
|
enabled: true
|
||||||
|
base_url: "http://gitea.hal9000.home" # env UPDATE_BASE_URL; пуст = обновление ОТКЛЮЧЕНО
|
||||||
|
token: "${UPDATE_TOKEN}" # env UPDATE_TOKEN — фоллбэк, если не вшит ldflag
|
||||||
|
package: "ratatoskr"
|
||||||
|
check_interval: 24h
|
||||||
|
```
|
||||||
|
|
||||||
|
**Пути «всё рядом с .exe»:** относительные `db`/`worktree` резолвятся к каталогу
|
||||||
|
бинаря (`config.ExeDir()`), а НЕ к cwd. `config.yaml` ищется сначала рядом с бинарём,
|
||||||
|
фоллбэк — cwd. Абсолютные пути не трогаются.
|
||||||
|
|
||||||
|
## Команды бота
|
||||||
|
|
||||||
|
| Команда | Описание |
|
||||||
|
|---|---|
|
||||||
|
| `/start` | начать новую задачу |
|
||||||
|
| `/cancel` | отменить активную задачу |
|
||||||
|
| `/skip` | пропустить шаг |
|
||||||
|
| `/retry N` | повторить задачу N |
|
||||||
|
| `/status N` | статус задачи N |
|
||||||
|
| `/continue N` | продолжить |
|
||||||
|
| `/update` | проверить + применить обновление из Gitea Packages |
|
||||||
|
| `/status` | версия бинаря + есть ли доступное обновление |
|
||||||
|
| `/help` | справка по всем командам |
|
||||||
|
|
||||||
|
## Автообновление из Gitea Packages
|
||||||
|
|
||||||
|
Бинарь умеет сам себя обновлять из generic-пакета в Gitea. Модель:
|
||||||
|
**авто = только Check + уведомление** владельцу («доступна версия X, выполни /update»),
|
||||||
|
**применение — только по `/update`**.
|
||||||
|
|
||||||
|
- **Источник версий:** `{update.base_url}/api/packages/{owner}/generic/{package}`,
|
||||||
|
где `owner = "kamelion"` (константа `packageOwner` в коде), `package = "ratatoskr"`.
|
||||||
|
- **Каждая публикация — в конкретную версию `commit-<sha7>/`** (никакого `latest/`:
|
||||||
|
при параллельных PUT матрицы CI псевдо-версия `latest` разъезжается, и контрольные
|
||||||
|
суммы не сходятся — класс ошибки U4). Новейшая применимая версия ищется листингом:
|
||||||
|
фильтр `commit-*`, проба скачать бинарь платформы, берём версию с макс `id`.
|
||||||
|
- **Companion-файлы** рядом с бинарём в той же версии: `.<file>.version` (id версии)
|
||||||
|
и `<file>.sha256` (контрольная сумма). Verify всегда сверяет все три файла **одной** версии.
|
||||||
|
- **Update-хост ОТЛЕН от git-хоста:** `update.base_url` свой (env `UPDATE_BASE_URL`),
|
||||||
|
фоллбэка на `git.base_url` НЕТ. Пустой `update.base_url` полностью отключает обновление.
|
||||||
|
- **Токен доступа — только `read:package`, НЕ `git.token`** (least-privilege: update ≠ git).
|
||||||
|
Приоритет: вшитый ldflag `-X main.updateToken` (секрет CI `TC_UPDATE_TOKEN`) >
|
||||||
|
`update.token` из конфига (`UPDATE_TOKEN`). Заголовок: `Authorization: token <токен>`.
|
||||||
|
- **Применение (свап):** POSIX — exec-перезапуск процесса; Windows — мгновенный exec
|
||||||
|
невозможен (файл занят), поэтому `.new` применяется при следующем старте
|
||||||
|
(`ApplyPendingSwap` в main.go до подсистем). Резервный `.old` для отката.
|
||||||
|
|
||||||
|
### Как опубликовать новую версию
|
||||||
|
|
||||||
|
1. Внеси правки, коммитни, запуши в `main`.
|
||||||
|
2. CI соберёт оба бинаря со вшитым `TC_UPDATE_TOKEN` и опубликует в
|
||||||
|
`.../generic/ratatoskr/commit-<sha7>/` (бинарь + `.version` + `.sha256`).
|
||||||
|
3. На обновляемой машине: бот сам уведомит о новой версии → `/update`.
|
||||||
|
|
||||||
|
Проверку автообновления после публикации гоняет владелец на своей
|
||||||
|
Windows-машине (не прогоняется агентом).
|
||||||
|
|
||||||
|
### Валидация опубликованной версии
|
||||||
|
|
||||||
|
```bash
|
||||||
|
B=http://gitea.hal9000.home
|
||||||
|
V=commit-<sha7>; f=ratatoskr-linux-amd64
|
||||||
|
curl -s -H "Authorization: token $TOKEN" -o /tmp/probe.bin "$B/api/packages/kamelion/generic/ratatoskr/$V/$f"
|
||||||
|
echo "actual: $(sha256sum /tmp/probe.bin)" # реальная сумма скачанного
|
||||||
|
curl -s -H "Authorization: token $TOKEN" "$B/api/packages/kamelion/generic/ratatoskr/$V/$f.version"
|
||||||
|
curl -s -H "Authorization: token $TOKEN" "$B/api/packages/kamelion/generic/ratatoskr/$V/$f.sha256"
|
||||||
|
# .version == $V и .sha256 == actual → версия целостна
|
||||||
|
```
|
||||||
|
|
||||||
|
## Классы ошибок
|
||||||
|
|
||||||
|
| Блок | Коды | Где |
|
||||||
|
|---|---|---|
|
||||||
|
| C | C1–C4 | `internal/config` |
|
||||||
|
| A | A1–A4 | `internal/analyst` |
|
||||||
|
| M | M1–M5 | `internal/chat` |
|
||||||
|
| O | O1–O4 | `internal/opencode` |
|
||||||
|
| S | S1–S5 | `internal/storage` |
|
||||||
|
| W | W1–W5 | `internal/worker` |
|
||||||
|
| E | E1–E4 | `internal/worker` (репозитории) |
|
||||||
|
| R | R1–R6 | `internal/worker/review_errors.go` (ревьюер) |
|
||||||
|
| U | U1–U6 | `internal/update` |
|
||||||
|
|
||||||
## Контракты (не ломать)
|
## Контракты (не ломать)
|
||||||
|
|
||||||
- `process_turn` всегда возвращает **3 значения**: `(reply, action, session)`.
|
- `App.New(configPath, version, updateToken string)` — сигнатура: при правках обновлять
|
||||||
- `decide_fn` возвращает кортеж/структуру `(decision, err)`.
|
все вызовы в `internal/app/app_test.go` (иначе `go vet` падает).
|
||||||
- `/proposal` передаёт аналитику **полный комплект**, а не digest.
|
- `packageOwner` — константа (`"kamelion"`) в `internal/app/app.go`: владелец пакета ==
|
||||||
- Вердикты агентов — строгий JSON; `_extract_json` устойчив к мусору.
|
владелец репо == создатель токена; инвариант на проект, НЕ плодить vars/ldflag/конфиг.
|
||||||
|
- `update.Updater` создаётся структурой `&update.Updater{...}` напрямую (конструктора
|
||||||
|
`NewUpdater` нет).
|
||||||
|
- `process_turn` (Python-версия) всегда возвращает 3 значения; `decide_fn` — кортеж
|
||||||
|
`(decision, err)`. Вердикты агентов — строгий JSON.
|
||||||
|
|
||||||
## Eval
|
## Дополнительно
|
||||||
|
|
||||||
Eval-suite (парсинг A / контракты B / качество C) — см.
|
- Дизайн eval-suite (парсинг A / контракты B / качество C) — `../wiki/concepts/ratatoskr-evals.md`.
|
||||||
[дизайн в вики](../wiki/concepts/ratatoskr-evals.md). Запуск после внедрения:
|
- Прочие команды/go-модель — см. скилл `ratatoskr-development`.
|
||||||
`go test ./...` для детерминированных уровней A+B.
|
|
||||||
Reference in New Issue
Block a user