Многие уже давно или активно используют или смотрят в сторону модели хранения и публикации документации как кода, это значит применять к документации все те же правила, инструменты и процедуры, что и к программному коду, например, хранить в репозитории, прогонять тесты, собирать и релизить в CI/CD. Этот подход позволяет поддерживать документацию актуальной к коду, версионировать и отслеживать изменения, используя привычные инструменты разработки.
Однако в то же время во многих компаниях годами существуют также и вики-системы, в которых к документации получают доступ другие команды и сотрудники, например, менеджеры проектов. Что если вам захотелось привести хранение и публикацию к единому виду, то есть наряду с HTML публиковать доки и в Confluence? В этой статье я дам обзор решений задачи публикации документов из репозитория в Confluence.
Одно решение я давно активно использую сама в команде разработки интерфейсов (связка RST-Sphinx+sphinxcontribbuilder), а остальные представлю в качестве альтернативы, сразу оговорюсь, что на практике я их не пробовала, только изучила конфигурацию.
Sphinx doc+sphinxcontribbuilder
Sphinx (не путать с одноименным поисковым индексом) — это генератор документации, написанный на Python и активно используемый сообществом, он вполне хорошо работает также и в других средах.
На его настройке мы останавливаться подробно не будем, оговорюсь лишь, что из коробки он умеет генерировать статический HTML, man, pdf, и еще ряд форматов, а для корректной сборки и публикации в репозитории должны быть файлы index.rst (разметка главной страницы), conf.py (файл конфигурации) и Makefile (файл, описывающий процесс генерации форматов, вот его вполне можно зашить в докер и запускать sphinx-build команду там).
Из коробки Sphinx умеет генерировать доки из легковесной разметки формата *.rst (RestructuredText), но мы добавили возможность писать и в Markdown (CommonMark flavor) для тех разработчиков, кому это удобнее (в этом нам помогло расширение m2r, которое конвертирует MD в RST).
У нас все окружение для Sphinx уже было настроено, а сборка документации зашита в отдельный стейдж в пайплайне в Jenkins, поэтому мы пошли дальше и использовали расширение sphinxcontrib.confluencebuilder, которое умеет собирать доки в нативном для Confluence формате, а затем публиковать их. Confluence в данном случае является одним из форматов вывода документации, наряду с HTML.
Чтобы это заработало, вам нужно подключить расширения в conf.py, ниже фрагмент конфигурации.
extensions = [
'sphinxcontrib.confluencebuilder',
'm2r'
]
templates_path = ['_templates']
source_suffix = ['.rst', '.md']
master_doc = 'index'
exclude_patterns = [
u'docs/warning-plate.rst',
u'FEATURE.md',
u'CHANGELOG.md',
u'builder/README.md'
]
А затем конфигурировать расширение, у него есть набор настроек:
confluence_publish = True
# включить или отключить публикацию в Confluence
confluence_space_name = 'YOURSPACEKEY'
# пространство, в которое мы будем публиковать из этого репозитория
confluence_parent_page = 'Raw Documentation'
# родительская страница, можно установить домашнюю страницу пространства
confluence_server_url/confluence_cloud_url = 'https://yourconfluence.site.net/'
# хост вашего Confluence
confluence_publish_prefix = 'WIP-'
# префикс перед названиями страниц, чтобы гарантировать их уникальность
confluence_publish_postfix = '-postfix'
# добавляет постфикс к названию страницы
confluence_header/footer_file
# путь к файлу, содержание которого включается вверху/внизу страницы (мы используем include из других страниц, например, вставляем плашку, которая просит не редактировать документ вручную, так как он генерируется автоматически)
confluence_page_hierarchy = True
# если в мастер-документе есть оглавление, то перечисленные в нем документы публикуются как дочерние для этого документа и так рекурсивно по всему дереву страниц
confluence_purge
# удаляет страницы, которые не публикуются в новом цикле, удобно если вы постоянно публикуете все страницы или меняете заголовки, но используйте с умом - эта настройка рекурсивно удаляет все документы в папке, которую вы указали
confluence_remove_title
# удалять дублирующийся title
confluence_publish_subset
# задать список документов к публикации
confluence_max_doc_depth
# максимальная глубина дочерних страниц, которые нужно публиковать как отдельные страницы в Confluence
confluence_prev_next_buttons_location = 'top'
# где будут располагаться кнопки Вперед, Назад
confluence_server_user = os.getenv("CONFLUENCE_USERNAME", "confluence-bot")
confluence_server_pass = os.getenv("CONFLUENCE_PASSWORD", "")
target = os.getenv("TARGET", "")
if target == "CONFLUENCE":
confluence_publish_prefix = ''
confluence_parent_page = 'Your Space'
#эта логика позволяет публиковать документы из локального репозитория в отдельную папку для ревью, они помечаются префиксом WIP-, а если публикация идет из релизного пайплайна, то документы едут в "прод"
Важный момент, в том, что даже если страница (исходник в .rst) не указана в toc и не добавлена в exclude_patterns, то она все равно будет опубликована, но вне иерархии.
Названия страниц в Confluence будут соответствовать первому title страницы, например, если у вас в файле example.rst указан заголовок Example, подчеркнутый знаками равно, он станет названием страницы в Confluence.
Правило гигиены, довольно очевидное, но все же: создайте бота с авторизационными данными которого будете публиковать документы, их можно передавать в виде переменных окружения в docker compose, использовать в пайплайнах.
Конечно, есть и подводные камни. Во-первых, не весь синтаксис RST поддерживается для публикации в Confluence (╯°□°)╯︵ ┻━┻), это неудобно, если вы хотите из одного исходника собирать HTML и Confluence. Не поддерживаются директивы сontainer, hlist, почти все атрибуты директив, например, подсвечивание строк в код блоке, нумерация в оглавлении, align и width для listtable. Список того, что поддерживается, он довольно неплох.
Из приятного, поддерживаются includes, это позволяет переиспользовать фрагменты контента между разными документами, autodoc для сборки документации из кода, math для математических формул, отрисовка тикетов и фильтров из jira (для этого придется в конфигурации прописать еще и Jira сервер), нумерованные заголовки и многое другое, буквально 3 января закатили большое обновление.
Кстати, поддержка Jira появилась и в мультиконвертер Pandoc, начиная с версии 2.7.3 Pandoc поддержал соответствующую confluence wiki разметку.
Для тех макросов и элементов Confluence, которые не поддерживаются есть грязный хак. В RST есть директива … raw::, и у нее есть атрибут сonfluence, она принимает conf разметку, если вам очень нужен какой-то макрос — можно скопировать его в режиме редактирования страницы в Confluence (режим исходного кода доступен по иконке <>) и вставить его «сырой» код туда. Но я вас этому не учила.
.. raw:: confluence
<ac:structured-macro ac:macro-id="c38bab13-b51e-4129-85ef-737eab8a1c47" ac:name="status" ac:schema-version="1">
<ac:parameter ac:name="colour">Green</ac:parameter>
<ac:parameter ac:name="title">Is used</ac:parameter>
</ac:structured-macro>
Результат получается такой:
Почему нам понадобилось настраивать публикацию из локального репозитория на тестовую страницу, а не сразу на «прод»? Дело в том, что при публикации все страницы каждый раз публикуются заново и перетирают изменения, сделанные вручную или комменты в строке (inline). Поэтому, когда документ находится в работе, мы решили публиковать его в какую-то отдельную страницу, этакий dev mode, чтобы добавлять опубликованные версии в ревью и собирать комментарии.
На CI публикация реализована в виде отдельного стейджа в пайплайне в Jenkins, внутрь этого стейджа зашит запуск docker образа на удаленном реджистри, в котором реализован запуск sphinx-build с нужной конфигурацией. Лучше сразу сделать этот шаг пропускаемым.
pipeline {
agent {
label "${AGENT_LABEL}"
}
stage("Documentation") {
steps { ansiColor('xterm') {
withCredentials([usernamePassword(
credentialsId: "${DOCUMENTATION_BOT}",
usernameVariable: 'CONFLUENCE_USERNAME',
passwordVariable: 'CONFLUENCE_PASSWORD'
)]) {
sh "docker-compose -p $COMPOSE_ID run sphinx-doc confluence"
}
}}
}
Внутри стейджа по факту запускается docker-compose -p release-branch-name run sphinx-doc confluence. В свою очередь Jenkinsfile описывает зависимости и среду, в которой будет выполняться шаг, процесс сборки и обновления информации в таргете. Из тестов пока есть только проверка синтаксиса .md и .rst с помощью doc8 и markdownlinter.
Еще один нюанс: при каждой публикации сабсета страниц Sphinx обновляет все дерево, каждую страницу. То есть, даже если контент не менялся, создается изменение, если у вас настроены уведомления в канал, то он будет засоряться множеством уведомлений.
Еще несколько способов
Foliant с Confluence в качестве бэкэнда
Инструмент для генерации документации Foliant с Mkdocs и множеством препроцессоров под капотом и бэкэндом в виде Confluence. Подробнее можно почитать тут, но если кратко, то он использует pandoc для конвертации md в HTML, а затем публикует его в Confluence. Нужно только сконфигурировать бэкэнд и установить pandoc в окружение в качестве зависимости.
Выгодные отличия от первого решения: он умеет восстанавливать inline комментарии в тех же местах, что они были до перепубликации страницы, позволяет создавать страницы, задав их в конфиге, редактировать их названия, а также вставлять контент внутрь уже существующей страницы, для этого нужно вручную задать якорь foliant на странице в Confluence.
Работает только с исходником на Markdown.
Metro
Мультитул, который публикует самые разные форматы источников в Confluence, от Google Docs до Salesforce Quip, и в Markdown тоже умеет.
Для публикации нужно в папке, где лежат ваши .md файлы положить файл manifest.json, в нем указать папку, файл, который нужно опубликовать, для каждого файла указать confluence page id. Названием страницы будет первый заголовок в файле (#). У этого инструмента есть небольшие извращения с Markdown разметкой, поэтому смотрите доки. Вложения и картинки нужно сложить в ту же папку, а еще инструмент позволяет задать использование оглавления прямо в конфиге.
Gem md2conf
Ruby gem md2conf, он конвертирует Markdown в нативный для Confluence XHTML. Дальше можно написать Rake таску, которую в свою очередь можно вызывать через Gitlab CI/Jenkins по пушу в master, затем дергать Confluence API, чтобы опубликовать страницу. Чтобы не заносить к себе Ruby окружение, заверните зависимости для этого gem в контейнер.
Как отсылать запросы в Confluence API описано тут.
Работает только с исходником на Markdown.
Из найденного в Github
На самом деле таких скриптов или cli-инструментов в сообществе уже наделали некоторое количество, но экспериментировала я только с md2conf, все они делятся на две группы.
Те, что просто конвертируют форматы (md, asciidoc, rst -> confluence/xhtml):
- markdown2confluence
- md2confluence
- jedi4ever/markdown2confluence
- Есть даже сервис с web интерфейсом для конвертации между md и Confluence.
Самый продуманный из них, что я видела — вот этот (https://github.com/rogerwelin/markdown2confluence-server), автор сразу написал Dockerfile, который поднимает cli-инструмент как REST сервер, далее к нему можно послать пачку запросов на конвертацию.
И те, что сразу реализовывают в себе и запросы к Confluence API, нужно только указать API ключ в конфиге:
Выбирайте любой из вариантов (в зависимости от вашего языка разметки и стека) и собирайте свой пайплайн в зависимости от задач, которые перед вами стоят.
P.S. Если вы поделитесь в комментариях другими найденными решениями задачи, я буду очень признательна.
Автор: Светлана Новикова