Как стать автором
Обновить
Флант
Специалисты по DevOps и Kubernetes

Проверка документации без боли: наш путь к автоматическому спелл-чеку через CI/CD (обзор и видео доклада)

Уровень сложностиПростой
Время на прочтение11 мин
Количество просмотров273

Привет! Меня зовут Константин Нежберт, я технический писатель команды Deckhouse в компании «Флант». В апреле я выступал на международной ИТ-конференции «Стачка» с докладом о проверке документации. Я рассказал, как мы с командой технических писателей проверяем документацию, зачем она нужна и какие есть подходы. Также подробно разобрал интеграцию инструмента для проверки.

Это текстовый вариант доклада, а ссылку на видео с выступлением можно найти в конце материала.

Зачем проверять документацию и как это делать

Проверка документации является важным этапом при работе с ней по следующим причинам:

  • Опечатки инженеров. Документация хранится вместе с кодом на GitHub, и её обычно пишут сами инженеры по мере добавления новых фич. Поскольку инженеры не всегда являются специалистами в работе с текстом, в документации могут встречаться ошибки и опечатки. Поэтому текст необходимо быстро обновлять.

  • Неверное использование терминов. В документации часто встречаются специфический DevOps-сленг и термины из области ИТ, которые могут быть понятны специалистам, но не всегда уместны в официальных текстах. 

  • Смешивание латинских и кириллических букв. Иногда в русских словах случайно используются похожие по форме латинские буквы (например, латинская «C» вместо русской «С»), что может приводить к путанице и снижать качество текста. 

Перечисленные выше проблемы можно устранить, и я выделил три следующих способа. 

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

Также можно нанять корректора, который будет отслеживать все изменения в документации и проверять их. Это действительно помогает повысить качество текста и обеспечивает единый стиль, ведь все вопросы, связанные с оформлением, будут сосредоточены в руках одного человека. Но и у этого подхода есть недостатки: корректор — тоже человек, он может что-то упустить из виду из-за усталости или по другим причинам.

Но мы выбрали третий вариант — автоматизировали проверку документации. Такой подход позволяет существенно снизить количество ошибок и обеспечить единообразие стиля, поскольку все проверки выполняются автоматически и не зависят от человеческого фактора. Однако у этого решения тоже есть свой минус — на его внедрение и настройку потребуются время и усилия.

Какой инструмент выбрали для проверки документации

Сначала мы рассмотрели готовые инструменты для автоматической проверки документации. Их главные плюсы — минимальные усилия на внедрение, наличие сообщества и поддержки разработчиков, к которым можно обратиться за помощью. Но такие решения часто слишком специфичны и не всегда подходят под конкретный кейс и особенности документации. 

Например, в одном из проектов использовался словарь неправильных написаний слов — словарь опечаток, по которому проверялись варианты ошибок. Однако мне это показалось сомнительным, ведь составить такой словарь корректно практически невозможно, так как вариантов ошибок может быть очень большое количество, а ещё могут появляться новые, что не всегда возможно отследить.

Пример неправильных написаний слов
Пример неправильных написаний слов

Потом мы обратили внимание на инструменты, на базе которых можно построить собственную систему проверки. Один из таких — LanguageTool. Это инструмент для проверки орфографии, пунктуации, повторов слов и других ошибок. Он позволяет глубоко анализировать текст, но требует разработки собственного решения для интеграции. Кроме того, LanguageTool достаточно тяжёлый — для работы нужен либо платный сервис с подпиской, либо собственный сервер, который нужно развёртывать и поддерживать в инфраструктуре. По этим причинам мы отложили его внедрение до лучших времён.

И тогда к нам пришла идея использовать Hunspell — консольную утилиту в виде одного бинарного файла, которая широко применяется в таких продуктах, как LibreOffice, Firefox, Chrome и многие другие. 

Почему мы выбрали именно Hunspell? Потому что это быстрый, лёгкий и простой инструмент, для которого не нужно запускать дополнительные сервисы. Он доступен в репозиториях большинства Linux-дистрибутивов, а мы, работая с Open Source и Kubernetes, используем Linux повсеместно. 

Также Hunspell поддерживает большое количество языков благодаря стандартным словарям, которые входят в его состав. Кроме того, мы придерживаемся принципа использования Open Source-решений в наших продуктах — и этот инструмент идеально вписался в нашу философию.

Как мы проводили первые тесты Hunspell

