Verwendung von Git-Hooks zur Automatisierung von Entwicklungs- und Bereitstellungsaufgaben

Einführung

Die Versionskontrolle ist zu einer zentralen Anforderung für die moderne Softwareentwicklung geworden. Es ermöglicht Projekten, Änderungen sicher zu verfolgen und unter anderem Rückgängigmachungen, Integritätsprüfungen und die Zusammenarbeit zu ermöglichen. Insbesondere das Versionskontrollsystem "+ git +" hat in den letzten Jahren aufgrund seiner dezentralen Architektur und der Geschwindigkeit, mit der Änderungen zwischen Parteien vorgenommen und übertragen werden können, große Akzeptanz gefunden.

Während die "+ git +" - Suite viele gut implementierte Funktionen bietet, ist eine der nützlichsten Eigenschaften ihre Flexibilität. Durch die Verwendung eines "Hooks" -Systems können Entwickler und Administratoren mit git die Funktionalität erweitern, indem Skripte angegeben werden, die git basierend auf verschiedenen Ereignissen und Aktionen aufruft.

In diesem Handbuch werden wir die Idee von Git-Hooks untersuchen und zeigen, wie Sie Code implementieren, der Sie bei der Automatisierung von Aufgaben in Ihrer eigenen einzigartigen Umgebung unterstützt. In diesem Handbuch wird ein Ubuntu 14.04-Server verwendet, aber jedes System, auf dem Git ausgeführt werden kann, sollte auf ähnliche Weise funktionieren.

Voraussetzungen

Bevor Sie anfangen, müssen Sie "+ git +" auf Ihrem Server installiert haben. Wenn Sie Ubuntu 14.04 folgen, können Sie unseren Leitfaden unter https://www.digitalocean.com/community/tutorials/how-to-install-git-on-ubuntu-14-04 durchsuchen, um git zu installieren auf Ubuntu 14.04] hier.

Sie sollten mit der Verwendung von Git im Allgemeinen vertraut sein. Wenn Sie eine Einführung benötigen, wird die Serie, zu der die Installation gehört, als Introduction to Git: Installation bezeichnet , Usage und Branches ist ein guter Anfang.

Wenn Sie mit den obigen Anforderungen fertig sind, fahren Sie fort.

Grundidee mit Git Hooks

Git-Hooks sind ein recht einfaches Konzept, das implementiert wurde, um einem Bedarf gerecht zu werden. Bei der Entwicklung von Software für ein freigegebenes Projekt, der Einhaltung von Styleguide-Standards oder bei der Bereitstellung von Software (alles Situationen, mit denen Git häufig zu tun hat) müssen Sie häufig bei jeder durchgeführten Aktion wiederkehrende Aufgaben ausführen.

Git-Hooks sind ereignisbasiert. Wenn Sie bestimmte git-Befehle ausführen, überprüft die Software das Verzeichnis "+ hooks +" im git-Repository, um festzustellen, ob ein zugehöriges Skript ausgeführt werden muss.

Einige Skripte werden vor dem Ausführen einer Aktion ausgeführt. Mit diesen Skripten können Sie sicherstellen, dass der Code den Standards entspricht, die Integrität überprüfen oder eine Umgebung einrichten. Andere Skripte werden nach einem Ereignis ausgeführt, um Code bereitzustellen, die richtigen Berechtigungen wiederherzustellen (etwas, das git nicht gut verfolgen kann) und so weiter.

Mit diesen Funktionen können Sie Richtlinien erzwingen, die Konsistenz sicherstellen, Ihre Umgebung steuern und sogar Bereitstellungsaufgaben ausführen.

