0062 — Условия сборки страниц в Antora
Постановка задачи
Есть два окружения, назовём их prod
и dev
.
prod
— продуктовое окружение. Результаты сборки документации показываются пользователям.Для сборки используется плейбук
antora-playbook-prod.yml
.dev
— окружение для служебного пользования. Результаты документации доступны только сотрудникам компании. Помимо страниц, доступных пользователям, есть страницы, которые им показывать не надо.Для сборки используется плейбук
antora-playbook-dev.yml
.
Цель: настроить Antora таким образом, чтобы при сборке проекта для окружения prod
в него не попадал контент, предназначенный только для служебного пользования.
Решение № 1, наивное
Попробуем решить задачу «в лоб»:
В
antora-playbook-dev.yml
добавим свойствоis-dev
:--- site: title: Documentation content: edit_url: false sources: # ... asciidoc: attributes: is-dev: true # ...
В файл навигации
nav.adoc
добавим условия:* xref:index.adoc[] * xref:pub-page-1.adoc[] * xref:pub-page-2.adoc[] ifdef::is-dev[] ** xref:private-page-1.adoc[] ** xref:pirvate-page-2.adoc[] endif::[] * xref:pub-page-3.adoc[]
Соберём проект:
make production
Проверим результат: страницы private-page-1
и private-page-2
отсутствуют в оглавлении, но по-прежнему доступны по прямым ссылкам. Файлы на диске тоже присутствуют.
Решение № 2, сложное
Это решение взято из обсуждения Issue 1080 в репозитории Antora. Оно дополняет решение, описанное выше, за счёт использования расширений.
В каталоге с проектом Antora создайте подкаталог
extensions
:mkdir extensions
Разместите в этом каталоге три указанных файла:
page-unpublish-tag-extension.js
"use strict"; module.exports.register = function() { this.on('documentsConverted', ({ contentCatalog }) => { contentCatalog.getPages((page) => { if (page.out && page.asciidoc?.attributes['page-unpublish'] != null) delete page.out; }); }); }
unlisted-pages-extension.js
module.exports.register = function ({ config }) { const { addToNavigation, unlistedPagesHeading = 'Unlisted Pages' } = config const logger = this.getLogger('unlisted-pages-extension') this .on('navigationBuilt', ({ contentCatalog }) => { contentCatalog.getComponents().forEach(({ versions }) => { versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => { const navEntriesByUrl = getNavEntriesByUrl(nav) const unlistedPages = contentCatalog .findBy({ component, version, family: 'page' }) .filter((page) => page.out) .reduce((collector, page) => { if ((page.pub.url in navEntriesByUrl) || page.pub.url === defaultUrl) return collector logger.warn({ file: page.src, source: page.src.origin }, 'detected unlisted page') return collector.concat(page) }, []) if (unlistedPages.length && addToNavigation) { nav.push({ content: unlistedPagesHeading, items: unlistedPages.map((page) => { const title = 'navtitle' in page.asciidoc ? page.asciidoc.navtitle : (page.src.module === 'ROOT' ? '' : page.src.module + ':') + page.src.relative return { content: title, url: page.pub.url, urlType: 'internal' } }), root: true, }) } }) }) }) } function getNavEntriesByUrl (items = [], accum = {}) { items.forEach((item) => { if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item getNavEntriesByUrl(item.items, accum) }) return accum }
unpublish-unlisted-pages-extension.js
module.exports.register = function ({ config }) { this.on('navigationBuilt', ({ contentCatalog }) => { contentCatalog.getComponents().forEach(({ versions }) => { versions.forEach(({ name: component, version, navigation: nav, url: defaultUrl }) => { const navEntriesByUrl = getNavEntriesByUrl(nav) const unlistedPages = contentCatalog .findBy({ component, version, family: 'page' }) .filter((page) => page.out) .reduce((collector, page) => { if ((page.pub.url in navEntriesByUrl) || page.pub.url === defaultUrl) return collector return collector.concat(page) }, []) if (unlistedPages.length) unlistedPages.forEach((page) => delete page.out) }) }) }) } function getNavEntriesByUrl (items = [], accum = {}) { items.forEach((item) => { if (item.urlType === 'internal') accum[item.url.split('#')[0]] = item getNavEntriesByUrl(item.items, accum) }) return accum }
В плейбук добавьте код регистрации указанных расширений:
# ... antora: extensions: - require: ./extensions/page-unpublish-tag-extension.js - require: ./extensions/unlisted-pages-extension.js - require: ./extensions/unpublish-unlisted-pages-extension.js
В начало страниц, которые нужно скрыть из публичной документации, добавьте код установки атрибута
page-unpublish
:[#private-page-1] = Private Page 1 ifdef::is-dev[]:page-unpublish:endif::is-dev[] // ...
Соберите документацию:
make clean && make prod
В терминал будет выведено несколько предупреждений о том, что некоторые файлы исключены из результатов сборки. Это именно то, чего мы и добивались.
Заключение
Ждём решения Issue 1080. Пока решения нет, актуальную версию расширений вы можете найти в документации Antora, раздел Extend Antora / Antora Extensions / Extension Use Cases.