PHP- bzw. WordPress-Projekte mit Git verwalten und deployen

Ich bin dazu übergegangen, selbst kleine PHP-Projekte, meist WordPress-Installationen, in einem Git-Repository zu halten und auch mit Git zu deployen. Im Folgenden möchte ich beschreiben, wie das funktioniert.

Vor-/Nachteile:

  • + Kein langsames und nerviges FTP
  • + Komplette History aller Code-Änderungen und Möglichkeit alte Stände zu reproduzieren
  • + Automatisches Deployment
  • - Einarbeitung in git erforderlich

Ich nutze als Git-Repository Bitbucket. Damit kommt als Vorteil noch ein abgespeckter Issue-Tracker und ein Wiki hinzu, die sich gut mit dem Repository integrieren.

Voraussetzungen

  • git auf dem lokalen Rechner
  • git auf dem Hosting-Rechner

Um den zweiten Punkt zu überprüfen, müssen zwei Varianten unterschieden werden. Falls es einen SSH-Zugang gibt, ist wahrscheinlich git verfügbar. Einfach per ssh anmelden und

git --version

aufrufen, um das zu testen. Bei günstigen Hosting-Angeboten gibt es keinen SSH-Zugang. Dann muss das Deployment über php ausgeführt werden. Um zu testen, ob git installiert ist und die Ausführung von git erlaubt ist, erstelle einfach eine Datei mit folgendem Inhalt und lade diese per FTP auf deinen Webspace:

<?php
Header('Content-Type: text/plain');
$path = trim(shell_exec('which git'));
if (!$path) {
 $path = str_replace('git is ', '', trim(shell_exec('type git')));
}
echo "git path: " . $path . "\n";
$version = shell_exec($path.' --version');
echo "git version: " . $version . "\n";

Wenn du die Datei aufrufst und die Ausgabe so oder so ähnlich aussieht, dann ist alles gut:

git path: /usr/bin/git
git version: git version 1.7.3.4

Lokales Git-Repository erstellen

Ich arbeite mit git auf der Kommandozeile, aber es gibt auch grafische Benutzeroberflächen für alle Betriebssysteme, die dieselben Funktionen unterstützen, die ich im Folgenden als Kommandozeilenbefehl aufführe.

Zum Erstellen eines lokalen Repositorys gehe ich in das Verzeichnis, in dem das Projekt liegt. Das ist ein Unterverzeichnis eines htdocs Ordners eines lokal installierten Apache-Servers, so dass ich das Projekt auch lokal aufrufen kann. Ich führe auf der Kommandozeile aus:

git init

Nun erstelle ich die Dateien für das Projekt. Da es sich in meinem Fall um ein WordPress-Projekt handelt, heißt das: WordPress installieren. Ich richte es komplett ein inklusive der automatischen Erstellung der wp-config.php. Anpassung an den Themes mache ich noch nicht.

Bevor ich nun mein erstes Commit ausführe, lege ich noch eine Datei .gitignore an, um manche Dateien auszuschließen. Meine sieht wie folgt aus:

.DS_Store
.project
.htaccess
wp-config.php
wp-content/uploads/

Nicht alle Einträge sind für jeden interessant. Im einzelnen bedeuten diese:

  • .DS_Store: Datei, die OSX automatisch anlegt. Hat im Repo nichts verloren
  • .project: Eclipse-Projekt-Datei (ich arbeite mit AptanaStudio).
  • .htaccess, wp-config.php: Diese Dateien sind abhängig von der Deploy-Umgebung und sollten nicht Teil der Codebasis sein
  • wp-content/uploads/: Hier verwaltet WordPress seine Bilder etc. Das ist nicht Teil der Code-Basis

Jetzt führe ich

git add -A

aus, um alle Dateien zu "stagen". Mit

git commit -m "Initial setup of WordPress"

mache ich mein erstes Commit. Ab jetzt werden alle Änderungen getrackt. Lösche ich Beispielsweise das Hello Dolly-Plugin (Sorry, Matt!), führe ich aus:

git rm wp-content/plugins/hello.php
git commit -m "Remove Hello Dolly plugin"

Ich versuche, dass jedes Commit möglichst nur eine abgeschlossene Aufgabe enthält.

Remote-Repository einrichten