Das Buch Pro Git von Scott Chacon versucht, die verschiedenen Arten von Hooks in Kategorien zu unterteilen. Er kategorisiert sie als solche:

  • Clientseitige Hooks: Hooks, die auf dem Computer des Committers aufgerufen und ausgeführt werden. Diese wiederum sind in einige separate Kategorien unterteilt:

  • Committing-Workflow-Hooks: Committing-Hooks werden verwendet, um Aktionen festzulegen, die ausgeführt werden sollen, wenn ein Commit ausgeführt wird. Sie werden verwendet, um die Integritätsprüfung durchzuführen, Commit-Nachrichten vorab auszufüllen und Nachrichtendetails zu überprüfen. Sie können dies auch verwenden, um Benachrichtigungen beim Festschreiben bereitzustellen.

  • E-Mail-Workflow-Hooks: Diese Kategorie von Hooks umfasst Aktionen, die beim Arbeiten mit per E-Mail versendeten Patches ausgeführt werden. Projekte wie der Linux-Kernel senden und überprüfen Patches mithilfe einer E-Mail-Methode. Diese sind ähnlich wie die Commit-Hooks, können jedoch von Betreuern verwendet werden, die für die Anwendung des übermittelten Codes verantwortlich sind.

  • Sonstige: Andere clientseitige Hooks umfassen Hooks, die beim Zusammenführen, Auschecken von Code, erneutem Basieren, Umschreiben und Reinigen von Repos ausgeführt werden.

  • Serverseitige Hooks: Diese Hooks werden auf Servern ausgeführt, die zum Empfangen von Pushs verwendet werden. In der Regel wäre dies das Haupt-Repo für ein Projekt. Auch hier teilte Chacon diese in Kategorien:

  • Pre-Receive und Post-Receive: Diese werden auf dem Server ausgeführt, der einen Push erhält, um beispielsweise die Projektkonformität zu überprüfen und nach einem Push bereitzustellen.

  • Update: Dies ist wie ein Pre-Receive-Vorgang, der jedoch Verzweigungsweise ausgeführt wird, um Code auszuführen, bevor jede Verzweigung akzeptiert wird.

Diese Kategorisierungen sind hilfreich, um sich einen Überblick über die Ereignisse zu verschaffen, für die Sie optional einen Hook einrichten können. Um jedoch zu verstehen, wie diese Elemente funktionieren, sollten Sie experimentieren und herausfinden, welche Lösungen Sie implementieren möchten.

Bestimmte Hooks akzeptieren auch Parameter. Das heißt, wenn git das Skript für den Hook aufruft, werden einige relevante Daten übergeben, mit denen das Skript dann Aufgaben ausführen kann. In vollem Umfang sind die Haken, die verfügbar sind:

Hook Name Invoked By Description Parameters (Number and Description)

applypatch-msg

git am

Can edit the commit message file and is often used to verify or actively format a patch’s message to a project’s standards. A non-zero exit status aborts the commit.

(1) name of the file containing the proposed commit message

pre-applypatch

git am

This is actually called after the patch is applied, but before the changes are committed. Exiting with a non-zero status will leave the changes in an uncommitted state. Can be used to check the state of the tree before actually committing the changes.

(none)

post-applypatch

git am

This hook is run after the patch is applied and committed. Because of this, it cannot abort the process, and is mainly used for creating notifications.

(none)

pre-commit

git commit

This hook is called before obtaining the proposed commit message. Exiting with anything other than zero will abort the commit. It is used to check the commit itself (rather than the message).

(none)

prepare-commit-msg

git commit

Called after receiving the default commit message, just prior to firing up the commit message editor. A non-zero exit aborts the commit. This is used to edit the message in a way that cannot be suppressed.

(1 to 3) Name of the file with the commit message, the source of the commit message (message, template, merge, squash, or commit), and the commit SHA-1 (when operating on an existing commit).

commit-msg

git commit

Can be used to adjust the message after it has been edited in order to ensure conformity to a standard or to reject based on any criteria. It can abort the commit if it exits with a non-zero value.

(1) The file that holds the proposed message.

post-commit

git commit

Called after the actual commit is made. Because of this, it cannot disrupt the commit. It is mainly used to allow notifications.

(none)

pre-rebase

git rebase

Called when rebasing a branch. Mainly used to halt the rebase if it is not desirable.

(1 or 2) The upstream from where it was forked, the branch being rebased (not set when rebasing current)

post-checkout

git checkout and git clone

Run when a checkout is called after updating the worktree or after git clone. It is mainly used to verify conditions, display differences, and configure the environment if necessary.

(3) Ref of the previous HEAD, ref of the new HEAD, flag indicating whether it was a branch checkout (1) or a file checkout (0)

post-merge

git merge or git pull

Called after a merge. Because of this, it cannot abort a merge. Can be used to save or apply permissions or other kinds of data that git does not handle.

(1) Flag indicating whether the merge was a squash.

pre-push

git push

Called prior to a push to a remote. In addition to the parameters, additional information, separated by a space is passed in through stdin in the form of “<local ref> <local sha1> <remote ref> <remote sha1>”. Parsing the input can get you additional information that you can use to check. For instance, if the local sha1 is 40 zeros long, the push is a delete and if the remote sha1 is 40 zeros, it is a new branch. This can be used to do many comparisons of the pushed ref to what is currently there. A non-zero exit status aborts the push.

(2) Name of the destination remote, location of the destination remote

pre-receive

git-receive-pack on the remote repo

This is called on the remote repo just before updating the pushed refs. A non-zero status will abort the process. Although it receives no parameters, it is passed a string through stdin in the form of “<old-value> <new-value> <ref-name>” for each ref.

(none)

update

git-receive-pack on the remote repo

This is run on the remote repo once for each ref being pushed instead of once for each push. A non-zero status will abort the process. This can be used to make sure all commits are only fast-forward, for instance.

(3) The name of the ref being updated, the old object name, the new object name

post-receive

git-receive-pack on the remote repo

This is run on the remote when pushing after the all refs have been updated. It does not take parameters, but receives info through stdin in the form of “<old-value> <new-value> <ref-name>”. Because it is called after the updates, it cannot abort the process.

(none)

post-update

git-receive-pack on the remote repo

This is run only once after all of the refs have been pushed. It is similar to the post-receive hook in that regard, but does not receive the old or new values. It is used mostly to implement notifications for the pushed refs.

(?) A parameter for each of the pushed refs containing its name

pre-auto-gc

git gc --auto

Is used to do some checks before automatically cleaning repos.

(none)

post-rewrite

git commit --amend, git-rebase

This is called when git commands are rewriting already committed data. In addition to the parameters, it receives strings in stdin in the form of “<old-sha1> <new-sha1>”.

(1) Name of the command that invoked it (amend or rebase)

Nachdem Sie alle allgemeinen Informationen erhalten haben, können wir Ihnen zeigen, wie Sie diese in wenigen Szenarien implementieren.

Repository einrichten

Zunächst erstellen wir ein neues, leeres Repository in unserem Ausgangsverzeichnis. Wir werden dies "+ proj +" nennen.

mkdir ~/proj
cd ~/proj
git init
Initialized empty Git repository in /home/demo/proj/.git/

Jetzt befinden wir uns im leeren Arbeitsverzeichnis eines git-kontrollierten Verzeichnisses. Bevor wir etwas anderes tun, springen wir in das Repository, das in der versteckten Datei mit dem Namen "+ .git +" in diesem Verzeichnis gespeichert ist:

cd .git
ls -F
branches/  config  description  HEAD  hooks/  info/  objects/  refs/

Wir können eine Reihe von Dateien und Verzeichnissen sehen. Wir interessieren uns für das Verzeichnis "+ hooks +":

cd hooks
ls -l
total 40
-rwxrwxr-x 1 demo demo  452 Aug  8 16:50 applypatch-msg.sample
-rwxrwxr-x 1 demo demo  896 Aug  8 16:50 commit-msg.sample
-rwxrwxr-x 1 demo demo  189 Aug  8 16:50 post-update.sample
-rwxrwxr-x 1 demo demo  398 Aug  8 16:50 pre-applypatch.sample
-rwxrwxr-x 1 demo demo 1642 Aug  8 16:50 pre-commit.sample
-rwxrwxr-x 1 demo demo 1239 Aug  8 16:50 prepare-commit-msg.sample
-rwxrwxr-x 1 demo demo 1352 Aug  8 16:50 pre-push.sample
-rwxrwxr-x 1 demo demo 4898 Aug  8 16:50 pre-rebase.sample
-rwxrwxr-x 1 demo demo 3611 Aug  8 16:50 update.sample

Wir können hier ein paar Dinge sehen. Zunächst sehen wir, dass jede dieser Dateien als ausführbar markiert ist. Da diese Skripte nur beim Namen aufgerufen werden, müssen sie ausführbar sein und in der ersten Zeile muss eine shebang magic number -Referenz stehen, um den richtigen Skriptinterpreter aufzurufen . Am häufigsten sind dies Skriptsprachen wie Bash, Perl, Python usw.

Das zweite, was Sie vielleicht bemerken, ist, dass alle Dateien mit "+ .sample a" enden. Das liegt daran, dass git einfach den Dateinamen überprüft, wenn versucht wird, die auszuführenden Hook-Dateien zu finden. Abweichend vom Namen des Skripts, nach dem git sucht, wird das Skript grundsätzlich deaktiviert. Um eines der Skripte in diesem Verzeichnis zu aktivieren, müssten wir das Suffix + .sample + entfernen.

Kehren wir in unser Arbeitsverzeichnis zurück:

cd ../..

Erstes Beispiel: Bereitstellung auf einem lokalen Webserver mit einem Post-Commit-Hook

