Подтвердить что ты не робот

Как я могу запросить пользователя из крюка 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 и предоставляет свой собственный вход. Как это исправить?

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

4b9b3361

Ответ 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