Inhaltsverzeichnis

Automatisches Deployment von TYPO3 Extensions via GitHub Actions

Diejenigen unter euch, welche bereits TYPO3 Extensions auf GitHub hosten, haben vermutlich schonmal von GitHub Actions gehört. Es ist die GitHub Variante von GitLab CI und ziemlich cool. Mit GitHub Actions kann man Workflows für seine Repositories erstellen, das heißt auf gewisse Events reagieren und zum Beispiel Builds generieren. Genau das habe ich mir zunutze gemacht, um die nervigen TYPO3 Extension Repository Uploads zu automatisieren und mir somit Arbeit zu ersparen.

Im Folgenden erläutere ich wie ich die GitHub Action erstellt habe und sie für meine Extensions benutze.

Ausgangssituation

Ursprünglich musste ich bei jedem Release einer neuen Version meiner Extensions zuerst den Code in der entsprechenden Version auschecken, das Kommando zum Erstellen des zip-Archivs heraussuchen, es erstellen und dieses dann manuell auf https://extensions.typo3.org hochladen. Da dies ziemlich aufwändig ist, habe ich mir schließlich eine Lösung über GitHub Actions ausgedacht, welche den praktischen TER Client von Helmut Hummel nutzt, um diesen Upload bei einem neuen Release automatisiert ins TER zu machen.

TER Client vorbereiten

 Der Client ist ein selbstständiges PHP Programm, welches über CLI mit einigen Parametern direkt aufgerufen werden kann. Damit dieses in den GitHub Actions funktioniert und damit ich eine immer gleiche Platform zum Ausführen des Clients habe, erstellte ich mir zunächst einen Docker Container, welcher den Client in einer PHP Umgebung enthält.

Dazu erstellen wir uns zunächst ein Dockerfile, welches von php:7.4-cli ableitet. Der Client arbeitet intern mittels SOAP, daher müssen wir noch einige PHP Extensions laden:

Dockerfile

 

FROM php:7.4-cli

RUN apt-get update && apt-get install -y libxml2-dev wget git zip libzip-dev
RUN docker-php-ext-install -j "$(nproc)" soap

 

Da der Client als composer Paket daher kommt, brauchen wir auch composer. Bei der composer Installation beachten wir den Hinweis zur programmatischen Installation von composer und erstellen uns ein Install-Script, welches wir beim Bauen des Docker Containers mit ausführen.

composer-installer.sh

 

#!/bin/sh

EXPECTED_CHECKSUM="$(wget -q -O - composer.github.io/installer.sig)"
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")"

if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]
then
    >&2 echo 'ERROR: Invalid installer checksum'
    rm composer-setup.php
    exit 1
fi

php composer-setup.php --quiet
RESULT=$?
rm composer-setup.php
exit $RESULT

 

Dockerfile

 

FROM php:7.4-cli

RUN apt-get update && apt-get install -y libxml2-dev wget git zip libzip-dev
RUN docker-php-ext-install -j "$(nproc)" soap

COPY composer-installer.sh /composer-installer.sh
RUN chmod +x /composer-installer.sh
RUN /composer-installer.sh

 

Zum Abschluss clonen wir uns den Client von GitHub und führen ein composer install aus.

Dockerfile

 

FROM php:7.4-cli

RUN apt-get update && apt-get install -y libxml2-dev wget git zip libzip-dev
RUN docker-php-ext-install -j "$(nproc)" soap

COPY composer-installer.sh /composer-installer.sh
RUN chmod +x /composer-installer.sh
RUN /composer-installer.sh

RUN git clone github.com/helhum/ter-client.git app; cd app; git checkout v0.1.1; /composer.phar install

 

Nun da wir den Client in unserem Docker Container haben, machen wir ihn noch der Außenwelt bekannt. Hierfür definieren wir einen Entrypoint, in dem wir den Client aufrufen.

Dockerfile

 

FROM php:7.4-cli

RUN apt-get update && apt-get install -y libxml2-dev wget git zip libzip-dev
RUN docker-php-ext-install -j "$(nproc)" soap

COPY composer-installer.sh /composer-installer.sh
RUN chmod +x /composer-installer.sh
RUN /composer-installer.sh

RUN git clone github.com/helhum/ter-client.git app; cd app; git checkout v0.1.1; /composer.phar install

ADD entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]

 

entrypoint.sh

 

/app/ter-client upload -u $1 -p $2 -m "$3" $4 $5

 

 

