- PVSM.RU - https://www.pvsm.ru -

Docs as code против или вместе с Confluence? Обзор нескольких способов публикации из репозитория в Confluence

Многие уже давно или активно используют или смотрят в сторону модели хранения и публикации документации как кода, это значит применять к документации все те же правила, инструменты и процедуры, что и к программному коду, например, хранить в репозитории, прогонять тесты, собирать и релизить в CI/CD. Этот подход позволяет поддерживать документацию актуальной к коду, версионировать и отслеживать изменения, используя привычные инструменты разработки.

Однако в то же время во многих компаниях годами существуют также и вики-системы, в которых к документации получают доступ другие команды и сотрудники, например, менеджеры проектов. Что если вам захотелось привести хранение и публикацию к единому виду, то есть наряду с HTML публиковать доки и в Confluence? В этой статье я дам обзор решений задачи публикации документов из репозитория в Confluence.

Одно решение я давно активно использую сама [1] в команде разработки интерфейсов (связка 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 [2], которое конвертирует MD в RST).

У нас все окружение для Sphinx уже было настроено, а сборка документации зашита в отдельный стейдж в пайплайне в Jenkins, поэтому мы пошли дальше и использовали расширение sphinxcontrib.confluencebuilder [3], которое умеет собирать доки в нативном для Confluence формате, а затем публиковать их. Confluence в данном случае является одним из форматов вывода документации, наряду с HTML.

Docs as code против или вместе с Confluence? Обзор нескольких способов публикации из репозитория в Confluence - 1

Чтобы это заработало, вам нужно подключить расширения в 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. Список того, что поддерживается, он довольно неплох [4].

Из приятного, поддерживаются includes, это позволяет переиспользовать фрагменты контента между разными документами, autodoc для сборки документации из кода, math для математических формул, отрисовка тикетов и фильтров из jira (для этого придется в конфигурации прописать еще и Jira сервер), нумерованные заголовки и многое другое, буквально 3 января закатили большое обновление [5].

Кстати, поддержка Jira появилась и в мультиконвертер Pandoc, начиная с версии 2.7.3 Pandoc [6] поддержал соответствующую 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>

Результат получается такой:

Docs as code против или вместе с Confluence? Обзор нескольких способов публикации из репозитория в Confluence - 2

Почему нам понадобилось настраивать публикацию из локального репозитория на тестовую страницу, а не сразу на «прод»? Дело в том, что при публикации все страницы каждый раз публикуются заново и перетирают изменения, сделанные вручную или комменты в строке (inline). Поэтому, когда документ находится в работе, мы решили публиковать его в какую-то отдельную страницу, этакий dev mode, чтобы добавлять опубликованные версии в ревью и собирать комментарии.

На CI публикация реализована в виде отдельного стейджа в пайплайне в Jenkins, внутрь этого стейджа зашит запуск docker образа на удаленном реджистри, в котором реализован запуск sphinx-build с нужной конфигурацией. Лучше сразу сделать этот шаг пропускаемым.

Docs as code против или вместе с Confluence? Обзор нескольких способов публикации из репозитория в Confluence - 3

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. Подробнее можно почитать тут [7], но если кратко, то он использует pandoc для конвертации md в HTML, а затем публикует его в Confluence. Нужно только сконфигурировать бэкэнд и установить pandoc в окружение в качестве зависимости.

Выгодные отличия от первого решения: он умеет восстанавливать inline комментарии в тех же местах, что они были до перепубликации страницы, позволяет создавать страницы, задав их в конфиге, редактировать их названия, а также вставлять контент внутрь уже существующей страницы, для этого нужно вручную задать якорь foliant на странице в Confluence.

Работает только с исходником на Markdown.

Metro

Мультитул, который публикует самые разные форматы источников в Confluence, от Google Docs до Salesforce Quip, и в Markdown тоже умеет.

Для публикации нужно в папке, где лежат ваши .md файлы положить файл manifest.json, в нем указать папку, файл, который нужно опубликовать, для каждого файла указать confluence page id. Названием страницы будет первый заголовок в файле (#). У этого инструмента есть небольшие извращения с Markdown разметкой, поэтому смотрите доки [8]. Вложения и картинки нужно сложить в ту же папку, а еще инструмент позволяет задать использование оглавления прямо в конфиге.

Gem md2conf

Ruby gem md2conf [9], он конвертирует Markdown в нативный для Confluence XHTML. Дальше можно написать Rake таску, которую в свою очередь можно вызывать через Gitlab CI/Jenkins по пушу в master, затем дергать Confluence API, чтобы опубликовать страницу. Чтобы не заносить к себе Ruby окружение, заверните зависимости для этого gem в контейнер.

Как отсылать запросы в Confluence API описано тут [10].

Работает только с исходником на Markdown.

Из найденного в Github

На самом деле таких скриптов или cli-инструментов в сообществе уже наделали некоторое количество, но экспериментировала я только с md2conf, все они делятся на две группы.

Те, что просто конвертируют форматы (md, asciidoc, rst -> confluence/xhtml):

Самый продуманный из них, что я видела — вот этот (https://github.com/rogerwelin/markdown2confluence-server), автор сразу написал Dockerfile, который поднимает cli-инструмент как REST сервер, далее к нему можно послать пачку запросов на конвертацию.

И те, что сразу реализовывают в себе и запросы к Confluence API, нужно только указать API ключ в конфиге:

Выбирайте любой из вариантов (в зависимости от вашего языка разметки и стека) и собирайте свой пайплайн в зависимости от задач, которые перед вами стоят.

P.S. Если вы поделитесь в комментариях другими найденными решениями задачи, я буду очень признательна.

Автор: Светлана Новикова

Источник [20]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/python/343411

Ссылки в тексте:

[1] использую сама: #Sphinx

[2] m2r: https://pypi.org/project/m2r/

[3] sphinxcontrib.confluencebuilder: https://github.com/sphinx-contrib/confluencebuilder/

[4] Список того, что поддерживается, он довольно неплох: https://sphinxcontrib-confluencebuilder.readthedocs.io/en/latest/markup.html

[5] большое обновление: https://sphinxcontrib-confluencebuilder.readthedocs.io/en/latest/changelog.html

[6] версии 2.7.3 Pandoc: https://pandoc.org/releases.html

[7] можно почитать тут: https://foliant-docs.github.io/docs/backends/confluence/#confluence-backend-for-foliant

[8] доки: https://metro.readthedocs.io/en/latest/using/prepare-to-import-markdown.html

[9] md2conf: https://github.com/pegasd/md2conf

[10] тут: https://developer.atlassian.com/server/confluence/confluence-rest-api-examples/

[11] markdown2confluence : https://github.com/chunpu/markdown2confluence

[12] md2confluence: https://github.com/jormar/md2confluence

[13] jedi4ever/markdown2confluence: https://github.com/jedi4ever/markdown2confluence

[14] сервис с web интерфейсом: https://github.com/Yunyun548/Markdown2Confluence

[15] md_to_conf: https://github.com/RittmanMead/md_to_conf

[16] mark: https://github.com/kovetskiy/mark

[17] confluence-python: https://github.com/m-vdb/confluence-python

[18] confluence-publisher: https://github.com/confluence-publisher/

[19] asciidoctor-confluence: https://github.com/asciidoctor/asciidoctor-confluence

[20] Источник: https://habr.com/ru/post/483898/?utm_source=habrahabr&utm_medium=rss&utm_campaign=483898