In unserem ersten Beispiel wird mit dem Hook "+ post-commit +" gezeigt, wie Sie bei jedem Commit eine Bereitstellung auf einem lokalen Webserver vornehmen. Dies ist nicht der Hook, den Sie für eine Produktionsumgebung verwenden würden, aber er lässt uns einige wichtige, kaum dokumentierte Elemente demonstrieren, die Sie bei der Verwendung von Hooks kennen sollten.

Zunächst installieren wir den Apache-Webserver, um Folgendes zu demonstrieren:

sudo apt-get update
sudo apt-get install apache2

Damit unser Skript das Web-Stammverzeichnis unter + / var / www / html + ändern kann (dies ist das Dokument-Stammverzeichnis unter Ubuntu 14.04. Ändern Sie nach Bedarf), benötigen wir eine Schreibberechtigung. Geben wir unserem normalen Benutzer den Besitz dieses Verzeichnisses. Sie können dies tun, indem Sie Folgendes eingeben:

sudo chown -R `whoami`:`id -gn` /var/www/html

Erstellen wir nun in unserem Projektverzeichnis eine "+ index.html +" - Datei:

cd ~/proj
nano index.html

Im Inneren können wir ein bisschen HTML hinzufügen, um die Idee zu demonstrieren. Es muss nicht kompliziert sein:

<h1>Here is a title!</h1>

<p>Please deploy me!</p>

Fügen Sie die neue Datei hinzu, um git anzuweisen, die Datei zu verfolgen:

git add .

Bevor Sie ein Commit durchführen, richten wir unseren Hook "+ post-commit " für das Repository ein. Erstellen Sie diese Datei im Verzeichnis ` .git / hooks +` für das Projekt:

vim .git/hooks/post-commit

Bevor wir uns ansehen, was in dieser Datei abgelegt werden soll, müssen wir ein wenig darüber lernen, wie git die Umgebung beim Ausführen von Hooks einrichtet.

Nebenbei zu Umgebungsvariablen mit Git Hooks

Bevor wir mit unserem Skript beginnen können, müssen wir ein wenig darüber lernen, welche Umgebungsvariablen git setzt, wenn Hooks aufgerufen werden. Damit unser Skript funktioniert, müssen wir eventuell eine Umgebungsvariable deaktivieren, die git beim Aufrufen des Hooks + post-commit setzt.

Dies ist ein sehr wichtiger Punkt, den Sie verinnerlichen sollten, wenn Sie Git-Hooks schreiben möchten, die zuverlässig funktionieren. Git legt verschiedene Umgebungsvariablen fest, je nachdem, welcher Hook aufgerufen wird. Dies bedeutet, dass die Umgebung, aus der git Informationen abruft, je nach Hook unterschiedlich ist.

Das erste Problem dabei ist, dass Ihre Skriptumgebung sehr unvorhersehbar werden kann, wenn Sie nicht wissen, welche Variablen automatisch festgelegt werden. Das zweite Problem ist, dass die gesetzten Variablen in der git-eigenen Dokumentation fast vollständig fehlen.

Glücklicherweise hat Mark Longair die Methode a entwickelt, um jede der Variablen zu testen, die git setzt, wenn diese Hooks ausgeführt werden. Dabei werden die folgenden Inhalte in verschiedene Git-Hook-Skripte eingefügt:

#!/bin/bash
echo Running $BASH_SOURCE
set | egrep GIT
echo PWD is $PWD

Die Informationen auf seiner Website stammen aus dem Jahr 2011 und arbeiten mit der Git-Version 1.7.1. Daher wurden einige Änderungen vorgenommen. Zum Zeitpunkt des Schreibens im August 2014 war die aktuelle Version von git in Ubuntu 14.04 1.9.1.

