Automatische Drupal Updates mit Drush

Updates können bei Drupal richtig unangenehm werden. Entweder ist der Update-Pfad eines der Module kaputt, Ihr müsst einen Patch für einen Bug hinzufügen, der immer noch nicht gefixt wurde oder irgend etwas anderes geht schief. In 7 Jahren Drupal-Entwicklung gab es auf jeden Fall nichts, was es nicht gab. Glücklicherweise habe ich einen Weg gefunden diesen Prozess zu automatisieren.

Ich hatte mich im Laufe der Zeit schon so sehr daran gewöhnt, dass ich Updates nur noch ein Modul nach dem anderen durchführte. Jedes Update bekam dabei einen eigenen Commit. Auf diese Weise konnte ich Updates je Modul sofort wieder zurück nehmen, wenn mal wieder irgendetwas schief gelaufen war.

Vor einiger Zeit hatte ich dann endlich die Nase voll und erstellte ein kleines Update-Skript für mich. Es nett sich drupdate und es macht quasi die ganze Arbeit für Euch, wenn Ihr nett zu ihm seid und Euch an ein paar Konzepte haltet. Ihr könnt die aktuellste Version von Gist herunter laden. Aber lasst mich Euch das Skript erst einmal Zeile für Zeile erklären.

Schritt 1

Erstmal definiere ich ein paar Variablen.

ROOT_DIR=$(pwd)
PUBLIC_DIR="$ROOT_DIR/web"
PATCHES_DIR="$ROOT_DIR/patches"
DRUPGNORE_PATH="$ROOT_DIR/.drupgnore"
  • ROOT_DIR speichert den Ordner in dem wir uns gerade befinden. Idealerweise sollte das der Projektordner sein.

  • PUBLIC_DIR ist der Web-Ordner des Projektes. Ich bevorzuge es einen separaten Web-Ordner zu erstellen, der die Drupal-Installation beinhaltet. Auf diese Weise ist dieser Ordner nicht vollgestopft mit Development-spezifischen Dateien, wie Linter-Konfigurationen, Deployment-Skripten und so weiter.

  • PATCHES_DIR definiert den Ordner in den ich alle meine Patches lege. Wenn immer ich ein Modul oder den Drupal-Code hacken muss, erstelle ich einen Patch und speichere ihn in besagtem Ordner. Normalerweise beginne ich den Dateinamen des Patches mit dem Namen des Moduls, damit ich auf einen Blick sehen kann, ob der Patch für ein Update relevant ist oder nicht.

  • DRUPGNORE_PATH definiert den Pfad zur .drupgnore-Datei. Diese Datei beinhaltet Namen von Modulen, die ich explizit vom Update ausschließen möchte.

Schritt 2

issue="$1"
if [[ $issue == "" ]]; then
  echo "Usage: drupdate [Issue number]"
  exit 1
fi

Meistens nutze ich eine Art Ticket, in dem ich die Zeit für das Update und den Prozess selbst tracke. Hier prüfe ich, ob ich daran gedacht habe, die Ticketnummer dem Skript hinzuzufügen. Auf diese Weise kann ich die Nummer später verwenden, um sie den Commit-Nachrichten anzufügen.

Schritt 3

ignore=""
if [ -f "$DRUPGNORE_PATH" ]; then
  ignore=$(cat $DRUPGNORE_PATH)
fi

Zuvor hatte ich einen Pfad für meine .drupgnore-Datei definiert. Hier prüfe ich, ob die Datei vorhanden ist und lade ihren Inhalt in eine Variable ignore.

Schritt 4

echo "Starting update..."
if [ -d "$PUBLIC_DIR" ]; then
  cd $PUBLIC_DIR
fi

In den neueren Projekten nutze ich einen Webordner, in dem sich die eigentliche Drupal-Installation befindet. In älteren Projekten ist dies möglicherweise nicht der Fall. Also prüfe ich, ob ein Ordner mit dem Namen web existiert und ändere gegebenenfalls das Verzeichnis. Drush muss im Drupal-Stammordner ausgeführt werden, um Update-Aufgaben durchführen zu können.

Schritt 5

modules=$(drush ups -p)
while read module; do
  if [[ $ignore == *"$module"* ]]; then
    echo "Ignoring $module..."
  else
    echo "Updating $module..."
    description=$(drush up $module -y)

    if [ "$module" == "drupal" ]; then
      git checkout HEAD -- .htaccess
      git checkout HEAD -- .gitignore
    fi

    git add --all
    git commit -m "Updated $module. #$issue" -m "$description"
  fi
done <<< "$(echo "$modules")"

echo "Updates finished."

Dieser Schritt ist ein wenig größer, denn hier geschieht die eigentlich Magie. Ich lade eine Liste aller verfügbaren Updates durch Drush und speichere die Ausgabe in einer Variable modules. Dann loope ich über diese Variable und verarbeite jedes Modul einzeln.

Innerhalb der Schleife prüfe ich, ob das Modul ignoriert wird. Momentan verwende ich einen ziemlich einfachen Check, der nicht in allen Fällen funktioniert. Wenn ich zum Beispiel webform ignorieren möchte, wird auch webform_features ignoriert. Derzeit ist die Logik aber ausreichend, denn die Verwendung von .drupgnore sollte die absolute Ausnahme sein.

Wenn das Modul nicht ignoriert wird, lade ich das Update herunter und führe es aus. Die Ergebnisse dieses Prozesses werden in einer Variable description gespeichert. Wenn das "Modul" der Drupal-Core ist, muss ich zwei Dateien zurücksetzen, bevor ich etwas committen kann, .htaccess und .gitignore. Meistens beinhalten diese Dateien bereits Custom-Logik, die ich gerne behalten möchte.

Zu guter Letzt füge ich alle Dateien zum Index hinzu und committe sie im aktuellen Branch. Bitte beachtet, dass Ihr einen sauberen Arbeitszustand haben solltet, bevor Ihr dieses Skript verwendet. Die Commit-Meldung enthält Informationen über das Update, die Ticketnummer und das Ergebnis der Aktualisierung.

Schritt 6

cd $ROOT_DIR
if [ -d "$PATCHES_DIR" ]; then
  echo "Applying patches.."
  for file in $PATCHES_DIR/*; do
    echo "Applying $(basename $file)..."
    git apply $file
  done

  git add --all
  git commit -m "Applied patches. #$issue"
fi

Im letzten Schritt loope ich über alle Patches im Patches-Ordner und versuche sie anzuwenden. Dies wird eine Menge Fehler werfen, da Patches möglicherweise nicht angewendet werden können. Meistens kann man diese Fehler einfach ignorieren. Es wäre sicherlich sauberer, nur die für die aktualisierten Module relevanten Patches zu verwenden. Aber ich kann nicht immer davon ausgehen, dass ich die Patches richtig benannt habe.

Installation

Ihr könnt das Skript unter Gist herunterladen und es an Eure persönlichen Bedürfnisse anpassen oder es einfach so verwenden, wie es ist.

wget -O drupdate https://gist.githubusercontent.com/christianhanne/eab02899f413c927104ce94e11510e53/raw/cbb9341ad0c18fc783728cf4dbbbfe0a94bf2f9e/drupdate
chmod +x drupdate
sudo mv drupdate /usr/local/bin/drupdate

Noch keine Kommentare vorhanden.