Continuous Integration für iOS mit Jenkins – Ein Erfahrungsbericht

Seit gut 3 Jahren verwenden wir Jenkins als Plattform für eine Continuous Integration Umgebung für iOS-Projekte.

Zu den Hauptaufgaben des Systems gehören die ständige Überprüfung der Baubarkeit von Projekten nachdem Quellcode in ein Source-Control-Management (SCM) System eingecheckt wurde und die Verteilung der Build-Produkte an eine interne Over-the-Air (OTA) Distribution Plattform sowie direkt an unsere Kunden.
Darüber hinaus unterstützen wir verschiedene Build-Job Konfigurationen zur automatischen Quellcode-Dokumentation und Code-Analyse:

  • Ausführung von OCUnit-Tests und Messen der Testabdeckung,
  • Statische Code-Analyse,
  • Erstellung von Code-Metriken,
  • Copy-Paste-Detection,
  • Appledoc und Headerdoc.

Unser System besteht aus einer Jenkins Master-Instanz (Ubuntu Server) an dem verschiedene Slaves angebunden werden können, welche die einzelnen Build-Jobs ausführen. Da es am praktischsten ist, iOS-Projekte auf einem Mac mit Mac OSX zu bauen, steht dafür ein eigener Mac mini als Slave zur Verfügung.

Damit Jenkins iOS-Projekte bauen kann, definiert man für einen Build-Job entsprechende Build-Schritte. Dabei kann man als einen Buildschritt eigene Skripte (bspw. Shell, Groovy) verwenden oder auf entsprechende Jenkins-Plugins zurückgreifen. So bietet es sich an, zum Bauen eines iOS-Projekts und Ausführen von OCUnit-Tests das Jenkins Xcode-Plugin zu verwenden. Darüber hinaus bietet das Plugin die Möglichkeit, die Keychain festzulegen, indem sich das Zertifikat auf dem Mac Slave zum Signieren der iOS-App befindet. Hierbei sollte man allerdings nicht vergessen, dass zu einem Zeitpunkt immer nur eine Keychain auf dem Mac geöffnet sein kann! Somit kann es bei parallelen Builds zu Problemen kommen, wenn zum Signieren der Apps die Zertifikate in verschiedenen Keychains enthalten sind.

Weitere Plugins stehen für die Distribution von Build-Artefakten (Publish over SSH Plugin) und Analyse von iOS-Projekten (Clang Scan-Build Plugin) bereit. Darüber hinaus lassen sich mit etwas Aufwand auch primär zur Analyse von Java-Projekten entwickelte Plugins einsetzen. So kann man zum Beispiel mittels eines Skripts Intermediate-Daten generieren, welche im nächsten Schritt als Input-Daten für ein Plugin dienen können. Auf diesem Weg kann man beispielsweise eine Copy-Paste-Detection (DRY Plugin) oder das Messen der Testabdeckung (Jenkins Cobertura Plugin) realisieren.

Wird der Jenkins oder einzelne Plugins geupdated, so ist es ratsam die korrekte Funktionalität der Continuous Integration Umgebung zu überprüfen. So wurden in der Vergangenheit Probleme mit dem SLOCCount Plugin (Version 1.8) in einer Master-Slave Umgebung festgestellt. Ähnliches galt auch für das Clang Scan-Build Plugin.

Derzeit haben wir rund 40 aktive iOS Build-Jobs, von denen rund 70% nach Codeänderungen im SCM-System automatisch ausgeführt werden. die restlichen Jobs werden entweder manuell oder als Nightly-Build-Job gestartet.

Damit ein Build-Job automatisch ausgeführt wird, pollt der jeweilige Job standardmäßig in regelmäßigen Zeitabständen gegen das SCM-System. Dies kann zu einer hohen Auslastung des SCM-Systems führen. Hierbei haben wir festgestellt, dass die Verwendung von Git als SCM-System weniger Ressourcen benötigt als SVN. Weiterhin sollte auch bei der Verbindung zwischen Jenkins und dem SCM-System “SSH” anstatt “HTTP(S)” verwendet werden, um den Overhead bei der Kommunikation zu minimieren.
Das grundlegende Problem ist allerdings das ständige Polling der einzelnen Build-Jobs in kurzen Zeitabständen. Um dies zu lösen, erfolgt eine Umstellung von einem Poll- zu einem Push-Mechanismus. Das heißt, das SCM-System notifiziert den Jenkins sobald für ein Projekt eine Änderung eingecheckt wurde.

Eine weitere Herausforderung stellt die Integration des Continuous Integration Build-Systems in die vorhandene IT-Infrastruktur dar. So sind neben dem CI-System auch weitere Systeme zur Projekt-, Ticket- und Nutzerverwaltung, Authentifizierung sowie Systeme zur Distribution von Build-Produkten im Einsatz. Das erfordert eine einheitliche Benutzerführung und Bedienung, um die Zugänglichkeit, die Akzeptanz und somit auch eine effektive und effiziente Nutzung des Systems zu gewährleisten.
Um Jenkins in andere Systeme einzubinden und über andere Systeme zu administrieren, verfügt der Jenkins-Server um Beispiel über eine REST-API. Diese nutzen wir, um selbst vordefinierte Job-Templates zu laden und als Vorlage für andere Jobs zu verwenden. Des Weiteren lassen sich mittels dieser API auch bestehende Job-Konfigurationen anpassen und updaten. Aber auch während des Betriebs des Jenkins Build Servers sind wir über das eine oder andere Problem gestolpert. Im folgenden eine kleine Auflistung.

