Howto

Wie man mit einer GitlabCI-Buildchain sicherstellt, dass das eigene TYPO3-Projekt den Coding Guidelines genügt und es vollautomatisch auf den Zielserver deployed.

 

Seit ich vor einigen Jahren GitLab und die damit verbundene Continous Integration-Lösung kennengelernt habe, bin ich ein großer Fan davon. In diesem Howto möchte ich vorstellen, wie man sein TYPO3-Projekt in einer CI-Buildchain realisiert und dabei auch die Einhaltung der Coding Guidelines berücksichtigt.

Ich bin grundsätzlich ein großer Fan von Automatisierung. Alle Jobs, die ein Programmierer mehr als 3x erledigen muß, gehören automatisiert. ;-) In meinen Projekten gibt es aus diesem Grund ein Quality-Gate, das die Einhaltung von den Coding-Guidelines kontrolliert und Alarm schlägt, wenn versucht wird, Code zu committen, der nicht gut genug ist.

 

Grundsätzlicher Aufbau

Wie in dem Bild zu sehen, lasse ich in einem ersten Schritt das komplette Projekt bauen, inklusive Dev-Dependencies. Viele der weiteren Jobs sind auf diesen Zustand angewiesen und es ist sehr viel schneller, das komplette Projekt einmal zu bauen als das von jedem Job selbst erledigen zu lassen - auch wenn die Jobs parallel laufen können. 

Die Build-Stage ist daher für viele der nachfolgenden Stages unbedingt erforderlich: 

 

build application with dev dependencies:
  stage: build
  image: composer:1.9.0
  before_script:
    - apk add bash --no-cache
    - apk add git --update
  script:
    - composer global require hirak/prestissimo
    - composer install --no-progress --no-ansi --no-interaction
  artifacts:
    name: typo3
    paths:
      - ./vendor/
      - ./web/
      - ./composer.json
      - ./composer.lock
    expire_in: '1h'
  tags:
    - docker

 

Auf die folgende Zeile möchte ich besonders aufmerksam machen: dieses Paket bewirkt, dass composer alle Pakete parallel installiert (statt nacheinander) und beschleunigt damit die Ausführung enorm!

 

composer global require hirak/prestissimo

Code Quality

In dieser Stage werden alle Jobs ausgeführt, die für die Einhaltung der Code-Qualität eine Rolle spielen. Welche das genau sind, kommt natürlich auf das Projekt und die Qualitätsansprüche an. 

TypoScript - Linter

Zu TYPO3-Projekten gehört eigentlich immer TypoScript-Code. Weil dieser wichtig für die ordnungsgemäße Funktion ist, gehört dieser selbstverständlich versioniert und nicht in die Datenbank. Und weil der Code versioniert ist, bietet es sich an, dafür einen Job im Quality-Gate zu spendieren, der die Qualität sicherstellt. 

Bei TypoScript-Code kann allerhand schiefgehen. Wichtig in dem Zusammenhang: TypoScript ist keine Programmiersprache und sie ist auch nicht Turing-Vollständig. Das bedeutet, ohne tief ins Detail gehen zu wollen: Man kann nicht alles mit TypoScript berechnen, was man grundsätzlich mit einem Computer berechnen kann. Da TypoScript im Wesentlichen zur Konfiguration des TYPO3-Systems verwendet wird, ist das aber auch in Ordnung und kein größeres Problem.

Da TypoScript keinem Ablaufplan folgt, kann das TypoScript selbst auch keinen Syntaxfehler beinhalten; unbekannte Anweisungen werden einfach ignoriert. Dies ist auch etwas, dass wir bei diesem Projekt im Hinterkopf behalten müssen: Der Linter prüft nicht (und kann dies prinzipiell auch nicht!), ob das TypoScript das tut, was es soll. Er prüft, ob Klammern konsistent gesetzt sind und gibt Tipps, um die Übersichtlichkeit zu bewahren.

Meine TypoScript-Link Stage sieht wie folgt aus: 

 

typoscript:
  stage: code quality
  image: composer:1.9.0
  script:
    - ./vendor/bin/typoscript-lint -c config/tslint.yaml
  tags:
    - docker

 

Wie man auch hier sehen kann, benutze ich auf dem GitlabCI-Server sehr gerne Docker-Container. Damit der TypoScript-Linter auch zur Verfügung steht, muss dieser im Projekt einmalig installiert werden: 

 

composer require --dev helmich/typo3-typoscript-lint

 

und steht dann sowohl auf der Entwicklermaschine als auch in der GitlabCI-Buildchain zur Verfügung.

