Автоматическая установка npm-зависимостей при выполнении git pull

Photo by XPS on Unsplash

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

Когда я вытягиваю последние изменения в свою текущую ветку или переключаюсь между ветками, мне приходится проверять, не изменился ли package-lock.json (lock-файл). Если изменился, мне нужно запустить npm install, чтобы обновить мои зависимости до последних изменений. В противном случае я рискую получить трудно выявляемые баги, когда код на одной машине работает, а на другой — нет, и все из-за устаревших зависимостей.

Здесь нам помогут хуки

Мы уже используем хук pre-commit для автоматического запуска линтинга и форматирования при git commit. С Git Hooks и таким инструментом, как Husky, это довольно просто. К счастью, Git также поддерживает хук post-merge, который запускается в локальном репозитории после выполнения git pull.

Именно в этот момент нам и нужно обновить зависимости, чтобы увидеть, не изменились ли они. Если вам нужны подробные инструкции по началу работы с хуками, я рекомендую вот это руководство.

Обнаружение изменений

Когда мы вытягиваем последние изменения с помощью git pull, нам нужен список всех измененных файлов.

Если в этом списке есть package-lock.json, нам нужно запустить npm install для обновления наших зависимостей. При работе в монорепозитории с несколькими пакетами (как в моем случае), нам приходится проделывать это для каждого измененного пакета.

Вывести список измененных файлов можно при помощи команды git diff:

git diff --name-only HEAD@{1} HEAD

Применив простое регулярное выражение, мы можем отфильтровать все пути, содержащие файл package-lock.json. Я поместил regex в переменную PACKAGE_LOCK_REGEX, потому что эта часть должна меняться в зависимости от структуры проекта.

Регулярное выражение в паттерне содержит группу (первая пара круглых скобок в примере). Группа начинается с packages/, потому что в нашем монорепозитории все пакеты живут в этой директории (за исключением зависимостей разработки, которые находятся в корневой директории проекта).

Результат фильтрации, осуществленной при помощи regex, сохраняется в виде массива в переменную PACKAGES.

IFS=$'\n'
PACKAGE_LOCK_REGEX="(^packages\/.*\/package-lock\.json)|(^package-lock\.json)"
PACKAGES=("$(git diff --name-only HEAD@{1} HEAD | grep -E "$PACKAGE_LOCK_REGEX")

Запуск установки

Наконец, нам нужно запустить npm install для каждого измененного пакета. Поскольку Git запускается в корневой директории проекта, а измененные файлы это, собственно, пути к lock-файлам, перед запуском установки нам нужно сменить директорию.

При помощи $(dirname package) мы можем без проблем выделить имена директорий из пути.

if [[ ${PACKAGES[@]} ]]; then
  for package in $PACKAGES; do
    echo "📦 $package was changed. Running npm install to update your dependencies..."
    DIR=$(dirname package)
    cd "$DIR" && npm install
  done
fi

Хук post merge

Все приведенные выше отрывки кода можно соединить в следующий shell-скрипт. Он будет выполняться Husky в качестве хука post-merge.

#!/bin/zsh
. "$(dirname "$0")/_/husky.sh"

IFS=$'\n'
# regex supports mono-repos with a package.json at root-level and at package-level
PACKAGE_LOCK_REGEX="(^packages\/.*\/package-lock\.json)|(^package-lock\.json)"
# extract all paths to package-lock.json files 
PACKAGES=("$(git diff --name-only HEAD@{1} HEAD | grep -E "$PACKAGE_LOCK_REGEX")")

if [[ ${PACKAGES[@]} ]]; then
  for package in $PACKAGES; do
    echo "📦 $package was changed. Running npm install to update your dependencies..."
    DIR=$(dirname package)
    cd "$DIR" && npm install
  done
fi

Файл нужно сохранить с именем post-merge (без расширения .sh) в папке .husky. Я запускаю его в macOS в дефолтной оболочке zsh (поэтому в начале скрипта стоит #!/bin/zsh), и все работает. Но в bash я этот скрипт не тестировал, так что при запуске в других оболочках могут потребоваться какие-то правки.

От редакции Techrocks. Возможно, вас заинтересуют статьи:

Тестирование

Чтобы проверить, работает ли наш хук post merge, мы можем сбросить текущую локальную ветку к предыдущему состоянию (например, отмотать на 20 коммитов назад), а затем вытащить заново эти изменения.

git reset --hard HEAD~20 && git pull

Если в одном из этих коммитов изменился package-lock.json, хук выведет небольшое сообщение по каждому lock-файлу и автоматически запустит npm install.

Если вы используете Git-интеграцию в редакторе, например, в VSCode, то чтобы увидеть, что происходит, вам нужно проверить вывод Git log.

От редакции Techrocks. Рекомендуем статью «Самые лучшие расширения VS Code для работы с Git».

Перевод статьи «Automatically Install NPM Dependencies on Git Pull».

[customscript]techrocks_custom_after_post_html[/customscript]

[customscript]techrocks_custom_script[/customscript]

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Прокрутить вверх