ADR 0071 — Симметричная публикация по ступеням и корень, который предлагает выбор
- Статус: принят
- Дата: 2026-08-14
- Расширяет: ADR 0034
Контекст
ADR 0034 установил, что каждая
постоянная ветка публикует собственную документацию. Несимметричным он оставил
адрес: main публиковала в корень (/brabo/), и только у qa и dev был
суффикс (/brabo/qa/, /brabo/dev/).
Асимметрия взимала три цены, все оплачиваемые читателем:
- Корень не говорил, что существуют другие ступени. Три сайта не
видели друг друга: попавший на
/brabo/не мог добраться до/qa/, а попавший на/dev/из Google — доnoIndex— не знал, что читает непроверенную версию. - Особый случай заражал механизм. Публикация в корень с удалением
содержимого унесла бы
/dev/и/qa/вместе с ним, поэтому у workflow был шаг, который выполнялся только наmain, чтобы вернуть два других внутрь дерева. Путь, отличный от двух других, — это путь, который задействуется в трети случаев. - Негде было объявить зрелость. "Стабильная", "кандидат" и "в разработке" были знанием тех, кто уже знал.
Решение
1. Три ступени в /brabo/<branch>/
main, qa и dev публикуются симметрично. Особый случай main исчезает, а
вместе с ним условный шаг workflow.
2. Корень — генерируемый индекс, а не перенаправление
Перенаправить /brabo/ на /brabo/main/ было бы проще и отвечало бы
меньше: вопрос, который заставляет смотреть больше одной ступени, — "есть ли уже в qa
то, что мне нужно?", а перенаправление проглатывает этот вопрос.
scripts/docs/landing.mjs генерирует страницу с тремя ступенями, меткой зрелости
каждой и проставленной версией, прочитанной из тегов самого репозитория
(vX.Y.Z для main, vX.Y.Z-<estagio>.N для остальных — формат
scripts/ci/tag-release.ts).
Она предлагает то, что существует в дереве, а не фиксированный список: отсутствующая ступень
не становится ссылкой, потому что ссылка, дающая 404, хуже отсутствия. И она выходит из индекса
поиска с canonical, указывающим на /main/, — корень является индексом, а не содержимым,
и, будучи проиндексированным, конкурировал бы с настоящей документацией.
3. 404.html в корне удерживает старые ссылки
Перенос main сломал бы каждую уже сохранённую глубокую ссылку: /brabo/architecture
перестала бы существовать. GitHub Pages отдаёт 404.html корня для
неизвестного пути, поэтому он перенаправляет /brabo/<algo> на /brabo/main/<algo>.
Есть защита, которая не является деталью: 404 внутри ступени (страница, которой действительно нет) не может быть перенаправлен снова, иначе браузер зацикливается. Поэтому тест защиты существует.
4. Ступень объявляется, а не выводится из пути
Это самое важное изменение, и оно невидимо.
Сайт узнавал, продакшен ли он, сравнивая BASE_URL === '/brabo/'. Когда все три в
подкаталоге, это сравнение становится ложным и для main — и следствием
был бы noIndex: true в настоящей документации: она молча исчезла бы из Google,
с зелёным CI, потому что ничто в сборке не проваливается из-за недоиндексации.
DOCS_BRANCH сопровождает DOCS_BASE_URL, а E_PRODUCAO становится
DOCS_BRANCH === 'main'. Выводить окружение из строки пути — это тот вид
связанности, который проявляется только при изменении пути.
5. Дерево собирается для всех трёх
Шаг, существовавший только на main, становится единственным путём: две другие ступени
берутся из текущей gh-pages, ступень push — из новой сборки, корень генерируется, и
публикуется всё вместе с keep_files: false.
Не keep_files: true: тот никогда ничего не удаляет, и страница, удалённая из
репозитория, осталась бы опубликованной навсегда. Сборка сохраняет то, что должно остаться,
и удаляет то, что не должно, — включая, бесплатно, миграцию: файлы, которые
main оставила в корне в старой раскладке, отсутствуют в собранном дереве, поэтому
первый push их уносит. Ручной очистки gh-pages не требуется.
6. Переход засевает /main/ из старого корня
В старой раскладке gh-pages:main не существует. Без обработки первый push
dev или qa оставил бы /brabo/main/ несуществующим — а поскольку 404.html
перенаправляет как раз туда, каждая старая ссылка указывала бы на 404 до
следующего продвижения до main, которое может занять дни.
Опубликованный сегодня корень — это буквально сборка main. Поэтому он засевает
/main/ при первой сборке, и сайт никогда не остаётся без стабильной документации.
При следующей публикации main нормальный путь берёт управление, и этот блок больше
не выполняется.
7. Переключатель использует абсолютный href
Ссылка пересекает сайты с разным baseUrl. Относительная разрешилась бы внутри
собственной ступени — /brabo/dev/main/, которой не существует.
Последствия
- About репозитория по-прежнему указывает на корень, который теперь является
индексом. Никакого изменения конфигурации не понадобилось — и хорошо, что не
понадобилось: ни один токен CI не имеет области администрирования репозитория
(
BRABO_BOT_TOKEN— этоrepo+workflow), так что автоматизация этого потребовала бы новых учётных данных. /brabo/перестаёт быть документацией и становится лишним кликом для того, кому нужна только стабильная. Это цена зафиксированного здесь выбора: показывать все три.README.mdиapps/web/src/routes/AuthLayout.tsxпо-прежнему указывают на корень, который является точкой входа.- Публикация остаётся прямым push в
gh-pages— третьим исключением из единственной двери PR, уже задокументированным в политике веток. Этот ADR зависит от этого больше, чем 0034: сборка всего дерева — ровно то, чегоactions/deploy-pagesне умеет.
Отброшенные альтернативы
- Перенаправить корень на
/main/: проще и отвечает меньше (пункт 2). - Оставить
mainв корне и суффикс только дляqa/dev: это текущее состояние, и оно сохраняет особый случай, задействуемый в трети случаев. - Версионирование Docusaurus (
docs:version): другая ось. Ступень конвейера — это зрелость ТОГО ЖЕ содержимого; версия документации — замороженное содержимое.CONTRIBUTING.mdуже фиксирует, что в0.xверсионирование добавляет сопровождение, ничего не давая взамен. - Публиковать
mainв корне И в/main/: сохранило бы старые ссылки без404.htmlценой дублирования всего сайта при каждой публикации и наличия двух индексируемых адресов с одинаковым содержимым.