Как я могу запросить пользователя из крюка commit-msg?
Я хочу предупредить пользователя о том, что сообщение об их фиксации не соответствует определенному набору руководств, а затем дает им возможность редактировать сообщение об их фиксации, игнорировать предупреждение или отменять фиксацию. Проблема в том, что у меня нет доступа к stdin.
Ниже мой файл commit-msg:
function verify_info {
if [ -z "$(grep '$2:.*[a-zA-Z]' $1)" ]
then
echo >&2 $2 information should not be omitted
local_editor=`git config --get core.editor`
if [ -z "${local_editor}" ]
then
local_editor=${EDITOR}
fi
echo "Do you want to"
select CHOICE in "edit the commit message" "ignore this warning" "cancel the commit"; do
case ${CHOICE} in
i*) echo "Warning ignored"
;;
e*) ${local_editor} $1
verify_info "$1" $2
;;
*) echo "CHOICE = ${CHOICE}"
exit 1
;;
esac
done
fi
}
verify_info "$1" "Scope"
if [ $# -ne 0 ];
then
exit $#
fi
verify_info "$1" "Affects"
if [ $# -ne 0 ];
then
exit $#
fi
exit 0
Вот результат, когда я оставляю информацию о области видимой:
Scope information should not be omitted
Do you want to:
1) edit the commit message 3) cancel the commit
2) ignore this warning
#?
Сообщение верное, но оно фактически не останавливается для ввода. Я также попытался использовать более простую команду "читать", и у нее такая же проблема. Похоже, что проблема в том, что на данный момент git имеет контроль над stdin и предоставляет свой собственный вход. Как это исправить?
Обновление: похоже, это может быть дубликат этого вопроса, который, к сожалению, кажется, что мне не повезло.
Ответы
Ответ 1
Вызов exec < /dev/tty
назначает стандартный ввод на клавиатуре. Работает для меня в post-commit git hook:
#!/bin/sh
echo "[post-commit hook] Commit done!"
# Allows us to read user input below, assigns stdin to keyboard
exec < /dev/tty
while true; do
read -p "[post-commit hook] Check for outdated gems? (Y/n) " yn
if [ "$yn" = "" ]; then
yn='Y'
fi
case $yn in
[Yy] ) bundle outdated --pre; break;;
[Nn] ) exit;;
* ) echo "Please answer y or n for yes or no.";;
esac
done
Ответ 2
Хук commit-msg
не запускается в интерактивной среде (как вы заметили).
Единственный способ достоверного уведомления пользователя состоит в том, чтобы записать ошибку в стандартный вывод, поместить копию сообщения коммита в файл BAD_MSG
и дать пользователю команду отредактировать файл и git commit --file=BAD_MSG
Если у вас есть некоторый контроль над средой, у вас может быть альтернативный редактор, который представляет собой скрипт-обертку, который проверяет предлагаемое сообщение и может перезапустить редактор с дополнительным прокомментированным сообщением.
По сути, вы запускаете редактор, проверяете сохраненный файл на соответствие вашим правилам. а в случае неудачи добавьте предупреждение (с указанием #
) в файл и перезапустите редактор.
Вы могли бы даже позволить им вставить в сообщение строку #FORCE=true
, которая подавит проверку и продолжит работу.
Ответ 3
Чтобы сделать select
stop для ввода, вы также можете перенаправить stdin
из select
из /dev/fd/3
(см. Прочитать ввод в bash внутри цикл while).
# sample code using a while loop to simulate git consuming stdin
{
echo 'fd 0' | while read -r stdin; do
echo "stdin: $stdin"
echo "Do you want to"
select CHOICE in "edit the commit message" "ignore this warning" "cancel the commit"; do
case ${CHOICE} in
i*) echo "Warning ignored"
;;
e*) echo ${local_editor} $1
echo verify_info "$1" $2
;;
*) echo "CHOICE = ${CHOICE}"
exit 1
;;
esac
done 0<&3 3<&-
done
} 3<&- 3<&0
Ответ 4
это отлично работает при запуске git commit из командной строки. В окнах (не пробовали на linux), если вы используете gitk или git -gui, вы не сможете запросить, потому что вы получаете сообщение об ошибке в строке "exec </dev/tty".
Решение заключается в вызове git - bash.exe в вашем крюке:
.git/hooks/post-commit содержит:
#!/bin/sh
exec /c/Program\ Files/Git/git-bash.exe /path/to/my_repo/.git/hooks/post-checkout.sh
Файл .git/hooks/post-commit.sh содержит:
# --------------------------------------------------------
# usage: f_askContinue "my question ?"
function f_askContinue {
local myQuestion=$1
while true; do
read -p "${myQuestion} " -n 1 -r answer
case $answer in
[Yy]* ) printf "\nOK\n"; break;;
[Nn]* ) printf "\nAbandon\n";
exit;;
* ) printf "\nAnswer with Yes or No.\n";;
esac
done
}
f_askContinue "Do you want to continue ?"
echo "This command is executed after the prompt !"
Ответ 5
Как это сделать в Node.js или TypeScript
ОБНОВЛЕНИЕ: Я сделал пакет в минуту
Я вижу людей, комментирующих, как это сделать для других языков, в ответе Элиота Сайкса, но решение JavaScript немного длинное, поэтому я сделаю отдельный ответ.
Я не уверен, требуется ли O_NOCTTY
, но, похоже, это ни на что не влияет. Я не очень понимаю, что такое управляющий терминал. Описание документов GNU. Я думаю, что это означает, что при включенном O_NOCTTY
вы не сможете отправить CTRL+C
процессу (если у него еще нет управляющего терминала). В этом случае я оставлю это включенным, чтобы вы не контролировали порожденные процессы. Я думаю, что процесс основного узла уже должен иметь управляющий терминал.
Я адаптировал ответ из этого вопроса GitHub
Я не вижу никаких документов о том, как использовать конструктор tty.ReadStream
, поэтому я сделал несколько проб и ошибок/копался в исходном коде Node.js.
Вы должны использовать Object.defineProperty
, потому что внутреннее устройство Node.js также использует его и не определяет сеттер. Альтернатива - сделать process.stdin.fd = fd
, но я получаю дубликат вывода таким образом.
В любом случае, я хотел использовать это с Husky.js, и, похоже, это работает до сих пор. Я должен, вероятно, превратить это в пакет npm, когда у меня будет время.
Node.js
#!/usr/bin/env node
const fs = require('fs');
const tty = require('tty');
if (!process.stdin.isTTY) {
const { O_RDONLY, O_NOCTTY } = fs.constants;
let fd;
try {
fd = fs.openSync('/dev/tty', O_RDONLY + O_NOCTTY);
} catch (error) {
console.error('Please push your code in a terminal.');
process.exit(1);
}
const stdin = new tty.ReadStream(fd);
Object.defineProperty(process, 'stdin', {
configurable: true,
enumerable: true,
get: () => stdin,
});
}
...Do your stuff...
process.stdin.destroy();
process.exit(0);
Машинопись:
#!/usr/bin/env ts-node
import fs from 'fs';
import tty from 'tty';
if (!process.stdin.isTTY) {
const { O_RDONLY, O_NOCTTY } = fs.constants;
let fd;
try {
fd = fs.openSync('/dev/tty', O_RDONLY + O_NOCTTY);
} catch (error) {
console.error('Please push your code in a terminal.');
process.exit(1);
}
// @ts-ignore: 'ReadStream' in @types/node incorrectly expects an object.
// https://github.com/DefinitelyTyped/DefinitelyTyped/pull/37174
const stdin = new tty.ReadStream(fd);
Object.defineProperty(process, 'stdin', {
configurable: true,
enumerable: true,
get: () => stdin,
});
}
...Do your stuff...
process.stdin.destroy();
process.exit(0);
Ответ 6
read -p "Question? [y|n] " -n 1 -r < /dev/tty
echo
if echo $REPLY | grep -E '^[Yy]$' > /dev/null; then
#do if Yes
else
#do if No
fi