Shuttle, was ist das eigentlich? - shuttle wird von lunarway, der primär entwickelnden Firma hinter dem Open-Source-Tool wie folgt beschrieben:

A CLI for handling shared build and deploy tools between many projects no matter what technologies the project is using.

Und das beschreibt es ziemlich gut, ein Tool mit dem man Copy/Paste-Abhängigkeiten für den Build vermeiden kann, dadurch eignet sich perfekt für CI/CD. Die Möglichkeiten sind aber riesig, überall wo man mit vielen Shell-Skripten zu tun hat oder Dateigenerierung kann Shuttle eine sinnvolle Ergänzung sein.

Warum der Kram?

Viele kennen Jenkinsfiles, .gitlab-ci.yml, .travis.yml, usw. sie basieren alle zu einem sehr sehr großen Teil aus Shell-Commands, meistens nur simple Aufrufe für das Build-Tool der Wahl oder um Shell-Skripte aufzurufen. Das ist bei einigen großen Projekten kein Problem. Wir leben allerdings in der Zeit von Microservices und SOLID so wichtig sind wie nie zu vor, was natürlich dazu führt das es meist sehr viele kleine Git-Repositories und Pipelines gibt.

Shuttle erlaubt es hierbei entsprechend sowohl Dateien als auch Befehle global weg zu kapseln, bevorzugt in einem Git-Repository. Das Repo dient hierbei als Single Source of Truth. Schöner Nebeneffekt ist an der Stelle das die Versionierung sich nur auf die Build-Skripte bezieht und so sauber von den eigentlichen Projekten getrennt werden kann.

Wie funktioniert das Ganze?

Grundsätzlich braucht jeder der Shuttle benutzen möchte ein Gerät mit Mac oder Linux installiert, das kann der Build-Server oder Entwickerlaptop sein. Zusätzlich wird Git benötigt, was quasi in 99.9999% aller Fälle schon installiert ist.

Shuttle ist mit Go geschrieben und liefert eine einzige Binary aus, die nur ausführbar gemacht werden muss, schon kann man anfangen. Dependencies hat man an dieser Stelle also keine.

Wenn man entsprechend das Tool lokal oder via CI installiert hat, braucht man nur noch eine shuttle.yaml, diese Datei enthält die Git-URI des sogennanten Plan, ein Git-Repository das mindestens eine plan.yaml enthalten muss. In dieser wird beschrieben welche Befehle man via shuttle run ausführen kann.

Das kann z. B. so aussehen:

plan: https://gitlab.my-company.com/shared/build-lib.git
plan.yaml
scripts:
  deploy_mvn_snapshot:
  	script:
      - shell: |
      	mvn build-helper:parse-version \
    	versions:set \
        -DnewVersion='${parsedVersion.majorVersion}.${parsedVersion.minorVersion}.${parsedVersion.nextIncrementalVersion}-SNAPSHOT' && \
        mvn deploy
shuttle.yaml

Im entsprechenden Projekt kann dann via Shuttle die Version des Maven-Projekts beim Patch um 1 erhöht werden und -SNAPSHOT anhängen, darauf erfolgt ein Deployment ins Snapshot-Repository:

shuttle run deploy_mvn_snapshot

Das liest sich natürlich deutlich leichter als der relativ lange Text mit dem man Maven aufrufen müsste. So kann man seine Pipelines von solchen Unübersichtlichkeiten verschonen und stattdessen einen Baukasten zur Verfügung stellen, aus dem man aus allen Projekten heraus zugreifen kann.

Integration mit Build-Tools

Am Beispiel von Gitlab CI lässt sich ein recht gutes Beispiel zeigen:

image: maven:3-jdk-8

before_script:
  - curl -LO https://github.com/lunarway/shuttle/releases/download/$(curl -Lso /dev/null -w %{url_effective} https://github.com/lunarway/shuttle/releases/latest | grep -o '[^/]*$')/shuttle-linux-amd64 && chmod +x shuttle-linux-amd64 && mv shuttle-linux-amd64 /usr/local/bin/shuttle
  
release_snapshot:
  script:
    - shuttle run mvn_deploy_snapshot
  only:
  	- development

Ich persönlich liefere Shuttle gerne mit dem Build-Image aus, das man selbst zusammenstellt, das erspart einen den CURL in jede Pipeline zu kopieren. So werden die Pipelines sehr kurz und übersichtlich, sollte sich beim Deployen des Snapshot etwas ändern, kann dies relativ einfach global für alle Projekte ergänzt werden.

Wo benutze ich Shuttle?

Aktuell nutze ich Shuttle in meinem Job sehr aktiv, hier gibt es eine Vielzahl an ähnlich strukturierten Projekten, die natürlich auch alle mit Conitous Integration und Deployment versorgt werden wollen. Hier sammelt sich schnell eine Menge Copy/Paste-Scripts an. Durch Shuttle bleibt jetzt nur noch eine cleane Pipeline-Definiton, die dabei auch noch sehr gut wartbar ist.

Wann lohnt sich der Aufwand?

Shuttle lohnt sich prinzipiell ab 3-4 Projekten die identisch funktionieren oder zumindest gemeinsame Steps haben, je nachdem wie flexibel man sein möchte kann man auch shuttle-Commands in entsprechenden Scripts wieder aufrufen, so hat man die maximale Flexibilität.

Persönliche Erfahrungen, Kritik und Lob

Ich bin sehr zufrieden mit Shuttle und nutze es jetzt allerdings erst seit fast einem halben Jahr. Grobe Bugs oder umständlich zu konfigurierende Features etc. konnte ich derzeit nicht erkennen und fand bisher alles ziemlich eingängig und sehr durchdacht.

Insgesamt ist das Tool sehr simpel gehalten und gut strukturiert, die Dokumentation ist allerdings an manchen Stellen noch etwas sehr dünn. Auch die Features sind sehr rudimentär, allerdings sehr stabil und reichen für den täglichen Einsatz.

Positiv ist das auch für Shuttle unbekannte Metadaten in den Plan eingetragen werden können, die ich z. B. zum Generieren einer Markdown-Dokumentation benutze, hier sind die Möglichkeiten durch YAML und freie Wahl der Programmiersprache für das verwerten der entsprechenden Zusatzinfos. Eine Issue für die Integration einer Markdown-Dokumention im Terminal auf GitHub exisitiert allerdings auch schon seit geraumer Zeit, allerdings ohne Aktivtät.

Für alle die sehr stark auf Windows angewiesen sind und ihre Builds evtl mit Windows betreiben, aus welchem Grund auch immer, ist Shuttle nicht zu empfehlen. Shuttle ist nicht kompatibel mit Windows, ein Versuch die Binary unter Windows selbst zu kompilieren führte zu einigen Problemen mit System-Aufrufen, was letztendlich dafür sorgt das hier kein Windows-Support angeboten wird. Zu bestreiten ist an dieser Stelle ob das ein wirklicher Nachteil ist.