The Github Action

Jetzt, da wir den TER-Client in einem Docker Container ausführen können, können wir uns an die Erstellung unserer Github Action machen. Dies kann sowohl direkt in dem Repository geschehen, in welchem der Extension Code liegt, oder aber generell als alleinstehendes Github Repository, welches dann in einen Workflow integriert werden kann.

Die eine Github Action braucht zunächst eine YAML Datei, weche diese beschreibt. Ein paar Basisangaben in dieser YAML sind:

  • name - Der Name der Action
  • description - Eine Beschreibung, was die Action tut
  • author - Die Angabe eines Autors
  • runs - Angaben darüber, wie die Action ausgeführt wird. Es gibt neben der Möglichtkeit, Docker Container zu starten auch noch andere Varianten. Mehr dazu vielleicht in einem späteren Beitrag. Es lohnt sich hier die Dokumentation von Github anzuschauen.

Wir werden in diesem Beispiel in der runs Konfiguration einen Docker Container eintragen, welchen wir erstellen werden, um den ter-client zu laden und auszuführen.

The YAML could look like this:

 

name: 'TYPO3 Extension Repository Uploader'
description: 'This action can pack and upload an extension to the TYPO3 Extension Repository (TER)'
author: 'Kevin Ditscheid <kevin@the-coding-owl.de>'
runs:
  using: 'docker'
  image: 'Dockerfile'

 

 

Secrets

Da man für den Upload der Extensions ins TER einen Username und ein Passwort benötigt, kümmern wir uns als nächstes um die Angabe der Credentials in Form von Secrets.

Diese Secrets stellt man in den Einstellungen des jeweiligen Repositories ein, welches die Action am Ende im Workflow aufruft.

 

Um diese Secrets nutzen zu könnnen, müssen wir diese zunächst in der entrypoint.sh abfragen. Dafür habe ich einige Environment Variablen benutzt.

entrypoint.sh

 

/app/ter-client upload -u ${SECRET_USERNAME} -p ${SECRET_PASSWORD} -m "${UPLOAD_MESSAGE}" ${EXTENSION_KEY} ${GITHUB_WORKSPACE}

 

Die Secrets mit dem Namen SECRET_USERNAME und SECRET_PASSWORD werden vom Workflow übergeben.

Eine Workflow YAML einer TYPO3 Extension auf Github könnnte am Ende wie folgt aussehen:

.github/workflows/publish.yaml

 

name: Publish on TER

on: 
  release:
    types: [published]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - uses: the-coding-owl/action-typo3-extension-repository-upload@0.0.1
      env: 
        SECRET_USERNAME: ${{ secrets.USERNAME }}
        SECRET_PASSWORD: ${{ secrets.PASSWORD }}
        EXTENSION_KEY: 'my_ext'
        UPLOAD_MESSAGE: ${{ github.event.release.body }}

 

Über die Einstellung env können wir der aufgerufenen Action diese Environment Variablen übergeben. Die Secrets aus den Repository Einstellungen befinden sich in der Variable ${{ secrets }}.

Die anderen beiden Environment Variablen beschreiben den Extension Key, welcher beim TER Upload dazu dient die Extension im TER zu identifizieren, und die UPLOAD_MESSAGE, welche in der Versionshistorie als Release Notes angezeigt werden. Hier habe ich den Beschreibungstext des Github Releases eingefügt, weil dieser in meinem Fall den selben Inhalt haben sollte, wie die entsprechenden Informationen im TER selbst.

Ergebnis

Das Ergebnis unserer Bemühungen haben wir nun eine Action, welche einen Docker Container mit dem ter-client baut und ihn in einer entrypoint.sh dem Workflow zur Verfügung stellt und einen Workflow, welcher beim Erstellen eines Releases die Action triggert, um mit dem neuen Extension Code ein Paket zu bauen und es im TER hochzuladen.

Den Workflow und die entsprechenden Actions habe ich im folgenden für meine Extension oclock benutzt:

https://github.com/the-coding-owl/oclock

Die Action habe ich in ein separates Repository ausgelagert, zur einfacheren Wiederverwendung:

https://github.com/the-coding-owl/action-typo3-extension-repository-upload

Und den ter-client habe ich ebenfalls in ein Repository ausgelagert und im Docker Hub hochgeladen:

https://github.com/the-coding-owl/ter-client

https://hub.docker.com/r/thecodingowl/ter-client