Jenkins OSX Slave für das automatisierte Bauen von iOS Apps

Die Aufgabe ist simpel: ich möchte in meine Jenkins Build Umgebung einen Rechner mit Mac OSX einbinden, der für mich automatisiert iOS Apps bauen soll. Für die Vereinfachung verwende ich dazu das xctool von Facebook, welches das Handling von XCode und co. übernimmt.

Mein Vorgehen war folgendes: Jenkins User auf dem Zielsystem eingerichtet und anschließend den Jenkins Slave per ssh vom Master aus gestartet. Das funktioniert auch alles wunderbar, aber sobald man im Build versucht auf eine Gui Applikation zuzugreifen – das macht z.B. der Aufruf der Unit Tests, weil diese den iPhone Simulator benötigen – knallt xctool ohne eine Fehlermeldung mit einem Exit Code 1 weg. Das Problem an der Stelle ist einfach, dass es in OSX einen Unterschied macht, ob man ein Programm per ssh oder direkt in einer Shell auf dem System startet. Die Variante über ssh darf eben nicht auf GUI Applikationen zugreifen. Und somit laufen die Tests nicht.

Korrekterweise muss man den Jenkins Slave als per JNLP starten. Dazu stellt man im Master in den Slave Einstellungen die „Startmethode“ auf „Starte Slave Agenten über JNLP“. Nach dem Speichern wir einem in der Übersicht des Knotens ein Shell Command angezeigt, den man auf dem Slave ausführen soll:

java -jar slave.jar -jnlpUrl http://[PFAD_ZUM_JENKINS_MASTER]/computer/[SLAVE_NAME]/slave-agent.jnlp -secret [SECRET_HASH]

In diesem Command ist die Datei „slave.jar“ verlinkt und kann heruntergeladen werden. Genau das sollte man auf dem OSX Slave machen und die Datei irgendwo auf der Platte ablegen. Anschließen startet man das Programm „Automator“. Dort wählt man „Applikation“ aus und sucht anschließend in der Bibliothek nach „Shell Script ausführen“. Diesen Eintrag zieht man rechts in die graue Fläche.

In das nun vorhandene Textfeld trägt man folgendes Shellscript ein:

cd [PFAD_ZUR_JAR_DATEI]
java -jar slave.jar -jnlpUrl http://[PFAD_ZUM_JENKINS_MASTER]/computer/[SLAVE_NAME]/slave-agent.jnlp -secret [SECRET_HASH]

Das ganze speichert ihr im Ordner Programme als „run_jenkins_slave“ ab. Nun geht ihr in die „Systemeinstellungen” -> “Benutzer und Gruppen“ und wählt bei Jenkins die „Startobjekte“ aus. Dort fügt ihr nun das eben erstellte Programm „run_jenkins_slave“ hinzu. Anschließend geht ihr noch auf „Anmeldeoptionen“ und stellt da den User „Jenkins“ für den automatischen Login ein.

Wenn ihr nun den OSX Rechner neu startet, dann sollte er automatisch einen Jenkins Slave starten und diesen beim Master registrieren. Nun sind wir genauso weit wie mit dem ssh Weg – aber: wenn man nun Jobs startet, dann dürfen diese auch auf GUI Applikationen zugreifen. Sprich, die Unit Tests mit dem iPhone Simulator laufen nun ohne Probleme durch. Der Jenkins Slave verhält sich in etwa so, als ob man direkt auf dem Desktop eine Shell startet und dann Befehle ausführt.

[Quicktip] Jenkins meckert mit einem reject HostKey trotz korrektem Eintrag in der known_hosts Datei

Ihr möchtet mit Jenkins auf einen anderen Server per ssh/scp zugreifen, habt auf der Shell bereits erfolgreich mit dem User Jenkins eine ssh Verbindung aufbauen können, aber im Build Prozess bekommt ihr folgenden Fehler:

com.jcraft.jsch.JSchException: reject HostKey: ...

Das Problem ist relativ simpel zu lösen – die Einträge in der ~/.ssh/known_hosts dürfen nicht verschlüsselt sein. Lösen könnt ihr das folgendermaßen:
– alten Hosts Eintrag löschen: ssh-keygen -R [SERVER_NAME]
– in eurer ssh_config den Parameter „HashKnownHosts“ auf “no” setzen
– per ssh auf den Server eine Verbindung aufbauen und die Frage, ob der Key hinzugefügt werden darf, mit ja beantworten

Nun sollte der Eintrag im Klartext in der known_hosts stehen und Jenkins sollte die Verbindung aufbauen können.

[Quicktip] Jenkins baut bereits gelöschte git Branches

Besonders wenn man einen Jenkins Job nicht auf einen bestimmten Branch einschränkt wird man dieses Problem schnell bemerken: Jenkins baut in bestimmten Fällen auf Basis von git Branches, die eigentlich bereits gelöscht sind. Der Fehler tritt auf, weil Jenkins per default das lokale git Repository nicht mit dem Origin bezüglich gelöschter Branches synchronisiert. 

Dieses Problem kann man relativ einfach beheben. Dazu geht ihr in die Konfiguration des betroffenen Jenkins Job und im Bereich “Source-Code-Management“ wählt ihr im Dropdown “Additional Behaviours“ den Punkt “Prune stale remote-tracking branches“. Damit wird vor dem Pull bzw. Fetch genau dieser Sync durchgeführt. Sprich, es werden lokal alle Branches gelöscht, die im Origin nicht mehr vorhanden sind.

[Quicktip] Globalen SSH Key in Atlassian Stash hinterlegen

Wenn ihr in Stash viele Repositories habt und ein CI Tool wie Jenkins oder Teamcity nutzt, dann möchtet ihr sicher nicht bei jedem einzelnen Repo den SSH Key des Tools hinterlegen. Da ich lange nach der entsprechenden Stelle in Stash gesucht habe und in den Settings nichts dazu zu finden ist, hier eine mögliche Lösung für das Problem:

Die eine globale Stelle für das Problem gibt es nicht, ABER ihr könnt pro Projekt Zugriffsschlüssel hinterlegen. Ruft dazu einfach das Projekt auf, geht dann in die Einstellungen und dort auf Zugriffsschlüssel. Alle hier hinterlegten Keys können nur lesend oder auch lesend und schreibend für ALLE Repositories dieses Projektes freigeschalten werden. So lange ihr also nicht über allzu viele Projekte verfügt, ist die Einrichtung schnell erledigt 😉