Je veux avertir l'utilisateur si son message de validation ne suit pas un certain ensemble de directives, puis lui donner la possibilité de modifier son message de validation, d'ignorer l'avertissement ou d'annuler la validation. Le problème est que je ne semble pas avoir accès à stdin.
Voici mon fichier 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
Voici la sortie lorsque je laisse les informations d'étendue vides:
Scope information should not be omitted
Do you want to:
1) edit the commit message 3) cancel the commit
2) ignore this warning
#?
Le message est correct, mais il ne s'arrête pas réellement pour la saisie. J'ai également essayé d'utiliser la commande "read" plus simple, et elle a le même problème. Il semble que le problème est qu'à ce stade, git a le contrôle de stdin et fournit sa propre entrée. Comment puis-je réparer ça?
Mise à jour: Il semble que cela pourrait être un doublon de cette question qui semble malheureusement suggérer que je n'ai pas de chance.
Appeler exec < /dev/tty
affecte une entrée standard au clavier. Fonctionne pour moi dans un hook git post-commit:
#!/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
Le commit-msg
hook n'est pas exécuté dans un environnement interactif (comme vous l'avez remarqué).
La seule façon de notifier l'utilisateur de manière fiable serait d'écrire une erreur dans stdout, de placer une copie du message de validation dans un BAD_MSG
fichier et demandez à l'utilisateur de modifier le fichier et git commit --file=BAD_MSG
Si vous avez un certain contrôle sur l'environnement, vous pouvez avoir un autre éditeur qui est un script wrapper qui vérifie le message proposé et peut redémarrer l'éditeur avec un message supplémentaire commenté.
Fondamentalement, vous exécutez l'éditeur, vérifiez le fichier enregistré par rapport à vos règles. et s'il échoue, ajoutez votre message d'avertissement (avec un #
) dans le fichier et redémarrez l'éditeur.
Vous pouvez même leur permettre de mettre un #FORCE=true
ligne dans le message qui supprimerait la vérification et continuerait.
Comment le faire dans Node.js ou TypeScript
EDIT: J'ai fait un paquet npm
Je vois des gens commenter comment le faire pour d'autres langues dans réponse Eliot Sykes , mais la solution JavaScript est un peu longue, je vais donc faire une réponse séparée.
Je ne sais pas si O_NOCTTY
est requis, mais il ne semble rien affecter. Je ne comprends pas vraiment ce qu'est un terminal de contrôle. description des documents GN . Je pense que cela signifie qu'avec O_NOCTTY
on, vous ne pourrez pas envoyer de CTRL+C
au processus (s'il n'a pas déjà de terminal de contrôle). Dans ce cas, je le laisse pour que vous ne contrôliez pas les processus générés. Le processus du nœud principal devrait déjà avoir un terminal de contrôle, je pense.
J'ai adapté la réponse de cette problème GitHub
Je ne vois aucun document sur la façon d'utiliser le tty.ReadStream
constructeur donc j'ai fait un peu d'essais et d'erreurs/en fouillant code source de Node.js .
Vous devez utiliser Object.defineProperty
car Node.js internals l'utilise aussi et ne définit pas de setter. Une alternative est de faire process.stdin.fd = fd
, mais j'obtiens une sortie en double de cette façon.
Quoi qu'il en soit, je voulais l'utiliser avec Husky.js et cela semble fonctionner jusqu'à présent. Je devrais probablement transformer cela en un paquet npm quand j'aurai le temps.
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);
Manuscrit:
#!/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);
cela fonctionne très bien lors de l'exécution de git commit à partir de la ligne de commande. Sous Windows (pas essayé sous linux) si vous utilisez gitk ou git-gui, vous ne pourrez pas faire d'invite car vous obtenez une erreur sur la ligne "exec </ dev/tty".
La sollicution est d'appeler git-bash.exe dans votre hook:
.git/hooks/post-commit contient:
#!/bin/sh
exec /c/Program\ Files/Git/git-bash.exe /path/to/my_repo/.git/hooks/post-checkout.sh
le fichier .git/hooks/post-commit.sh contient:
# --------------------------------------------------------
# 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 !"
Pour arrêter select
pour la saisie, vous pouvez également essayer de rediriger le stdin
de select
de /dev/fd/3
(Voir: Lire l'entrée en bash dans une boucle 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