コミットメッセージが特定のガイドラインに従っていない場合はユーザーに警告し、コミットメッセージを編集するか、警告を無視するか、コミットをキャンセルするかを選択できるようにします。問題は、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を制御し、独自の入力を提供していることです。どうすれば修正できますか?
更新:これは この質問 の複製である可能性があります。残念ながら、私は運が悪いと思われます。
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
commit-msg
フックは、インタラクティブ環境では実行されません(お気付きのとおり)。
ユーザーに確実に通知する唯一の方法は、エラーをstdoutに書き込み、コミットメッセージのコピーをBAD_MSG
ファイルに入れ、ファイルを編集してgit commit --file=BAD_MSG
にユーザーに指示することです。
環境をある程度制御できる場合は、提案されたメッセージをチェックし、コメント付きのメッセージを追加してエディターを再起動できるラッパースクリプトである代替エディターを使用できます。
基本的には、エディターを実行し、ルールに対して保存されたファイルを確認します。失敗した場合は、警告メッセージを(先頭に#
を付けて)ファイルに追加し、エディターを再起動します。
チェックを抑制して続行するメッセージの#FORCE=true
行への書き込みを許可することもできます。
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);
コマンドラインから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 !"
入力のためにselect
を停止するには、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
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