Die Ergebnisse der Tests für diese Version von git sind nachstehend aufgeführt (einschließlich des Arbeitsverzeichnisses, das git beim Ausführen der einzelnen Hooks anzeigt). Das lokale Arbeitsverzeichnis für den Test lautete "+ / home / demo / test_hooks " und die nackte Gegenstelle lautete " / home / demo / origin / test_hooks.git +":

  • * Hooks *: + applypatch-msg +, + pre-applypatch +, + post-applypatch +

  • Umgebungsvariablen:

  • + GIT_AUTHOR_DATE = 'Mo, 11.08.2014 11:25:16 -0400' +

  • + GIT_AUTHOR_EMAIL = demo @ example.com +

  • + GIT_AUTHOR_NAME = 'Demo User' +

  • + GIT_INTERNAL_GETTEXT_SH_SCHEME = gnu +

  • + GIT_REFLOG_ACTION = am +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Hooks *: + pre-commit,` + prepare-commit-msg`, + commit-msg +, + post-commit

  • Umgebungsvariablen:

  • + GIT_AUTHOR_DATE = '@ 1407774159 -0400' +

  • + GIT_AUTHOR_EMAIL = demo @ example.com +

  • + GIT_AUTHOR_NAME = 'Demo User' +

  • + GIT_DIR = .git +

  • + GIT_EDITOR =: +

  • + GIT INDEX FILE = .git / index

  • + GIT_PREFIX = +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Hooks *: + Pre-Rebase +

  • Umgebungsvariablen:

  • + GIT_INTERNAL_GETTEXT_SH_SCHEME = gnu +

  • + GIT_REFLOG_ACTION = rebase +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Haken *: + Post-Checkout +

  • Umgebungsvariablen:

  • + GIT_DIR = .git +

  • + GIT_PREFIX = +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Hooks *: + Post-Merge

  • Umgebungsvariablen:

  • + GITHEAD_4b407c …​ +

  • + GIT_DIR = .git +

  • + GIT_INTERNAL_GETTEXT_SH_SCHEME = gnu +

  • + GIT_PREFIX = +

  • + GIT_REFLOG_ACTION = 'anderen Master ziehen' +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Hooks *: + pre-push +

  • Umgebungsvariablen:

  • + GIT_PREFIX = +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

  • * Hooks *: + Pre-Receive,` + Update`, + Post-Receive,` + Post-Update`

  • Umgebungsvariablen:

  • + GIT_DIR =. +

  • * Arbeitsverzeichnis *: + / home / demo / origin / test_hooks.git +

  • * Hooks *: + pre-auto-gc +

  • (unbekannt, da dies nur schwer zuverlässig auszulösen ist)

  • * Hooks *: + Post-Rewrite +

  • Umgebungsvariablen:

  • + GIT_AUTHOR_DATE = '@ 1407773551 -0400' +

  • + GIT_AUTHOR_EMAIL = demo @ example.com +

  • + GIT_AUTHOR_NAME = 'Demo User' +

  • + GIT_DIR = .git +

  • + GIT_PREFIX = +

  • * Arbeitsverzeichnis *: + / home / demo / test_hooks +

Diese Variablen haben Einfluss darauf, wie git seine Umgebung sieht. Wir werden die obigen Informationen zu Variablen verwenden, um sicherzustellen, dass unser Skript die Umgebung korrekt berücksichtigt.

Zurück zum Skript

Nachdem Sie eine Vorstellung von der Art der Umgebung haben, die eingerichtet werden soll (sehen Sie sich die Variablen an, die für den Hook "+ post-commit +" festgelegt wurden), können Sie mit dem Skript beginnen.

Da Git-Hooks Standard-Skripte sind, müssen wir Git mitteilen, welcher Interpreter verwendet werden soll:

#!/bin/bash

Danach werden wir nur git selbst verwenden, um die neueste Version des Repository nach dem Festschreiben in unser Webverzeichnis zu entpacken. Dazu müssen wir unser Arbeitsverzeichnis auf Apaches Dokumentenstamm setzen. Wir sollten auch unser Git-Verzeichnis auf das Repo setzen.

Wir möchten diese Transaktion erzwingen, um sicherzustellen, dass dies jedes Mal erfolgreich ist, auch wenn Konflikte zwischen dem aktuellen Arbeitsverzeichnis bestehen. Es sollte so aussehen:

#!/bin/bash
git --work-tree=/var/www/html --git-dir=/home//proj/.git checkout -f

Jetzt sind wir fast fertig. Wir müssen uns jedoch die Umgebungsvariablen genauer ansehen, die jedes Mal gesetzt werden, wenn der Hook + post-commit + aufgerufen wird. Insbesondere wird "+ GIT_INDEX_FILE " auf " .git / index" gesetzt.

Dieser Pfad bezieht sich auf das Arbeitsverzeichnis, das in diesem Fall "+ / var / www / html +" lautet. Da der Git-Index an dieser Stelle nicht existiert, schlägt das Skript fehl, wenn wir es unverändert lassen. Um dies zu vermeiden, können wir die Variable manuell unset, was dazu führt, dass git wie üblich im Verhältnis zum Repo-Verzeichnis sucht. Wir müssen dies * über * der Kassenzeile hinzufügen:

#!/bin/bash

git --work-tree=/var/www/html --git-dir=/home//proj/.git checkout -f

Diese Arten von Konflikten sind der Grund, warum Git-Hook-Probleme manchmal schwer zu diagnostizieren sind. Sie müssen sich darüber im Klaren sein, wie git die Umgebung erstellt hat, in der es arbeitet.

