Привет, Хабр! На связи снова Юрий Никулин. В первой части статьи я рассказывал, почему в Cloud.ru решили перейти на doc as code и какой технологический стек мы выбрали. В этой части поделюсь самим процессом — расскажу, как настраивали, тестировали и внедряли новую систему и что из этого вышло.
На старте
Итак, напомню, что для реализации docs as code мы выбрали такой технологический стек:
reStructuredText — язык разметки.
GitLab — система версионирования исходников.
Visual Studio Code — редактор исходников.
Sphinx — система сборки для генерации статических страниц HTML.
Всё, что было у нас на тот момент — горящий дедлайн 4 недели, руководитель документирования (я), 5 техписателей и двое разработчиков на парт-тайм. И нам был нужен план ?
План
Чтобы структурировать процесс и уложиться в срок, мы наметили план. Он отражал ключевые этапы перехода:
В реальности, конечно, часть процессов мы выполняли параллельно. Как всё было — рассказываем дальше.
Знакомимся с Git / Gitlab
Раньше техписатели хранили пользовательскую документацию по всем сервисам локально на своих компьютерах в формате Word. А для публикации на сайте переводили Word в PDF и загружали на сервер.
Теперь же нас ждал принципиально новый подход к документированию. Чтобы переход на docs as code был максимально прозрачным и безболезненным, мы начали знакомить техписателей с новыми инструментами на самых ранних этапах — еще до разработки и тестирования системы.
Начать решили со знакомства с Git и GitLab. Успех на этом этапе помог бы решить сразу две прикладные задачи:
освоить работу с системой контроля версий;
перенести документы Word в репозиторий GitLab — для сохранения документов, инвентаризации и подготовки к этапу конвертации документов из Word в reStructuredText.
Погружение техписателей началось с лекции-ликбеза про систему контроля версий (СКВ) — поговорили о том, зачем нужна СКВ и почему без нее не получится построить docs as code.
Для практических занятий мы завели тестовый репозиторий в GitLab и пригласили разработчика. Он провел несколько воркшопов по теме — показал, как создавать рабочие ветки, работать с основными командами в консоли (add, commit, push), как выглядят самые частые ошибки и как их побеждать. Мы записали воркшопы и сделали тайм-коды к каждому, чтобы было удобно к ним возвращаться.
Техписатели тренировались загружать документы Word в тестовый репозиторий. Они сразу учились работать с Git только через командную строку — это уберегло нас в будущем от множества багов, которые часто возникают при работе с клиентами Git. К тому же в консоль выводится лог операций, что дает +100 к прозрачности процесса и удобству траблшуттинга. Полученные навыки работы с консолью также пригодились при конвертации Word в RST — об этом дальше в статье.
После «лабораторных» работ на тестовом репозитории, мы завели боевой репозиторий и техписатели перенесли в него все документы Word со своих компьютеров.
Что в итоге:
Техписатели освоили Git и перенесли в репозиторий 100+ документов Word. Достижение этой промежуточной цели прибавило ребятам уверенности на следующих уровнях — при изучении языка разметки, системы сборки и редактора кода.
Собираем и тестируем прототип системы
Пока техписатели переносили документы в GitLab, я занялся прототипом системы документирования.
Настраивать и тестировать прототип решили на Linux. В этот момент мы еще не до конца понимали, как будет выглядеть архитектура системы документирования. Но было очевидно, что в целевой картине сборка должна проходить на серверах под Linux.
Немаловажная деталь — по корпоративному стандарту техписатели работали только на Windows. Мы понимали, что то, что сейчас протестируем на Linux, под Windows и Mac может работать немного иначе (кэп). Но разбираться с Windows было некогда и мы приняли эти риски.
Совет: если у вас есть возможность и достаточно времени — настраивайте и тестируйте прототип сразу на целевой ОС. Так вам не придется разбираться со странными багами, которые точно будут, если ОС для тестирования и основная ОС отличаются.
Шаг 1. Разбираемся в reStructuredText и Sphinx
Я не буду здесь подробно рассказывать о языке и системе сборки — вся документация есть в открытом доступе. Остановлюсь на некоторых особенностях Sphinx, из-за которых наш механизм сборки в итоге стал немного нестандартным.
reStructuredText
У reStructuredText (RST), в отличие от Markdown, богатый синтаксис и есть фундамент логической разметки. Чтобы быстро обучиться языку и передать знания техписателям, мы придумали образцовый документ.
Образцовый документ — это сверстанный пример использования основных элементов языка RST (ролей и директив).
Из обширной спецификации RST в образцовый документ попал ограниченный набор директив и ролей. Например, маркированные списки у нас обозначаются только дефисами «-». В то время как спецификация разрешает использовать разные символы — «*», «+», «●», «▶», и в вебе это один и тот же маркированный список. Мы не стали использовать всё многообразие синтаксиса RST, так как хотели добиться однозначности и логической связности элементов в исходниках.
Образцовый документ создавался не в вакууме, а во время конвертации документов из Word в RST. Мы взяли 4 документа по самым популярным сервисам, конвертировали и параллельно дополняли образцовый документ. На этих же документах, переведенных в RST, потом тестировали прототип системы и сборку.
Образцовый документ мы разместили в отдельном проекте GitLab и опубликовали на внутреннем домене компании. Но он стал не только методическим руководством по языку — его можно было клонировать и поработать с ним локально. Например, поиграть с разметкой, чтобы лучше ее понять, протестировать новые директивы и роли.
Sphinx
Sphinx генерирует HTML из RST. При этом у механизма сборки есть ряд особенностей:
Параметры сборки определяются в конфигурационном файле conf.py.
При сборке Sphinx смотрит, где находится conf.py и от него рекурсивно вниз по каталогам ищет файлы с расширением .rst — так собирается документ.Sphinx умеет собирать только один проект с одним файлом conf.py.
В терминах Sphinx «проект» — это документ, например, руководство пользователя.Генерация внешних и кросс-документных ссылок подключается через расширение intersphinx.
При сборке генерируется специальный файл objects.inv — он содержит список объектов, на которые ссылаются из документа.
Эти особенности Sphinx повлияли на структуру хранения исходников и подход к настройке механизма сборки.
Шаг 2. Продумываем структуру хранения исходников и механизм сборки
Sphinx отчасти диктовал нам структуру хранения исходников — в корне проекта (документа) должен лежать конфиг-файл conf.py, а во вложенном каталоге на этом же уровне — файлы с расширением .rst.
Если эту логику применить не к одному, а к 100+ документам по разным сервисам на русском и английском языке, то на выходе получается такая структура:
documents — корневой каталог.
source — каталог с разбивкой по языкам документации (ru/en).
ru/en — каталог с документацией по всем сервисам на русском и английском языке.
product — каталог с файлом conf.py и с RST-исходниками документации по определенному сервису.
Но, как мы помним, Sphinx умеет собирать только 1 документ в один момент времени — такая механика нам не подходила. Готовые расширения Sphinx для сборки нескольких документов тогда найти не удалось. Так нам сразу представился шанс написать свой алгоритм сборки ?
Требования к сборке были такие:
собирать документы по предопределенному списку;
документы должны быть связаны между собой — ссылками и переиспользованием;
собирать документы все вместе, чтобы все ссылки и связи работали, и были актуальными.
Чтобы собирать документы по предопределенному списку, мы написали скрипт и создали файл со списком путей до каталогов с нужными документами. Скрипт брал conf.py документов из списка и последовательно запускал сборку каждого.
Файл со скриптом (build.sh) и файл со списком документов (docs-to-build.txt) разместили в корне репозитория.
Теперь Sphinx умел собирать несколько документов. Профит! Оставалось разобраться с синтаксисом ссылок.
В Sphinx расширение intershpinx отвечает за генерирование ссылок на внешние ресурсы и кросс-документные ссылки.
С настройкой внешних ссылок проблем не было — в пути достаточно указать нужную директиву и URL внешнего ресурса.
С кросс-документными ссылками было сложнее. Мы хотели использовать относительные пути, когда ссылаемся на соседний документ или статью в текущем документе, чтобы автоматически подтягивать заголовки и верифицировать работоспособность ссылок.
Sphinx предлагал не самый простой синтаксис относительных ссылок, а в сумме с нашей вложенной структурой хранения исходников пути получались очень длинными — не ошибиться было невозможно. Тогда мы написали скрипт, который подставлял часть пути автоматически — теперь техписатели прописывалив conf.py только путь до целевого документа.
Теперь ссылаться из текущего документа на статью другого документа мы могли очень просто:
Итак, мы собрали структуру исходников, написали скрипт для сборки документов по списку и разобрались с путями для кросс-документных ссылок.
Sphinx при сборке документа считывает исходные файлы RST, создает дерево, а затем генерирует файлы HTML в каталоге out. Там же создается файл objects.inv, в котором хранится список объектов, на которые ссылаются из документа. При сборке система обращается к этому файлу и формирует ссылки.
Нюанс — когда запускаешь сборку нескольких документов, они собираются последовательно, один за другим. Файлы objects.inv для части документов в этот момент просто не существуют — так и появляются битые кросс-документные ссылки.
Решением проблемы стал, возможно, не очень изящный, но рабочий способ — скрипт, который запускал сборку документации два раза подряд.
Идея в том, что при первой сборке формировались файлы objects.inv для всех документов. При этом в каталоге out появлялись HTML-файлы с битыми ссылками, но это не страшно — ценность этого этапа в том, что создавались objects.inv. Вторая сборка перезаписывала HTML-файлы в out, но ссылки уже были рабочими, так как objects.inv для документов уже существовали.
Двойная сборка точно удлинила процесс, но и дала ощутимый профит — список objects.inv и остальной контент всегда были актуальными.
Что в итоге:
Sphinx собирал все документы по предопределенному списку с помощью скрипта.
Двойная сборка решила проблему с битыми кросс-документными ссылками.
Настраиваем окружение и доставку на прод
Мы настроили и протестировали на Linux прототип системы сборки. Теперь нужно было настроить правильное окружение на Windows-ноутбуках техписателей, сделать удобный инструмент сборки, а также наладить процесс доставки документов в прод.
Окружение
Для настройки правильного окружения на Windows-ноутбуках нужно было установить:
Python правильной версии;
Sphinx правильной версии;
расширения для Sphinx.
Важно было не просто один раз установить это окружение, а сделать так, чтобы оно одинаково работало у всех техписателей, а версии Python, Sphinx и расширений обновлялись на всех устройствах своевременно.
Для решения этих задач мы выбрали Docker — положили всё, что нужно для сборки в Dockerfile и загрузили его в репозиторий к исходникам.
Таким образом, чтобы получить окружение для работы с документацией, техписателю нужно было просто скачать и установить:
Git
VS Code
Docker Desktop
При клонировании репозитория ребята получали на свой локальный компьютер всё, что нужно для работы: исходники RST, файл со скриптом сборки, файл со списком документов для сборки и Dockerfile. А при каждом git pull из мастера — свежую версию окружения.
Сборка
Сборка запускалась вызовом скрипта build.sh через командную строку. Мы решили упростить процесс — сделать так, чтобы техписатель запускал сборку нативно в VS Code и не переключался между окнами. Это оказалось несложно.
VS Code поддерживает инструменты автоматизации таких задач как линтинг, сборка, упаковка и другие. Эти инструменты запускаются внутри VS Code, без использования командной строки.
В репозитории мы создали каталог .vscode, где разместили файл tasks.json с описанием задач и команд для запуска сборки.
Теперь сборка запускалась через VS Code простой комбинацией клавиш Ctrl + Shift + P.
А благодаря расширению Docker для VS Code при запуске сборки автоматически поднимался контейнер и собранная документация становилась доступна на локальном хосте.
Доставка
Итак, окружение на Windows-ноутбуках настроено, документация собирается локально в Docker-контейнере. В первой версии процесс сборки и деплоя документации на прод выглядел так:
Техписатель работал с документом в VS Code.
Коммитил изменения в локальную рабочую ветку.
Пушил изменения в ветку на сервере.
Сливал изменения в мастер.
Переключался локально на мастер и подтягивал изменения с сервера, когда было нужно собрать документацию.
Запускал сборку мастера через VS Code.
После окончания сборки переносил содержимое из каталога out репозитория Source в каталог out репозитория Build, где лежала предыдущая сборка.
Отправлял изменения на сервер в репозиторий Build.
На файловой системе прода 1 раз в минуту с помощью Cron запускался git pull из мастера Build. Если изменений в репозитории не было — ничего не происходило. Если были — обновленная документация публиковалась в прод.
В этом процессе нет автоматизации и достаточно много ручного труда, но он быстро настраивался и доказал свою надежность — мы жили по нему примерно год без каких-либо проблем. Такой алгоритм доставки может подойти, если у вас есть задача запустить docs as code быстро или нет ресурсов на автоматизацию (или она вам просто не нужна — такое тоже бывает).
Спустя год мы настроили автоматический пайплайн в GitLab с покоммитной сборкой на dev-стенде и выкладкой в прод. Все эти 3 года занимаемся автоматизацией: настроили линтер для отслеживания стоп-слов в документации, инструмент для поиска похожих фрагментов текста и много чего еще — обязательно расскажем про это в следующих статьях.
Что в итоге:
Настроили окружение на Windows-ноутбуках с помощью Docker.
Настроили запуск сборки через VS Code.
Опубликовали первую версию документации в проде.
Настраиваем тему
Sphinx как система сборки имеет большое количество готовых тем оформления — мы выбрали Read the Docs. Она достаточно популярна, а значит при необходимости мы могли рассчитывать на помощь сообщества и советы по траблшуттингу в сети.
На кастомизацию темы не пришлось тратить много времени — фронтенд-разработчик всего за 5 часов пропатчил ее под корпоративные цвета и добавил логотип.
Тему разместили не в основном репозитории, а в отдельном, чтобы не смешивать исходники и оформление. С доработкой темы нам иногда помогают разработчики и нам удобнее разделять эти репозитории.
Что в итоге:
Настроили внешний вид портала документации с помощью темы Read the Docs. Все было готово — мы запустились в прод с первыми четырьмя документами ?
Конвертируем Word в reStructuredText
После настройки темы и выкатки портала в прод, все новые документы по продуктам мы уже создавали по принципу docs as code.
Но у нас все еще оставалось около 100 документов Word, которые нужно было конвертировать в RST. Ручная конвертация первых четырех документов, которую мы делали, когда писали образцовый документ, заняла много времени. Хотелось хоть немного автоматизации.
Быстрый поиск в Google предложил Pandoc — бесплатный универсальный конвертер документов. У него подробная документация и несложный синтаксис команд. Всё, что было нужно для работы, — командная строка и установленный Pandoc.
В итоге процесс конвертации выглядел так:
Запускаем Git Bash.
Переходим в каталог с файлом Word, который нужно конвертировать:
$ cd Documents/2convert/ug_vdc
Выполняем команду:
pandoc -f docx --extract-media . -t rst -o index.rst vdc.docx
В результате внутри каталога с документом Word формировался один документ в формате RST (index.rst) и каталог media, содержащий все картинки, которые встретились в документе.
Конечно, результат конвертации был далек от идеала. Pandoc неправильно строил таблицы, нумерованные списки отмечались неверно, встречались лишние элементы в виде рамок. Но все же какая-то часть разметки была правильной: заголовки, маркированные списки, деление на абзацы.
Техписатель разбивал этот длинный index.rst на отдельные файлы, правил верстку и пушил в репозиторий. На этом этапе очень пригодился образцовый документ — с его помощью ребята быстро изучили и запомнили язык разметки.
Pandoc при конвертации документа отправлял скриншоты в отдельный каталог. Мы избавлялись от большей части скриншотов — оставляли их только в тех местах документации, где у пользователя с большой долей вероятности могли возникнуть сложности. Этому принципу мы следуем до сих пор — не подкреплять каждый шаг в инструкциях скриншотами. Их избыточность, на наш взгляд, вредит восприятию материала, а также увеличивает размер репозитория и затрудняет его обслуживание.
Также Pandoc автоматически присваивал скриншотам и схемам в качестве имен номера. Для экономии времени в первой итерации мы их не меняли и загружали в репозиторий как есть. Но в техдолг первой очереди завели задачу сделать для всех картинок семантически значимые имена.
Что в итоге:
Немного ускорили ручную конвертацию из Word в RST с помощью Pandoc. Но нам потребовалось еще около двух месяцев, чтобы окончательно забыть про Word.
Делаем гайды для писателей
В реальности отдельного этапа подготовки гайдов не было. Мы начали копить материал по новой системе документирования с самого начала — когда знакомились с Git и записывали воркшопы.
Дальше на каждом этапе настройки, тестирования и внедрения системы мы не ленились писать инструкции. Делали это сразу, как только добивались успеха в настройке инструмента и процессов. Конечно, настройка системы и параллельное документирование — это трудозатратно. Но если бы отложили это на потом, точно упустили бы много важных нюансов и усложнили работу техписателей с системой.
Инструкции стали основой нашей будущей базы знаний по техстеку системы документирования. Со временем база превратилась в большой портал на Confluence, где можно найти всё про корпоративный docs as code и не только.
Что в итоге:
К моменту запуска портала у нас были все основные материалы по методологии и инструкции:
Инструкция по скачиванию и настройке софта для работы с системой документирования.
Записи воркшопов по Git/GitLab с таймлайнами.
Образцовый документ — для освоения языка разметки.
Инструкции по конвертации Word в RST.
Инструкции по сборке и деплою.
Итог
Мы перешли на docs as code за 4 недели??? — настроили систему и запустили портал с 4 документами. Все новые документы по сервисам уже писали в RST. Дальше команду техписателей Cloud.ru ждали 2,5 месяца конвертации остальных документов и отработки техдолга по скриншотам. Началась новая жизнь ?
Несколько мыслей для тех, кто планирует переезд на docs as code:
Выбирайте техстек под себя
Для реализации docs as code выбирайте техстек, который подходит именно под ваши возможности, задачи и инфраструктуру.
Мы хотели и собрали техстек только из open source решений — было важно в будущем не тратить время на закупки лицензий и согласования бюджета. А также иметь возможность кастомизировать систему, что почти недоступно при использовании проприетарных решений.
Sphinx + RST — возможно для кого-то не самое очевидное и популярное сочетание для реализации docs as code. Но Sphinx написан на Python — языке, на котором в основном ведется разработка продуктов в нашей компании. Благодаря этому мы можем дорабатывать и настраивать систему под себя силами разработчиков Cloud.ru. К тому же у Sphinx и RST обширное сообщество и много советов в сети.
Тестируйте прототип в целевой среде
Старайтесь использовать для настройки и работы одинаковое окружение, чтобы не ловить неожиданные результаты. Совет от кэпа, но тем не менее ?
Включайтесь в догфуддинг
Не поддавайтесь соблазну взять какие-то готовые решения и без тестирования внедрить в процессы отдела. Например, мы могли бы не писать образцовый документ, а дать техписателям ссылку на спецификацию RST. Но нам было важно в будущем сделать работу отдела комфортной и эффективной, а не просто в моменте поставить галочку в списке to-do.
Будьте прозрачны и открыты с вашей командой
Рассказывайте команде о процессе переезда, ваших трудностях, привлекайте на помощь коллег. В команде техписателей Cloud.ru есть ребята с навыками разработки — они активно участвовали во всех этапах внедрения и мотивировали остальных. Обсуждайте все вопросы и слушайте обратную связь — это поможет сделать вашу систему документации действительно удобной.
Что еще есть в блоге Cloud.ru: