Colocando ganchos do git no repositório

197

É uma má prática - colocar .git / hooks no repositório de projetos (usando links simbólicos, por exemplo). Se sim, qual é a melhor maneira de fornecer os mesmos ganchos para diferentes usuários do git?

shabunc
fonte

Respostas:

143

Eu geralmente concordo com Scytale, com algumas sugestões adicionais, o suficiente para que valha a pena uma resposta separada.

Primeiro, você deve escrever um script que crie os links simbólicos apropriados, especialmente se esses ganchos forem para impor políticas ou criar notificações úteis. As pessoas terão muito mais probabilidade de usar os ganchos se puderem digitar apenas do bin/create-hook-symlinksque se tiverem que fazer isso sozinhas.

Segundo, os ganchos de ligação direta impedem que os usuários adicionem seus próprios ganchos pessoais. Por exemplo, eu gosto bastante do gancho de pré-confirmação de amostra, que garante que não haja erros de espaço em branco. Uma ótima maneira de contornar isso é inserir um script de wrapper de gancho em seu repositório e vincular todos os ganchos a ele. O wrapper pode então examinar $0(supondo que seja um script bash; um equivalente como o argv[0]contrário) para descobrir em qual gancho ele foi chamado e, em seguida, chamar o gancho apropriado em seu repositório, bem como o gancho do usuário apropriado, que deverá ser renomeado , passando todos os argumentos para cada um. Exemplo rápido da memória:

#!/bin/bash
if [ -x $0.local ]; then
    $0.local "$@" || exit $?
fi
if [ -x tracked_hooks/$(basename $0) ]; then
    tracked_hooks/$(basename $0) "$@" || exit $?
fi

O script de instalação moveria todos os ganchos preexistentes para o lado (anexado .localaos nomes) e vincularia todos os nomes de ganchos conhecidos ao script acima:

#!/bin/bash
HOOK_NAMES="applypatch-msg pre-applypatch post-applypatch pre-commit prepare-commit-msg commit-msg post-commit pre-rebase post-checkout post-merge pre-receive update post-receive post-update pre-auto-gc"
# assuming the script is in a bin directory, one level into the repo
HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks

for hook in $HOOK_NAMES; do
    # If the hook already exists, is executable, and is not a symlink
    if [ ! -h $HOOK_DIR/$hook -a -x $HOOK_DIR/$hook ]; then
        mv $HOOK_DIR/$hook $HOOK_DIR/$hook.local
    fi
    # create the symlink, overwriting the file if it exists
    # probably the only way this would happen is if you're using an old version of git
    # -- back when the sample hooks were not executable, instead of being named ____.sample
    ln -s -f ../../bin/hooks-wrapper $HOOK_DIR/$hook
