Мы с командой работаем в одном репозитории проекта со множеством пакетов — монорепозитории. Естественно, мы используем 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]



