
Всем привет! Сегодня я расскажу как реализовать автоматическую публикацию 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 проекта с включенным именем проекта
