🏢 GitHub-организация: выгоды и момент миграции

Статус: анализ для решения · 2026-08-17. Все утверждения о планах (Free/Team/Enterprise) и механике переноса проверены по официальной документации GitHub и Cloudflare в этот день; там, где дока противоречит сама себе, это отмечено явно. Организация fortemate создана (Free), репозитории пока под rabestro. Решение по фазе D (brand-fortemate §8): переносить строго после фазы C, или не переносить вовсе — эта заметка даёт критерии.

1. Что реально даёт организация (и работает на Free)

CI и раннеры — главный практический выигрыш

  • Один self-hosted runner на все репозитории. Раннер регистрируется на уровне организации и обслуживает задания всех её репо. На личном аккаунте уровня «организация» не существует в принципе — раннер привязывается только к одному репозиторию (ровно та стена, в которую упирались с homelab-раннером: 30 репо = 30 регистраций). Org-уровень доступен на Free; управление доступом — через единственную default-группу с политикой «All / Selected repositories». ⚠️ Дополнительные runner groups — противоречие в самой доке GitHub (врезка «Who can use this feature?» говорит Free, абзац ниже — только Team); для одного homelab-раннера default-группы достаточно.
  • Практический эффект для нас: sbt-сборки движка/play-api на homelab-раннере с тёплыми кешами (coursier, sbt, JMH-бейзлайны) вместо холодных GitHub-hosted машин — быстрее и не тратит минуты.
  • Org-secrets/variables: один INGEST_TOKEN/CLOUDFLARE_API_TOKEN на организацию вместо копий в каждом репо, одна точка ротации. Лимиты щедрые (1000 секретов, 48 KB). ⚠️ На Free org-секреты видны только публичным репо — приватным нужен Team.
  • Reusable/starter workflows между репо работают на Free — но это НЕ аргумент за миграцию: публичные reusable workflows работают и между репозиториями личного аккаунта.

Люди и bus factor

  • Роли: в организации на каждом репо доступны Read / Triage / Write / Maintain / Admin + орг-роли (Owner, Member, Billing manager, Security manager, Moderator). На личном репо ролей две: владелец и collaborator, причём collaborator — это всегда write, в приватном личном репо нельзя дать даже read-only. Для второго участника (супруга) это разница между «полный push-доступ или ничего» и, например, triage-ролью (разбирать issues без права трогать код).
  • Teams + CODEOWNERS с @fortemate/team — автоназначение ревьюеров.
  • 🔑 Bus factor — самый недооценённый пункт. У личного аккаунта один владелец, и наследование доступа — юридически мутная процедура через поддержку GitHub. У организации может быть два owner’а: супруга как второй owner означает, что проект переживает потерю доступа к личному аккаунту (или его владельцу). Для проекта с прод-сервисами и пользователями это страховка, которую нельзя получить никак иначе.

Бренд и доверие

  • github.com/fortemate — витрина open-core-проекта: профиль организации, README, pinned repos. Для AGPL-стратегии «серьёзная платформа, а не хобби одного человека» — это часть позиционирования.
  • Верификация домена fortemate.com → бейдж Verified на профиле организации — доступно на Free (подтверждено докой).
  • GitHub Sponsors на организацию — отдельный от личного спонсор-профиль (регистрация по waitlist). Слой 0 монетизации может собирать спонсорство на бренд, а не на частное лицо — чище и для доверия, и для будущей бухгалтерии.

Политики и безопасность

  • Audit log организации: 180 дней на Free (ограничения «без стриминга, Git-события 7 дней» касаются всех не-Enterprise).
  • Централизованный контроль fine-grained PAT: организация может требовать одобрения токенов, запрашивающих доступ к её репо, — на Free.
  • GitHub Apps (CodeRabbit, Cloudflare Pages, …): один инсталл на организацию с «All repositories», включая будущие репо, — вместо поштучной установки.

2. Чего организация НЕ даёт на Free (границы и мифы)

Хотелось быРеальность
Org-rulesets (центральная защита веток на все репо)Team (доки противоречат: Team vs Enterprise; специализированная страница говорит Team)
Org-секреты для приватных репоTeam
Branch protection / rulesets в приватных репоНедоступны на Free вообще — и в организации, и на личном аккаунте. Миграция здесь ничего не меняет
Дополнительные runner groupsВероятно Team (противоречие в доке)
Кастомные роли репоEnterprise

🔑 Лайфхак с минутами Actions: пул 2000 бесплатных минут/мес для приватных репо у Free-организации — отдельный от такого же пула личного аккаунта. Если приватные репо (hunter, house-bots, lab) остаются под rabestro, а публичные едут в fortemate — получается два независимых бесплатных пула, и приватные ничего не теряют (org-фич для приватных на Free всё равно нет). Это аргумент за частичную миграцию: публичное — в org, приватное — на личном.

3. Цена переноса: что ломается (проверено по докам)

Переезжают автоматически: issues, PR, stars, watchers, wiki, releases, webhooks, deploy keys, репо-секреты; git- и веб-ссылки редиректятся (редирект живёт, пока на старом месте не создан репо с тем же именем).

