Что такое LaTeX и почему не Word

Каждый разработчик знает, что исходный код проекта должен храниться в Git, одинаково собираться на любой машине и не зависеть от того, какой редактор установлен у автора.
Почему с документами должно быть иначе?
Для большинства людей документ — это файл Word. Для меня документ — это исходный код, который компилируется в PDF. Именно поэтому выбор практически сразу пал на LaTeX.
Я давно хотел поработать с ним на реальном проекте. Диплом оказался отличной возможностью наконец это сделать.
В LaTeX меня всегда привлекали несколько вещей:
Во-первых, воспроизводимость. Если репозиторий клонируется на другую машину, документ должен собраться точно так же, как у автора. Без ручной настройки шрифтов, без «у меня все открывается нормально», без сюрпризов после переустановки операционной системы.
Во-вторых, открытость. Практически весь стек — от самого движка до большинства используемых пакетов — имеет открытый исходный код. Если чего-то не хватает, это можно не ждать в следующем обновлении, а реализовать самостоятельно.
В-третьих, свобода. LaTeX — это не столько язык разметки, сколько программируемая система верстки. Можно переопределять практически любые команды, писать собственные пакеты, использовать Lua внутри движка, подключать Python, автоматически генерировать части документа и встраивать весь процесс в CI/CD.
Ну и, наверное, стоит честно признаться еще в одной причине:
Я просто не люблю Microsoft Word.
Не потому, что это плохой редактор. Он отлично подходит для большинства задач. Но мне никогда не нравилась сама модель работы, в которой оформление постоянно смешивается с содержимым.
В LaTeX подход противоположный. Сначала описывается структура документа, а его внешний вид определяется правилами верстки через команды. Благодаря этому диплом довольно быстро перестал быть «документом» и превратился в обычный проект с исходным кодом, системой сборки, Docker, GitHub Actions, автоматическими проверками и собственными утилитами.
Именно про это и будет дальнейший рассказ.
Контекст

Важно понимать, что мой диплом — это не только текст.
Основную часть работы занимает описание реализации собственного проекта. А значит, в документе постоянно встречаются листинги кода, фрагменты конфигурационных файлов, SQL-запросы, диаграммы, графики, таблицы и ссылки между ними.
Фактически диплом сам становится частью проекта.
Если бы работа писалась в Microsoft Word, большинство этих элементов пришлось бы сопровождать вручную.
Например, изменения в исходном коде потребовали бы заново копировать листинги в документ. При изменении цветовой схемы редактора пришлось бы повторно экспортировать изображения. Если бы понадобилось изменить оформление всех листингов, это снова превратилось бы в ручную работу. То же касается диаграмм, которые после каждого изменения пришлось бы пересобирать и заново вставлять в документ.
LaTeX позволяет организовать этот процесс иначе.
Листинги вставляются непосредственно из исходных файлов проекта.
\lstinputlisting[ language=Python, caption={Реализация генерации ключа}, label={lst:keygen}]{vault/key_manager.py}
В результате документ всегда содержит актуальную версию кода. Исправил ошибку в проекте — после следующей сборки диплом автоматически использует новое содержимое файла. Исчезает риск забыть обновить какой-нибудь листинг перед сдачей работы.
С диаграммами ситуация аналогичная. Вместо хранения десятков PNG-файлов в репозитории используются их исходники на Mermaid. Во время сборки они автоматически преобразуются в PDF и сразу попадают в документ. Исходный код диаграммы также хранится в Git, поэтому любые изменения можно просмотреть в истории коммитов. Мы подробно поговорим о том, что из себя вообще представляет верстка в LaTeX и диаграммы в Mermaid
В итоге диплом перестал быть отдельным документом. Он стал еще одним модулем проекта, который собирается теми же инструментами, проходит те же проверки и подчиняется тем же принципам, что и исходный код приложения.
Что из себя представляет верстка в LaTeX

