Ganchos de pre-push de Git

115

Me gustaría ejecutar pruebas unitarias antes de cada envío de git y, si las pruebas fallan, cancelar el envío, pero ni siquiera puedo encontrar el gancho pre-push, solo hay pre-commit y pre-rebase.

pastor de ovejas
fuente

Respuestas:

14

Prefiero ejecutar la prueba en un pre-commit-hook. Porque el cambio ya está registrado al comprometerse. Empujar y tirar solo intercambia información sobre cambios ya registrados. Si una prueba falla, ya tendrá una revisión "rota" en su repositorio. Ya sea que lo esté presionando o no.

ordnungswidrig
fuente
203
En general, estoy de acuerdo, aunque si tiene la costumbre de realizar muchas confirmaciones incrementales para aplastar más tarde, y el conjunto de pruebas es grande, esto podría no ser práctico.
Cascabel
Veo. Por lo tanto, sugeriría que las pruebas se ejecuten antes de fusionarse con la rama principal, pero tampoco hay un gancho previo a la fusión. Sin embargo, hay un gancho de "actualización" que se puede usar para evitar la actualización de una referencia en el repositorio remoto: "Justo antes de actualizar la referencia en el repositorio remoto, se invoca el gancho de actualización. Su estado de salida determina el éxito o el fracaso de la referencia update. El gancho se ejecuta una vez para que se actualice cada ref, y toma tres parámetros: el nombre de la ref que se actualiza, el nombre del objeto antiguo almacenado en la ref y el nombre del nuevo objeto que se almacenará en la ref. "
ordnungswidrig
18
Votó en contra porque, aunque informativo, ignora por completo la pregunta del OP.
The Dembinski
1
@TheDembinski No diría que ignora la pregunta de OP. De hecho lo toma en consideración y dice que hay una mejor manera de hacerlo que la que tenía en mente el OP. En general, ese es el tipo de respuesta que me gustaría obtener.
calder.ty
9
@ calder.ty - No. manojlds aborda mejor lo que importa. De hecho, los ganchos de confirmación previa que ejecutan pruebas son generalmente una mala idea en mi opinión. Asume que todas las cosas que se comprometen deben pasar pruebas. Lo cual es malo para los flujos de trabajo comunes que se centran en la colaboración. Así que sí ... no estoy de acuerdo; no es una mejor manera de "hacerlo" ni aborda la pregunta.
El Dembinski
209

Git consiguió el pre-pushgancho en el 1.8.2lanzamiento.

pre-pushScript de muestra : https://github.com/git/git/blob/87c86dd14abe8db7d00b0df5661ef8cf147a72a3/templates/hooks--pre-push.sample

Notas de la versión 1.8.2 que hablan sobre el nuevo gancho pre-push: https://github.com/git/git/blob/master/Documentation/RelNotes/1.8.2.txt

manojlds
fuente
1
@manojlds, ¿sabes para qué está diseñado este gancho? Me gustaría usarlo para enviar mi binario a mis clientes al presionar a una rama específica (es decir, construir la versión nocturna y cargarla con curl, antes de presionar). El problema es que lleva un tiempo construir y cargar, y el control remoto cierra la conexión. Así que termino con mi binario creado y cargado a los clientes, pero no enviado a un repositorio, porque el repositorio remoto cierra la conexión. ¿Alguna idea de cómo solucionar esto? O tal vez sea una mala idea en su raíz.
igrek
@igrek, ¿encontró una solución al problema de cierre de la conexión?
Mario Estrada
1
@MarioEstrada, sí, no recuerdo exactamente cómo, pero lo hice presionar dos veces: el primer comando git ejecuta pruebas unitarias y luego, si no se desconecta, presiona e inicia otro empuje en otro hilo, si el primero presiona veces hacia fuera, el segundo de otro hilo funciona para mí. Si el primero y el segundo tienen éxito, el primero empuja los cambios y el segundo no empuja nada. El truco es que tuve un argumento agregado, que omite las pruebas unitarias (que se usó para el segundo empuje de git, por lo que no comenzó las pruebas unitarias nuevamente)
igrek
24

Git obtuvo el gancho pre-push en la versión 1.8.2.

Los ganchos previos al empuje son lo que necesitaba junto con los ganchos previos al compromiso. Además de proteger una sucursal, también pueden proporcionar seguridad adicional combinada con ganchos de confirmación previa.

Y para ver un ejemplo sobre cómo usar (tomado, adoptado y mejorado de esta bonita entrada )

Ejemplo simple para iniciar sesión en vagrant, ejecutar pruebas y luego presionar