done
Cascabel
fonte
6
Eu adicionei chmod +x .git/hooks/*ao seu bin/create-hook-symlinks para trabalhar.
guneysus
6
@ guneysus Você não precisa disso, porque os ganchos já devem ser executáveis ​​(eles devem ser verificados dessa maneira) e os links não precisam de permissões especiais, apenas os arquivos aos quais estão vinculados.
Cascabel
13
Uma maneira melhor de obter o diretório do gancho é HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks.
Arnold Daniels
2
Eu coloquei um sistema simples baseado nesta gerir os ganchos no meu projeto: ell.io/tt$Paws.js/blob/Master/Scripts/install-git-hooks.sh
ELLIOTTCABLE
6
Peguei apenas o essencial e coloquei-o em um repositório github.com/sjungwirth/githooks
Scott Jungwirth
111

Não, colocá-los no repositório é bom, eu até sugiro fazê-lo (se eles também forem úteis para outros). O usuário precisa ativá-los explicitamente (como você disse, por exemplo, por meio de links simbólicos), o que, por um lado, é um pouco trabalhoso, mas protege os usuários, por outro lado, de executar código arbitrário sem o seu consentimento.

assustador
fonte
13
e se for uma questão de política da empresa, o código não é "arbitrário", é um código obrigatório, portanto isso seria considerado uma limitação no GIT, por não ter outro diretório (predefinido), que é rastreado, que também recebe executado junto com os ganchos regulares
Tobias Hagenbeek 24/12/2014
14
A entrega automática de ganchos é um problema de segurança. Fico feliz que o Git não faça isso diretamente - para impor a política de equipe / empresa, usar ganchos no lado do servidor ou permitir que os usuários decidam ativá-los manualmente, como o @scy descreve :)
Mark K Cowan
4
"protege os usuários [...] de executar código arbitrário sem o seu consentimento". Se um desenvolvedor fizer o que você sugere (ligação simbólica), o gancho poderá ser alterado por outra pessoa e executar "código arbitrário sem o consentimento deles"
MiniGod
24
MiniGod: Claro. Se você for suficientemente paranóico, poderá copiar os ganchos em vez de associá-los, auditá-los e somente depois ativá-los. No entanto, a maioria dos repositórios Git (necessários) contém código-fonte que deve ser executado na máquina do usuário, portanto é provável que você execute constantemente códigos não auditados em constante mudança. Mas sim, você tem razão. ;)
scy
46

Hoje em dia você pode fazer o seguinte para definir um diretório que está sob controle de versão para ser seu diretório ganchos git, por exemplo, MY_REPO_DIR/.githooksseria

git config --local core.hooksPath .githooks/

Ainda não é diretamente aplicável, mas, se você adicionar uma nota no seu README (ou o que seja), isso requer um mínimo de esforço da parte de cada desenvolvedor.

bbarker
fonte
3
Um truque que encontrei em viget.com/articles/two-ways-to-share-git-hooks-with-your-team é definir a opção na sua configuração Makefile / CMake.
Julius Bullinger
6

Em http://git-scm.com/docs/git-init#_template_directory , você pode usar um desses mecanismos para atualizar o diretório .git / hooks de cada repositório git recém-criado:

O diretório do modelo contém arquivos e diretórios que serão copiados para o $ GIT_DIR após a criação.

O diretório do modelo será um dos seguintes (em ordem):

  • o argumento fornecido com a opção --template;

  • o conteúdo da variável de ambiente $ GIT_TEMPLATE_DIR;

  • a variável de configuração init.templateDir; ou

  • o diretório padrão do modelo: / usr / share / git-core / templates.

DavidN
fonte
5

Armazenar no projeto e instalar na construção

Como outros afirmam em sua resposta, se seus ganchos são específicos para seus projetos em particular, inclua-os no próprio projeto, gerenciado pelo git. Eu levaria isso ainda mais longe e diria que, como é uma boa prática ter seu projeto compilado usando um único script ou comando, seus ganchos devem ser instalados durante a compilação.

Eu escrevi um artigo sobre gerenciamento de ganchos Git , se você estiver interessado em ler sobre isso um pouco mais a fundo.

Java e Maven

Isenção total; Eu escrevi o plugin Maven descrito abaixo.

Se você estiver lidando com o gerenciamento de compilação com o Maven para seus projetos Java, o seguinte plugin do Maven manipula a instalação de ganchos a partir de um local no seu projeto.

https://github.com/rudikershaw/git-build-hook

Coloque todos os seus ganchos do Git em um diretório do seu projeto e configure-o pom.xmlpara incluir a seguinte declaração, objetivo e configuração do plugin.

<build>
  <plugins>
    <plugin>
      <groupId>com.rudikershaw.gitbuildhook</groupId>
      <artifactId>git-build-hook-maven-plugin</artifactId>
      <configuration>
        <gitConfig>
          <!-- The location of the directory you are using to store the Git hooks in your project. -->
          <core.hooksPath>hooks-directory/</core.hooksPath>
        </gitConfig>
      </configuration>
      <executions>
        <execution>
          <goals>       
            <!-- Sets git config specified under configuration > gitConfig. -->
            <goal>configure</goal>
          </goals>
        </execution>
      </executions>
    </plugin>
      <!-- ... etc ... -->
  </plugins>
</build>

Quando você executa o projeto, o plugin configura o git para executar hooks fora do diretório especificado. Isso configurará efetivamente os ganchos nesse diretório para todos que trabalham no seu projeto.

JavaScript e NPM

Para o NPM, existe uma dependência chamada Husky, que permite instalar ganchos, incluindo os escritos em JavaScript.

// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "npm test",
      "pre-push": "npm test",
      "...": "..."
    }
  }
}

Outras

Além disso, há pré-commit para projetos Python, overcommit para projetos Ruby, e Lefthook para Ruby ou projectos nó.

Rudi Kershaw
fonte
1
Obrigado por criar este plugin, tornou a integração do meu arquivo pré-confirmação super fácil.
Michiel Bugher
3

O pacote https://www.npmjs.com/package/pre-commit npm lida com isso de maneira elegante, permitindo que você especifique ganchos de pré-confirmação em seu package.json.

Greg Magolan
fonte
1

Para projetos PHP baseados no Composer, você pode distribuir automaticamente aos engenheiros. Aqui está um exemplo para ganchos de pré-confirmação e commit-msg.

Crie uma hookspasta e, em seguida, no seu compositor.json:

 },
 "scripts": {
     "post-install-cmd": [
         "cp -r 'hooks/' '.git/hooks/'",
         "php -r \"copy('hooks/pre-commit', '.git/hooks/pre-commit');\"",
         "php -r \"copy('hooks/commit-msg', '.git/hooks/commit-msg');\"",
         "php -r \"chmod('.git/hooks/pre-commit', 0777);\"",
         "php -r \"chmod('.git/hooks/commit-msg', 0777);\"",
     ],

Em seguida, você pode atualizá-los à medida que o projeto continua, pois todo mundo está executando composer installregularmente.

Elijah Lynn
fonte
0

Aqui está um script, add-git-hook.sh, que você pode enviar como um arquivo regular no repositório e pode ser executado para anexar o gancho git ao arquivo de script. Ajuste qual gancho usar (pré-confirmação, pós-confirmação, pré-envio, etc.) e a definição do gancho no heredoc do gato.

#!/usr/bin/bash
# Adds the git-hook described below. Appends to the hook file
# if it already exists or creates the file if it does not.
# Note: CWD must be inside target repository

HOOK_DIR=$(git rev-parse --show-toplevel)/.git/hooks
HOOK_FILE="$HOOK_DIR"/post-commit

# Create script file if doesn't exist
if [ ! -e "$HOOK_FILE" ] ; then
        echo '#!/usr/bin/bash' >> "$HOOK_FILE"
        chmod 700 "$HOOK_FILE"
fi

# Append hook code into script
cat >> "$HOOK_FILE" <<EOF

########################################
# ... post-commit hook script here ... #
########################################

EOF

Esse script pode fazer sentido ter permissões executáveis ​​ou o usuário pode executá-lo diretamente. Eu usei isso para puxar automaticamente o git-pull em outras máquinas após o comprometimento.

EDIT-- Respondi à pergunta mais fácil, que não era o que era solicitado e não era o que o OP estava procurando. Eu opinava sobre os casos de uso e os argumentos para enviar scripts de gancho no repositório versus gerenciá-los externamente nos comentários abaixo. Espero que seja mais o que você está procurando.

mathewguest
fonte
Agradeço seu esforço e acredito que há aqui uma informação valiosa - ela não responde à pergunta indicada.
shabunc
Na minha opinião, se os ganchos são específicos para um repositório específico ou são componentes integrais dos fluxos de trabalho usados, eles pertencem ao repositório como arquivos. É difícil colocá-los em qualquer outro lugar sem criar mais problemas do que resolve. Você pode armazenar ganchos gerais em um repositório próprio ou em uma unidade compartilhada que possa manter o repositório do projeto completamente limpo, mas com o custo de ser muito menos prático. Concordo com os outros usuários ao dizer que os ganchos devem ser fáceis de adicionar. Links simbólicos podem criar dependência desnecessária de um sistema ou estrutura de arquivos específica.
mathewguest
Além disso, os links simbólicos quebram a capacidade dos usuários de adicionar seus próprios ganchos. O diretório .git / hooks não é rastreado; portanto, a fonte deve iniciar no repositório e entrar no script hooks, e não o contrário. Eu acho que o contra-argumento seria que os ganchos do git estão mais relacionados ao fluxo de trabalho ou equipe do que ao projeto e, portanto, não pertencem ao repositório. Dependendo do seu caso de uso específico, você está mais bem em potencialmente poluir o repositório git com ganchos menos relacionados ou prefere renunciar a um monte de complexidade ao colocá-los em outro lugar?
mathewguest
0

Você pode usar uma solução gerenciada para gerenciamento de gancho pré-confirmação, como pré-confirmação . Ou uma solução centralizada para ganchos do lado do servidor, como o Datree.io . Possui políticas internas, como:

  1. Detectar e impedir a fusão de segredos .
  2. Aplicar a configuração correta do usuário Git .
  3. Impor a integração do ticket Jira - mencione o número do ticket no nome da solicitação pull / na mensagem de confirmação.

Ele não substitui todos os seus ganchos, mas pode ajudar seus desenvolvedores com os mais óbvios, sem o inferno de configuração de instalar os ganchos em todos os computadores / repositórios de desenvolvedores.

Disclaimer: Eu sou um dos fundadores da Datrees

Shimon Tolts
fonte
3
Acho que você está criando um produto interessante, mas também acho que isso não responde à pergunta e basicamente é uma autopromoção e nada mais.
shabunc