Program Tip

commit-msg hook 내에서 사용자에게 어떻게 프롬프트합니까?

programtip 2020. 11. 7. 10:24
반응형

commit-msg hook 내에서 사용자에게 어떻게 프롬프트합니까?


커밋 메시지가 특정 지침을 따르지 않으면 사용자에게 경고하고 커밋 메시지를 편집하거나 경고를 무시하거나 커밋을 취소 할 수있는 옵션을 제공하고 싶습니다. 문제는 내가 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을 제어하고 자체 입력을 제공한다는 것입니다. 이 문제를 어떻게 해결합니까?

업데이트 : 이것은 불행히도 내가 운이 없다는 것을 암시하는 이 질문 의 중복 일 수 있습니다 .


호출 exec < /dev/tty은 키보드에 표준 입력을 할당합니다. 커밋 후 자식 후크에서 나를 위해 작동합니다.

#!/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

commit-msg(당신이 발견 한 것처럼) 후크는 대화 형 환경에서 실행되지 않습니다.

사용자에게 신뢰할 수있는 알림을 제공하는 유일한 방법은 stdout에 오류를 작성하고 커밋 메시지의 복사본을 BAD_MSG파일에 배치하고 사용자에게 파일을 편집하도록 지시하는 것입니다.git commit --file=BAD_MSG


환경을 제어 할 수있는 경우 제안 된 메시지를 확인하는 래퍼 스크립트 인 대체 편집기를 사용할 수 있으며 추가 주석 메시지로 편집기를 다시 시작할 수 있습니다.

기본적으로 편집기를 실행하고 저장된 파일을 규칙에 따라 확인합니다. 실패하면 #파일 앞에 경고 메시지 (앞에)를 추가 하고 편집기를 다시 시작하십시오.

#FORCE=true체크를 억제하고 계속하는 메시지에 라인 을 넣도록 허용 할 수도 있습니다 .


하려면 select입력을위한 정지, 당신은 또한 리디렉션을 시도 할 수 stdinselect에서 /dev/fd/3(: 참조 잠시 루프 내에서 bash는 읽기 입력 ).

# 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

이것은 명령 줄에서 git commit을 실행할 때 잘 작동합니다. Windows (리눅스에서 시도하지 않음)에서 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 !"

Node.js 또는 TypeScript에서 수행하는 방법

편집 : npm 패키지를 만들었습니다.


I see people commenting on how to do it for other languages in Eliot Sykes answer, but the JavaScript solution is a bit long so I'll make a separate answer.

I'm not sure if O_NOCTTY is required, but it doesn't seem to affect anything. I don't really understand what a controlling terminal is. GNU docs description. I think what it means is that with O_NOCTTY on, you wouldn't be able to send a CTRL+C to the process (if it doesn't already have a controlling terminal). In that case, I'll leave it on so you don't control spawned processes. The main node process should already have a controlling terminal, I think.

I adapted the answer from this GitHub issue

I don't see any docs on how to use the tty.ReadStream constructor so I did a bit of trial and error / digging through Node.js source code.

You have to use Object.defineProperty because Node.js internals uses it too, and doesn't define a setter. An alternative is to do process.stdin.fd = fd, but I get duplicate output that way.

Anyway, I wanted to use this with Husky.js and it seems to work so far. I should probably turn this into an npm package when I get the time.

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);

TypeScript:

#!/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);

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

참고URL : https://stackoverflow.com/questions/3417896/how-do-i-prompt-the-user-from-within-a-commit-msg-hook

반응형