Если максимально упростить, то LaTeX — это язык программирования, который вместо исполняемого файла генерирует PDF.
Конечно, технически это не совсем так. Но именно такая модель мышления, на мой взгляд, лучше всего помогает понять, как с ним работать.
Документ состоит из исходного кода. Во время компиляции движок последовательно обрабатывает этот код, выполняет команды, рассчитывает разметку страниц, формирует оглавление, расставляет ссылки, строит библиографию и в итоге генерирует PDF.
Практически всё, что встречается в документе, является командой.
Например, команда создания раздела:
\section{Архитектура приложения}
Команда вставки изображения:
\includegraphics{architecture.pdf}
Команда создания ссылки:
\ref{fig:architecture}
При этом команды ничем не отличаются от функций в обычном языке программирования. Они могут принимать параметры, вызывать другие команды и даже полностью переопределяться.
Именно возможность переопределения — одна из самых сильных сторон LaTeX.
Допустим, по требованиям университета все подписи рисунков должны начинаться со слова «Рисунок», а не сокращения «Рис.». Вместо поиска нужной настройки в интерфейсе достаточно один раз изменить поведение соответствующей команды. После этого изменение автоматически применяется ко всему документу.
Точно так же можно изменить оформление заголовков, библиографии, приложений, подписей таблиц, списков, листингов и практически любого другого элемента документа.
Чем больше пишешь на LaTeX, тем больше становится понятно, что это не набор готовых функций (фреймворк), а конструктор. Большая часть пакетов лишь определяет набор новых команд, которые затем можно использовать или переопределять под свои задачи.
Результат работы
Прежде чем переходить к технической части, покажу, к чему в итоге привела вся эта история.
То, что начиналось как обычная дипломная работа, постепенно превратилось в полноценный проект с собственной инфраструктурой. Некоторые его части впоследствии были выделены в отдельные open source проекты.
В результате получились:
-
репозиторий диплома — исходный код документа, система сборки, Docker-окружение, GitHub Actions, Python-инструменты, LuaLaTeX, автоматическая генерация диаграмм, проверка качества и множество вспомогательных утилит;
-
шаблон дипломной работы — универсальная основа для написания дипломов на LuaLaTeX, выросшая из основного проекта после нескольких месяцев рефакторинга;
-
compile-mermaid — утилита для быстрой компиляции Mermaid-диаграмм в PDF, пригодный для последующей вставки в LaTeX;
-
diff-pdf-commits — инструмент для сравнения PDF-документов между двумя коммитами Git, который оказался полезен не только при написании диплома.
В статье я постараюсь показать не столько сам LaTeX, сколько подход к разработке документов как программных проектов: с воспроизводимой сборкой, автоматизацией, контролем качества и инструментами, которые избавляют от ручной работы.
Разработка
В начале
Первые несколько дней проект выглядел примерно так:
diploma/├── diploma.tex├── bibliography.bib├── preamble.tex├── title.tex├── figures/│ ├── architecture.pdf│ └── database.pdf└── README.md
По сути, это был обычный LaTeX-проект: основной документ, изображения и библиография.
Этого уже было достаточно, чтобы хранить документ в Git и получать воспроизводимый PDF.
Однако по мере роста работы начали появляться новые требования, которые постепенно меняли архитектуру проекта.
Построение архитектуры сборки
Когда документ растёт, становится понятно, что одного .tex-файла мало. Появляются отдельные настройки для списков, таблиц, рисунков, подписей, библиографии, приложений, листингов, сносок и оглавления. Если всё держать в одном месте, преамбула превращается в свалку, где изменение одного параметра внезапно ломает что-то в другой части документа.
Поэтому следующим шагом стало разделение проекта на модули. Часть настроек уехала в отдельные файлы, диаграммы — в отдельные директории, вспомогательные скрипты — в scripts/, а сборка постепенно стала превращаться в самостоятельный процесс.
В результате структура проекта стала выглядеть уже примерно так:
diploma/├── preamble/│ ├── bibliography.tex│ ├── captions.tex│ ├── callouts.tex│ ├── fonts.tex│ ├── hyperlinks.tex│ ├── listings.tex│ ├── lists.tex│ ├── page_layout.tex│ ├── references.tex│ └── ...├── figures/├── mermaid/├── python_diagrams/├── scripts/│ ├── build.py│ ├── clean.py│ ├── crop_pdf.py│ ├── diff_pdf.py│ └── ...├── docker/├── tasks/├── tests/├── .github/│ └── workflows/├── bibliography.bib├── Taskfile.yml├── compose.yml├── latexmkrc└── diploma.tex
Сначала документ можно было собрать вручную. Процесс выглядел примерно так: сначала запускался LuaLaTeX — движок, отвечающий непосредственно за компиляцию документа, затем Biber, который формирует библиографию, после чего LuaLaTeX приходилось запускать ещё несколько раз, чтобы обновились ссылки, номера рисунков, оглавление и другие перекрёстные ссылки.
Для небольших документов это вполне рабочая схема. Но по мере роста проекта она начинает раздражать. Каждый раз приходится помнить правильную последовательность команд, а со временем к ней добавляются генерация диаграмм, подготовка изображений, конвертация вспомогательных файлов и различные проверки.
В какой-то момент стало очевидно, что диплом должен собираться одной командой.
Для этого в проекте появился latexmk — инструмент, который сам определяет, сколько раз необходимо запустить LuaLaTeX и Biber, чтобы документ оказался в согласованном состоянии. Вокруг него постепенно выросла остальная инфраструктура: Docker-профили для воспроизводимой сборки, Taskfile как единая точка входа для всех команд и Python-скрипты, автоматизирующие подготовку проекта.
LaTeX при этом остался исключительно движком вёрстки. Всё остальное — проверка окружения, очистка артефактов, генерация диаграмм, сравнение PDF между версиями, публикация релизов и вычисление контрольных сумм — было вынесено во внешние инструменты.
Отдельной проблемой являются внешние зависимости. Для успешной сборки документа недостаточно установить только TeX Live. Проект использует Python, Mermaid, Biber, утилиты для обработки PDF, а для некоторых задач — LibreOffice и Ghostscript. Проверять наличие всех этих компонентов вручную быстро надоело, поэтому появился отдельный инструмент проверки окружения, который позволяет ещё до начала сборки убедиться, что все необходимые зависимости установлены и готовы к работе. Он написан на Python и проверяет следующие вещи:
-
наличие необходимых инструментов в
PATH; -
готовность окружения проекта (наличие необходимых
.envпеременных).
Воспроизводимость

