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