Хотя концепция семантического управления версиями широко известна и используется в индустрии программного обеспечения, ее выполнение может быстро превратиться в запутанный беспорядок. Номер версии объявляется в нескольких местах (package.json, переменные среды, теги версии, конфигурации CI и т. д.). Запоминание об обновлении всех этих местоположений является нагрузкой на память разработчика, и частое запоминание об обновлении номера версии вообще часто не работает. После того, как эта проблема постоянно мешала нашей стратегии развертывания в контрактном клубе, мы решили, что пришло время разработать длительный процесс. Чтобы увидеть рабочий пример, перейдите на https://bitbucket.org/Scfast/pipelines-autoversion или https://github.com/scfast/autoversion.
Вот как это сделать:
Шаг 1. Сократите все ссылки на номера версий до одного места.
Прямо вперед — поищите в базе кода и инструментах CI ссылки на номер версии и убедитесь, что все они ссылаются на одно и только одно место. По традиции мы использовали поле версии по умолчанию в package.json. Чтобы сослаться на это значение в любом файле javascript, используйте следующее объявление:
const currentVersion = require(‘./package.json’).version;
Намеренное использование const для сохранения неизменности номера версии во время выполнения.
Для ссылки на номер версии в bash наша команда использовала инструмент командной строки jq (https://stedolan.github.io/jq/download/). Подробнее об этом ниже.
Шаг 2. Настройте автоматическую пометку Git во время процесса непрерывной интеграции.
Часть CI нашей команды использует Bitbucket Pipelines. Команды, используемые для захвата номера версии и автоматической маркировки отправки репозитория, который инициировал сборку:
- declare -x VERSION=$(jq -r '.version' package.json) - echo $VERSION - git tag $VERSION - git push origin --tags
Единственной магией здесь может быть первая команда. По сути, инструмент командной строки «jq» ищет в файле package.json поле «версия». Затем jq возвращает значение «версия» (скажем, «1.2.3»). Флаг ‘-r’ убирает кавычки с возвращаемого значения (теперь только 1.2.3). Значение сохраняется в переменной VERSION, которая создается как тег и передается в ветку, запустившую сборку. Вы можете использовать теги с кавычками, но после того, как я лично столкнулся с этим, я рекомендую не использовать теги с кавычками по причинам, которые будут слишком долго объясняться здесь.
Шаг 3. Определите стратегию управления версиями, которая больше похожа на рабочий процесс вашей команды.
Здесь все становится более самоуверенным. Я опишу то, что сделала наша команда, с полным пониманием того, что это может не работать/не применимо к рабочим процессам других команд.
Для Contract Club мы решили автоматизировать номер патча с каждым объединенным запросом на включение в мастер. Незначительные и основные обновления являются более субъективными и изменяются вручную, когда это оправдано ходом разработки. Это было успешным с нашим стилем разработки на основе ствола.
Возвращаясь к конвейерам Bitbucket, сначала реализуйте стратегию рабочего процесса ветки (см. https://confluence.atlassian.com/bitbucket/branch-workflows-856697482.html). Во-вторых, создайте скрипт для обновления номера исправления в package.json. Мы только что использовали gulp-задачу под названием автоверсия, чтобы выполнить это:
const gulp = require('gulp'); const runSequence = require('run-sequence'); // Run tasks sequentially const jsonModify = require('gulp-json-modify'); gulp.task('upversion', function () { let ver = require('./package.json').version; //version defined in the package.json file console.log('current version: ', ver) let splitString = ver.split('.', 3) let patchVersion = splitString[2].split('"',1)let patchNumber = Number(patchVersion[0]) patchNumber++ splitString[2] = String(patchNumber); process.env.VERSION = splitString.join('.'); console.log(process.env.VERSION) }) gulp.task('saveversion', function () { return gulp.src(['./package.json']) .pipe(jsonModify({ key: 'version', value: process.env.VERSION })) .pipe(gulp.dest('./')) }) gulp.task('autoversion', function () { runSequence('upversion','saveversion'); })
Наконец, примените эту задачу к конвейеру для мастера и попросите конвейер зафиксировать это изменение обратно в мастер. Все вместе этапы пайплайна будут выглядеть так:
pipelines: default: - step: script: # Regular CI process for feature branches branches: master: - step: script: # PACKAGE INSTALLATIONS - npm install - npm install -g gulp # AUTO VERSIONING - git config remote.origin.url https://<URL to repository here> - gulp autoversion - git init - git config user.name "<your username>" - git config user.email "<your email>" - git add package.json - git commit -m "[skip CI]" - git push - declare -x VERSION=$(jq -r '.version' package.json) - echo $VERSION - git tag $VERSION - git remote -v - git push origin --tags
Пара замечаний:
- В этом примере используется HTTPS. Использование SSH предпочтительнее. Если вы используете HTTPS, убедитесь, что у пользователя есть доступ на запись к master.
- Сообщение коммита «[пропустить CI]» необходимо, чтобы избежать повторного запуска сборки. Без него сработает бесконечный каскад билдов.
Вывод:
В этот момент вы можете спросить себя, зачем автоматизировать изменение одной цифры? Во-первых, потому что мы можем. Во-вторых, это избавляет разработчиков от необходимости помнить об обновлении с каждым пул-реквестом и проверять его при просмотре пул-реквестов. В-третьих, репозиторий автоматически лучше организован, что делает операции более предсказуемыми и автоматизированными. В-четвертых, и это наиболее важно, создается петля обратной связи между процессом CI и процессом контроля версий.
Мы можем обучать конвейеры для автоматизации рутинных изменений в нашем репозитории, таких как обработка пакетов контролируемых узлов, удаление кода на основе использования, форматирование и т. д. Возможность совершенствовать эти системы бизнес-логики в виде конфигураций кода не только избавит разработчиков от головной боли, но и лучше управляйте долгосрочной сложностью и помогайте бизнесу побеждать!