Автоматизированное семантическое управление версиями с помощью GitVersion

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

Особенно сложным раз от раза было автоматизировать стратегию. Внутри репозитория всегда выполняются определенные действия вручную, чтобы задать следующую версию. И в данном случае рассматривается только “хороший путь” (happy path). Как быть с исправлениями ошибок? Нужно ли менять магическое свойство каждый раз, когда требуется развертывание? Одним словом, много работы.

Но что, если есть инструмент, способный вычислить правильный, удобночитаемый номер версии? Вот тут-то и вступает в игру GitVersion!

Что такое управление версиями программного обеспечения?

Когда вы слышите о желании объяснить управление версиями программного обеспечения, то поначалу можете подумать: зачем этим заниматься? Первоначальное определение уже звучит тривиально. Когда люди начинают вдумываться в него, то обычно делают вывод, что всё совсем не так просто.

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

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

Какие существуют схемы управления версиями?

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

Распространенные схемы управления версиями таковы:

  • Номер сборки: здесь используется число (инкремент), определяемое запуском автоматического конвейера сборки.
  • Дата и время: здесь временная метка сборки используется в качестве уникальной временной метки-определителя версии.
  • Семантическая версия: сокращенно Semver; здесь версия определяется на основе схемы major.minor.patch.

У первых двух схем есть один общий недостаток: они не описательные. При сравнении нескольких версий, следующих одна за другой, пользователю трудно понять, были ли внесены в новую версию неразрушающие изменения. Смысл новой версии нельзя вывести исключительно из ее номера.

Семантическое версионирование предлагает решение, которое будет более описательным. Семантический номер версии соответствует структуре MAJOR.MINOR.PATCH.

Различные разделы здесь  —  это числа. Мы увеличиваем:

  • основную часть, когда вводим несовместимые/меняющие API изменения;
  • второстепенную часть, когда добавляем функциональность обратно совместимым образом;
  • патч-часть, когда делаем обратно совместимые исправления ошибок.

Номер версии может быть дополнен тегом, например 0.1.0-alpha. Таким образом, наименование версии становится более описательным.

Семантическая стратегия стала отраслевым стандартом для версионирования приложений. Для данной стратегии существует четко определенная спецификация. Рекомендуется прочитать ее до конца, так как понимание спецификации поможет в дальнейшем правильно определять номер версии.

Интеграция семантического управления версиями в процессы CI/CD

Семантическое управление версиями выглядит потрясающе! Но как внедрить его в автоматизированные процессы?

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

При помощи GitVersion, задействовав ветви git и конвейеры CI/CD, возможна интеграция, где автоматически генерируются номера версий. GitVersion представляет собой интерфейс командной строки, чтобы произвести эти цифры версии. GitVersion хорошо работает с существующими стратегиями ветвления Git, такими как GitFlow или GitHub Flow. Хотя рекомендуется пользоваться стандартизированной стратегией ветвления, благодаря гибкой конфигурации GitVersion можно настроить в соответствии с любыми желаемыми потребностями.

GitVersion обеспечивает плавную интеграцию с Azure DevOps и GitHub Actions. Если решение CI/CD допускает установку пользовательских инструментов командной строки, вы можете так и сделать. Существуют пошаговые руководства, доступные для нескольких серверов сборки, таких как Bamboo и Octopus Deploy. Единственные три требования, которые есть у GitVersion,  —  это обязательное использование Git, правильная настройка стратегии ветвления и правильная конфигурация.

Что мы будем устанавливать в целях демонстрации?

Для примера мы создадим версию пакета NPM с помощью Azure DevOps. Для создания пакета NPM понадобится CLI NPM, который необходимо загрузить вместе с Node.js. Установщики для Windows, Linux или MacOS можно найти на сайте Node.js.

Мы будем использовать клиент командной строки GitVersion для создания собственной конфигурации GitVersion. Этот клиент можно установить через chocolatey или HomeBrew. GitVersion для Linux устанавливается с помощью Mono. Руководство по этому процессу можно найти здесь.

Предположим, что у нас уже есть пустой репозиторий Git в Azure DevOps. Если нет, вы можете воспользоваться следующим руководством.

Настройка пакета NPM

Для нашего примера мы создадим простую функцию, которая может использоваться другими приложениями. Во-первых, необходимо сгенерировать package.json через клиент командной строки NPM.

Начнем создавать модуль, открыв терминал (MacOS/Linux) или CMD (Windows). Затем выполним внутри репозитория команду npm init. Эта команда проведет вас через весь процесс создания файла package.json. Когда вас попросят указать желаемую версию, установите ее как 0.0.1. Позже она будет автоматически заменена внутри конвейера сборки.

NPM CLI — процесс инициализации

