Постановка задачи

Для создания документации используется Sphinx.

Требования:

  • нужно собирать документацию для публичного и служебного пользования;
  • документация для служебного пользования должна содержать дополнительные разделы и части контента, которых не должно быть видно обычным пользователям.

Решения

ifconfig

Самое первое, что приходит на ум — использовать директиву .. ifconfig::.

  1. Добавляем в conf.py код, позволяющий получить значение переменной окружения IS_INTERNAL (её значение задаётся в настройках используемой системы сборки):

    ```python from os import environ from typing import Final

    extensions = [ “sphinx.ext.ifconfig”, # … ]

    def setup (app): IS_INTERNAL: Final[bool] = bool(environ.get(‘IS_INTERNAL’, False)) app.add_config_value(‘IS_INTERNAL’, IS_INTERNAL, ‘env’)

  2. Добавляем код в макет:

    ```rst .. ifconfig:: IS_INTERNAL

    Список пользователей и паролей см. на `этой странице <https://internal.example.com/passwords/>`.
    

    .. ifconfig:: not IS_INTERNAL

    Для получения списка пользователей и паролей обратитесь к системному администратору.
    

Неплохо. Даже работает так, как и ожидается.

Теперь давайте расширим постановку задачи:

  • нужно собирать документацию для публичного и служебного пользования;
  • документация для служебного пользования должна содержать дополнительные разделы и части контента, которых не должно быть видно обычным пользователям;
  • из документации для публичного пользования нужно убрать некоторые разделы.

Попробуем убрать раздел описанным выше способом:

.. ifconfig:: IS_INTERNAL

   .. _users_passwords:

   Пароли служебных пользователей
   ==============================

   ...

Ой! А почему на рендере у нас заголовок второго уровня (<h2>) вдруг стал третьим (<h3>)? Может, баг какой-то? Нет, ребята, это нормальное поведение .. ifconfig::. Читаем вместе:

This directive is designed to control only content of document. It could not control sections, labels and so on.

То есть работать это не будет.

only

К счастью, у нас есть ещё теги и директива .. only::.

Однако, для её использования нужна небольшая подготовка:

  1. Правим conf.py:

    ```python from os import environ from typing import Final

    # …

    def setup (app): IS_INTERNAL: Final[bool] = bool(environ.get(‘IS_INTERNAL’, False)) if IS_INTERNAL: app.tags.add(‘is_internal’)

  2. Правим вёрстку:

    ```rst .. only:: is_internal

    .. _users_passwords:
    
    Пароли служебных пользователей
    ==============================
    
  3. Собираем:

     export IS_INTERNAL=true; make dirhtml
    

    Или так (даже проще чем возня с переменными окружения):

     sphinx-build dirhtml --tag is_internal source/ build/
    

Вот сейчас всё рендерится как надо: целый раздел спрятан от анонимусов, зато доступен на внутреннем сервере компании.

Скрытие страниц

А если мы хотим спрятать целую страницу или раздел?

  1. Добавьте в начало скрытых страниц макрос .. orphan::. Теперь Sphinx не будет падать с ошибкой «Страница не включена ни в одно оглавление».

  2. В conf.py добавьте код, управляющий исключениями:

     from os import environ
     from typing import Final
    
     # ...
    
     IS_INTERNAL: Final[bool] = bool(environ.get('IS_INTERNAL', False))
    
     if IS_INTERNAL:
         exclude_patterns = [
             'secret/*',
             'images/internal'
         ]
    

    В публичную документацию не попадут все страницы в каталоге secret/ и страница images/internal.rst.

  3. В оглавлении нужных разделов вместо конкретных имён файлов используйте wildcard:

     .. toctree::
    
        secret/*
        images/*
    

    Документация, собранная таким образом, будет содержать все нужные страницы, кроме перечисленных в параметре exclude_patterns.

    Если важен порядок следования разделов — измените имена файлов таким образом, чтобы они шли в нужном порядке, например:

     01-index.rst
     02-ssh.rst
     03-users.rst
     04-files.rst