Wenn Sie mit diesen Änderungen fertig sind, speichern und schließen Sie die Datei.

Da dies eine reguläre Skriptdatei ist, müssen wir sie ausführbar machen:

chmod +x .git/hooks/post-commit

Jetzt sind wir endlich bereit, die Änderungen zu übernehmen, die wir in unserem Git-Repo vorgenommen haben. Stellen Sie sicher, dass Sie wieder im richtigen Verzeichnis sind, und übernehmen Sie die Änderungen:

cd ~/proj
git commit -m "here we go..."

Wenn Sie nun den Domainnamen oder die IP-Adresse Ihres Servers in Ihrem Browser aufrufen, sollte die von Ihnen erstellte Datei "+ index.html +" angezeigt werden:

http://

Wie Sie sehen, wurden unsere letzten Änderungen beim Festschreiben automatisch in das Dokumentenstammverzeichnis unseres Webservers übertragen. Wir können einige zusätzliche Änderungen vornehmen, um zu zeigen, dass es bei jedem Commit funktioniert:

echo "<p>Here is a change.</p>" >> index.html
git add .
git commit -m "First change"

Wenn Sie Ihren Browser aktualisieren, sollten Sie sofort die neuen Änderungen sehen, die Sie angewendet haben:

Wie Sie sehen, kann diese Art der Einrichtung das Testen von Änderungen vor Ort vereinfachen. Sie möchten jedoch so gut wie nie in einer Produktionsumgebung veröffentlichen. Es ist viel sicherer, den Code zu pushen, nachdem Sie ihn getestet haben und sicher sind, dass er bereit ist.

Verwenden von Git Hooks zum Bereitstellen auf einem separaten Produktionsserver

In diesem nächsten Beispiel wird eine bessere Möglichkeit zum Aktualisieren eines Produktionsservers gezeigt. Wir können dies tun, indem wir das Push-to-Deployment-Modell verwenden, um unseren Webserver jedes Mal zu aktualisieren, wenn wir auf ein nacktes Git-Repository pushen.

Wir können denselben Server verwenden, den wir für unsere Entwicklungsmaschine eingerichtet haben. Hier werden wir unsere Arbeit machen. Wir werden in der Lage sein, unsere Änderungen nach jedem einzelnen Commit zu sehen.

Auf unserer Produktionsmaschine werden wir einen weiteren Webserver einrichten, ein nacktes Git-Repository, auf das wir Änderungen übertragen, und einen Git-Hook, der ausgeführt wird, wenn ein Push empfangen wird. Führen Sie die folgenden Schritte als normaler Benutzer mit sudo-Berechtigungen aus.

Richten Sie den Post-Receive-Hook des Produktionsservers ein

Beginnen Sie auf dem Produktionsserver mit der Installation des Webservers:

sudo apt-get update
sudo apt-get install apache2

Auch hier sollten wir dem Benutzer, als den wir arbeiten, das Eigentum an dem Dokumentenstamm übertragen:

sudo chown -R `whoami`:`id -gn` /var/www/html

Wir müssen daran denken, git auch auf diesem Rechner zu installieren:

sudo apt-get install git

Jetzt können wir ein Verzeichnis im Home-Verzeichnis unseres Benutzers erstellen, in dem sich das Repository befindet. Wir können dann in dieses Verzeichnis wechseln und ein nacktes Repository initialisieren. Ein Bare-Repository hat kein Arbeitsverzeichnis und ist besser für Server geeignet, mit denen Sie nicht viel direkt arbeiten werden:

mkdir ~/proj
cd ~/proj
git init --bare

Da dies ein nacktes Repository ist, gibt es kein Arbeitsverzeichnis und alle Dateien, die sich in einem herkömmlichen Setup in + .git + befinden, befinden sich im Hauptverzeichnis selbst.

Wir müssen einen weiteren Git-Hook erstellen. Diesmal interessiert uns der Hook "+ post-receive ", der auf dem Server ausgeführt wird, der ein " git push +" empfängt. Öffnen Sie diese Datei in Ihrem Editor:

nano hooks/post-receive

Wieder müssen wir damit beginnen, den Typ des Skripts zu identifizieren, das wir schreiben. Danach können wir den gleichen Checkout-Befehl eingeben, den wir in unserer Datei + post-commit + verwendet haben, geändert, um die Pfade auf diesem Computer zu verwenden:

#!/bin/bash
git --work-tree=/var/www/html --git-dir=/home//proj checkout -f