Das Remote-Repository soll in meinem Fall privat sein, so dass niemand meinen Source-Code sehen kann. Daher verwende ich als Remote-Repository Bitbucket, da dies für kleine Projekte mit weniger als 5 Commitern umsonst ist. Github ist eine Alternative, allerdings sind hier private Repositorys nur gegen monatliches Entgelt möglich.

Nachdem ich einen User in Bitbucket angelegt habe, lege ich in Bitbucket ein Git-Repository an. Anschließend fragt mich Bitbucket, ob ich schon ein Projekt habe, und bietet mir folgende Hilfe an:

cd /path/to/my/repo
git remote add origin <repository_url>
git push -u origin --all # pushes up the repo and its refs for the first time
git push -u origin --tags # pushes up any tags

Das ist nett. In meinem lokalen Verzeichnis bin ich bereits und Tags habe ich noch keine, aber die 2. und 3. Zeile sind interessant. Ich führe sie aus:

git remote add origin <repository_url>
git push -u origin --all

Ich muss mein Bitbucket-Password eingeben. Um beim Zugriff auf Bitbucket nicht immer das Passwort eingeben zu müssen, verwende als <repository_url> die URL-Variante SSH (siehe "Overview") und erstelle einen SSH Key. In Bitbucket gehe dazu auf Manage Accout -> SSH keys. Dort wird das Vorgehen erklärt und ich kann einen Key hinzufügen.

Anschließend sind alle Sourcen im Bitbucket-Repository und ich kann diese im Reiter "Source" sehen.

Dateien auf Webspace deployen

Meine Dateien sind noch nicht auf meinem Webspace, das steht nun an. Ich habe nun zwei Möglichkeiten. Welche ich wähle hängt auch von meinem Hosting ab.

Mit SSH-Zugang

In diesem Fall öffne ich eine SSH-Session und führe im Verzeichnis, in dem das Projekt liegen soll, aus:

git clone <repository_url>

Die URL findest du bei Bitbucket im Tab "Overview". Um beim Zugriff auf Bitbucket nicht immer das Passwort eingeben zu müssen wähle die URL-Variante SSH und richte SSH so ein, wie es für den lokalen Rechner notwendig war (siehe oben).

Nachdem du nun zukünftig Code-Änderungen von deinem lokalen Rechner commitet hast, einfach wieder per SSH anmelden und

git pull

ausführen und dein Webspace ist wieder aktuell.

Mit dem WordPress-Plugin Revisr

Revisr setze ich mittlerweile häufig ein. Es ist ein WordPress-Plugin und Git-Client und bietet eine komfortable Oberfläche zur Verwendung von Git.

Ich habe mein Vorgehen mit Revisr hier ausführlich beschrieben: Update zu Git vs. WordPress: Deployment mit Revisr.

Per PHP-Skript

Ich habe vor einiger Zeit ein Skript gefunden, was sehr gut geeignet ist. Es wird bereitgestellt von Marko Marković unter https://github.com/markomarkovic/simple-php-git-deploy/.

Lade das Projekt am besten als ZIP herunter oder zieh dir nur die Dateien deploy.php und deploy-config.php. Kopiere die Dateien in ein Verzeichnis außerhalb deines Projekts und bearbeite diese nun.

Die Datei deploy-config.php enthält die projektspezifischen Einstellungen, wie Repo-URL oder Serververzeichnisse. Zu allen Einstellungen enthält diese Datei nützliche Kommentare. Zusammen mit der Datei README.md, die auch auf der Github-Seite angezeigt wird, besteht also eine gute Dokumentation. Zunächst setze ich das Secret Access Token. Dazu gehe ich auf http://www.passwort-generator.com/ und generiere ein Passwort mit 64 Zeichen bestehend aus kleinen und großen Buchstaben sowie aus Zahlen (Beispiel: xGeTyGDfr7VZkuWLeNax5x2EpJnEkXgGwmsUTVZ9TMY46MFarFym3rQgY3L9RSGd). Diese Zeichenfolge setze ich in der deploy-config.php. Sie wird dazu verwendet, um die deploy.php über den Browser aufzurufen und damit den Deploy-Vorgang zu starten (Beispiel: http://www.example.com/deploy.php?sat=      xGeTyGDfr7VZkuWLeNax5x2EpJnEkXgGwmsUTVZ9TMY46MFarFym3rQgY3L9RSGd). Ein unautorisierter Aufruf (ohne sat-Parameter) ist nicht möglich.