Одна из главных причин, по которой я выбрал LaTeX, — воспроизводимость.
Для меня было принципиально важно, чтобы диплом можно было собрать на любой машине одной командой и получить идентичный PDF.
Это сразу породило несколько проблем.
Во-первых, LaTeX зависит не только от самого движка. Для сборки требуются десятки пакетов TeX Live, Biber, Python, Mermaid, Ghostscript, LibreOffice и другие инструменты.
Во-вторых, разные версии пакетов способны менять результат верстки. Иногда достаточно обновить один пакет, чтобы рисунок переехал на следующую страницу или изменилось разбиение абзацев.
Поэтому вокруг проекта постепенно появились Docker, Taskfile и автоматическая проверка окружения. Теперь сборка диплома практически не зависит от состояния рабочей машины.
В Docker жестко зафиксированы digest-ы пакетов, чтобы даже через время сборка оставалась одинаковой:
FROM texlive/texlive:latestENV DEBIAN_FRONTEND=noninteractiveRUN echo "deb http://deb.debian.org/debian bookworm contrib non-free" | tee /etc/apt/sources.list.d/contrib.listRUN echo "deb http://snapshot.debian.org/archive/debian/20260502T000000Z bookworm main contrib non-free" > /etc/apt/sources.list
Исходный код как часть документа
Следующей проблемой оказался исходный код.
В работе постоянно встречались листинги Python, SQL, конфигурационные файлы и фрагменты LaTeX.
Самый простой путь — копировать их в документ вручную.
Но тогда после каждого изменения проекта пришлось бы искать соответствующий листинг и обновлять его самостоятельно.
Вместо этого все листинги вставляются непосредственно из исходных файлов проекта.
Это означает, что диплом никогда не может содержать устаревший код. Если изменилась реализация — после следующей сборки автоматически обновится и документ.
Такой подход избавил не только от ручной работы, но и от одной из самых неприятных ошибок технической документации — несоответствия между текстом и реальным исходным кодом.
В конечном итоге вместо вставки более 40 страниц кода, в проекте написано лишь 24 строки:
\lstset{style=modern} \lstinputlisting{main.py}\lstinputlisting{pytest.ini}\lstinputlisting{requirements.txt}\lstinputlisting{vault/\_\_init\_\_.py}\lstinputlisting{vault/api.py}\lstinputlisting{vault/auth.py}\lstinputlisting{vault/cli.py}\lstinputlisting{vault/crypto.py}\lstinputlisting{vault/db.py}\lstinputlisting{vault/db\_schemas.py}\lstinputlisting{vault/key\_manager.py}\lstinputlisting{vault/schemas.py}\lstinputlisting{vault/settings.py}\lstinputlisting{tests/conftest.py}\lstinputlisting{tests/test\_api.py}\lstinputlisting{tests/test\_auth.py}\lstinputlisting{tests/test\_cli.py}\lstinputlisting{tests/test\_crypto.py}\lstinputlisting{tests/test\_db\_migration.py}\lstinputlisting{tests/test\_load\_flow.py}\lstinputlisting{tests/test\_schemas.py}\lstinputlisting{scripts/check\_all.py}
Этот участок кода можно было укоротить еще сильнее, добавив макрос, который принимает имя файла и генерирует \lstinputlisting с нужной подписью и ссылкой. Но в данном случае автора устроил спагетти-код, поскольку этот участок кода практически не менялся.
Команда \lstinputlisting в этом случае использует 3 аргумента:
-
caption— подпись под листингом; -
label— ссылка, на которую можно обратиться из кода и получить либо номер страницы, на которой расположен объект, к которому применена ссылка, либо порядковый номер этого объекта. Например, чтобы получить страницу, на которой начинается листингlst:tests/test_schemas.py, достаточно написать кодСтраница \pageref{lst:tests/test_schemas.py}, а чтобы обратиться по номеру —Листинг \ref{lst:tests/test_schemas.py}. Таким образом можно ставить метки в любом месте файла, не только на листингах; -
аргумент в фигурный скобках (обязательный) — путь вставляемого файла.
Callout’ы в листингах
После того как листинги стали вставляться напрямую из исходных файлов, появилась следующая задача.
Мне нужно было не просто показать код, а ссылаться на конкретные места внутри него.
Обычной ссылки на листинг оказалось недостаточно. Иногда в тексте нужно было сказать не просто «см. листинг с реализацией модуля», а указать на конкретную функцию или участок кода.
В технических книгах для этого часто используют callout’ы: небольшие пронумерованные метки прямо внутри листинга, а под кодом или в тексте дают пояснение к каждой метке.
Хотелось получить похожий механизм, но при этом сохранить главное требование: код должен продолжать вставляться из настоящих исходных файлов проекта.
То есть вариант «скопировать код в LaTeX и вручную расставить метки» не подходил.
В итоге решение получилось довольно необычным.
В исходный Python-код были добавлены специальные строки, которые выглядят как обычные строковые литералы:
'(*@\needspace{3\baselineskip}@*)''(*@\codecalloutlabel{lst:func:validate_master_kek_hex}@*)'def validate_master_kek_hex(master_kek_hex: str) -> str: normalized = normalize_hex_or_raise(master_kek_hex) if len(bytes.fromhex(normalized)) != 32: raise ValueError("VAULT_MASTER_KEK_HEX must be 32 bytes (64 hex chars).") return normalized
Для Python это просто строка без присваивания. Она не влияет на поведение программы и не меняет бизнес-логику.
Но для LaTeX это управляющая инструкция.
В настройках listings включён специальный escape-режим:
escapeinside={'(*@}{@*)'}
Благодаря этому всё, что находится между '(*@ и @*)', воспринимается уже не как часть листинга, а как LaTeX-код.
В результате при вставке файла через \lstinputlisting LaTeX видит не просто Python-код, а ещё и команды для расстановки меток.
Команда \codecalloutlabel делает сразу несколько вещей:
-
увеличивает счётчик callout’ов;
-
создаёт обычную LaTeX-метку через
\label; -
запоминает номер листинга, внутри которого была поставлена метка;
-
рисует маленький кружок с номером прямо в коде.
После этого на callout можно ссылаться из текста так же, как на рисунок, таблицу или листинг:
\calloutfullref{lst:func:validate_master_kek_hex}
На выходе получается ссылка вида:
1 (см. с. 42, рисунок 7, приложение Б)
В итоге код остаётся настоящим исходным файлом, но при этом получает дополнительный слой разметки, понятный только системе сборки документа.
С точки зрения Python эти строки ничего не делают.
С точки зрения LaTeX они превращают обычный листинг в аннотируемый фрагмент технической книги.
Это один из тех моментов, где особенно хорошо видно, почему мне нравится LaTeX. Вместо того чтобы искать готовый инструмент, можно собрать собственный механизм из нескольких базовых возможностей: listings, escape-последовательностей, счётчиков, \label, \ref и немного TikZ.
Диаграммы как исходный код
С диаграммами возникла практически та же проблема, что и с листингами.
Большинство редакторов предлагают нарисовать схему мышкой, экспортировать её в PNG или SVG и вставить в документ. Такой подход вполне работает, пока диаграмма не начинает регулярно изменяться. Однако для своей работы я решил выбрать другой подход — инструмент декларативного описания диаграмм Mermaid.
При этом подходе, вместо хранения изображений репозиторий содержит только их исходный код.
graph LRA[CLI] --> B[Service]B --> C[Repository]C --> D[(SQLite)]
Во время сборки диплома этот код автоматически компилируется в PDF, который затем вставляется в документ обычной командой \includegraphics.
Такой подход дал сразу несколько преимуществ.
Во-первых, диаграммы стали нормально храниться в Git. Изменения можно просматривать через обычный diff, а не пытаться понять, что изменилось между двумя бинарными PNG-файлами.
Во-вторых, исчезла необходимость вручную экспортировать изображения после каждого изменения.
И наконец, диаграммы стали частью процесса сборки. Если исходник изменился, следующая сборка автоматически использует новую версию.
Со временем вокруг этого процесса появился отдельный инструмент compile-mermaid, который был вынесен в самостоятельный open source проект. Сейчас он позволяет одной командой скомпилировать как отдельную диаграмму, так и весь каталог с исходниками.
Однако и здесь всё оказалось не так просто.
PyLuaTeX: когда LaTeX перестал справляться
Несмотря на огромное количество возможностей, некоторые задачи средствами LaTeX решать либо неудобно, либо вообще нецелесообразно.
Поэтому в проекте появился PyLuaTeX.
Он позволяет выполнять Python-код прямо во время сборки документа и передавать результат обратно в LaTeX.
Первоначально это казалось скорее любопытной возможностью. Но довольно быстро оказалось, что она отлично подходит для автоматизации рутинных задач.
Одним из первых применений стала генерация реферата.
По требованиям университета в нём необходимо автоматически указывать количество страниц, рисунков, таблиц, листингов, приложений, источников и других элементов документа.
Конечно, все эти числа можно было обновлять вручную перед каждой сборкой.
Но стоило добавить ещё один рисунок или удалить приложение — информация в реферате сразу становилась неверной.
С помощью PyLuaTeX эти значения вычисляются автоматически непосредственно во время сборки документа. Если количество рисунков изменилось, соответствующее число в реферате обновится само.
Однако здесь возникла ещё одна проблема.
Русский язык требует правильного склонения существительных.
Например:
1 рисунок2 рисунка5 рисунков
Простой подстановкой числа такую задачу уже не решить.
Для реализации этой задачи сначала была написана функция, которая принимает на вход число и 3 формы слова:
def russian_plural_form(number, one, few, many): n = abs(int(number or 0)) last_two = n % 100 last = n % 10 if 11 <= last_two <= 14: return many if last == 1: return one if 2 <= last <= 4: return few return many
Далее в LaTeX эта функция используется следующим образом:
% --------------------------------------% НАСТРОЙКИ МНОЖЕСТВЕННЫХ ФОРМ ДЛЯ РЕФЕРАТА% --------------------------------------\begin{python}from pathlib import Pathcandidates = ( Path("preamble/python/russian_plural.py"), Path("../preamble/python/russian_plural.py"), Path("../../preamble/python/russian_plural.py"),)for candidate in candidates: if candidate.is_file(): exec(candidate.read_text(encoding="utf-8")) breakelse: raise FileNotFoundError("Cannot find preamble/python/russian_plural.py")\end{python}\newcommand{\russianPlural}[4]{% \py{russian_plural_form("#1", "#2", "#3", "#4")}%}\newcommand{\russianTables}[1]{% \russianPlural{#1}{таблицу}{таблицы}{таблиц}%}\newcommand{\russianAppendices}[1]{% \russianPlural{#1}{приложение}{приложения}{приложений}%}\newcommand{\russianSources}[1]{% \russianPlural{#1}{источник}{источника}{источников}%}\newcommand{\russianFigures}[1]{% \russianPlural{#1}{рисунок}{рисунка}{рисунков}%}
Этот код сначала ищет Python файл с функцией, затем определяет команду LaTeX, которая будет вызывать эту функцию. Таким образом, например, при вызове команды \russianFigures с аргументом 1, команда вернет слово рисунок, при аргументе 2 — слово рисунка, и.т.д.
В результате реферат полностью синхронизирован с документом и не требует никакого ручного обновления.
PyLuaTeX пригодился и для другой задачи.
Каждая сборка документа теперь получает собственный идентификатор (Build ID), который автоматически записывается в метаданные PDF.
В коде это реализовано следующим образом:
\begin{python}import secretsbuild_id = secrets.token_hex(4)\end{python}\hypersetup{ pdftitle={Разработка системы защещенного хранения данных}, pdfauthor={Куприянов Артём Максимович}, pdfsubject={Дипломная работа}, pdfkeywords={latex, диплом, безопасность}, pdfcreator={LuaLaTeX}, pdfproducer={TeX Live. Build id: \py{build_id}}}
Таким образом, в метаданные документа, в поле «Производитель PDF» попадет строка TeX Live. Build id: <случайная строка>
Это позволяет однозначно определить, какая именно версия документа была собрана, даже если внешне несколько PDF выглядят одинаково.
Подобная практика давно используется при сборке программного обеспечения, но применительно к дипломной работе она оказалась не менее полезной. Если PDF был отправлен научному руководителю или приложен к письму, всегда можно определить, какой именно сборке он соответствует.
В итоге Python стал выполнять роль небольшого «помощника» LaTeX. Всё, что удобно описывать средствами системы вёрстки, остаётся в TeX. Всё, что связано с вычислениями, обработкой строк или взаимодействием с внешним окружением, выполняется на Python ещё до того, как документ попадёт на страницу.
Почему появился compile-mermaid
Изначально для компиляции использовался обычный mmdc.
Всё работало до тех пор, пока диаграмм не стало несколько десятков.
Приходилось запускать компиляцию вручную, следить за путями, настраивать формат выходных файлов и каждый раз помнить необходимые параметры командной строки.
Кроме того, у mmdc есть ещё одна особенность.
Получившийся PDF содержит довольно большие пустые поля вокруг диаграммы. При вставке в LaTeX это выглядит не очень красиво — приходится либо вручную обрезать документ, либо постоянно подбирать масштаб изображения.
В итоге вокруг Mermaid постепенно появился небольшой Python-инструмент, который автоматизировал весь процесс.
Теперь достаточно выполнить одну команду:
uvx compile-mermaid
После этого утилита самостоятельно:
-
находит все исходники Mermaid;
-
компилирует их в PDF;
-
обрезает лишние поля через
pdfcrop; -
помещает готовые файлы в нужную директорию.
Со временем этот инструмент перестал зависеть от конкретного диплома и был выделен в отдельный open source проект.
Проблема: Git не умеет сравнивать PDF

К этому моменту практически всё уже превратилось в обычный программный проект.
Исходный код диплома хранится в Git. Листинги автоматически подтягиваются из проекта. Диаграммы представлены в виде текста на Mermaid и тоже прекрасно отслеживаются системой контроля версий.
Но оставалась одна проблема.
Git прекрасно умеет показывать изменения в исходном коде:
git diff
Сразу видно, какие строки были добавлены, удалены или изменены.
Но с PDF всё не так просто, поскольку структура PDF не предназначена для чтения человеком.
Git сообщает, что файл изменился, но совершенно не помогает понять, что именно произошло.
Для обычного документа это может быть не так критично. Для диплома, который постоянно подвергается мелким правкам, ситуация другая.
Например, после очередного изменения хотелось быстро ответить на довольно простые вопросы:
-
не переехал ли рисунок на следующую страницу;
-
не изменилась ли верстка после обновления пакета;
-
не исчезла ли случайно подпись рисунка;
-
не «поехали» ли переносы;
-
не изменилась ли пагинация документа.
Конечно, можно открыть две версии PDF рядом и искать отличия глазами. Но когда документ приближается к ста страницам, такое занятие быстро перестаёт приносить удовольствие.
Поэтому появился ещё один небольшой инструмент — diff-pdf-commits.
Он решает довольно простую задачу: автоматически сравнить PDF, собранный из текущего коммита, с PDF любой другой ревизии репозитория.
Достаточно выполнить одну команду:
uvx diff-pdf-commits HEAD~5 HEAD
-
собирает документ из указанного коммита;
-
собирает текущую версию проекта (если это необходимо);
-
запускает визуальное сравнение страниц;
-
открывает готовый diff.
В результате проверка изменений перестала быть ручной задачей. Вместо просмотра двух документов страница за страницей достаточно открыть готовый отчёт и посмотреть только те места, где действительно произошли изменения.
Пример вывода скрипта представлен ниже
Как и в случае с compile-mermaid, со временем этот инструмент перестал зависеть от дипломного проекта и был выделен в отдельный open source репозиторий.
CI/CD для дипломной работы
После того как локальная сборка стала полностью автоматизированной, возник вполне закономерный вопрос.
Почему вообще собирать диплом вручную?
Если документ уже находится в Git, имеет воспроизводимую сборку и собирается одной командой, то логично поручить эту задачу CI/CD.
Для этого был настроен GitHub Actions.
Теперь каждый день Actions сам собирает PDF и кладет его в релиз под тэгом Nightly
При этом GitHub Actions выполняет не только компиляцию PDF.
В процессе работы автоматически:
-
проверяется корректность окружения;
-
компилируются Mermaid-диаграммы;
-
собирается диплом;
-
выполняются проверки вспомогательных инструментов;
-
вычисляются контрольные суммы итоговых артефактов;
-
публикуются GitHub Releases;
-
обновляется GitHub Pages с актуальной документацией проекта.
Благодаря этому исчезла необходимость что-либо публиковать вручную.
Любое изменение, попавшее в репозиторий, автоматически проходит тот же путь, что и обычное программное обеспечение.
Это оказалось неожиданно удобным.
Во-первых, всегда существует готовая собранная версия диплома, которую можно скачать без установки каких-либо зависимостей.
Во-вторых, история сборок хранится вместе с историей проекта. Если понадобится посмотреть, как выглядел документ несколько недель назад, достаточно открыть соответствующий workflow или релиз.
Архив версий документа
После настройки CI/CD появилась ещё одна приятная возможность.
Теперь каждая ночная сборка автоматически публикуется в GitHub Releases под тегом nightly.
На первый взгляд кажется, что это просто очередной артефакт сборки.
На практике оказалось, что это очень удобный архив.
В любой момент можно скачать PDF, каким он был неделю, месяц или несколько месяцев назад, не поднимая старое окружение и не переключаясь на нужный коммит.
Получился своеобразный аналог nightly-сборок, которые часто используются при разработке программного обеспечения. Разница лишь в том, что вместо бинарного файла публикуется очередная версия дипломной работы.
Для реализации архива я выбрал Mkdocs с темой Material — такой выбор сочетает приятный и просто UI, а также кастомизируемость, которая была нужна для внедрения своей структуры сайта.
Документация проекта
Также логичным шагом стало появление собственной документации.
В ней описаны структура проекта, процесс сборки, используемые инструменты, требования к окружению, работа CI/CD и вспомогательные утилиты.
Для создания собственной документации существует множество готовых популярных генераторов статических сайтов (SSG), такие как MKDocs, MDBook, Hugo, и другие. Мой же выбор пал на Zensical: он написан на Python, поддерживает i18n, шаблонизацию, и имеет красивый UI
Заключение
Каждый сам решает, нужно ли ему разбираться с экосистемой LaTeX для написания работы. Если вы пишите работу в пределах 40 страниц, скорее всего, вам вообще не нужен LaTeX, и вы потратите больше времени на настройку окружения, чем на получение практической пользы от работы с ним. Однако если ваша работа объемная и содержит больше количество ссылок, или же вы просто хотите изучить новую технологию, то LaTeX — отличный выбор. На старте он не требует от вас ничего особенного. Знания базовых команд будет достаточно, чтобы писать документацию для своей команды, или писать отчеты о работе. Для LaTeX также существует популярный онлайн-редактор Overleaf. Он поддерживает коллаборацию в реальном времени, что позволяет работать над документами одновременно с коллегами.
ссылка на оригинал статьи https://habr.com/ru/articles/1052848/