Da dies ein nacktes Repository ist, sollte "+ - Git-Dir +" auf das oberste Verzeichnis dieses Repos verweisen. Der Rest ist ziemlich ähnlich.

Wir müssen diesem Skript jedoch eine zusätzliche Logik hinzufügen. Wenn wir versehentlich einen "+ test-feature " - Zweig auf diesen Server pushen, möchten wir nicht, dass dieser bereitgestellt wird. Wir möchten sicherstellen, dass wir nur den Zweig " master +" bereitstellen.

Für den Hook "+ post-receive +" haben Sie in der Tabelle bereits bemerkt, dass git den Commit-Hash der alten Revision, den Commit-Hash der neuen Revision und die Referenz, die als Standardeingabe in das Skript übertragen wird, übergibt. Wir können dies verwenden, um zu überprüfen, ob die Referenz der Hauptzweig ist oder nicht.

Zuerst müssen wir die Standardeingabe lesen. Für jede Referenz, die verschoben wird, werden die drei Informationen (alte, neue, ref) als Standardeingabe in das Skript eingegeben, die durch Leerzeichen voneinander getrennt sind. Wir können dies mit einer "+ while" -Schleife lesen, um den "+ git" -Befehl zu umgeben:

#!/bin/bash
while read oldrev newrev ref
do
   git --work-tree=/var/www/html --git-dir=/home//proj checkout -f
done

Nun werden wir drei Variablen basierend auf dem, was gepusht wird, festlegen. Bei einem Master-Branch-Push enthält das Objekt "+ ref " etwas, das aussieht wie " refs / heads / master ". Wir können überprüfen, ob die Referenz, die der Server empfängt, dieses Format hat, indem wir ein " if +" -Konstrukt verwenden:

#!/bin/bash
while read oldrev newrev ref
do
   if [[ $ref =~ .*/master$ ]];
   then
       git --work-tree=/var/www/html --git-dir=/home//proj checkout -f
   fi
done

Bei serverseitigen Hooks kann git Nachrichten tatsächlich an den Client zurückgeben. Alles, was an standard out gesendet wird, wird an den Kunden weitergeleitet. Dies gibt uns die Möglichkeit, den Benutzer ausdrücklich darüber zu informieren, welche Entscheidung getroffen wurde.

Wir sollten einen Text hinzufügen, der beschreibt, welche Situation erkannt und welche Maßnahmen ergriffen wurden. Wir sollten einen "+ else +" - Block hinzufügen, um den Benutzer zu benachrichtigen, wenn eine Nicht-Master-Verzweigung erfolgreich empfangen wurde, obwohl die Aktion keine Bereitstellung auslöst:

#!/bin/bash
while read oldrev newrev ref
do
   if [[ $ref =~ .*/master$ ]];
   then
       echo "Master ref received.  Deploying master branch to production..."
       git --work-tree=/var/www/html --git-dir=/home//proj checkout -f
   else
       echo "Ref $ref successfully received.  Doing nothing: only the master branch may be deployed on this server."
   fi
done

Wenn Sie fertig sind, speichern und schließen Sie die Datei.

Denken Sie daran, wir müssen das Skript ausführbar machen, damit der Hook funktioniert:

chmod +x hooks/post-receive

Jetzt können wir den Zugriff auf diesen Remote-Server auf unserem Client einrichten.

Konfigurieren Sie den Remote-Server auf Ihrem Client-Computer

Gehen Sie auf Ihrem Client-Computer (Entwicklungscomputer) zurück in das Arbeitsverzeichnis Ihres Projekts:

cd ~/proj

Fügen Sie den Remote-Server als Remote mit dem Namen "+ production" hinzu. Sie müssen den Benutzernamen, den Sie auf Ihrem Produktionsserver verwendet haben, sowie die IP-Adresse oder den Domänennamen kennen. Sie müssen auch den Speicherort des Bare-Repositorys kennen, das Sie in Bezug auf das Basisverzeichnis des Benutzers eingerichtet haben.

Der von Ihnen eingegebene Befehl sollte ungefähr so ​​aussehen:

git remote add production @:proj

Verschieben wir unseren aktuellen Master-Zweig auf unseren Produktionsserver:

git push production master

Wenn Sie keine SSH-Schlüssel konfiguriert haben, müssen Sie möglicherweise das Kennwort Ihres Produktionsserver-Benutzers eingeben. Sie sollten etwas sehen, das so aussieht:

Counting objects: 8, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (4/4), 473 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)

To [email protected]:proj
  009183f..f1b9027  master -> master

