Всем привет! Сегодня я расскажу как реализовать автоматическую публикацию npm пакета в cicd gitlab, с помощью каких инструментов мы генерируем CHANGELOG файл и обновляем версию package.json. А так же как публикуем изменения в gitlab репозитории.
Я постараюсь дать вам простую инструкцию, расскажу с какими сложностями мы столкнулись и как их решили.
Задача
Настроить ci/cd таким образом, чтобы новая версия npm пакета автоматически публиковалась в реестре пакетов при изменении master ветки в git репозитории.
Автоматически определить следующий номер версии npm пакета
Сгенерировать CHANGELOG.md файл
Опубликовать изменения в gitlab репозитории
Опубликовать пакет в реестре npm пакетов
Реализация:
Давайте посмотрим на результат и разберем код более подробно
# .gitlab-ci.yml
image: "node:16-slim"
stages:
- publish
publish:
stage: publish
variables:
GIT_STRATEGY: clone
before_script:
- apt-get update && apt-get install git -y
script:
# Конфигурация npm
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
# Конфигурация git
- git config --global user.email "${GIT_USER_EMAIL}"
- git config --global user.name "${GIT_USER_NAME}"
# Установка зависимостей и сборка проекта
- yarn
- yarn build
# Определение новой версии npm пакета и генерация CHANGELOG.md файла
- yarn standard-version
- commitMessage=$(git log -1 --pretty=%B)
- tagname=$(git tag --points-at HEAD)
- version=${tagname:1}
# Решение проблемы с циклическим вызовом
- git tag -d $tagname
- git commit --amend -m "[ci skip] ${commitMessage}" --no-verify
- git tag -a $tagname -m ''
# Публикация в gitlab
- git push <https://${GIT_SYNC_USER}:${GIT_SYNC_TOKEN}@git.nlmk.com/$CI_PROJECT_PATH.git> --follow-tags master:master
# Публикация в npm
- yarn publish --new-version $version --verbose
only:
- master
Для публикации пакета в npm и для отправки коммита в репозиторий нам требуется настроить конфигурацию для npm и git, а так же установить зависимости проекта.
Опубликовать пакет в npm репозитории можно c помощью авторизованного пользователи, либо с применением accessToken. Использование accessToken в ci/cd более предпочтительный вариант, потому что вам не придется хранить логин и пароль пользователя в переменных gitlab.
Конфигурация npm:
# Конфигурация npm
- echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > .npmrc
NPM_TOKEN
- имя ci переменной, в которой хранится npm токен
Более подробно про access tokens в npm можно прочитать в документации.
Более подробно ознакомиться с gitlab переменными можно здесь
Конфигурация git:
Для конфигурации git пользователя мы используем технического пользователя, чьё имя и email хранятся в ci переменных:
# Конфигурация git
- git config --global user.email "${GIT_USER_EMAIL}"
- git config --global user.name "${GIT_USER_NAME}"
GIT_USER_EMAIL
GIT_USER_NAME
- имя ci переменной, в которой хранится имя и email git пользователя, от которого будет создаваться коммит (В нашем случае это специальный технический пользователь)
Установка зависимостей и сборка проекта
С установкой зависимостей и сборкой проекта все просто:
# Установка зависимостей и сборка проекта
- yarn
- yarn build
Данные команды установят зависимости и соберут наш проект.
Определение новой версии npm пакета и генерация CHANGELOG.md файла
Для генерации CHANGELOG файла мы используем библиотеку standard-version.
При запуске команды standard-version
происходит следующее:
Вычисляется новая версия npm пакета в соответствие с conventional commit и semver
Обновляется версия пакета в package.json
Обновляется
CHANGELOG.md
файлСоздается тег указывающий на новую версию (например
v1.13.2
)Создается коммит с изменениями (текст коммита
chore(release): 1.13.2
)
# Определение новой версии npm пакета и генерация CHANGELOG.md файла
- yarn standard-version
- commitMessage=$(git log -1 --pretty=%B)
- tagname=$(git tag --points-at HEAD)
- version=${tagname:1}
После выполнения команды standard-version
я сохраняю в переменных сообщение созданного коммита commitMessage (chore(release): 1.13.2
), имя созданного тега tagname (v1.13.2
) и номер новой версии пакета: version (1.13.2
) Они понадобятся нам позже
Решение проблемы с циклическим вызовом
После обновления версии пакета нам требуется вылить наши изменения в git, но есть одна маленькая проблема: если мы просто опубликуем новую версию в git и обновим master ветку то запустится новый pipeline и так далее, по бесконеному циклу. чтобы этого не происходило коммит должен начинаться с [ci-skip]. Для того, чтобы решить эту проблему, следует изменить текст созданного коммита, и не забыть про теги.
# Решение проблемы с циклическим вызовом
# Удаляем поседний созданный тег
- git tag -d $tagname
# Добавляем [ci-skip] в последний коммит
git commit --amend -m "[ci skip] ${commitMessage}" --no-verify
# Создаем новый тег на последнем коммите
git tag -a $tagname -m ''
Как вы можете заметить я удаляю тег, меняю сообщение коммита и снова добавляю тег.
Если мы просто попытаемся изменить сообщение коммита с помощью команды - git commit --amend
то будет создан новый коммит. Такое поведение связано с тем, что к изменяемому коммиту привязан тег. Чтобы решить эту проблему мы удаляем тег, далее меняем сообщение коммита, а потом создаем тег на измененном коммите.
Публикация в gitlab:
Для обновления кодовой базы нам требуется внести изменения в master ветке.
# Публикация в gitlab
- git push <https://${GIT_SYNC_USER}:${GIT_SYNC_TOKEN}@git.nlmk.com/$CI_PROJECT_PATH.git> --follow-tags master:master
GIT_SYNC_USER
- имя пользователя, которое будет указано в коммитеGIT_SYNC_TOKEN
- gitlab токен технического пользователя. Более подробно про gitlab токены можно прочитать тут)CI_PROJECT_PATH
- предопределенная переменная, в которой хранится path проекта с включенным именем проекта. Более подробно со списком доступных переменных можно ознакомиться в документации
Публикация в npm
# Публикация в npm
- yarn publish --new-version $version --verbose
При публикации пакета используется переменная $version
, в которой сохранена новая версия пакета. Мы явно указываем с какой версией требуется опубликовать npm пакет.
CI/CD переменные
NPM_TOKEN
- npm токен.
GIT_USER_EMAIL
- email технического пользователя gitlab
GIT_USER_NAME
- username технического пользователя gitlab
GIT_SYNC_USER
- имя пользователя, которое будет указано в коммите
GIT_SYNC_TOKEN
- gitlab токен технического пользователя
CI_PROJECT_PATH
- path проекта с включенным именем проекта
Ссылки по теме:
Gitlab CI/CD variables: https://docs.gitlab.com/ee/ci/variables/#list-all-environment-variables
Gitlab access tokens: https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html
Standart-version utility: https://www.npmjs.com/package/standard-version
Semver: https://semver.org/
Conventional Commits: https://www.conventionalcommits.org/en/v1.0.0/
Predefined Gitlab variables reference: https://docs.gitlab.com/ee/ci/variables/predefined_variables.html