User Interaction is not Allowed

Dieses Problem tritt typischerweise während der Signierung von iOS-Apps auf und bedeutet, dass es ein Problem mit dem Zugriff auf den Schlüsselbund (Keychain) des Macs gab, auf dem der Build-Job ausgeführt wurde. Die Gründe dafür können verschieden sein:

  • Es erfolgte vor der Signierung kein Unlock der Keychain.
    Lösung: Bevor die App signiert wird, per Script in einem Build-Step die Keychain freigeben:
    security unlock-keychain –p password <path to the keychain>
  • Zum Zeitpunkt der Signierung wurde die Keychain wieder vom System geschlossen.
    Lösung: Die Zeitspanne erhöhen, wie lange die Keychain nach dem letzten Zugriffe geöffnet bleiben soll:

    1. Die Schlüsselbund-Verwaltung öffnen und den Schlüsselbund auswählen.
    2. Bearbeiten -> Einstellungen für Schlüsselbund ändern …
    3. Die Zeitspanne erhöhen, nach wie viel Minuten Inaktivität der Schlüsselbund wieder geschützt werden soll.
  • Der Zugriff auf den privaten Schlüssel des Zertifikats ist nicht für alle Programme gestattet.
    Lösung: Den Zugriff für alle Programme erlauben:

    1. Die Schlüsselbund-Verwaltung öffen und den privaten Schlüssel des entsprechenden Zertifikats auswählen.
    2. Bearbeiten -> Informationen -> Zugriff
    3. “Allen Programmen den Zugriff ermöglichen” selektieren.

${SRCROOT} Not Found

Die Variable ${SRCROOT} ist eine Xcode interne Variable und kennzeichet das Verzeichnis in dem das Xcode-Projekt liegt. Häufig treten Probleme auf, wenn in den Build-Settings zum Target diese Variable verwendet wird.
Lösung: Aus den Build-Settings diese Variable entfernen und die Pfade relativ angeben.

java.lang.OutOfMemoryError: PermGen space

Wenn Jenkins ein bestimmte Zeit lang lief, kann es ab und zu vorkommen, dass ein Build mit der Meldung Caused by: java.lang.OutOfMemoryError: PermGen space fehlschlägt.
Lösung: Den Speicher für den PermGen erhöhen.

  • Den PermGen-Speicher für die Slaves erhöhen.
    1. In der Verwaltung der Nodes den Slave-Node auswählen.
    2. Konfiguration -> Startmethode (Erweitert):
      JVM Options: -XX:MaxPermSize=1024M -XX:+CMSClassUnloadingEnabled
  • Den PermGen-Speicher für den Master erhöhen.
    Analog zur Slave-Konfiguration die JVM Optionen anpassen.
    Oftmals hilft auch ein Neustart des Masters. 😉

Die Verwendung von Jenkins als Continuous Integration Plattform für iOS-Projekte hat sich im täglichen Einsatz bewährt. So ist durch ein schnelles Feedback von der CI-Plattform die Zeitdauer gesunken, bis eingecheckter nicht baubarer Code gefixt wurde. Weiterhin kann durch spezielle Analyse-Jobs die Code-Qualität überwacht und verbessert werden. Ferner können Releases mit kundenspeziefischen Zeritifikaten gebaut werden, ohne dass ein einzelner Entwickler direkten Zugang zu den entsprechenden Zertifikaten benötigt.

Weitere Vorteile ergeben sich aus der Kombination der CI-Umgebung mit der OTA-Plattform. Designer können direkt im SCM Grafiken austauschen und mit Hilfe des automatisierten Build- und Deployment-Prozesses die Änderungen zeitnah in der App überprüfen, ohne das ein Entwickler ihnen ein entsprechendes Build zur Verfügung stellen muss. Ebenfalls können Vertrieb und Tester schneller auf Zwischenstände zugreifen, wodurch Fehler schneller gefunden werden können. Darüber hinaus erhalten Kunden einen einfachen Zugriff auf Releases und können die Apps direkt auf ihre iOS-Devices installieren. Eine manueller Versand oder das manuelle Ablegen von Build-Produkten auf Datei-Servern entfällt.

Links

http://jenkins-ci.org – offizielle Jenkins Webseite
https://wiki.jenkins-ci.org/display/JENKINS/Plugins#Plugins-Pluginsbytopic – Übersicht über Jenkins Plugins

Bewerte diesen Beitrag
Please follow and like us:
Facebook
Facebook
YouTube
YouTube
0 Kommentare

Dein Kommentar

Want to join the discussion?
Feel free to contribute!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

  Newsletter abonnieren (Jederzeit wieder abbestellbar)