Git Hook Терпит Неудачу Молча

у меня есть post-checkout и post-merge githook с этим содержимым:

#!/bin/bash
# MIT © Sindre Sorhus - sindresorhus.com
set -eux  

changed_files="$(git diff-tree -r --name-only --no-commit-id HEAD@{1} HEAD)"

check_run() {
    echo "$changed_files" | grep --quiet "" && eval ""
}

echo ''
echo 'running git submodule update --init --recursive if .gitmodules has changed'
check_run .gitmodules "git submodule update --init --recursive"

echo ''
echo 'running npm install if package.json has changed'
check_run package.json "npm prune && npm install"

echo ''
echo 'running npm build:localhost'
npm run build:localhost 

странно, если нет изменений .gitmodules, скрипт заканчивается, а не выполняется для проверки пакета.формат JSON. (он даже не выполняет Эхо-строки после строки 12)

удаление вызовов check_run и размещение только прямых команд вместо этого, похоже, работает нормально.

удаление check_run .gitmodules "git submodule update --init --recursive" также работает. Однако такое же поведение проявляется в следующая строка: check_run package.json "npm prune && npm install" если пакет.json не был изменен

есть ли что-то, что мне не хватает, что заставляет check_run заканчивать сценарий при первом изменении файла не найден?

Визуальные Доказательства: enter image description here

1 ответов


на set -e проблема: -e означает "выход, если что-то не удается", где "не удается" определяется как "выход ненулевой".

мы видим на выходе, как последние несколько строк:

+ check_run .gitmodules 'git submodule update --init --recursive'
+ echo ''
+ grep --quiet .gitmodules

(и больше ничего). Сценарий вышел после grep --quiet.

вот фактическое определение check_run:

check_run() {
    echo "$changed_files" | grep --quiet "" && eval ""
}

это разбирает, как left && right здесь левый - это echo ... | grep ... и право - это eval "".

мы видим, что левая часть побежала, а правая-нет. Вот где нам нужно знать что-то о снарядах: даже с -e установить, они не тут выход, если что-то не удается, пока что что-то является частью теста.1 так что это не сразу проблема.

но это все еще проблема, потому что left && right имеет, как его статус выхода, статус выхода последней вещи, которую он запускает. Последний что он побежал это левый, который был echo ... | grep .... Состояние выхода конвейера - это состояние выхода его последнего компонента,2 то есть,grep. Grep выходит из нуля, если находит строку (и с --quiet, также подавляет его вывод), 1, если нет, и 2 на общих ошибках, поэтому статус выхода был 1.

таким образом, статус выхода left && right также 1.

С -e был в действии, оболочка вышел!

лекарство либо избежать -e (но это означает, что если что-то еще неожиданно терпит неудачу, снаряд пашет, поэтому это может быть немного опасно), или убедиться в том, что left && right не делает выход оболочки.

есть один простой способ сделать последнее: заменить left && right С if left; then right; fi:

if echo "$changed_files" | grep --quiet ""; then
    eval ""
fi

обратите внимание, что если eval "" сбой, оболочка все равно выйдет.

существует другой и немного сложный способ сделать это с другим эффектом: replace left && right С left && right || true. Выражение " и " связывается более плотно, поэтому это означает:

  • оценить левый
  • если это удастся (выход из нуля), оцените право
  • если && не удается (либо левый выходы равны нулю или право и право выходит ненулевым), оцените || true часть, которая выходит 0.

отсюда:

echo "$changed_files" | grep --quiet "" && eval "" || true

всегда выходит 0, eval - ing "" если и только если левая сторона терпит неудачу (не находит выражение grepped-for).

если вы хотите check_run пахать, даже если eval "" не удается, используйте второй (|| true) версию. Если вы хотите check_run, чтобы остановить (и иметь весь выход оболочки) под -e если eval "" не используйте первую (if ...; then) версию.


1поистине древний 4BSD /bin/sh задолго до того, как пошел с открытым исходным кодом, была ошибка: -e б сделайте выход оболочки в этих случаях. (Я думаю, что я сам исправил это в какой-то момент, но когда 4BSD перешел на новый sh, не основанный на оригинальном коде Стива Борна, ошибки просто не было.)

2в bash, вы можете контролировать это более подробно. В частности, вы можете получить статус компонент трубопровода в $PIPESTATUS переменной массива.