web-dev-qa-db-fra.com

Comment inviter l'utilisateur à partir d'un hook commit-msg?

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.

70
Ben Hocking

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
144
Eliot Sykes

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.

4
Frosty

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

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 !"
1
Louis BAYLE

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