![](https://habrastorage.org/getpro/habr/upload_files/c2e/9b4/4f4/c2e9b44f42a31f2814f06fd4be87270f.jpg)
Всем привет! Мы продолжаем разбирать наши решения. Сегодня расскажем о том, как, используя генератор Material for MkDocs, можно создать несложный, но удобный статический сайт с документацией (и не только!).
А ещё как встроить его в CI/CD для автосборки и автопубликации (мы используем Gitlab CI, о чём подробно рассказывалось в предыдущем туториале), а также как использовать плагины к генератору чтобы, к примеру, создавался не только сайт, но и его pdf-представление.
Добро пожаловать под кат!
Оглавление
Что такое генератор статического сайта? И почему был выбран именно MkDocs?
Как и на каком языке писать материалы для генератора MkDocs?
Перед началом
В этот раз мы публикуем не просто куски кода — мы выложили в открытый доступ весь «боевой» репозиторий с текущей документацией, а также скриптами сборки этого сайта.
Самые нетерпеливые могут сразу посмотреть репозиторий на сайте gitlab.com.
Что такое генератор статического сайта? И почему был выбран именно MkDocs?
Генератор статического сайта это программа, собирающая полноценный сайт из одного или нескольких шаблонов, а также материалов для сайта.
Создание или редактирование статического сайта выполняется предельно просто:
Владелец создаёт новый файл или редактирует существующий в человекочитаемом формате (например, в markdown).
Далее запускается генератор статического сайта — и результатом работы он выдаёт сайт в виде набора html-страниц с уже применённой темой, форматированием, построенным оглавлением и пр.
Полученный сайт владелец уже может использовать локально. Ну или выложить на каком-либо хостинге — чтобы к нему могли получить доступ другие люди.
И важно отметить: для статических сайтов не требуется поддержка каких-либо серверных языков и/или баз данных — всё уже свёрстано и готово к использованию.
Минусом же статических сайтов является то, что все пользователи будут видеть один и тот же контент (то есть сделать авторизацию, разделение на роли и прочее не получится).
Немного личных впечатлений
19 мая 2015 года Rich Lander сделал первый коммит в репозиторий с комментарием «Experimenting with docs». Похоже, что именно с этого коммита и началась история docs.microsoft.com — проекта, содержащего на текущий момент документацию по огромному количеству языков, технологий и продуктов Microsoft.
Впервые попав на этот сайт, я был поражён:
на сайте имелась «гибкая» вёрстка (было удобно смотреть сайт и с компьютеров, и с мобильных устройств),
присутствовала удобная навигация с фильтром: находясь в документации по .NET, можно было ввести название класса или метода и сразу перейти к нему.
![Как выглядит docs.microsoft.com Как выглядит docs.microsoft.com](https://habrastorage.org/getpro/habr/upload_files/a49/810/d05/a49810d0562f176fb92fcf2066530c3c.png)
В дальнейшем на сайте появился автоперевод статей на русский язык (а также возможность переключения и чтения статьи на языке оригинала).
Но самое главное, что меня зацепило — кто угодно (а не только сотрудник Microsoft) мог предложить правки, дополнения или перевод материалов. Ведь все материалы лежали в публичном репозитории github!
И эта идея хранения информации в стиле «предложить новое может каждый» так плотно засела у меня в голове, что, обретя возможность сделать документацию для одного из разрабатываемых приложений, я решил попробовать сделать маленький, но аналог.
На сегодняшний день список самых популярных систем, способных сгенерировать статические сайты, достаточно небольшой:
Jekyll. Написан на Ruby, его использует Github для Github Pages (вероятно потому, что один из создателей Jekyll — сооснователь github?).
Hugo. Написан на Go, позиционирует себя как самый быстрый генератор статических сайтов.
Sphinx. Написан на Python. Именно с его помощью сгенерирована документация к языку Python.
MkDocs. Также написан на Python.
MkDocs был выбран как самый популярный генератор на Github (на момент написания статьи у репозитория MkDocs было более 12 тыс. «звёздочек»). А больше всего приглянулся его форк с красивой (по моему мнению) встроенной темой, называемый Material for MkDocs. А всё потому, что сгенерированный с его помощью сайт из коробки предоставлял и гибкую вёрстку, и человекочитаемые url-ы, и встроенный быстрый поиск по всем статьям (который не требует какой-либо серверной реализации).
Создание и генерация тестового сайта
Для того, кто не очень знаком с языком Python, но немного представляет что такое контейнеризация, проще всего воспользоваться MkDocs с использованием их docker-образа.
При подготовке этого материала использовалась ОС Linux (дистрибутив Debian 10).
Перед созданием первого сайта на компьютере выполним в терминале (bash) команду docker pull squidfunk/mkdocs-material
для загрузки docker-образа mkdocs.
На заметку: команды docker pull
и docker run
могут требовать прав суперпользователя. Соответственно, если для выполнения команды не хватает прав, перейдите в режим суперпользователя командой su
или воспользуйтесь командой sudo
.
Как создать новую папку с материалами для сайта
Выберите папку, в которой будут храниться материалы для генерации сайта (например,
/home/user/test/
).Откройте в указанной папке терминал (или перейдите в существующем терминале в нужную папку (например, командой
cd /home/user/test/
).Выполните в открытом терминале команду
docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material new .
В результате в нашей папке появятся два новых файла:
.
├─ docs/
│ └─ index.md
└─ mkdocs.yml
Файл mkdocs.yml
— это файл конфигурации, используемый MkDocs. Файлы, находящиеся в папке docs
(в частности, index.md) — это исходные материалы, из которых в дальнейшем будет сгенерирован наш сайт.
Как сгенерировать сайт по существующим материалам
Выполните в терминале, открытом в папке с материалами (папке с файлом mkdocs.yml) команду
docker run --rm -it -v ${PWD}:/docs squidfunk/mkdocs-material build
В результате в папке с материалами появилась подпапка site
, в которую генератор записал наш только что созданный сайт. Выглядит он следующим образом:
![Первый сгенерированный сайт Первый сгенерированный сайт](https://habrastorage.org/getpro/habr/upload_files/1f5/0dc/4d1/1f50dc4d102406f71e8223108b3294de.png)
Как пересоздавать сайт «на лету» при внесении изменений в файлы
MkDocs умеет также создавать превью сайта «на лету», отслеживая изменения в исходных материалах в реальном времени и перестраивая только их. Для включения такого режима выполните в терминале, открытом в папке с материалами (папке с файлом mkdocs.yml) команду
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
Пока команда выполняется (прервать её можно, нажав комбинацию клавиш Ctrl+C), на вашем компьютере на порту 8000 будет запущен веб-сервер, который вы можете использовать для просмотра результатов ваших правок на сайте. Просто откройте браузер, введите в строке адреса http://localhost:8000/ — и смотрите как выглядит сгенерированный сайт (правда, для обновления текущий страницы придётся нажимать F5).
Как и на каком языке писать материалы для генератора MkDocs?
Генератор сайтов MkDocs в качестве исходных материалов поддерживает файлы как формата markdown (.md) так и формата html.
Стоп-стоп, скажете вы. Html? Зачем нам генерировать сайт из "материалов" на языке html, если результат — всё равно html?!
Дело в том, MkDocs не только преобразовывает markdown в html, но и корректно применяет к этим страницам темы, а также даёт простор для работы своим многочисленным плагинам, которые: осуществляют подсветку кода, индексируют содержимое для быстрого поиска, могут сгенерировать PDF-содержимое одной страницы или всего сайта и т.д.
Таким образом давайте условимся: исходные материалы это то, что содержит информацию и минимум применения сложных разметки, стилей и прочего, а сгенерированный сайт это результат работы генератора, содержащий те же тексты, что и в исходных материалах, но уже с применёнными темами и иным добавленным генератором содержимым.
Что же писать в исходных материалах?
Да в принципе всё, что захотите. MkDocs полностью поддерживает синтаксис markdown. Для примера картинка с markdown-текстом слева и сгенерированным из него html справа:
![Текст в формате markdown и как он выглядит после преобразования в html Текст в формате markdown и как он выглядит после преобразования в html](https://habrastorage.org/getpro/habr/upload_files/4f6/88a/97b/4f688a97b81c7b7eec4544a815d7b515.png)
Более подробно о формате markdown и о том, как оформлять такие документы, написано в шпаргалке по markdown.
И, — что для меня оказалось очень важно — MkDocs совершенно нормально относится к html-тэгам как внутри html, так и внутри markdown-файлов.
Нам это пригодилось для вставки в начало некоторых страниц справки html-плеера с видеоуроком по той же теме, что описана в материале.
![Пример встраивания youtube-плеера в наш help Пример встраивания youtube-плеера в наш help](https://habrastorage.org/getpro/habr/upload_files/5fa/64e/a47/5fa64ea47a530ec082285e4710756471.png)
Разбираем структуру файла mkdocs.yml
Давайте подробнее изучим содержимое созданного на предыдущем шаге файла mkdocs.yml
site_name: My Docs
Негусто.
К счастью, все сайты с документацией по MkDocs собраны с помощью генератора MkDocs и можно посмотреть содержимое mkdocs.yml, к примеру, сайта https://www.mkdocs.org/.
Приведём содержимое этого файла с дополнительными комментариями, на что влияют указанные в файле параметры:
# [Обязательное] Название сайта
site_name: MkDocs
# Базовый адрес сайта
site_url: https://www.mkdocs.org/
# Описание сайта. Добавляется в meta в html-заголовке каждой страницы.
site_description: Project documentation with Markdown.
# Автор (или авторы) сайта. Добавляется в html-заголовок каждой страницы.
site_author: MkDocs Team
# Ссылка на репозиторий, в котором хранятся исходные материалы.
repo_url: https://github.com/mkdocs/mkdocs/
# Относительный путь, который в сочетании со ссылкой на репозиторий (repo_url)
# и относительным адресом страницы даст адрес, по которому можно вызвать
# редактор страницы. Для repo_url-ов известных площадок (например,
# GitHub или GitLab можно оставить пустым - он подставится автоматически)
edit_uri: ""
# Информация о теме, которая будет применена для сгенерированного сайта.
# И её параметрах
theme:
name: mkdocs
locale: en
analytics: {gtag: 'G-274394082'}
highlightjs: true
hljs_languages:
- yaml
- django
# Структура навигации генерируемого сайта.
nav:
- Home: index.md
- Getting Started: getting-started.md
- User Guide:
- Overview: user-guide/index.md
- Installation: user-guide/installation.md
- Writing Your Docs: user-guide/writing-your-docs.md
- Choosing Your Theme: user-guide/choosing-your-theme.md
- Customizing Your Theme: user-guide/customizing-your-theme.md
- Localizing Your Theme: user-guide/localizing-your-theme.md
- Configuration: user-guide/configuration.md
- Deploying Your Docs: user-guide/deploying-your-docs.md
- Developer Guide:
- Overview: dev-guide/index.md
- Themes: dev-guide/themes.md
- Translations: dev-guide/translations.md
- Plugins: dev-guide/plugins.md
- About:
- Release Notes: about/release-notes.md
- Contributing: about/contributing.md
- License: about/license.md
extra_css:
- css/extra.css
# Список подключённых расширений для препроцессора языка Markdown
markdown_extensions:
- toc:
permalink:
- admonition
- attr_list
- def_list
- mdx_gh_links:
user: mkdocs
repo: mkdocs
copyright: Copyright © 2014 <a href="https://twitter.com/_tomchristie">Tom Christie</a>.
# Список использованных плагинов MkDocs
plugins:
- search
- redirects:
redirect_maps:
user-guide/plugins.md: dev-guide/plugins.md
user-guide/custom-themes.md: dev-guide/themes.md
user-guide/styling-your-docs.md: user-guide/choosing-your-theme.md
На что хочется обратить внимание в структуре навигации из примера выше:
Путь страницы в url будет сгенерирован из
site_url
и относительного пути страницы в репозитории. Например, страницаUser Guide -> Overview
будет доступна по urlhttps://www.mkdocs.org/user-guide/index.html
(потому, что файл, из которого сгенерировали страницу, имел относительный путьuser-guide/index.md
).Один файл можно указать в навигации и более одного раза.
Если файл отсутствует в разделе
nav
— соответствующая страница всё равно будет сгенерирована, но не будет показана в навигации сайта. При этом перейти на неё, зная её url (см. первый пункт списка), вполне себе можно.Плагин redirects (подробнее об использовании плагинов расскажу чуть ниже) позволяет настроить переадресацию страниц со старого расположения на новое.
Более подробно о структуре файла mkdocs.yml а также на то, как он влияет на используемую тему, расскажет подробная справка на сайтах mkdocs.org, Material for mkdocs а также примеры файлов mkdocs.yml в других репозиториях.
Настраиваем Gitlab CI и Gitlab Runner для генерации статического сайта
Следующий этап — это добавление сайта в систему контроля версии (СКВ) и написание скрипта, который сам собирал бы нам сайт как только мы сохраним новую его версию.
Воспользуемся git-ом как СКВ и Gitlab CI для обработки действий.
Внимание. У вас может возникнуть вопрос: а зачем мы, собственно, настраиваем Gitlab CI, если выше была показана команда serve, которая на лету создаёт сайт и поддерживает его в актуальном состоянии при изменениях в файлах?
Ответ включает как минимум следующие пункты:
Команда
serve
не обеспечивает сохранение истории изменений, а СКВ для этого и предназначена.Команда
serve
не предназначена для работы в production-окружении. В частности, serve может «сломаться» если при очередной сборке произошла ошибка (например, структура mkdocs.yml стала некорректной).
Поскольку многие читатели знакомы с git, я уберу под спойлер инструкцию по созданию репозитория и его сохранению (пушу) в Gitlab.
Инструкция по созданию репозитория и пушу его в Gitlab
Нам потребуется установленный консольный или графический клиент git-а. По устоявшейся уже в сообществе традиции процесс в этой статье будет описан консольными командами.
Итак, для создания нового репозитория откройте терминал в папке с материалами, после чего:
Создадим файл .gitignore и внесём в него 1 строку (команду игнорировать папку site); для этого в терминале введём команду
echo '[Ss]ite/' > .gitignore
Создадим репозиторий командой
git init
Создадим первый коммит, состоящий из всех созданных нами файлов
git add -A git commit -m 'Сайт в состоянии hello, world'
Если при попытке выполнения команды git gommit команда выдаст ошибку из-за на отсутствия имени и/или электронной почты, то введите следующие две команды
git config user.name "<имя, которое будет отображаться в коммитах в этом репозитории>"
git config user.email "<электронная почта, которая будет отображаться в коммитах в этом репозитории>"
После чего повторите команду git commit.
Теперь осталось создать в gitlab новый проект, перейти на страницу созданного проекта, найти и скопировать на странице ссылку на репозиторий (она выделена в отдельную рамочку и снабжена кнопкой «скопировать»).
Далее воспользуемся этой ссылкой для сохранения созданного репозитория в созданный проект gitlab:
git remote add origin <ссылка на проект gitlab>
git push -u origin master
Теперь, когда репозиторий создан и связан с Gitlab, создадим в папке с исходными материалами файл .gitlab-ci.yml. О структуре этого файла мы кратко рассказывали в предыдущей публикации. Посему не будем подробно останавливаться на структуре, лишь приведём код файла:
stages:
- build
build_docs_job:
stage: build
# tags: docker # раскомментируйте эту строку если вам
# потребуются метки для определения конкретного Runner-а
# (см. ниже)
only:
- /^master$/
- merge_requests
image:
name: squidfunk/mkdocs-material
entrypoint: [""]
script:
- 'mkdocs build --site-dir site'
artifacts:
name: "site_$($CI_PIPELINE_IID)"
paths:
- site
Этим файлом мы указали Gitlab CI собирать сайт каждый раз, когда появляются новые коммиты в ветке master или в процессе создания или обновления merge request-ов.
Конечно, для того, чтобы это заработало, потребуется установить Gitlab Runner на машину, которая и будет собирать сайт по написанной в файле .gitlab-ci.yml «инструкции».
На заметку: подробную и актуальную инструкцию по установке Gitlab Gunner на ту сборку ОС Linux, что установлена у вас, можно посмотреть на сайте документации Gitlab.
Ниже для примера мы приведём пошаговую инструкцию как это сделать для ОС Debian Linux:
Посмотрите токен для регистрации Runner-а в вашем Gitlab. В зависимости от того, где будет доступен ваш Runner:
только в одном проекте — смотрите токен в меню проекта Settings > CI/CD в разделе Runners,
в группе проектов — смотрите токен в меню группы Settings > CI/CD в разделе Runners,
для всех проектов Gitlab-а — смотрите токен в секции администрирования, меню Overview > Runners.
Найдите актуальную для вашей архитектуры версию Gitlab Runner на сайте https://gitlab-runner-downloads.s3.amazonaws.com/latest/index.html.
Скачайте нужный deb-пакет с помощью браузера или команды в терминале
curl -LJO "https://gitlab-runner-downloads.s3.amazonaws.com/latest/deb/gitlab-runner_${arch}.deb"
, где${arch}
следует заменить на выбранную вами архитектуру.Выполните в терминале команду для установки скачанного пакета Gitlab Runner
sudo dpkg -i gitlab-runner_<arch>.deb
где<arch>
опять же, следует заменить на выбранную вами архитектуру.Зарегистрируйте раннер командой
sudo gitlab-runner register
Далее потребуется ввести ответы на вопросы мастера регистрации Runner-а:coordinator URL — http или https адрес вашего сервера gitlab;
gitlab-ci token — введите токен, полученный на предыдущем шаге;
gitlab-ci description — описание Runner-а, которое будет показываться в интерфейсе Gitlab-а;
gitlab-ci tags — через запятую введите тэги для Runner-а. Если вы не знакомы с этим механизмом, оставьте поле пустым. Отредактировать его можно позднее через интерфейс самого gitlab-а. Тэги можно использовать для того, чтобы определённые задачи выполнялись на определённых Runner-ах (например, чтобы настроить сборку ПО на Runner-е, развёрнутом на компьютере ОС Windows, а подготовку документации на Runner-е с ОС Linux);
enter the executor — ответьте
docker
. На этом шаге указывается оболочка, в которой будут выполняться команды.
На заметку: если у вас несколько runner-ов с разными исполнителями (например, shell для сборки ПО, а docker для сбора документации), настройте тэги для этого раннера. Для этого добавьте в тэги раннера, например, слово docker
— и уберите первый символ решётки (#) из файла .gitlab-ci.yml. Ну или аналогичным образом настройте тэг с другим именем.
Проверим работу того, что настроили — сделаем коммит с созданным кодом. Можно прямо в ветку master, если, конечно, вы работаете в тестовом репозитории. В ином случае рекомендуем создать отдельную ветку с этими изменениями, затем создать на эту ветку merge request.
![Так выглядит настроенная автосборка Так выглядит настроенная автосборка](https://habrastorage.org/getpro/habr/upload_files/3b9/952/332/3b9952332c1125c526f5ba2ca7f7697e.png)
Делаем свой docker-образ и добавляем в него плагин для генерации PDF-файла («печать всего сайта»)
Что ж, в целом проект уже работает. Однако для того, чтобы им воспользоваться, требуется либо соединение с интернетом, либо передать клиенту много html-страничек. Но что если требуется, к примеру, распечатать все материалы? Давайте настроим экспорт всего сгенерированного сайта в формат PDF и добавим его в артефакты Gitlab CI.
Стандартный функционал MkDocs сделать печать в PDF не позволят — но не беда, для этого и многих других функций сам генератор поддерживает плагины. И нас будет интересовать плагин mkdocs-with-pdf.
Чтобы добавить его в docker-образ генератора, потребуется модифицировать этот образ. И сделать это можно буквально в пару строк:
Создайте в папке с файлом mkdocs.yml новый файл с именем dockerfile
Добавьте в него следующие строки:
FROM squidfunk/mkdocs-material
RUN pip install mkdocs-with-pdf
Откройте страницу Container Registry в вашем проекте в Gitlab (<адрес вашего проекта в gitlab>/container_registry, на картинке стрелочки 1 и 2), подсмотрите на ней имя образа, которое следует использовать при сборке (стрелочка 3).
Страница Container Registry в Gitlab Откройте терминал в папке с файлом Dockerfile и выполните в нём команду сборки модифицированного образа:
sudo docker build -t <имя образа, которое следует использовать> .
С вероятностью 100% сборка образа закончится ошибкой так как образ
squidfunk/mkdocs-material
основан на Alpine Linux, где многие компоненты (типа компилятора gcc) по умолчанию отсутствуют. Чтобы образ собрался корректно. нам понадобится перед установкой пакета загрузить все необходимые не связанные с python-ом зависимости для него.
После дописывания зависимостей (которые были у плагина на момент написания статьи) файл Dockerfile будет выглядеть следующим образом:FROM squidfunk/mkdocs-material
RUN apk add build-base ttf-ubuntu-font-family libffi-dev zlib-dev
libwebp-dev jpeg-dev harfbuzz-dev fribidi-dev freetype-dev
cairo-dev musl-dev pango-dev gdk-pixbuf-dev
&& pip install mkdocs-with-pdfКогда сборка завершилась успехом, авторизуемся на сервере gitlab через докер, после чего загружаем полученный образ на сервер:
docker login <имя образа для сервера gitlab, указанное на странице Container Registry>
docker push <имя образа для сервера gitlab, указанное на странице Container Registry>
И осталось только отредактировать mkdocs.yml чтобы включить в нём сборку сайта в PDF-файл. Для этого добавим раздел plugins, в котором перечислим все включаемые плагины (в данном случае только один —with-pdf
), а также их параметры.
site_name: My Docs
# На заметку: без явного указания темы пересобранный с плагином образ material-mkdocs сайт собрать не может
theme:
name: material
plugins:
- with-pdf:
cover: true
cover_title: My Docs
cover_subtitle: версия для PDF
copyright: (C) 2021 Rostelecom PJSC
toc_title: Оглавление
toc_level: 3
output_path: ./help.pdf
Пара слов об использованных параметрах:
cover_title — заголовок, который будет указан на сгенерированной странице PDF. Может отличаться от названия сайта.
cover_subtitle — подзаголовок, который будет указан на сгенерированной странице PDF.
copyright — копирайт.
toc_title — название оглавление (по умолчанию contents).
toc_level — оглавление по умолчанию строится сначала по структуре навигации, а затем по заголовкам в документе. Параметр toc_level указывает максимальный уровень заголовка, который включается в оглавление. Например, значение 3 говорит о том, что все заголовки от первого до третьего уровня (h1 — h3).
output_path — путь имя сгенерированного pdf-файла.
Полный их список доступен на странице плагина.
Запускаем в терминале сборку сайта (уже нашим образом)
docker run --rm -it -v ${PWD}:/docs <имя образа для сервера gitlab, указанное на странице Container Registry> build
И убеждаемся, что в корневой папке появился файл help.pdf
со всем содержимым сайта, а также кликабельным оглавлением
![Сгенерированный вместе с сайтом pdf-файл Сгенерированный вместе с сайтом pdf-файл](https://habrastorage.org/getpro/habr/upload_files/1f4/c12/2f6/1f4c122f662fcdedf7f18514c00ad1f2.png)
Послесловие
На текущий момент мы продолжаем улучшать генерацию сайта со справочной информацией, а также стараемся наполнять и поддерживать его.
В рамках этой статьи были рассказаны лишь основы, однако мы уже двигаемся дальше:
Добавлен плагин
monorepo
для сборки сайта из нескольких отдельно живущих папок с материалами. Мы скомбинировали плагинmonorepo
с плагиномwith-pdf
чтобы можно было собирать и отдельные части сайта в PDF (будут только некоторые тонкости при указании папки с переопределениями темы при генерации части сайта).Включены плагины
search
(поиск по сайту) иgit-revision-date
(чтобы отображать дату последнего изменения страницы).Тема сайта дорабатывается под корпоративные стандарты.
Также дорабатывается возможность автогенерации PDF-файла для справки прямо в процессе сборки нашего ПО (так как, в частности, материалы из раздела «Настольное приложение» поставляются вместе с ПО «Настольное приложение Видеонаблюдение»).
Если у вас будут вопросы — пишите в комментариях, мы с радостью ответим. Есть предложения по улучшению? — разделы issues и merge requests мы тоже просматриваем достаточно оперативно.
И спасибо за прочтение!