Nun setze ich die Repository-URL. Ich verwende die HTTPS-Variante. Wichtig: Der Username darf nicht enthalten sein. Also die Zeichenfolge <dein_username>@ aus der URL entfernen.

Die nächste Einstellung ist das TARGET_DIR. In diesem Verzeichnis liegt dein Projekt auf dem Webspace, allerdings aus Sicht des Dateisystems des Webspaces. Eine Möglichkeit, diesen Pfad herauszubekommen, ist der phpinfo()-Befehl. Dazu einfach eine PHP-Datei phpinfo.php erstellen mit folgendem Inhalt, auf den Webspace hochladen und aufrufen.

<?
  phpinfo();

In der Ausgabe findet sich unter dem Punkt DOCUMENT_ROOT das gesuchte Verzeichnis.

Als nächstes ist das TMP_DIR zu setzen. Dazu muss ein temporärer Ordner im Webspace bestehen. Dieser sollte außerhalb des Projekts liegen. In meinem Fall liegt das Projekt in einem Ordner /webseiten und der temporäre Ordner liegt daneben (/tmp).

Im Deploy-Skript setze ich nun den Ordner, so dass dort steht:

define('TMP_DIR', '<temp_ordner>/spgd-'.md5(REMOTE_REPOSITORY).'-'.time().'/');

Die Dateien deploy.php und deploy-config.php lade ich nun hoch per FTP. Bevor ich diese nun aufrufe, ist Folgendes zu bedenken. Das Skript ruft intern git auf. Sollte git nach einem Passwort für das Remote-Repository (z.B. Bitbucket) fragen, gibt es keine Möglichkeit, dieses einzugeben, da eine Benutzerinteraktion auf diese Art und Weise nicht möglich ist. Es muss also gewährleistet sein, dass git nicht nach einen Passwort fragt. Hier gibt es nun zwei Möglichkeiten. Entweder die Erstellung eines SSH Keys wie es per SSH-Zugang notwendig ist, oder über eine .netrc Datei, die im Benutzerverzeichnis (üblicherweise das oberste Verzeichnis) auf dem Webspace liegen muss. Nachteil der .netrc Datei: Das Passwort ist dort im Klartext hinterlegt. Ich nehme das in Kauf, um mit git und dem Deploy-Skript arbeiten zu können, denn in meinem Hosting-Angebot ist SSH nicht enthalten. Die Datei sieht wie folgt aus

machine bitbucket.org
login <dein_username>
password <dein_passwort>

Jetzt rufe ich das Deploy-Skript inklusive sat-Parameter (siehe oben) auf.

Das Skript listet alle Dateien, die deployt wurden auf und gibt am Ende ein Done aus.

Löschen von Dateien, die nicht im Repo sind

Mit den Default-Einstellungen werden Dateien, die aus dem Repo gelöscht werden, nicht im Webspace gelöscht. Das ist nicht so gut, denn wenn ich beispielsweise ein WordPress lösche, dann soll das nicht mehr im Webspace und damit in meiner Plugin-Liste im Adminbereich von WordPress auftauchen.

Dazu bietet das Deploy-Skript die Option DELETE_FILES. Diese setze ich auf true. Nun werden alle Dateien, die nicht im Repo sind, beim Deployen im Webspace entfernt. Das ist noch nicht ganz das was ich will, denn beispielsweise sind die Dateien .htaccess oder wp-config.php nicht im Repo, aber wichtig für mein Projekt. Aus diesem Grund bietet das Skript die Möglichkeit, mit der Option EXCLUDE Dateien vom Abgleich auszuschließen. Für diese Konfiguration lohnt sich ein Blick in die Datei .gitignore, da die gelisteten Dateien und Verzeichnisse potentielle Kandidaten. Hier ein Beispiel für die Option aus einem WordPress-Projekt:

define('EXCLUDE', serialize(array(
    '.htaccess',
    'deploy.php',
    'deploy-config.php',
    'wp-config.php',
    'wp-content/uploads/',
    'wp-content/bps-backup/',
    'sitemap.*',
    'google*.html',
    'BingSiteAuth.xml'
)));

Nun noch einmal deploy-config.php per FTP hochladen (ein letztes Mal FTP, versprochen) und ausführen.

Get into the flow…