После прохождения этого процесса создается нужный нам файл package.json. Откройте свою любимую IDE, создайте файл index.js, а потом скопируйте и вставьте туда следующее содержимое:

function hello(name) {
 return "Hello, {0}!".format(name);
}

module.exports = hello;

Приведенная выше функция принимает аргумент name. При вызове он вернет текст: “Hello, [имя]!”.

Настройка конфигурации GitVersion

Чтобы запускать задачи GitVersion внутри конвейеров, нужно установить расширение GitTools из Visual Studio Marketplace. Оно поможет определить правильную версию и изменить номер запуска сборки на сгенерированную семантическую версию.

Давайте создадим собственную конфигурацию GitVersion, открыв терминал (MacOS/Linux) или CMD (Windows) и введя команду gitversion init.

GitVersion CLI — настройка правильной стратегии ветвления

В этом меню выберем опцию (2): Run getting started wizard. Этот мастер поможет настроить GitVersion в соответствии с вашей стратегией ветвления. В примере был сделан выбор следовать стратегии GitHub Flow. Если вы не можете определиться, то выберите (3) Unsure, tell me more. Вы получите несколько вопросов, которые помогут определить, какие настройки подходят вам лучше всего.

Затем вам будет предложено выбрать режим инкремента по умолчанию:

GitVersion CLI — установка режима инкремента по умолчанию

Возможны следующие варианты:

  • Следование SemVer и применение инкремента только когда релиз был помечен тегом (режим непрерывной доставки). При выборе этого параметра числа в номере сборки увеличиваются только при добавлении тегов к коммитам. Сборки, следующие после помеченной тегом, будут иметь тот же номер версии с увеличенным номером в качестве метки. Пример: 1.0.1+3 (третья по счету сборка).
  • Инкремент на основе конфигурации ветки каждого коммита (режим непрерывного развертывания). В этом режиме номера версий будут увеличиваться при каждом коммите, к сборкам на ветке разработки будет прибавлен тег alpha, а к сборкам на ветке релиза  —  beta.
  • Каждое слияние ветки с мастер-веткой будет увеличивать версии (магистральный режим). В этом режиме каждый коммит слияния (например, пулл-реквест для ветки) вызовет увеличение версии патча.

Поскольку мы создаем пакет NPM, первый вариант не подходит. Мы хотим выпускать новые версии приложения, когда сливаемся в главную ветвь. Менеджеры пакетов, такие как NPM и NuGet, не поддерживают одни и те же версии с разными метками. Так что остановимся на варианте №3. После чего выбираем опцию “Сохранить и выйти” (save and quit).

Подробнее об инкременте версии можно прочитать здесь.

При проверке репозитория мы заметим, что был создан файл GitVersion.yml. Это файл YAML, содержащий конфигурацию GitVersion.

mode: Mainline
branches: {}
ignore:
  sha: []
merge-message-formats: {}

Внутри него можно задать несколько параметров конфигурации. К примеру, какой режим инкрементирования следует использовать для каждой ветки, или несколько сообщений bump, которые будут применяться для ручного инкрементирования основных и второстепенных версий патчей. Подробнее об этом позже.

Возможные параметры конфигурации можно найти на странице конфигураций GitVersion.

Настройка конвейера сборки

Теперь самая важная часть  —  создание конвейера сборки. Внутри Azure DevOps функциональность конвейеров Azure должна использоваться для интеграции конвейеров CI. Большинство задач внутри этого конвейера будут специфичны для нужд приложения, которое необходимо спроектировать, но одна задача у них будет общая. А именно  —  определить версию через GitVersion.

Поскольку мы создаем модуль NPM, нам понадобится выполнить следующие задачи:

  1. Определение версии с помощью GitVersion.
  2. Установка номера версии прямо в файле package.json.
  3. Публикация модуля.

Как и файл конфигурации GitVersion, скрипты конвейеров Azure DevOps определены в YAML. Другой вариант  —  классическая форма, где задачи можно создавать путем навигации по графическому веб-интерфейсу Azure DevOps.

С учетом трех указанных выше задач, файл azure-pipelines.yml будет в результате выглядеть следующим образом:

trigger:
  - master

pool:
  vmImage: 'ubuntu-latest'
  
steps:  
  - task: [email protected]
    displayName: Determine Version Number
    inputs:
      versionSpec: '5.x'
  - task: [email protected]
    displayName: 'Set version number in package.json'
    inputs:
      targetType: 'inline'
      script: sed -i "s/0.0.1/$GITVERSIONNUMBER/g" package.json
    env:
      GITVERSIONNUMBER: $(GitVersion.SemVer)
  - task: [email protected]
    displayName: Publish Package
    inputs:
      command: publish
      publishRegistry: useFeed
      publishFeed: Demonstration Organisation/demonstration #projectName/feedName

