# YouGile и GitHub: yogile_connector

*Для разработчиков. Состояние на 28.09.2026. Переменные и все ручки — README репозитория `Buharestskie/yogile_connector`.*

## Коротко

`yogile_connector` связывает задачи YouGile с ветками GitHub и двигает задачу по доске вслед за кодом: ветки нет — задача ждёт ветку; ветка появилась — задача в работе; ветка слита в основную — задача закрыта; после слияния пришли новые коммиты — задача снова в работе. Внутри YouGile скрипт-витрина показывает состояние бейджем на карточке и блоком «GitHub» в открытой задаче. Сервис доступен по адресу `https://dev-tasks.donula26.ru`, настройки — в админ-панели `/admin`.

## Как задача находит свои ветки

- **По ID задачи в имени ветки.** Номер задачи из YouGile плюс префикс проекта дают ID вида `PRI-18`. Любая ветка, в имени которой есть этот токен (`feat/PRI-18-cart`, `pri_18`), считается веткой задачи — в каком бы репозитории организации она ни появилась.
- **Веток может быть несколько**, по одной в разных репозиториях. Каждая ведётся отдельно, общее состояние собирается из них: пока не слита хоть одна — задача в работе, слиты все — закрыта.
- **«Слито»** — у ветки нет коммитов вне основной (`compare`, `ahead_by == 0`), и при этом собственные коммиты на ней хоть раз были. Основная ветка у каждого репозитория своя: у части проектов это `dev`, а не `main`.
- **Стикер «Репозиторий»** на задаче выбирает один репозиторий — тот, где ветка создаётся автоматически. Задачи без стикера и без привязанных репозиториев сервис не двигает (состояние «репозиторий не выбран»). Стикер сервис заводит и поддерживает сам: вешает на включённые доски, дописывает новые репозитории организации, убирает исчезнувшие.

## Что сервис делает сам

<table id="bkmrk-%D0%A1%D0%BE%D0%B1%D1%8B%D1%82%D0%B8%D0%B5%D0%A7%D1%82%D0%BE-%D0%BF%D1%80%D0%BE%D0%B8%D1%81%D1%85%D0%BE%D0%B4%D0%B8"><thead><tr><th>Событие</th><th>Что происходит</th></tr></thead><tbody><tr><td>Задачу перенесли в рабочую колонку</td><td>Создаётся ветка в репозитории из стикера (если на доске включено автосоздание)</td></tr><tr><td>Для задачи появилась ветка</td><td>Задача переезжает в рабочую колонку</td></tr><tr><td>Все ветки слиты</td><td>Задача уезжает в «Готово» и отмечается выполненной</td></tr><tr><td>После слияния пришли новые коммиты</td><td>Задача возвращается в работу</td></tr><tr><td>Задача ушла из рабочей колонки</td><td>Пустая ветка (без своих коммитов) удаляется — кроме колонок, где ветки велено сохранять</td></tr><tr><td>Задача закрыта</td><td>Слитая ветка удаляется. Неслитая не удаляется никогда — в чат задачи один раз приходит сообщение с её именем и числом неслитых коммитов</td></tr></tbody></table>

Автосоздание ветки на доске по умолчанию выключено, удаление пустых и слитых веток — включено; всё это переключается по доскам в админ-панели. Каждый переход пишется в чат задачи, если задан `YOUGILE_BOT_USER_ID`. Цвет карточки меняется под состояние: серый — нет репозитория или ветки, зелёный — слито.

## Откуда события

- **GitHub** → `POST /webhooks/github`: события `push`, `create`, `delete`, `pull_request`. Подпись проверяется по `GITHUB_WEBHOOK_SECRET`, повторные доставки отбрасываются. Push в основную ветку пересчитывает все задачи досок этого репозитория.
- **YouGile** → `POST /webhooks/yougile`: события `task-created` и `task-moved`.
- **Плановая сверка** всей доски раз в `RECONCILE_INTERVAL_MINUTES` (по умолчанию 15) — страховка на случай потерянного вебхука.
- **Витрина** (`yougile-script/github-widget.js`) раз в 30 секунд читает `GET /api/board/state` и ничего не решает сама. Ответ кешируется на 30 секунд.

## Админ-панель