Начнём с исходных данных. В нашем случае это сайты наших продуктов. Например, сайт утилиты werf хранится в репозитории на GitHub и представляет собой статически сгенерированный сайт на базе Jekyll.

Jekyll — инструмент, который берет Markdown-файлы и собирает из них статические HTML-страницы, которые затем можно разместить на сервере. Для описания различной логики при генерации страниц на них используется язык разметки Liquid. 

В репозитории каждого сайта лежат следующие файлы: шаблоны страниц с контентом, страницы, из которых генерируются конечные файлы, и дополнительные элементы Jekyll, позволяющие переиспользовать контент с одной страницы на другую с помощью специальной разметки. Всё это автоматически собирается с помощью GitHub Actions: мы вносим изменения, оформляем pull request, после чего процесс сборки и деплоя запускается автоматически, и сайт становится доступен нашим клиентам. 

Первым делом мы запустили всё «как есть». Взяли страницу нашего самоучителя по Kubernetes и прогнали по ней Hunspell, не внося предварительных изменений:

---
title: Самоучитель по Kubernetes
permalink: /guides.html
layout: plain
breadcrumbs: none
---

{% asset overview.css %}
{% asset guides.css %}

<h1 class="docs__title">Самоучитель по Kubernetes</h1>
<p>Самоучитель ориентирован на разработчиков, которые хотят научиться работать с Kubernetes и доставлять в него код своих приложений. Также эти материалы будут полезны DevOps-инженерам, которые хотят эффективнее решать задачи по CI/CD в K8s и познакомиться с werf на практике.</p>

<p>Самоучитель — это и пошаговые практические инструкции, и необходимая теория. Он разбит на несколько разделов: от базового уровня до более продвинутых фич. В руководствах учтена специфика языков/фреймворков и приложены примеры исходного кода приложения и инфраструктуры (IaC).</p>

<p>Выберите наиболее близкую вам технологию:</p>

{% include common/guides-landing-tiles.html %}

Страница содержит метаданные: title, ссылку для генерации, шаблон. В теле страницы видны элементы Liquid (asset, include), HTML-разметка (h1, p и т. д.) и текст. Мы запустили Hunspell с русским словарём на этом файле и получили список непонятных слов — например, framework, фича и другие: 

Также туда попали все английские слова, что натолкнуло нас на идею добавить английский словарь. С двумя словарями результат улучшился, но в основном оставались термины вроде framework, Kubernetes, werf, DevOps:

Далее мы взяли страницу, написанную только на Markdown, и запустили Hunspell на ней:

Конфигурация для Redis, которая поможет с этим:

```shell
maxmemory 500mb   # Если данные начнут занимать 500 Мб...
maxmemory-policy allkeys-lru   # ...Redis удалит редко используемые ключи.
```