Ломается и требует рук:

  1. Cloudflare Pages git-интеграция не переживает transfer (официальная позиция Cloudflare: пересоздать проект под новым владельцем) — затрагивает play и docs. Это самый болезненный пункт.
  2. GitHub Apps не переезжают — installation привязан к владельцу; CodeRabbit/CF Pages ставить на организацию и выдавать доступ заново.
  3. Fine-grained PAT с resource owner = rabestro теряют доступ к перенесённым репо — перевыпуск с resource owner = fortemate (и включить поддержку fine-grained PAT в настройках организации).
  4. GitHub Pages не редиректится (в отличие от git-ссылок): project-сайты переезжают на fortemate.github.io/<repo>; user-сайт rabestro.github.io (contributor-docs на jc.id.lv) в организацию не переносится в принципе — у org может быть только свой fortemate.github.io.
  5. GitHub Packages: Maven/Docker-пакеты переезжают вместе с репо, но URL реестра меняется (maven.pkg.github.com/rabestro/… → …/fortemate/…) без редиректа — все потребители ломаются разом. Поэтому фаза D — строго после фазы C (движок уже на Central, GH Packages пуст). npm/RubyGems/NuGet-пакеты: связка «пакет↔репо» рвётся.
  6. Приватный репо, перенесённый в Free-организацию, лишается Pages (если был опубликован).
  7. SonarCloud-проекты не переносятся между SonarCloud-организациями (привязка 1:1 к GitHub-аккаунту): пересоздание с нуля, история обнуляется, новые project key + SONAR_TOKEN в CI — детали в oss-services. Затрагивает 4 Scala-репо.

4. Когда мигрировать: триггеры

Предусловие всегда одно: фаза C выполнена (пакеты ушли с GitHub Packages на Central) — иначе п. 5 выше ломает всех потребителей движка.

Дальше миграция оправдана, когда наступают хотя бы два из четырёх:

  1. Общий homelab-раннер реально нужен — CI-минуты или скорость sbt-сборок стали болью (это может случиться и раньше остальных: раннер — самая осязаемая выгода).
  2. Второй человек работает регулярно — нужны роли/teams, а не «collaborator с полным write»; и/или пора оформить второго owner’а как bus-factor-страховку.
  3. Бренд публичен (фаза B сделана) — github.com/fortemate становится витриной open-core, спонсорство направляется на организацию.
  4. Слой 0 монетизации запущен и хочется собирать Sponsors на организацию, а не на частное лицо.

Рекомендуемый порядок самой миграции (когда время придёт): пилот на некритичном публичном репо без интеграций → волна простых публичных репо → play/docs с Cloudflare Pages последними (по чеклисту: снять конфиг → перенести → переустановить Apps → пересоздать Pages-проект → перевыпустить PAT) → приватные не переносить вовсе до апгрейда на Team (лайфхак минут + отсутствие выгод).

Отдельно: второй owner можно оформить хоть завтра — организация уже существует, и добавление супруги owner’ом не требует переноса ни одного репозитория. Bus-factor-страховка доступна без миграции.

5. Пилот выполнен: dicechess-bot-java (2026-08-17)

Первый репозиторий перенесён в организацию и проверен снаружи end-to-end. Результаты — эталон для остальных волн:

ПроверкаРезультат
Transfer✅ issues/PR/секреты/история ранов переехали; github.com/rabestro/dicechess-bot-java отдаёт 301 на новый адрес
CI: доступ к GH Packages чужого владельца✅ главный неизвестный вопрос решён положительно: воркфлоу под GITHUB_TOKEN организации fortemate успешно тянет dicechess-engine-scala_3 из maven.pkg.github.com/rabestro/... (публичный пакет, кросс-владельческая аутентификация работает). Org-секрет с PAT не понадобился
CD: Docker-образ✅ пуш-цель сама переехала на ghcr.io/fortemate/dicechess-bot-java (имя из github.repository); старые образы остались под rabestro
Docs/Pages✅ публикуется на fortemate.github.io/dicechess-bot-java (200); ⚠️ старый URL jc.id.lv/dicechess-bot-java — 404 без редиректа (как и предсказывалось: Pages не редиректится)
Release workflow✅ ручной dispatch прошёл под новым владельцем
Локальная копия✅ remote обновлён на git@github.com:fortemate/... (SSH-схема сохранена)

Хвосты после пилота (ручные, не блокируют):

  • Перенастроить потребителя образа (хостинг, где бот запущен) с ghcr.io/rabestro/... на ghcr.io/fortemate/....
  • Обновить ссылки на докс-сайт бота (jc.id.lv/dicechess-bot-java → fortemate.github.io/dicechess-bot-java) в bots.jc.id.lv и README соседних репо.
  • Убедиться, что CodeRabbit и прочие Apps имеют доступ к репо в организации (инсталлы не переезжают).
  • Не создавать под rabestro репозиторий с именем dicechess-bot-java — это убьёт редиректы.

Вывод для следующих волн: перенос простого публичного репо занимает минуты, все три «типовые грабли» (пакетная auth, ghcr-namespace, Pages-URL) ведут себя ровно как задокументировано выше. Порядок остальных волн — без изменений (§4): простые публичные → play/docs с Cloudflare Pages последними → приватные не переносить до Team.

🔗 Связанное