Und weil's so schön ist, noch einmal kurz demonstriert, wie es nun weiter geht. In meinem Fall handelt es sich ja um eine WordPress-Installation. Als Theme möchte ich das neue twentythirteen verwenden und es für meine Zwecke anpassen. Als erstes schmeiße ich die anderen Themes aus der Codebasis

git rm -r wp-content/themes/twentyten
git rm -r wp-content/themes/twentyeleven
git rm -r wp-content/themes/twentytwelve

Mit git rm sind die Dateien schon gestaget, jetzt reicht ein einfaches commit

git commit -m "Remove unused themes"
git push

Anschließend Deploy-Skript im Browser aufrufen.

Das twentythirteen ist cool, aber die Überschrift ist mir ein bisschen zu groß. Ich ändere das in der style.css Klasse.

Wenn ich viele Änderungen gemacht habe, nutze ich manchmal den Befehl

git diff

Nun sehe ich alle Änderungen, in diesem Fall

diff --git a/wp-content/themes/twentythirteen/style.css b/wp-content/themes/twentythirteen/style.css
index 60d0416..a5872b9 100644
--- a/wp-content/themes/twentythirteen/style.css
+++ b/wp-content/themes/twentythirteen/style.css
@@ -809,7 +809,7 @@ img.wp-smiley,
       display: block;
       margin: 0 auto;
       max-width: 1080px;
-       min-height: 230px;
+       min-height: 160px;
       padding: 0 20px;
       text-decoration: none;
       width: 100%;
@@ -820,11 +820,11 @@ img.wp-smiley,
}

.site-title {
-       font-size: 60px;
+       font-size: 30px;
       font-weight: bold;
       line-height: 1;
       margin: 0;
-       padding: 58px 0 10px;
+       padding: 64px 0 10px;
}

Ich kann mich nochmal vergewissern, dass ich nur das committe, was ich committen möchte.

Nun rufe ich auf

git commit -a -m "Style header"
git push

Anschließend rufe ich Deploy-Skript im Browser auf.

Die Option -a macht ein git add überflüssig, funktioniert aber nur bei Dateiänderungen, nicht bei neuen Dateien!

Manch einer mag sich wundern, warum ich direkt das WordPress-Theme überschreibe, und nicht ein Konzept wie Child Theme nutze. Ich bin der Meinung, dass es keine 100 %ig saubere Lösung gibt, wenn man A) ein fertiges Produkt inklusive zukünftiger Updates oder Bugfixes nutzen und B) dieses fertige Produkt anpassen will. Daher finde ich die Verwendung eines Git-Repositorys so gut. Zwar entsteht eine Herausforderung mit einem Update von WordPress, Themes oder Plugins, diese bekomme ich aber gelöst wie hier beschrieben.

 

bjoerne_com_bjoern_weinbrenner_softwareentwickler_icon_leistungen_02

Lust auf mehr? Als Experte für PHP, WordPress und individuelle WordPress-Entwicklung kann ich Sie in Ihren Projekten unterstützen. Melden Sie sich gerne bei mir.

2017-01-28T13:52:56+00:00 23.08.2013|Tags: , , , , |16 Comments