Über eine YAML-Datei kann man den TypoScript-Lint auf die eigenen Bedürfnisse konfigurieren. Die Standardwerte sind durch die Bank sinnvoll gewählt; Änderungen daran lassen sich einfach konfigurieren: 

 

paths:
  - path/to/sitepackage/Configuration/TsConfig/
  - path/to/sitepackage/Configuration/TypoScript/

sniffs:
  - class: Indentation
    parameters:
      indentConditions: true
      useSpaces: true
      indentPerLevel: 2
  - class: RepeatingRValue
    disabled: true

 

Es empfiehlt sich bei allen CodeQuality-Gates natürlich, diese zunächst auf der Entwicklungsmaschine laufen zu lassen und entsprechend gefundene Fehler zu beheben. 

Weitere Informationen zum TypoScript-Linter: https://github.com/martin-helmich/typo3-typoscript-lint#configuration

SQL

In einigen Projekten deploye ich SQL-Migrations. Das ist zugegeben ein kariertes Maiglöckchen und vermutlich in den meisten Projekten unüblich. Gerade solche Aufgaben sollten aber durch das Quality-Gate geprüft werden, um nicht kaputtes SQL versehentlich auszuspielen. 

Auf die Migrations gehe ich zu einem anderen Zeitpunkt nochmals genauer ein. Für diesen Beitrag belasse ich es bei der Konfiguration des Jobs: 

 