#!/bin/bash
# Run the following command in the root of your project to install this pre-push hook:
# cp git-hooks/pre-push .git/hooks/pre-push; chmod 700 .git/hooks/pre-push

CMD="ssh [email protected] -i ~/.vagrant.d/insecure_private_key 'cd /vagrant/tests; /vagrant/vendor/bin/phpunit'"
protected_branch='master'

# Check if we actually have commits to push
commits=`git log @{u}..`
if [ -z "$commits" ]; then
    exit 0
fi

current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')

if [[ $current_branch = $protected_branch ]]; then
    eval $CMD
    RESULT=$?
    if [ $RESULT -ne 0 ]; then
        echo "failed $CMD"
        exit 1
    fi
fi
exit 0

Como puede ver, el ejemplo usa una rama protegida, sujeta al gancho pre-push.

Jimmy Kane
fuente
14

Si está utilizando la línea de comandos, la forma más sencilla de hacerlo es escribir un script push que ejecute sus pruebas unitarias y, si tienen éxito, complete el push.

Editar

A partir de git 1.8.2, esta respuesta está desactualizada. Vea la respuesta de manojlds arriba.

kubi
fuente
¿Te refieres a no usar ganchos en absoluto? simplemente reemplace "git pull" con, por ejemplo, "git uinttestspull"? eso no es exactamente lo que necesito
Sheepwalker
1
@sheepwalker: s / pull / push /, y usa un alias para hacerlo agradable y corto.
Cascabel
@sheepwalker Sí, eso no es exactamente lo que pediste, pero como dijo @calmh, no hay ganchos previos al empuje.
kubi
8

No hay un gancho para ello, porque un push no es una operación que modifica su repositorio.

Sin embargo, puede hacer los controles en el lado receptor, en el post-receivegancho. Ahí es donde normalmente rechazaría un empujón entrante. La ejecución de pruebas unitarias puede ser un poco intensiva para hacer en un gancho, pero eso depende de usted.

Jakob Borg
fuente
6

Para el registro, hay un parche para Git 1.6 que agrega un gancho pre-push . No sé si funciona contra 1.7.

En lugar de meterse con eso, puede ejecutar un script push como lo recomienda @kubi. También puede convertirlo en una tarea de Rake para que esté en su repositorio. ruby-git podría ayudar con esto. Si verifica el repositorio de destino, puede ejecutar pruebas solo al enviarlo al repositorio de producción.

Finalmente, puede ejecutar sus pruebas en su pre-commitgancho, pero verifique con qué rama se está comprometiendo. Entonces podría tener, digamos, una productionrama que requiera que todas las pruebas pasen antes de aceptar una confirmación, pero masterno le importa. limerick_rake puede ser útil en ese escenario.

Turadg
fuente
gracias, en realidad ya he elegido la última variante (Finalmente, podría ejecutar sus pruebas en su gancho de confirmación previa ...)
Sheepwalker
1

El script vinculado por la respuesta altamente votada muestra los parámetros, etc. del pre-pushgancho ( $1es el nombre remoto, $2URL) y cómo acceder a las confirmaciones (las líneas readde stdin tienen estructura <local ref> <local sha1> <remote ref> <remote sha1>)

#!/bin/sh

# An example hook script to verify what is about to be pushed.  Called by "git
# push" after it has checked the remote status, but before anything has been
# pushed.  If this script exits with a non-zero status nothing will be pushed.
#
# This hook is called with the following parameters:
#
# $1 -- Name of the remote to which the push is being done
# $2 -- URL to which the push is being done
#
# If pushing without using a named remote those arguments will be equal.
#
# Information about the commits which are being pushed is supplied as lines to
# the standard input in the form:
#
#   <local ref> <local sha1> <remote ref> <remote sha1>
#
# This sample shows how to prevent push of commits where the log message starts
# with "WIP" (work in progress).

remote="$1"
url="$2"

z40=0000000000000000000000000000000000000000

while read local_ref local_sha remote_ref remote_sha
do
    if [ "$local_sha" = $z40 ]
    then
        # Handle delete
        :
    else
        if [ "$remote_sha" = $z40 ]
        then
            # New branch, examine all commits
            range="$local_sha"
        else
            # Update to existing branch, examine new commits
            range="$remote_sha..$local_sha"
        fi

        # Check for WIP commit
        commit=`git rev-list -n 1 --grep '^WIP' "$range"`
        if [ -n "$commit" ]
        then
            echo >&2 "Found WIP commit in $local_ref, not pushing"
            exit 1
        fi
    fi
done

exit 0
serv-inc
fuente