CONTINUOUS INTEGRATION FÜR NODE.JS-PROJEKTE

CONTINUOUS INTEGRATION FÜR NODE.JS-PROJEKTE

Qualität steigern

Dieser Artikel zeigt, wie sich das CI-System Jenkins für Node.js-Projekte einsetzen Lässt.
Continuous Integration kann bei Softwareprojekten dabei helfen, die Qualität der Software zu steigern. Werden Änderungen in das jeweilige Versionskontrollsystem (Git, Subversion et cetera) committet, dann führt das CI-System (Continuous Integration System) automatisch kontinuierlich bestimmte Prozesse aus. Dazu zählen beispielsweise das Kompilieren von Quelltext, Unit- oder Integrationstests, Tests bezüglich der Codequalität, Ermittlung der Testabdeckung oder Ähnliches.

Schlagen dabei einzelne Prozesse beziehungsweise Tests fehl, wird der Entwickler, der den jeweiligen Commit durchgeführt hat, oder beliebige andere Personen per E-Mail oder auf anderen Wegen darüber informiert und man hat somit zeitnah eine Rückmeldung. Sind die Tests erfolgreich beziehungsweise kann die Software erfolgreich gebaut werden, kann sie auch direkt ausgeliefert werden. Dann spricht man nicht nur von Continuous Integration, sondern von Continuous Deployment. Gemeint ist damit die kontinuierliche Bereitstellung von Versionen einer Software (Bild 1).

Installation

Prinzipiell lassen sich mit CI-Systemen beliebige automatisierte Workflows dieser Art konfigurieren. Das Ziel dabei ist immer, den Prozess des Erstellens von Software zuverlässiger zu machen und Fehler im Entwicklungsprozess frühzeitig erkennen zu können.

