# Gitea pull-mirror из GitVerse — сессия 2026-08-30 Цель: зеркалировать репозиторий с gitverse.ru в локальный Gitea для катастрофоустойчивости (на случай поломки bigbox). ## Контекст - Gitea 1.26.2 (docker, /opt/gitea.nixg.ru), API http://127.0.0.1:3000/api/v1, домен gitea.nixg.ru. - GitVerse: только SSH (`git@gitverse.ru:kpa39l/.git`), REST-API не открыт анонимно, https-клон требует PAT (которого нет). SSH-ключ ~/.ssh/gitverse аутентифицирует как kpa39l. - Стек gitea: gitea-db (postgres), gitea-redis, gitea, act-runner. ## Что РАБОТАЕТ (проверено) ### 1. Токен Gitea API без пароля (CLI от имени git) Gitea в docker НЕ запускается от root: `docker exec gitea gitea ...` падает с "Gitea is not supposed to be run as root". Но есть системный пользователь `git` (uid 1000): ``` docker exec -u 1000 gitea gitea admin user list docker exec -u 1000 gitea gitea admin user generate-access-token \ --username estorozhenko --token-name monitoring-mirror \ --scopes "read:repository,write:repository,read:user,write:user" # → "Access token was successfully created: " ``` Это обходит неизвестный пароль пользователя целиком. ### 2. Дать контейнеру gitea SSH-ключ для внешнего git-хоста Volume gitea: `/opt/gitea.nixg.ru/gitea -> /data`. Ключ кладётся в home пользователя git: ``` mkdir -p /opt/gitea.nixg.ru/gitea/git/.ssh cp ~/.ssh/gitverse /opt/gitea.nixg.ru/gitea/git/.ssh/id_rsa chown -R 1000:1000 /opt/gitea.nixg.ru/gitea/git/.ssh && chmod 600 .../id_rsa docker exec -u 1000 gitea ssh -o BatchMode=yes -T git@gitverse.ru # → "Hi there, kpa39l! You've successfully authenticated..." ``` Gitea зеркалит от имени владельца SSH-ключа (kpa39l). ### 3. Разрешить SSH-протокол для миграций По умолчанию Gitea отклоняет SSH в migrate. В docker-compose gitea добавить: ``` - GITEA__service__MIGRATIONS_ALLOWED_PROTOCOLS=ssh,git,http,https ``` Затем пересоздать КОНТЕЙНЕР (env-переменные пишутся в app.ini при старте): `docker compose up -d --force-recreate gitea` (простой `restart`/`up -d` может не перечитать). ## РАБОЧИЙ РЕЦЕПТ: HTTPS + oauth2 PAT (проверено 2026-08-30, 13 репо мигрировано) Штатный migrate API работает, если clone_addr — **https с PAT gitverse**: ``` git ls-remote "https://oauth2:@gitverse.ru/kpa39l/.git" HEAD # тест PAT curl -X POST http://127.0.0.1:3000/api/v1/repos/migrate \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{"clone_addr":"https://oauth2:@gitverse.ru/kpa39l/.git", "repo_name":"","repo_owner":"estorozhenko","service":"git", "mirror":true,"mirror_interval":"8h"}' # 201 = создан; затем POST /api/v1/repos///mirror-sync (200 = принято) ``` - PAT gitverse лежит в URL уже существующего mirror: `docker exec gitea cat /data/git/repositories/estorozhenko/monitoring.git/config` → `https://oauth2:@gitverse.ru/kpa39l/monitoring.git` (значение — из `/opt/hermes/.hermes/secrets/git-tokens.env`) - Проверка синхронизации: `git ls-remote git@gitverse.ru:kpa39l/.git HEAD` vs `docker exec gitea git --git-dir=/data/git/repositories/estorozhenko/.git rev-parse HEAD`. - PAT gitverse в URL уже существующего mirror: `docker exec gitea cat /data/git/repositories/estorozhenko/<существующий-repo>.git/config` → `https://oauth2:@gitverse.ru/kpa39l/.git` (токен в secrets) - Gitea-токен без пароля: `docker exec -u 1000 gitea gitea admin user generate-access-token --username estorozhenko --token-name X --scopes "read:repository,write:repository,read:user,write:user" --raw` ## НЕ РАБОТАЕТ / НЕ РЕШЕНО (проверено, потрачено много шагов) - `POST /api/v1/repos/migrate` с `clone_addr: "git@host:path"` → 422 "the provided url is invalid". - `POST /api/v1/repos/migrate` с `clone_addr: "ssh://git@host/path"` → 422 "the provided url protocol is not allowed" — даже после добавления ssh в MIGRATIONS_ALLOWED_PROTOCOLS и --force-recreate. Это РАСХОЖДЕНИЕ: конфиг в app.ini показывает `ssh,git,http,https`, но migrate всё равно режет. Дальнейшее копание в бинаре (strings), swagger, UI-форме `/repo/migrate` (требует сессионную cookie, не токен) успеха не дало. ВАЖНО: признать тупик и не листать это заново вслепую. Вывод: для pull-mirror использовать HTTPS+PAT (см. выше), ssh:// не трогать. - `POST /user/repos` с полями mirror/clone_addr — НЕ создаёт пулл-миро: CreateRepoOption таких полей не имеет, репозиторий создаётся обычным (mirror=False, empty=True). - Пароль пользователя gitea неизвестен — Basic auth в API не работает. ## Обходные пути, проверенные частично - Локальная копия через git-remote: создать обычный repo в gitea, добавить в его bare remote на gitverse и гонять `git fetch` по cron (вместо штатного mirror-механизма). Надёжный, уродливый, но не требует migrate API. НЕ доведён до конца в сессии. - PAT GitVerse по https — если у пользователя появится, migrate с `https://@gitverse.ru/...` должен сработать штатно. ## Правило для агента Если `POST /repos/migrate` с ssh:// вернул 422 протокол — НЕ перезапускать gitea ещё раз и не копать strings: сообщить о блокаде и предложить обходной путь (локальный remote+cron или PAT). Повторение уже проверенных тупиков — потеря времени.