16 Kommentare

  1. Björn Weinbrenner 26. Februar 2014 um 10:29 Uhr- Antworten

    Überarbeitung des Artikels. Die Option EXCLUDE_GITIGNORE ist wieder aus dem Projekt von Marko Marković entfernt worden, so dass ich diese nicht mehr erwähne. Außerdem wurde zwischenzeitlich eine nützliche Trennung zwischen deploy.php und deploy-config.php eingefügt. Good Job, Marko!

  2. Sumit 9. Mai 2014 um 15:52 Uhr- Antworten

    Oh man das würde ich zu gerne einrichten. Bin gerade dran, mich in GIT einzuarbeiten. Leider scheint Strato auf ihren Webservern kein GIT installiert zu haben :-/
    Habe bereits an den Support geschrieben.

  3. Björn Weinbrenner 9. Mai 2014 um 19:24 Uhr- Antworten

    Ich hoste einige Projekte bei Domaingo. Dort ist Git verfügbar. Das Hosting ist zudem sehr günstig.

  4. Thomas 17. August 2014 um 17:30 Uhr- Antworten

    Hallo. Ich arbeite mich gerade auch etwas in git ein und bin auf der Suche nach der Frage wie ich denn meine Website deploye auf deine Erklärung gestoßen.
    Nun habe ich folgendes gemacht:
    Auf dem Server ein bare-Repository erstellt.
    Lokal dieses Repository geklont.
    Nun habe lokal eine Text-Datei erstellt, geaddet, einen commit durchgeführt und via push an mein Remote-Repository gesendet.

    Nun hast du in deiner Anleitung geschrieben, das man ja im 'www'-Ordner des Servers, also da wo die Website liegt ja mittels "git clone..." einen Klon des Repositorys einfügen kann.
    Das klappt auch soweit nur dann habe ich ja den ".git" Ordner auch in diesem Ordner und der könnte ja theoretisch von jemanden via 'www.example.org/.git/config' oder so aufgerufen werden.

    Wie kann ich denn NUR die Datein die zu der Website gehören klonen?

    Ich hoffe du/ihr versteht mein Anliegen.

  5. Thomas 17. August 2014 um 18:18 Uhr- Antworten

    Das verbergen hat aber keinen Einfluss dann auf mein "clone" später, ja?

  6. Björn Weinbrenner 17. August 2014 um 18:31 Uhr- Antworten

    Richtig, das hat keinen Einfluss.

  7. Thomas 17. August 2014 um 18:32 Uhr- Antworten

    Super! Perfekt! Danke, jetzt ergibt das auch alles einen Sinn das so zu machen! Danke!

  8. Boris Bojic 28. Oktober 2014 um 12:24 Uhr- Antworten

    Interessanter wäre die Frage, wie du die lokale und Online-Datenbank deployst und auch aktuell / gesynct hälst? 😉

    • Björn Weinbrenner 28. Oktober 2014 um 14:03 Uhr- Antworten

      Hallo Boris!
      Ich halte die lokale und Online-Datenbank nicht ständig synchron. Nur gelegentlich synchronisiere ich diese. Dann lade ich manuell den Dump der Quelldatenbank herunter. Für die Konvertierung der URLs eignet sich das WordPress Command Line Interface (http://wp-cli.org/) sehr gut, denn es berücksichtigt auch serialisierte Daten. Alternativ der gute alte Texteditor mit Suchen-Ersetzen-Funktion.

      • Boris Bojic 28. Oktober 2014 um 14:39 Uhr- Antworten

        Hm, das mag für eine kleine oder eigene Website ja kein Problem sein, aber eine größere WordPress-Installation von Kunden, wo tagtäglich neue Artikel geschrieben werden, ist das nicht praktikabel.

        Aber der heilige Gral um dieses Thema wurde bisher anscheinend noch nicht gefunden 😉

  9. David Ma 26. September 2016 um 13:48 Uhr- Antworten

    Auch wenn so späte Kommentare immer wie Spam wirken… 😀
    Super Artikel, sehr ausführlich und mein Hoster (all-inkl) erlaubt Git (danke auch für das Skript zum fix Testen, ob es geht).
    Ich hab zwar auch immer mit Git versioniert, aber nur zum Sichern oder falls ich die Dateien mal auf einem anderen Rechner brauchte oder teilen wollte, hochgeladen habe ich immer via FTP. Nervig. So ists besser, auch wenn es auf den ersten Blick umständlicher wirkt.
    Vielen Dank für die Anleitung 🙂

    • bjoerne 26. September 2016 um 23:01 Uhr- Antworten

      Hallo David!
      Auf all-inkl.com habe ich die Erfahrung gemacht, dass Git per Kommandozeile aus PHP heraus (exec-Funktion) nur aufrufbar ist, wenn die PHP-Datei die Endung .phpx ist. Etwas nervige Einschränkung, die das Arbeiten mit WordPress und Revisr untersagt. Auf all-inkl.com arbeite ich immer mit der PHPShell und benenne die phpshell.php Datei entsprechend in .phpx um. Absurderweise funktioniert zwar in der PHPShell git, aber einfache Befehle wie ls nicht.
      Viele Grüße
      Björn

      • David 29. September 2016 um 13:14 Uhr- Antworten

        Vielen Dank für die Ergänzung 🙂

  10. Simone 4. Oktober 2016 um 10:15 Uhr- Antworten

    Hat schon jemand Erfahrungen mit VersionPress gemacht?

  11. anonymous 6. Juni 2017 um 20:02 Uhr- Antworten

    Immer noch ein super Beitrag, der mir extrem weiter geholfen hat. Super und verständlich geschrieben. Großes Dankeschön an dich 🙂

Hinterlassen Sie einen Kommentar