Wie Sie sehen, befindet sich der Text von Ihrem "+ post-receive" -Hook in der Ausgabe des Befehls. Wenn wir den Domänennamen oder die IP-Adresse unseres Produktionsservers in unserem Webbrowser aufrufen, sollte die aktuelle Version unseres Projekts angezeigt werden:

Es sieht so aus, als hätte der Hook unseren Code erfolgreich in die Produktion gebracht, sobald er die Informationen erhalten hat.

Testen wir nun einen neuen Code. Zurück auf dem Entwicklungscomputer erstellen wir einen neuen Zweig, um unsere Änderungen zu speichern. Auf diese Weise können wir sicherstellen, dass alles betriebsbereit ist, bevor wir es in der Produktion einsetzen.

Erstellen Sie einen neuen Zweig mit dem Namen "+ test_feature +" und überprüfen Sie den neuen Zweig, indem Sie Folgendes eingeben:

git checkout -b test_feature

Wir arbeiten jetzt im Zweig "+ test_feature +". Nehmen wir eine Änderung vor, die wir _ möglicherweise_ in die Produktion verlagern möchten. Wir werden es auf diesen Zweig übertragen:

echo "<h2>New Feature Here</h2>" >> index.html
git add .
git commit -m "Trying out new feature"

Wenn Sie zu diesem Zeitpunkt die IP-Adresse oder den Domänennamen Ihres Entwicklungscomputers aufrufen, sollten Ihre Änderungen angezeigt werden:

Dies liegt daran, dass unsere Entwicklungsmaschine bei jedem Commit immer noch neu bereitgestellt wird. Dieser Workflow eignet sich hervorragend zum Testen von Änderungen, bevor sie in die Produktion übernommen werden.

Wir können unseren Zweig "+ test_feature +" auf unseren Remote-Produktionsserver übertragen:

git push production test_feature

Sie sollten die andere Nachricht von unserem "+ post-receive +" - Hook in der Ausgabe sehen:

Counting objects: 5, done.
Delta compression using up to 2 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 301 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)

To [email protected]:proj
  83e9dc4..5617b50  test_feature -> test_feature

Wenn Sie den Produktionsserver in Ihrem Browser erneut auschecken, sollten Sie feststellen, dass sich nichts geändert hat. Dies ist, was wir erwarten, da die Änderung, die wir vorangetrieben haben, nicht im Master-Zweig war.

Nachdem wir unsere Änderungen auf unserer Entwicklungsmaschine getestet haben, sind wir sicher, dass wir diese Funktion in unsere Hauptniederlassung integrieren möchten. Wir können unseren "+ master " - Zweig auschecken und in unserem " test_feature +" - Zweig auf unserer Entwicklungsmaschine zusammenführen:

git checkout master
git merge test_feature

Jetzt haben Sie das neue Feature in der Hauptniederlassung zusammengeführt. Durch das Pushen auf den Produktionsserver werden unsere Änderungen bereitgestellt:

git push production master

Wenn wir den Domain-Namen oder die IP-Adresse unseres Produktionsservers auschecken, werden wir unsere Änderungen sehen:

image: https: //assets.digitalocean.com/articles/git_hooks/new_prod.png

Mithilfe dieses Workflows können wir über eine Entwicklungsmaschine verfügen, die festgeschriebene Änderungen sofort anzeigt. Die Produktionsmaschine wird jedes Mal aktualisiert, wenn wir die Hauptniederlassung verschieben.

Fazit

Wenn Sie so weit gegangen sind, sollten Sie in der Lage sein, die verschiedenen Möglichkeiten zu erkennen, mit denen Git-Hooks bei der Automatisierung einiger Ihrer Aufgaben helfen können. Sie können Ihnen bei der Bereitstellung Ihres Codes oder bei der Einhaltung von Qualitätsstandards helfen, indem Sie nicht konforme Änderungen ablehnen oder Nachrichten festschreiben.

Während die Nützlichkeit von Git-Hooks schwer zu diskutieren ist, kann die tatsächliche Implementierung schwierig zu erfassen und die Fehlerbehebung frustrierend sein. Wenn Sie üben, verschiedene Konfigurationen zu implementieren, mit dem Parsen von Argumenten und der Standardeingabe zu experimentieren und zu verfolgen, wie git die Hook-Umgebung erstellt, lernen Sie auf lange Sicht, wie man effektive Hooks schreibt. Auf lange Sicht lohnt sich die Zeitinvestition in der Regel, da Sie und Ihr Team im Laufe des Projektlebens viel Handarbeit sparen können.