А для Sidekiq это может быть [Sidekiq worker killer](https://github.com/klaxit/sidekiq-worker-killer):

```shell
require 'sidekiq/worker_killer'
Sidekiq.configure_server do |config|
 config.server_middleware do |chain|
   # Корректно завершить Sidekiq при достижении им потребления в 500 Мб.
   chain.add Sidekiq::WorkerKiller, max_rss: 500
 end
end
```

Понятное дело, что во всех этих случаях `limits.memory` должен быть выше, чем пороги срабатывания вышеуказанных механизмов.

Оказалось, что он проверяет весь текст без исключений, включая блоки кода, например shell-скрипты, хотя их проверять не нужно:

Также он разбивает слова по апострофу — у нас есть особенность: некоторые термины пишутся на английском с русским окончанием через апостроф (например, job’ы). Hunspell считает суффикс неправильным, что частично верно, но с точки зрения контекста — приемлемо.

Третий тип файлов — YAML, где хранится определённая информация. Например, страница со ссылками на публикации о наших продуктах генерируется из YAML-файла с ссылками и краткими описаниями:

...
- title: "Вышла werf 2.0: новый движок развёртывания Nelm и 300+ релизов за четыре года"
  habr_url: "https://habr.com/ru/companies/flan..."
  img: "/assets/images/publications/ru_160524.png"
  created: 2024-05-16
  comment: |
    <p>Четыре года мы развивали и улучшали werf 1.2, но теперь наконец‑то выпустили стабильную werf 2.0. Причина простая — последовательно накопилось множество улучшений (300+ релизов!), а кроме того, мы доработали новый движок развёртывания Nelm, и в werf 2.0 это единственный движок. Старый движок удалён. Nelm обратно совместим с Helm 3, поэтому никаких особых изменений в чартах не потребуется — они будут развёртываться так же, как и раньше.</p>

Hunspell проверяет каждую строку YAML, но нам нужно, чтобы он анализировал только комментарии и текстовые поля.

Минусы, которые мы обнаружили: необходимо исключать блоки кода из проверки, так как там много ненужных слов. Либо нужно писать «обёртку», чтобы проверять только комментарии в коде. В YAML тоже следует проверять не все поля, а только определённые.

Как мы реализовывали проверку орфографии

Далее нам пришла новая идея: сгенерировать сайт в HTML в том виде, в каком он должен быть доступен всем, и проверить уже готовый HTML. Для этого мы взяли исходники, собрали их с помощью Jekyll и получили каталог с множеством HTML-файлов, многие из которых повторяются. Затем очистили HTML от лишнего — убрали блоки кода и элементы, которые не нужны для проверки. Далее запустили Hunspell в режиме HTML, используя ключ, который позволяет работать именно с HTML-контентом. 

Для реализации мы сделали следующее: 

Во-первых, процесс должен был запускаться в CI/CD. Во-вторых, предусматривался ручной запуск на локальной машине для проверки документации во время её написания. В обоих случаях запускается сборка Docker-контейнера с сайтом.

Далее берётся контейнер со спелл-чекером, то есть с Hunspell и всем необходимым — словарями и подготовленными скриптами для очистки. После этого мы забираем собранный контент из контейнера с Jekyll и передаём его в контейнер с Hunspell для проверки, выводя результат.

Сборка контейнера с сайтом и генерация контента

Контейнер с сайтом собирается из Git-репозитория с исходниками, где хранятся тексты для русской и английской версий. Jekyll генерирует внутри два каталога — ru и en:

Сборка контейнера со спелл-чекером, словарями и всем необходимым

Контейнер с Hunspell — это базовый образ Ubuntu, в который устанавливаются Hunspell, Python, библиотека Beautiful Soup для обработки HTML, а также необходимые словари и скрипты для очистки:

Передача контента из контейнера сайта в контейнер спелл-чекера

Передача контента между контейнерами происходит просто — путём копирования файлов:

Запуск проверки документации в контейнере

Запуск проверки документации осуществляется двумя bash-скриптами:

#!/bin/bash
set -e

arg_site_lang="${1:?ERROR: Site language \'en\' or \'ru\' should be specified as the first argument.}"

script=$(cat <<EOF
cd /spelling/$arg_site_lang && \
 ./container_spell_check.sh $arg_site_lang
EOF
)

werf run spell-checker --env='test' --dev --docker-options="--entrypoint=bash" -- -c "$script"

Первый определяет язык и запускает контейнер с Hunspell. Внутри контейнера запускается второй скрипт, который выполняет следующие действия:

1. Перекодирует словари в UTF-8:

cp /usr/share/hunspell/en_US.aff  /usr/share/hunspell/en_US.aff.orig
cp /usr/share/hunspell/en_US.dic  /usr/share/hunspell/en_US.dic.orig
iconv --from ISO8859-1 /usr/share/hunspell/en_US.aff.orig > /usr/share/hunspell/en_US.aff
iconv --from ISO8859-1 /usr/share/hunspell/en_US.dic.orig > /usr/share/hunspell/en_US.dic
rm /usr/share/hunspell/en_US.aff.orig
rm /usr/share/hunspell/en_US.dic.orig
sed -i 's/SET ISO8859-1/SET UTF-8/' /usr/share/hunspell/en_US.aff

Почему это необходимо — до сих пор неясно, так как локально всё работает корректно, а в контейнере без перекодировки — нет.

2. Проверяет на игнорирование файла. Мы создали файл исключений с перечнем файлов, которые не нужно проверять из-за большого количества «мусора». 

3. С помощью Python-скрипта, который проходит по всем страницам, чистит их от блоков кода:

python3 clear_html_from_code.py $file

#!/usr/bin/python3
# -*- coding: utf-8 -*-
 
from bs4 import BeautifulSoup
import sys
 
 
if len (sys.argv) > 1:
    html = open(sys.argv[1]).read()
    root = BeautifulSoup(html, 'html.parser')
    if root.find('code'):
        for code in root.select('code'):
            code.decompose()
    snippetcuts = root.find_all("div", {"class": "snippetcut"})
    for snippetcut in snippetcuts:
        snippetcut.decompose()
    code_wrappers = root.find_all("div", {"class": "viewer__wrap"})
    for wrapper in code_wrappers:
        wrapper.decompose()
    print(root)

4. Удаляет закомментированные участки. Некоторые элементы на страницах неизменно приводили к падению Hunspell с ошибками. Для них мы добавили HTML-комментарии, оборачивающие эти элементы. Перед проверкой эти участки вырезаются с помощью sed — утилиты Linux для работы с текстом: 

<!-- spell-check-ignore -->
<img src="/assets/images/cncf-logo-small.svg" alt="">
<!-- end-spell-check-ignore -->
sed '/<!-- spell-check-ignore -->/,/<!-- end-spell-check-ignore -->/d'

5. Переводит HTML в plain text. Мы пришли к выводу, что лучше сначала конвертировать HTML в текст, а затем запускать Hunspell на полученном тексте:

html2text -utf8

6. Запускает Hunspell. Проверка проводится с использованием русских и английских словарей в зависимости от контента: 

hunspell -d $language -l

Все эти шаги повторяются для каждого файла внутри контента.

Результаты

Hunspell выводит список слов с опечатками — тех, которые не найдены в словаре и считаются неправильными. Для каждого файла формируется такой список. Если ошибки обнаружены, мы выводим результат и возвращаем код возврата 1, чтобы GitHub Actions завершился с ошибкой и задача (Job) на GitHub была отмечена красным крестиком. Это сигнализирует, что что-то пошло не так. Если ошибок нет, возвращается код 0, а GitHub показывает зелёную галочку — всё в порядке.

В какие моменты происходит запуск? Проверка запускается при каждом pull request на GitHub — то есть при внесении изменений в документацию. В списке задач можно увидеть статус: зелёная галочка или красный крестик. Также проверка выполняется при мерже в ветку main. Хотя запуск при мерже необязателен, мы решили делать это дважды — для надёжности.

Ниже примеры результатов, которые есть на GitHub. Успешно завершённые проверки отображаются зелёными — spellcheck.ru и spellcheck.en прошли без ошибок: 

Красным отмечена упавшая русская проверка, где в файле index.html Hunspell обнаружил термин, отсутствующий в словаре:

После запуска мы добавили несколько важных улучшений. Первое — это генерация кастомного словаря. Многие наши термины отсутствовали в стандартных словарях, поэтому мы сформировали собственный. Для этого создали отдельную команду, которая собирает все найденные слова, сортирует их, подсчитывает количество и генерирует словарь для Hunspell. Этот словарь — обычный .dic-файл, совместимый с такими программами, как GoldenDict.

Также добавили команды для работы с кастомным словарём — как локально, так и в CI/CD:

  • site:generate-special-dictionary — генерирует словарь специальных терминов из набора слов в wordlist'е;

  • site:get-words-with-typos — собирает все слова с опечатками в два файла — для русской и английской версий сайта:

    • site:get-words-with-typos:en — подкоманда для английской версии;

    • site:get-words-with-typos:ru — подкоманда для русской версии;

  • site:run-spell-check — запускает проверку русской и английской версий сайта:

    • site:run-spell-check:en — только для английской версии. Позволяет выбрать конкретный файл для проверки, если указать параметр -- ./path/to/file;

    • site:run-spell-check:ru — только для русской версии. Позволяет выбрать конкретный файл для проверки, если указать параметр -- ./path/to/file;

  • site:sort-custom-dict — сортирует wordlist перед отправкой изменений в Git;

  • site:view-plain-text-of-target-html — отображает очищенное содержимое целевого файла;

    • site:view-plain-text-of-target-html:en — только для английской версии;

    • site:view-plain-text-of-target-html:ru — только для русской версии.

Выводы

Внедрение этой системы дало нам следующие преимущества:

  • Мы сразу проверили всю документацию и нашли множество пропущенных опечаток — реально много.

  • Теперь ошибки пресекаются на стадии разработки, что позволяет оперативно их исправлять.

  • Работа корректора упростилась: если проверка показывает зелёный статус, значит, явных ошибок нет и можно сосредоточиться на содержании.

Видео с выступлением (~22 минуты):

P. S.

Читайте также в нашем блоге:

Теги:
Хабы:
+9
Комментарии0

Публикации

Информация

Сайт
flant.ru
Дата регистрации
Дата основания
Численность
201–500 человек
Местоположение
Россия
Представитель
Александр Лукьянов