web-dev-qa-db-ja.com

commit-msgフック内からユーザーにプロンプ​​トを出すにはどうすればよいですか?

コミットメッセージが特定のガイドラインに従っていない場合はユーザーに警告し、コミットメッセージを編集するか、警告を無視するか、コミットをキャンセルするかを選択できるようにします。問題は、stdinにアクセスできないようです。

以下は私のコミットメッセージファイルです:

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
#?

メッセージは正しいですが、実際には入力を停止しません。また、より単純な「read」コマンドを使用してみましたが、同じ問題があります。問題は、この時点でgitがstdinを制御し、独自の入力を提供していることです。どうすれば修正できますか?

更新:これは この質問 の複製である可能性があります。残念ながら、私は運が悪いと思われます。

70
Ben Hocking

exec < /dev/ttyを呼び出すと、キーボードに標準入力が割り当てられます。コミット後のgitフックで私のために働きます:

#!/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
144
Eliot Sykes

commit-msgフックは、インタラクティブ環境では実行されません(お気付きのとおり)。

ユーザーに確実に通知する唯一の方法は、エラーをstdoutに書き込み、コミットメッセージのコピーをBAD_MSGファイルに入れ、ファイルを編集してgit commit --file=BAD_MSGにユーザーに指示することです。


環境をある程度制御できる場合は、提案されたメッセージをチェックし、コメント付きのメッセージを追加してエディターを再起動できるラッパースクリプトである代替エディターを使用できます。

基本的には、エディターを実行し、ルールに対して保存されたファイルを確認します。失敗した場合は、警告メッセージを(先頭に#を付けて)ファイルに追加し、エディターを再起動します。

チェックを抑制して続行するメッセージの#FORCE=true行への書き込みを許可することもできます。

4
Frosty

Node.jsまたはTypeScriptでそれを行う方法

EDIT:npmパッケージを作成しました


Eliot Sykesの回答 で他の言語でそれを行う方法についてコメントしている人がいますが、JavaScriptソリューションは少し長いので、別の回答をします。

O_NOCTTY が必要かどうかはわかりませんが、何も影響はないようです。制御端末とは何なのかよくわかりません。 GNUドキュメントの説明O_NOCTTYをオンにすると、CTRL+Cをプロセスに送信できない(制御端末がまだない場合)ことを意味します。その場合は、オンのままにして、生成されたプロセスを制御しないようにします。メインノードプロセスにはすでに制御ターミナルがあるはずです。

私はこれからの答えを採用しました GitHubの問題

tty.ReadStreamコンストラクターの使用方法に関するドキュメントが見当たらないため、少し試行錯誤しました/ Node.jsソースコードを掘り下げました

Node.js internals もそれを使用し、セッターを定義しないため、Object.definePropertyを使用する必要があります。もう1つの方法は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);

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

コマンドラインからgit commitを実行すると、これは正常に機能します。 Windows(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 !"
1
Louis BAYLE

入力のためにselectを停止するには、stdinselect/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
1
perx
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
0
NickUnuchek