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

Gitlab access tokens

Standart-version utility

Semver

Conventional Commits

Predefined Gitlab variables reference

Комментарии (4)


  1. pavelsc
    29.12.2021 11:18

    Ко третьему дню копания CI/CD и гитлаба приходит осознание, что в мануалах проскальзывает какой-то GitLab Runner без которого никакие пайплайны не работают. Оказывется эта штука должна стоять на какой-то вашей билд-тачке, но это настолько упущено везде, как нечто само собой разумеющееся и всем со школы известное.


    1. andreykp
      29.12.2021 16:48

      Нiт. В настройках проекта галочку поставить достаточно.

      Ну или инструкции читать чуть более внимательно, если у вас self-hosted. Там все есть.


  1. mayorovp
    30.12.2021 00:28
    +1

    Какие-то вредные советы получились.


    Для начала, откуда вообще идея публиковать результат сборки обратно в гите? 2021й год на дворе, пора бы уже научиться разделять исходники и артефакты сборки.


    А во-вторых, скрипт сборки проекта не должен иметь доступа к NPM_TOKEN!


  1. olegy
    31.12.2021 13:11

    Чудеса, занимаюсь похожей штукой, читая Continuous Integration with Gitlab, а тут в ленте статья