Настройки живут в базе (SQLite в `./data` по умолчанию) и правятся на `/admin`: репозиторий и ключи по умолчанию, параметры YouGile, таблица досок — какие синхронизировать, какой репозиторий у каждой, какие колонки рабочая и завершающая, создавать ли ветку автоматически. При первом запуске значения переносятся из окружения, дальше окружение нужно только для базы и токенов входа. Кнопки панели ходят в `/admin/api/*` с заголовком `X-Admin-Token`:

<table id="bkmrk-%D0%A0%D1%83%D1%87%D0%BA%D0%B0%D0%A7%D1%82%D0%BE-%D0%B4%D0%B5%D0%BB%D0%B0%D0%B5%D1%82-post"><thead><tr><th>Ручка</th><th>Что делает</th></tr></thead><tbody><tr><td>`POST /admin/api/reconcile`</td><td>Полная сверка досок</td></tr><tr><td>`POST /admin/api/webhooks/yougile`</td><td>Создать подписки YouGile, тело `{"public_url": "https://dev-tasks.donula26.ru"}`</td></tr><tr><td>`POST /admin/api/webhooks/github`</td><td>Завести вебхук сервиса во всех репозиториях (или в переданном списке `repos`)</td></tr><tr><td>`POST /admin/api/reload`</td><td>Перечитать настройки и структуру досок</td></tr><tr><td>`GET /admin/api/status`</td><td>Сводка состояния</td></tr></tbody></table>

Проверка живости — `GET /healthz`, отвечает `{"status":"ok"}`.

## Где код и как выкатывается

- Репозиторий `Buharestskie/yogile_connector`: FastAPI, SQLAlchemy, httpx. Локально — `uvicorn app.main:app --reload --port 8000` с заполненным `.env`. Вебхуки на ноутбук не придут — пересчёт вызывайте через `POST /admin/api/reconcile`.
- На сервере — git-клон: `git pull`, затем `docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d --build`. Прод-файл — надстройка: убирает публикацию порта и подключает контейнер к сети внешнего прокси. Собранный без неё контейнер опубликует порт наружу и потеряет прокси.
- У сервиса есть внешняя проба «Связь с YouGile» (на статус-странице скрыта) — выкатка в окне обслуживания.
- **Витрина ставится внутри YouGile** отдельно: конфигуратор скриптов (`Ctrl + ~`, только у администратора компании) → «добавить скрипт». Проще всего взять готовый текст с `/widget/script.js?token=…` — адрес сервиса и токен в нём уже подставлены.

## На что наступают

- **Кириллица в скрипте YouGile ломается** при передаче в песочницу: регулярка с `[а-яё]` валит скрипт целиком, строки превращаются в мусор. Код витрины пишется только латиницей, все русские подписи отдаёт сервис вместе с состоянием.
- **Витрина работает в песочнице без `window`** и не видит ID задачи вида `PRI-18` — поэтому сопоставлением занимается сервис.
- **`App.httpGet` из API конфигуратора не годится для GitHub:** нет `User-Agent`, GitHub отвечает `403`. Работает обычный `fetch`. `UI.safeHtml` вырезает SVG — плашки рисуются через `UI.rawHtml` с ручным экранированием.
- **Стикер к доске привязывается только своим.** Если отправить в `PUT /boards/{id}` весь набор стикеров, YouGile отвергнет запрос из-за текстовых стикеров.
- **Первое появление задачи ничего не двигает** — сервис только запоминает состояние. Переходы работают со второго наблюдения, иначе первый запуск перетасовал бы всю доску.
- **Токену GitHub нужна запись.** Для создания и удаления веток — `Contents: Read and write`; с токеном только на чтение GitHub отвечает `403`, сервис пишет об этом в чат задачи. Без токена лимит — 60 запросов в час, полная сверка съедает его быстро.
- **События на одну задачу приходят пачкой** (`create` и `push` почти одновременно), поэтому обработка задачи идёт под мьютексом. Не убирайте его — задача получала по три одинаковых сообщения.
- **Зеркальную колонку нельзя делать рабочей:** задача туда переезжает, но пропадает из виду своей доски.
- **Задача, унесённая на ненастроенную доску,** больше не двигается, но её состояние в базе остаётся.
- **YouGile периодически отвечает `502`/`503`.** Событие, упавшее на такой ошибке, доделает плановая сверка — не выключайте её (`RECONCILE_INTERVAL_MINUTES=0`).