Dabei lassen sich die diversen CI-Systeme, die es auf dem Markt gibt, wie etwa Jenkins (https://jenkins.io), Travis CI (https://travis-ci.org) oder Circle CI (https://circleci.com) prinzipiell für beliebige Programmiersprachen verwenden.

Der folgende Artikel soll nun zeigen, wie sich konkret das CI-System Jenkins für JavaScript- beziehungsweise Node.js-Projekte einsetzen lässt. Damit die Beispiele dabei auch ohne entsprechende Server-Infrastruktur lokal auf dem eigenen Entwicklungsrechner nachvollzogen werden können, wird gezeigt, wie Jenkins mit Hilfe von Docker lokal genutzt wer-
Installation von Docker für macOS (Bild 2)
Docker startet als Dienst ... (Bild 3)
den kann. Zudem wird ein lokaler Git-Server aufgesetzt, ebenfalls als Docker-Container, von dem Jenkins dann den Quelltext bezieht. Hier bietet sich Gogs (https://gogs.io) an, ein in Go geschriebener Git-Server inklusive einer GitHub-ähnlichen Oberfläche.


(D localhost:10080/user/sign_up
Startseite ErKunden Hilfe
X Registrieren p Anmelder!
Registrieren
Be nutzem am e’ cleancoderocker
E-Mail’ info@philipackermann.de
Passwort’
Erneut eingeben’
Captcha’
1 Neues Konto ers:ellen
Haben Sie bereits ein Konto? Jetzt anmelden!

Git-Account: Die Registrierung eines Git-Accounts (Bild 6)
© localhost; 10080/user/login
Startseite Erkunden Hilfe A Registrieren (•f Anmelden

Anmelden
Benutzername oder E-Mail * cleancoderocker Passwort* .........

@ Angemeldet bleiben

Passwort vergessen?

Benötigen Sie ein Konto? Registrieren Sie sich jetzt.

Anmeldung: Anmelden über den Git-Account (Bild 7)
Anlegen eines neuen Git-Repositorys (Bild 8)
Konfiguration: Hier erfolgt die Konfiguration von Gogs (Bild 5)
Als Voraussetzung muss also Docker installiert sein. Die Installation gestaltet sich dabei für alle gängigen Betriebssysteme sehr einfach, die einzelnen Installationsanweisungen findet man auf der Docker-Homepage unter https://docs.do cker.com/engine/installation. Im Folgenden wird Docker unter macOS ausgeführt. Docker for Mac (https://docs.docker. com/engine/installation/mac) lässt sich - wie bei macOS gewohnt - per Drag and Drop installieren (Bild 2) und nistet sich dann als Dienst in der Toolbar von macOS ein (Bild 3, Bild 4).

Die Installation des Git-Servers Gogs beziehungsweise das Erstellen eines entsprechenden Docker-Containers gestaltet sich relativ einfach. Eine detaillierte Anleitung dazu findet man unter https://github.com/gogits/gogs/tree/master/do cker. Im Wesentlichen reicht es, die im Folgenden beschriebenen Befehle auf der Kommandozeile auszuführen. Zunächst wird das Docker-Image von Gogs heruntergeladen:

docker pull gogs/gogs

Anschließend wird ein lokales Verzeichnis erstellt, in dem Gogs seine Daten speichert, sprich die Git-Repositories, Kon-
figurationsdateien, Logs et cetera. Prinzipiell kann dies ein beliebiges Verzeichnis sein:

mkdir -p /var/gogs

Im nächsten Schritt wird dann basierend auf dem DockerImage gogs/gogs ein neuer Container erstellt und direkt gestartet:

docker run --name=gogs -p 10022:22 -p 10080:3000 -v / var/gogs:/data gogs/gogs

Über den Parameter -p (beziehungsweise --publish) werden dabei die Ports 22 und 3000 über die Ports 10022 und 10080 auf dem Host-Rechner zur Verfügung gestellt. Ersterer dient dem SSH-Zugriff auf die Git-Repositories, über Letzteren gelangt man an die Weboberfläche von Gogs.

Über den Parameter -v (beziehungsweise --volume) wird zudem das zuvor auf dem Host-Rechner erstellte Verzeichnis angegeben, in dem Gogs seine Daten speichern soll. Außerdem ist es sinnvoll, dem Container über den Parameter ►


--name einen Namen zuzuweisen (im Beispiel lautet dieser gogs). Dann kann der Container - für den Fall, dass er gestoppt wurde - bequem über docker start gogs erneut gestartet werden, und dies muss nicht etwa über die interne Container-ID erfolgen.

Nach abgeschlossener Konfiguration und erfolgreichem Starten des DockerContainers sollte nun auf dem Entwicklungsrechner die Weboberfläche von Gogs unter http://localhost:10080 zur Verfügung stehen und den Entwickler mit einem entsprechenden Installationsdialog begrüßen (Bild 5).

In diesem Dialog lassen sich nun verschiedene Konfigurationen angeben, wobei für das Beispiel fast alle Einstellungen auf den Standardwerten belassen werden. Das Einzige, was der Einfachheit halber angepasst wird, ist die verwendete Datenbank: Hier ist es für den Moment am einfachsten, SQLite3 anstelle des vorselektierten MySQL auszuwählen. Hat man die Einstellungen bestätigt, geht es nun daran, einen neuen Git-Nutzer einzurichten (Bild 6) und sich anschließend mit diesem am System anzumelden (Bild 7).

Ein neues Repository erzeugen

GitHub-Nutzern dürften die in Gogs vorhandenen Funktionalitäten bereits sehr bekannt vorkommen, und die Verwendung von Gogs wird ihnen intuitiv von der Hand gehen. Um beispielsweise ein neues Repository zu erzeugen, wählt man einfach oben rechts im Menü den Eintrag Neues Repository.

In der anschließenden Eingabemaske (Bild 8) wählt man den Nutzer, dem das Repository zugeordnet werden soll, vergibt einen Namen, stellt die Sichtbarkeit ein (privat oder öffentlich) und fügt optional eine Beschreibung, eine .gitignore-Vorlage, eine Lizenz sowie eine Readme-Datei hinzu. Damit
die Vorlagen auch wirklich verwendet werden, muss man zusätzlich die Checkbox Repository mit ausgewählten Dateien und Vorlagen initialisieren aktivieren.

Zu guter Letzt muss noch ein SSH-Schlüssel hinzugefügt werden, um vom eigenen Rechner auf das angelegte Repository auch zugreifen zu können. Unter Linux und Mac kann man dies beispielsweise über das Kommandozeilen-Tool ssh-keygen erledigen:

ssh-keygen -t rsa

Standardmäßig wird der hierdurch erzeugte Schlüssel in dem Verzeichnis ~/.ssh/id_rsa.pub gespeichert, dessen Inhalt sich wie folgt ausgeben lässt:

cat ~/.ssh/id_rsa.pub

Über das Menü ganz rechts oben in der Gogs-Weboberfläche gelangt man über Ihre Einstellungen und SSH-Schlüssel in die Verwaltung der SSH-Schlüssel und kann dort den eben generierten Schlüssel hinzufügen (Bild 9).

Anschließend ist das Repository einsatzbereit und kann beispielsweise wie folgt auf den lokalen Entwicklungsrechner geklont werden:
Erzeugen eines neuen SSH-Schlüssels (Bild 9)
Listing 1: Eine einfache Klasse

'use strict';

module.exports = class Calculator

{

static sum(x, y) { return x + y;

}

static subtract(x, y) { return x - y;

}

static product(x, y) { return x * y;

}

static divide(x, y) { return x / y;

}

}
Listing 2: Unit-Test für die Klasse

'use strict';

const assert = require('assert');

const Calculator = require('../lib/Calculator');

describe('Calculator', () =>

{

it('should calculate the sum', () =>

{

let result = Calculator.sum(5, 6); assert.equal(result, 11);

});

it('should calculate the product', () =>

{

let result = Calculator.product(5, 6); assert.equal(result, 30);

});

});



Jenkins steht in verschiedenen Varianten zur Verfügung (Bild 10)
git clone http://localhost:10080/cleancoderocker/ hello-world.git

Damit ist auf Git-Seite eigentlich alles vorbereitet. Bevor es nun an die Installation von Jenkins geht, sei aber schnell noch ein kleines Mini-Projekt hinzugefügt, das der Einfachheit halber lediglich aus einer einzelnen Klasse (Listing 1) und einem dazugehörigen Unit-Test (Listing 2) besteht:

git init

git add lib/Calculator.js

git add test/CalculatorTest.js

git commit -m "Added example class and unit test"

git push -u origin master

Jenkins ist eines der bekannteren CI-Systeme und steht für unterschiedliche Betriebssysteme und in verschiedenen Formen zur Verfügung (Bild 10), unter anderem auch als DockerImage (https://hub.docker.com/r/jenkinsci/jenkins).

Bei der Installation bietet es sich an, wie unter https://www. cloudbees.com/blog/get-started-jenkins-20-docker beschrieben, Jenkins auf zwei Docker-Images zu verteilen: Ein Image enthält die eigentliche Jenkins-Installation inklusive CI-Ser-ver, das andere Image enthält die Konfigurationsdaten für die
^ Listing 3: Dockerfile

FROM debian:jessie

RUN useradd -d "/var/jenkins_home" -u 1000 -m -s /

bin/bash jenkins

RUN mkdir -p /var/log/jenkins

RUN chown -R jenkins:jenkins /var/log/jenkins

VOLUME ["/var/log/jenkins", "/var/jenkins_home"]

USER jenkins

CMD ["echo", "Daten-Container Jenkins"]
^ Listing 4: Dockerfile für Jenkins

FROM jenkinsci/jenkins USER root

RUN curl -sL https://deb.nodesource.com/setup_7-x | bash -

RUN apt-get install -y nodejs

RUN mkdir /var/log/jenkins

RUN mkdir /var/cache/jenkins

RUN chown -R jenkins:jenkins /var/log/jenkins

RUN chown -R jenkins:jenkins /var/cache/jenkins

USER jenkins

ENV JAVA_0PTS="-Xmx4096m"
einzelnen Jenkins-Jobs. Durch diese Trennung ist es prinzipiell auch möglich, die Job-Konfigurationen mit verschiedenen Jenkins-Installationen zu verwenden.

Folglich werden im nächsten Schritt zwei Dockerfile-Da-teien benötigt: Dockerfile-data enthält den in Listing 3 gezeigten Code, Dockerfile den in Listing 4 gezeigten Code. Basierend auf diesen Images erstellt man nun über die folgenden beiden Befehle zwei Docker-Container:

docker build -t jenkins-data -f Dockerfile-data . docker build -t jenkins-master .
Unlock Jenkins

To ensure Jenkins is securely set up by the administrator, a password has been written to the log (not sure where to find it?) and this file on the server: /var/jenkinsJiome/secrets/initialAdminPassword

Please copy the password from either location and paste it below.
Anschließend lassen sich die beiden Container wie folgt star-Vor der ersten ten:

Benutzung

muss das Ad- docker run --name=jenkins-data jenkins-data min-Passwort docker run -p 8080:8080 -p 50000:50000

eingegeben werden (Bild 11)
Willkommen zu Jenkins

Plugins extend Jenkins with additional features to support many different needs.
Install suggested Select plugins to
plugins install
Install plugins the Jenkins Select and install plugins
community finds most most suitable for your needs.
useful.

Installation der Standard-Plug-ins (Bild 13)
Auswahl der

zu installierenden Plug-ins (Bild 12)


--name=jenkins-master --volumes-from=jenkins-data -d jenkins2

Sind die beiden Container gestartet, steht die Weboberfläche von Jenkins unter der Adresse http://localhost:8080 zur Verfügung. Standardmäßig ist dabei ein Nutzer mit dem Nutzernamen admin registriert, das entsprechende Passwort zur Eingabe in die Eingangsmaske (Bild 11) kann man sich über folgenden Befehl aus dem Jen-kins-Container ausgeben lassen:

docker exec jenkins-master cat /var/ jenkins_home/secrets/ initialAdminPassword

Anschließend hat man die Möglichkeit, entweder eine Vorauswahl populärer Jenkins-Plug-ins zu installieren oder sich selbst eine Auswahl zusammenzustellen (Bild 12). Für den Moment reicht Ersteres (Bild 13), weitere Plug-ins werden später manuell nachinstalliert. Nach Installation der Standard-Plugins hat man im nächsten Schritt die Möglichkeit, einen weiteren Administrator-Nutzer anzulegen (Bild 14).

Abschluss der Installation

Für den Moment soll aber auch hier der Standardnutzer admin ausreichen, sodass man entsprechend Continue as admin auswählt (übrigens fällt hier und an vielen anderen Stellen in der Weboberfläche etwas negativ auf, dass die Sprache in

Jenkins nicht konsequent immer Englisch beziehungsweise immer Deutsch ist). Damit ist die Installation abgeschlossen (Bild 15) und man hat Zugriff auf die Weboberfläche (Bild 16).

Bevor im nächsten Schritt endlich gezeigt wird, wie man einzelne Jenkins-Jobs für Node.js- beziehungswei-
se JavaScript-Projekte anlegt und konfiguriert, müssen noch einige Plug-ins manuell nachinstalliert werden. Die entsprechende Maske zur Verwaltung der Plug-ins findet man unter Jenkins, Jenkins verwalten und Plugins verwalten. Unter dem Reiter Verfügbar und mit Hilfe des dortigen Suchfelds (Bild 17) installiert man nun mindestens folgende Plug-ins aus der Plug-in-Registry:

TAP Plugin: zur Darstellung von Testergebnissen,

Checkstyle Plugin: zur Darstellung von Code-Qualität beziehungsweise Code-Style,

NodeJS Plugin: zur Konfiguration von Node.js.

Darüber hinaus muss ein weiteres Plug-in, das Clover-Plug-in, installiert werden, das zur Darstellung von Code-Cove-rage-Berichten dient. Dessen Installation geschieht allerdings nicht wie bei den anderen Plug-ins über die globale Plug-in-Registry, weil die Version dort nicht mit der aktuellen Jenkins-Version funktioniert. Stattdessen muss man die entsprechende Datei von http://repo.jenkins-ci.org/releases/org /jenkins-ci/plugins/clover/4.6.0/clover-4.6.0.hpi herunterladen und anschließend über den Reiter Erweiterte Einstellungen unter dem Bereich Plugin hochladen manuell installieren (Bild 18). Übrigens: Wer sich an dem teilweise etwas veralteten Design von Jenkins stört, kann sich optional auch noch das Simple Theme Plugin installieren, das in dieser Hinsicht etwas nachbessert (Tabelle 1).
Eingabemaske für die Installation weiterer Plug-ins  
Optionales Anlegen eines weiteren Admin-Accounts  
So sieht die Willkommensseite von Jenkins aus  
Manuelles Hochladen von Jenkins-Plug-ins  )

Nachdem Jenkins nun so weit für den ersten Einsatz konfiguriert ist, ist der nächste Schritt, einen neuen Job anzulegen. Dies kann man entweder, sofern noch keine Jobs vorhanden sind, über den Link Legen Sie einen neuen Job an, um loszulegen, oder über den Menüeintrag Element anlegen.

Anschließend öffnet sich die in Bild 19 gezeigte Eingabemaske, über die man den Namen des Jobs sowie dessen Typ auswählen kann. Für Node.js-Projekte eignet sich hierbei die Auswahl Free Style Softwareprojekt bauen. Die Konfiguration des Jobs erstreckt sich über verschiedene Bereiche. Un-
Tabelle 1: Auswahl an nützlichen Jenkins-Plug-ins
Name URL
Checkstyle Plug-in https://wiki.jenkins-ci.org/display/JENKINS/  Checkstyle+Plugin
Clover Plugin https://wiki.jenkins-ci.org/display/JENKINS/  Clover+Plugin
NodeJS Plugin https://wiki.jenkins-ci.org/display/JENKINS/  NodeJS+Plugin
Simple Theme Plugin https://wiki.jenkins-ci.org/display/JENKINS/  Simple+Theme+Plugin
TAP Plugin https://wiki.jenkins-ci.org/display/JENKINS/  TAP+Plugin

ter dem Reiter General (Bild 20) lässt sich beispielsweise eine Beschreibung des Jobs ergänzen; man kann angeben, ob alte Builds verworfen werden sollen, und einiges mehr.

Im Reiter Source-Code-Management definiert man das Versionskontrollsystem, von dem Jenkins seine Daten beziehen soll (Bild 21). Zur Auswahl stehen hier standardmäßig Git und Subversion, wobei für das in diesem Artikel beschriebene Beispiel Ersteres verwendet wird. Unter Repository URL gibt man den URL zu dem Git-Repository ein. Allerdings kann hier nicht die localhost-Adresse verwendet werden, die zuvor beim Klonen des Repositorys verwendet wurde.

Der Grund: Für den Docker-Container, in dem Jenkins läuft, ist Gogs (das ja ebenfalls in einem anderen DockerContainer läuft) nicht über localhost sichtbar. Stattdessen muss in diesem Fall die interne Adresse von Docker verwendet werden. Herausfinden lässt sich diese wie folgt:

docker run -i -t ubuntu /bin/bash

apt-get update

apt-get install -y netstat

netstat -nr | grep 'A0\.0\.0\.0' | awk '{print $2}'

Unter dem Reiter Build-Auslöser lässt sich konfigurieren, welche Ereignisse den Jenkins-Job auslösen (Bild 22). Dies kann durch ein externes Skript geschehen (Auswahl 1), nach einem anderen Jenkins-Job (Auswahl 2), zeitgesteuert (Auswahl 3), bei neuen Commits (Auswahl 4) oder durch eine zeitgesteuerte Prüfung, ob neue Commits vorhanden sind ►
Angabe des Git-Repositorys für den Jenkins-Job (Bild 21)
Erzeugen eines neuen Jobs (Bild 19)
Allgemeine Konfiguration eines Jenkins-Jobs (Bild 20)
Festlegen der Build-Auslöser (Bild 22)

Ql Konsolenausgabe
0. s.™ Started b user admin
....... BrgirLv-pIrsIS-!n-iIsi2!work!«errtiIleout*i0ellO"WOrld
refs'heads/*•ref s/remotes/ori in/*
□ No Tags «1 Vorherige- Build

Die Ausgabe eines Jobs kann jederzeit eingesehen werden (Bild 23)
Festlegen der genauen Build-Schritte (Bild 24)
(Auswahl 5). Bei den zeitgesteuerten Varianten muss unter Zeitplan ein Muster in Cron-Syntax angegeben werden: Das Pattern H * * * * gibt beispielsweise an, dass einmal pro Stunde nach neuen Commits geprüft werden soll.

Zusätzlich ist es aber auch möglich, jeden Job manuell über die Weboberfläche von Jenkins zu starten, was insbesondere zu Testzwecken ganz hilfreich ist. Ebenfalls hilfreich ist dabei die Tatsache, dass sich jederzeit der Output einsehen lässt, den der jeweilige Job auf der Kommandozeile erzeugt (Bild 23).

Konfiguration der Ablauflogik

Die eigentliche Konfiguration der Ablauflogik eines Jenkins-Jobs geschieht über den Reiter Buildverfahren (Bild 24). Hier lassen sich unter Shell ausführen beliebige Shell-Skripts ausführen. Im Fall von Node.js-Projekten liegt es dabei nahe, die konkreten Build-Schritte über Grunt- oder (wie im Beispiel) über Gulp-Skripts einzubinden und diese dann über NPM anzustoßen:

npm cache clean npm install npm run test-ci npm run coverage-ci npm run eslint-ci

Durch obige Befehle wird der Cache von NPM geleert (Zeile 1), die Abhängigkeiten vom Projekt installiert (Zeile 2), die Unit-Tests ausgeführt (Zeile 3), die Testabdeckung ermittelt (Zeile 4) sowie die Codequalität geprüft (Zeile 5).

Für die letzten drei Schritte sind dabei entsprechende Befehle in dem scripts-Bereich in der Datei package.json angegeben (Listing 5), über die wiederum verschiedene Gulp-Tasks ausgeführt werden (Listing 6).

Der Task test im Gulp-Skript erzeugt die Testergebnisse im TAP-Format, der Befehl gulp test -silent > test.tap sorgt da-
^ Listing 5: Die Datei package.json

{

"name": "hello-world",

"version": "1.0.0",

"description": "Beispielprojekt zur Demonstration von Continuous Integration für JavaScript-Projekte",

"main": "index.js",

"scripts": {

"test": "gulp test",

"test-ci": "gulp test --silent > test.tap", "coverage": "gulp test-coverage",

"coverage-ci": "gulp test-coverage",

"eslint": "gulp lint",

"eslint-ci": "gulp lint --silent > checkstyle-result.xml"

},

"repository": {

"type": "git",

"url": "http://localhost:10080/cleancoderocker/ hello-world.git"

},

"keywords": [

"javascript",

"ci"

],

"author": "Philip Ackermann",

"license": "MIT",

"devDependencies": {

"gulp": "A3.9.1",

"gulp-eslint": "A3.0.1",

"gulp-istanbul": "A1.1.1",

"gulp-mocha": "A3.0.1",

"istanbul": "A0.4.5",

"mocha": "A3.2.0"

}

}
für, dass diese Ergebnisse in die Datei test.tap geschrieben werden. Um die Ergebnisse in Jenkins zu integrieren, fügt man dort die Post-Build-Aktion Publish TAP Results hinzu und gibt den Namen der Datei an (Bild 25). Nach Ausführen des Jobs stehen dann die Testergebnisse in Jenkins unter TAP Extended Test Results zur Verfügung (Bild 26).

Integration in Jenkins

Die Task test-coverage im Gulp-Skript ermittelt die Testabdeckung und speichert die Ergebnisse direkt im Verzeichnis coverage. Für die Integration in Jenkins müssen dabei unter reporters mindestens die Reporter lcov und clover angegeben werden.

'use strict';

const gulp = require('gulp'); const mocha = require('gulp-mocha'); const istanbul = require('gulp-istanbul'); const eslint = require('gulp-eslint');

gulp.task('test', () => gulp.src('./test/*.js', {read: false})

.pipe(mocha({reporter: 'tap', timeout: 5000}))

);

gulp.task('pre-test-coverage', function () { return gulp.src(['./lib/**/*.js']) .pipe(istanbul())

.pipe(istanbul.hookRequire());

});

gulp.task('test-coverage', ['pre-test-coverage'],
function () {

return gulp.src(['./test/*.js']) .pipe(mocha({timeout: 5000})) .pipe(istanbul.writeReports({ dir: './coverage', reporters: [ 'lcov', 'clover', 'json', 'text', 'text-summary'], }))
.pipe(istanbul.enforceThresholds({ thresholds: { global: 50 } }));
});
gulp.task('lint', () => {

return gulp.src(['**/*.js','!node_modules/**']) .pipe(eslint())

.pipe(eslint.format('checkstyle',

process.stdout));

});
^ Listing 6: Die Datei gulpfile.js
Darstellung der Code-Coverage-Ergebnisse (Bild 28)
Konfiguration der TAP-Darstellung als Post-Build-Aktion (Bild 25)
Jenkins
Jenkins . hello-world . #11 ► TAP Test Ri esults
^ Zurück zum Projekt Status TAP Extended Test Results
^ Änderungen
Konsolenausgabe File: test.tap
S Build-Informationen editieren Number Description Directive
0 Build löschen I Calculator shi Duid calculate the sum
m 1 Calculator should calculate the product
□ No Tags Parse errors
Q TAP Test Results No parse errors f bund
§8| TAP Extended Test Results
^ Checkstyle Warnungen
Clover Summary Report
^ Vorheriger Build

Darstellung der TAP-Testergebnisse (Bild 26)
all files / Mb/ Calculator.js

60% Statements 3/5 100% Branches 0/0 50% Functions 2/4 60% Lines 3/5
static product(x, y) { return x * y;

>

static divide(xf y) { ■ returnÜSH
Code-Coverage pro Datei (Bild 29)
Konfiguration von Clover als Post-Build-Aktion (Bild 27)
Konfiguration von Checkstyle als Post-Build-Aktion (Bild 30)
1 1 use strict1;
2
3 lx module.exports = class Constructor {
4 static sum(x, y) {
5 lx return x + y;
6 >
7 static subtract(x„ y) {
8 return x
9 >
y;


Über die Post-Build-Aktion Publish Clover Coverage Report (Bild 27) fügt man das Verzeichnis hinzu, das die Berichte zur Testabdeckung enthält (coverage), sowie den Namen der Datei, welche die Ergebnisse im Clover-Format enthält (clover.xml).

Nach Ausführen des Jobs kann man dann die Ergebnisse unter Clover Summary Report und Clover HTML Report als Übersicht (Bild 28) beziehungsweise im Detail pro JavaScriptDatei (Bild 29) einsehen.

Fehlt noch die Ermittlung der Codequalität. Dazu gibt es im Gulp-Skript einen Task lint, das Ergebnis landet in der Datei checkstyle-result.xml.

In Jenkins fügt man die Post-Build-Aktion Veröffentliche Ergebnisse der Checkstyle-Analyse hinzu (Bild 30), trägt dort den Namen der erzeugten XML-Datei ein und erhält anschließend unter Checkstyle Warnungen einen entsprechenden Report (Bild 31).
Darstellung der Checkstyle-Ergebnisse (Bild 31)

Jenkins mit Node.js steuern

Die Konfigurationen für einzelne Jobs speichert Jenkins intern im XML-Format. Ein Beispiel hierzu zeigt Listing 7. Über ein von Jenkins bereitgestelltes API (https://wiki.jenkins-ci.
<?xml version='1.0' encoding='UTF-8'?>

<project>

<actions/>

<description>Beispielprojekt zur Demonstration von Continuous Integration für JavaScript-Projekte </description>

<keepDependencies>false</keepDependencies>

<properties/>

<scm class="hudson.plugins.git.GitSCM" plugin="git@3.0.1">

<configVersion>2</configVersion>

<userRemoteConfigs>

<hudson.plugins.git.UserRemoteConfig>

<url>http://172.17.0.1:10080/cleancoderocker/

hello-world.git</url>

<credentialsId>

12c1e5a2-8876-414e-b98d-ae8112618c68

</credentialsId>

</hudson.plugins.git.UserRemoteConfig>

</userRemoteConfigs>

<branches>

<hudson.plugins.git.BranchSpec>

<name>*/master</name>

</hudson.plugins.git.BranchSpec>

</branches>

<doGenerateSubmoduleConfigurations>false

</doGenerateSubmoduleConfigurations>

<submoduleCfg class="list"/>

<extensions/>

</scm>

<canRoam>true</canRoam>

<disabled>false</disabled>

<blockBuildWhenDownstreamBuilding>false
<blockBuildWhenUpstreamBuilding>false

</blockBuildWhenUpstreamBuilding>

<triggers>

<hudson.triggers.SCMTrigger>

<spec>H * * * *</spec> <ignorePostCommitHooks>false </ignorePostCommitHooks> </hudson.triggers.SCMTrigger>

</triggers>

<concurrentBuild>false</concurrentBuild>

<builders>

<hudson.tasks.Shell>

<command>npm cache clean npm install npm run test-ci npm run coverage-ci npm run eslint-ci</command> </hudson.tasks.Shell>

</builders>

<publishers>

<org.tap4j.plugin.TapPublisher plugin="tap@2.0.1"> <testResults>test.tap</testResults> <failIfNoResults>false</failIfNoResults> <failedTestsMarkBuildAsFailure>false </failedTestsMarkBuildAsFailure> <outputTapToConsole>false</outputTapToConsole> <enableSubtests>false</enableSubtests> <discardOldReports>false</discardOldReports> <todoIsFailure>false</todoIsFailure> <includeCommentDiagnostics>false </includeCommentDiagnostics> <validateNumberOfTests>false
^ Listing 7: Jenkins-Job-Konfigurationsdatei (Teil 1)

</blockBuildWhenDownstreamBuilding>

org/display/JENKINS/Remote+access+API) lassen sich existierende Konfigurationen aktualisieren oder auch Konfigurationen für neue Jobs erstellen. Dies bietet sich insbesondere dann an, wenn man viele Jobs auf einmal anlegen oder verändern möchte und nicht jede Änderung über die Weboberfläche durchführen möchte.

Ein praktisches Modul für Node.js, das in diesem Zusammenhang einen Blick wert ist, ist das Modul node-jenkins (https://github.com/silas/node-jenkins). Hier handelt es sich um ein Plug-in für Grunt (für Gulp gibt es leider noch nichts Vergleichbares), über das sich Jobs anlegen, aktualisieren oder einfach (im XML-Format) als Backup sichern lassen.

Insgesamt stehen drei Grunt-Tasks zur Verfügung: jenkins-install-jobs erstellt ausgehend von den lokalen XML-Konfi-gurationen in Jenkins neue Jobs oder aktualisiert bestehende Jobs. jenkins-backup-jobs erstellt ausgehend von existierenden Jenkins-Jobs lokal neue XML-Konfigurationen oder aktualisiert bestehende XML-Konfigurationen. jenkins-ve-rify-jobs prüft, ob die lokalen XML-Konfigurationen mit den bestehenden Jenkins-Jobs übereinstimmen.
Fazit

Jenkins lässt sich relativ einfach für die Verwendung in No-de.js-Projekten konfigurieren. Dieser Artikel hat gezeigt, wie sich Jenkins und ein lokaler Git-Server mit Hilfe von Docker installieren und konfigurieren lassen. Außerdem wurde an zahlreichen Beispielen demonstriert, wie sich innerhalb des Entwicklungsprozesses das Ausführen von Unit-Tests sowie das Ermitteln der Testabdeckung und der Codequalität automatisieren und integrieren lassen.
Philip Ackermann

arbeitet beim Fraunhofer-Institut für Angewandte Informationstechnik FIT und ist Autor mehrerer Fachbücher und Fachartikel über Java und JavaScript. http://philipackermann.de
Listing 7: Jenkins-Job-Konfigurationsdatei (Teil 2)

</validateNumberOfTests>

<planRequired>true</planRequired>

<verbose>true</verbose>

<showOnlyFailures>false</showOnlyFailures>

<stripSingleParents>false</stripSingleParents>

<flattenTapResult>false</flattenTapResult>

<skipIfBuildNotOk>false</skipIfBuildNotOk>

</org.tap4j.plugin.TapPublisher>

<hudson.plugins.checkstyle.CheckStylePublisher

plugin="checkstyle@3.47">

<healthy></healthy>

<unHealthy></unHealthy>

<thresholdLimit>low</thresholdLimit>

<pluginName>[CHECKSTYLE] </pluginName>

<defaultEncoding></defaultEncoding>

<canRunOnFailed>false</canRunOnFailed>

<usePreviousBuildAsReference>false

</usePreviousBuildAsReference>

<useStableBuildAsReference>false

</useStableBuildAsReference>

<useDeltaValues>false</useDeltaValues>

<thresholds plugin="analysis-core@1.81"> <unstableTotalAll></unstableTotalAll> <unstableTotalHigh></unstableTotalHigh> <unstableTotalNormal></unstableTotalNormal> <unstableTotalLow></unstableTotalLow> <unstableNewAll></unstableNewAll> <unstableNewHigh></unstableNewHigh> <unstableNewNormal></unstableNewNormal> <unstableNewLow></unstableNewLow>
<failedTotalAll></failedTotalAll>

<failedTotalHigh></failedTotalHigh>

<failedTotalNormal></failedTotalNormal>

<failedTotalLow></failedTotalLow>

<failedNewAll></failedNewAll>

<failedNewHigh></failedNewHigh>

<failedNewNormal></failedNewNormal>

<failedNewLow></failedNewLow>

</thresholds>

<shouldDetectModules>false</shouldDetectModules>

<dontComputeNew>true</dontComputeNew>

<doNotResolveRelativePaths>false

</doNotResolveRelativePaths>

<pattern>**/checkstyle-result.xml</pattern>

</hudson.plugins.checkstyle.CheckStylePublisher>

<hudson.plugins.clover.CloverPublisher

plugin="clover@4.6.0">

<cloverReportDir>coverage</cloverReportDir>

<cloverReportFileName>clover.xml

</cloverReportFileName>

<healthyTarget>

<methodCoverage>70</methodCoverage>

<conditionalCoverage>80</conditionalCoverage>

<statementCoverage>80</statementCoverage>

</healthyTarget>

<unhealthyTarget/>

<failingTarget/>

</hudson.plugins.clover.CloverPublisher>

</publishers>

<buildWrappers/>

</project>





You may also like

Keine Kommentare:

Blog-Archiv

Powered by Blogger.