Пройдемся по скрипту и объясним, что на самом деле происходит.

  • Строки 1–2: этот триггер определяет, на каких ветках сделанные коммиты будут запускать конвейер сборки.
  • Строки 4–5: блок пула описывает, какой пул агентов (сборку) следует использовать. В нашем случае требуется виртуальная машина Ubuntu последней версии. Поддерживаемые операционные системы и их предустановленное программное обеспечение можно найти здесь.
  • Строки 8–11: это задача GitVersion для определения правильной версии. При выполнении этой задачи встроенный интерфейс командной строки GitVersion вычислит правильный номер версии и сделает его доступным через переменные конвейера.
  • Строки 12–18: это задача программной замены номера версии в файле package.json. В соответствии с лучшими практиками промежуточный номер GITVERSIONNUMBER определяется и заполняется переменной конвейера $(GitVersion.SemVer). Эта переменная содержит номер версии, который был определен задачей GitVersion.
  • Строки 19–24: это задача публикации NPM, необходимая, чтобы опубликовать NPM-модуль в реестре NPM. В нашем случае это артефакты Azure (Azure Artifacts), часть среды Azure DevOps.

Поместите файл azure-pipelines.yml в корневой каталог репозитория.

Если вы всё сделали правильно, в папке у вас должны быть следующие файлы:

  • azure-pipelines.yml;
  • GitVersion.yml;
  • index.js;
  • package.json.

Запуск первой сборки

Пора начинать! Сделаем коммит и отправим наше творение в Azure DevOps, а потом посмотрим, что произойдет в разделе “build pipelines”.

Azure DevOps — пустой раздел конвейеров

Похоже, тут пусто. Файл иногда не попадает в Azure DevOps автоматически.

Давайте добавим конвейер, нажав кнопку создать конвейер. Выберите Azure Repos Git, а затем правильный репозиторий, чтобы сообщить разделу конвейеров Azure Pipelines, где хранятся azure-конвейеры. Теперь конвейеры Azure должны забрать ранее закоммиченный файл конвейеров. Нажмите кнопку запуска, чтобы сделать это вручную.

Azure DevOps — первая успешная сборка!

Примерно через двадцать секунд сборка завершится успешно, и результат будет виден в обзоре. Если посмотреть в колонку “Последний запуск”, то можно найти и первую версию. В нашем случае это будет первая версия по умолчанию, 0.1.0. Поскольку сборка прошла успешно, внутри артефактов Azure также добавляется опубликованный пакет.

Azure DevOps — артефакты для первой сборки

NPM-пакет действительно оказался в Azure Artifacts. Сгенерированный номер версии был соблюден, поскольку в качестве начального номера версии внутри нашего файла package.json установлен 0.0.1.

Инкремент патча, минорной и мажорной версий

Таким образом можно создавать новые версии. Давайте проверим это, сделав коммит и отправив небольшое изменение в репозиторий. Это немедленно запускает новую сборку, которая завершается примерно через двадцать секунд.

Эта новая сборка автоматически увеличивает версию патча. Но как насчет минорной и мажорной версий? Как их можно увеличить?

GitVersion предлагает множество возможностей для увеличения версий вручную.

  • Установка свойства next-version в конфигурации GitVersion. Подойдёт только в качестве базовой версии.
  • Имя ветви. Установив имя ветви release-1.0.1 или release/1.0.1, GitVersion будет использовать эту версию для всех сборок на этой ветви, с увеличенным номером в качестве метки.
  • Пометка в коммите. GitVersion будет использовать этот тег в качестве номера версии.
  • Сообщения коммита.

В последнем случае добавление +semver: breaking или +semver: major к сообщению о коммите увеличит основную версию. С помощью +semver: feature или +semver: minor можно увеличить номер минорной версии. И наконец, с +semver: patch или +semver: fix будет увеличиваться патч-версия.

При отправке с коммитом сообщения “Big change +semver: major” GitVersion действительно увеличивает основную версию. Теперь она принимает значение 1.0.0.

Azure DevOps — первая мажорная версия

При проверке артефактов Azure приходим к выводу, что определенная версия действительно соблюдается и используется.

Azure DevOps — итоговая версия

Заключение

С помощью семантического версионирования можно создавать описательные версии для приложений.

GitVersion предлагает простой подход для интеграции автоматически генерируемых семантических версий в конвейеры CI/CD. С помощью стратегий ветвления Git и конфигурации GitVersion интеграция становится простой и гибкой. Основные, минорные и патч-версии могут быть легко обновлены с помощью различных средств.

Читайте также:

Читайте нас в Telegram, VK и Яндекс.Дзен


Перевод статьи Mattias te Wierik: Automatic Semantic Versioning By Using GitVersion