SQL:
  image: composer:1.9.0
  stage: code quality
  script:
    - vendor/bin/php-sqllint path/to/sql-files/*
  tags:
    - docker

 

und auch hier muss das Paket einmalig installiert werden:

 

composer require --dev cweiske/php-sqllint

 

 

Unit-Tests

Natürlich schreiben wir alle Unit-Tests, oder sind uns hoffentlich darüber einig, dass es sinnvoll wäre, welche zu haben. Ich schreibe meine Tests gerne mit Codeception und daher ist natürlich auch der entsprechende Job in der Build-Chain darauf ausgelegt. Ganz analog funktioniert es aber natürlich auch mit PHPUnit oder was immer Du als Testumgebung bevorzugst: 

 

unit tests:
  image: composer:1.9.0
  stage: code quality
  script:
    - vendor/bin/codecept run unit
  tags:
    - docker
  artifacts:
    name: tests
    paths:
      - ./tests/_output
    expire_in: 2 weeks
    when: always

 

Hier werden zudem noch Artefacts konfiguriert. Dies hat zur Folge, dass der Output des Jobs (in diesem Fall also die Ergebnisse des Unit-Tests) in Gitlab-CI zum Download bereitgestellt wird. Für den Fall, dass ein Unit-Test rot wird, hilft das ungemein, das Problem einzugrenzen. 

Normalerweise sollte dieser Job natürlich immer grün werden: als gute Entwickler haben wir die Tests ja schon lokal laufen lassen und uns vergewissert, dass wir nichts kaputt gemacht haben. 

YAML

YAML wird mehr und mehr zu einer wichtigen Konfigurationsmöglichkeit in TYPO3. Ich bin mit diesem Dateiformat nicht unbedingt glücklich; ich komme aber daran nicht mehr vorbei. 

Dieses Format ist eines der wenigen in meiner Build-Chain, dass ich nicht per PHP teste - vielleicht hat ja jemand einen Tipp für mich, wo ich einen guten YAML-Linter in PHP finde (das vereinfacht die lokale Ausführung auf dem Entwicklungsrechner für mich). 

 

yaml:
  image: python:alpine3.7
  stage: code quality
  before_script:
    - pip install yamllint==1.10.0
  script:
    - yamllint -c config/*.yaml .gitlab-ci.yml path/to/sitepackage/Configuration/Yaml
  tags:
    - docker

 

Neben den hier vorgestellten Tools sind wichtige YAML-Dateien die CKEditor-Konfigurationen und die TYPO3-Formulare.

PHPstan - statische Code Analyse

Weil wir alle nicht immer perfekten Code schreiben, gibt es Werkzeuge für uns Entwickler. Ein wichtiges ist phpstan - der static code analyzer. Phpstan arbeitet direkt auf dem PHP-Quellcode, führt diesen also nicht aus. Es erkennt aber sehr gut und sehr schnell Syntaxfehler, toten Code und bemängelt schlecht geschriebenen oder veralteten Code. 

Ein ausgezeichnetes Werkzeug, um seinen Code immer besser zu machen. Phpstan kennt verschiedene Level, mit der man seine Ansprüche nach und nach steigern kann. 

 

static code analysis:
  image: composer:1.9.0
  stage: code quality
  script:
    - vendor/bin/phpstan analyse -l 4 -c config/phpstan.neon
  tags:
    - docker

 

und die dazugehörige Konfigurationsdatei

 

parameters:
  paths:
    - '../path/to/'
  ignoreErrors:
      - '#Constant TYPO3_MODE not found#'
      - '#Undefined variable: \$_EXTKEY#'

 

 

Reconstructor

Nun kommt mein Highlight: Insbesondere wenn man eine ältere Codebase refakturieren muss, ist rector ein Lebensretter. Ich baue rector aber auch gerne bei Neuprojekten ein, um zu verhindern, dass schlechter Code überhaupt ins Projekt kommt. 

 

code review:
  image: composer:1.9.0
  stage: code quality
  script:
    - vendor/bin/rector process --config config/rector.yaml --dry-run
  tags:
    - docker

 

 Auch rector muss einmalig installiert werden:

 

 composer require --dev rector/rector 

 

 

und bringt eine Konfigurationsdatei mit: 

 

# rector.yaml
parameters:
  paths:
    - '/path/to'
  sets:
    - 'code-quality'
    - 'dead-code'
    - 'php73'

 

Welche sets und Einstellungen Du für Dein Projekt benötigst, musst Du natürlich selbst entscheiden. Rector liefert Dir dann aber wirklich gute und hilfreiche Vorschläge, den Code zu verbessern. 

Auf dem Entwicklungsrechner kannst Du Dir diese mit 

 

vendor/bin/rector process --config config/rector.yaml --dry-run

 

ansehen und auf Wunsch mit 

 

vendor/bin/rector process --config config/rector.yaml

 

auch direkt ändern lassen.

Prepare Deployment

Wenn unsere Änderung durch das Quality-Gate gekommen ist, müssen wir dne Code noch für die Auslieferung auf den Server vorbereiten. Da wir das System in der ersten Stage inkl. aller Dev-Dependencies bereits gebaut haben, besteht die Aufgabe im Wesentlichen darin, genau diese wieder zu entfernen. Nichts leichter als das: 

 

remove dev dependencies:
  stage: prepare for deployment
  image: composer:1.9.0
  before_script:
    - apk add bash --no-cache
    - apk add git --update
  script:
    - composer install --no-dev
  artifacts:
    name: typo3
    paths:
      - ./vendor/
      - ./web/
      - ./composer.json
      - ./composer.lock
    expire_in: '1h'
  tags:
    - docker

 

 

Frontend

Auch wenn ich zum Entwickeln natürlich alle Frontend-Assets auf meinem Entwicklungsrechner bereits gebaut habe, möchte ich in meinen Projekten, dass die Frontend-Assets immer auf dem Gitlab-CI-Server erstellt werden, bevor sie ausgeliefert werden. 

Der Vorteil besteht darin, dass es nicht mehr vorkommen kann, dass ein Team-Mitglied die Frontend-Assets bauen kann und ein anderes aufgrund eines Versionskonfliktes in der Build-Chain nicht mehr. 

Gleichzeitig stelle ich so sicher, dass nicht nur das fertige CSS und JavaScript, das aus mehreren Dateien zusammengebaut, minifiziert und evtl. sogar obfuscated wurde, im Repository enthalten ist. Ich möchte, dass im Repository die Quellcode-Dateien im SASS-Format, die ordentlich aufgebauten JavaScript-Dateien und die Anweisungen, wie diese zum Endergebnis zusammengebaut werden, im Repository liegen. 

Dieser Job hängt stark davon ab, wie in Deinem Projekt die Frontend-Assets gebaut werden. 

Und ausliefern

Nun ist alles fertig zusammengebaut, die Code-Qualität geprüft, ggf. die Frontend-Assets erstellt - wir sind bereit. Als nächstes muss der generierte Code auf den Server übermittelt werden.

Hierzu gibt es viele, viele Möglichkeiten. Du kannst Tools wie Deployer, capistrano, TYPO3 surf u.v.m. verwenden. Du kannst die generierten Daten per FTP auf den Zielhost übertragen lassen. 

Im Wesentlichen machen alle Tools dasselbe: Daten in ein Parallelverzeichnis installieren, ggf. weitere notwendige Kommandos feuern und wenn alles bereit ist, einen Symlink von der Vorgängerversion auf die neue Version legen: die Änderungen sind ausgespielt und die neue Version ist live.

Wenn man das richtig macht, geht das übrigens mit Zero Downtime. Aber dazu ein andermal mehr. 



Kommentare (2)

  • CArsten
    CArsten
    am 30.08.2021
    Super Artikel, Danke!

    Insbesondere würde mich jetzt noch die Zero-Downtime Deployment Pipeline interessieren - gibts dazu auch schon einen Link?

Neue Antwort auf Kommentar schreiben