1. Objectifs
1.1. Une vérification systématique de l’état du code

1.2. Environnement
On utilise gitlab-ci-multi-runner 1.6.0
.
1.3. eXtreme Programming
Continuous Integration is a software development practice where members of a team integrate their work frequently, […] leading to multiple integrations per day.
1.4. Principe général

1.5. YAML
YAML: YAML Ain’t Markup Language
.yml
--- receipt: Oz-Ware Purchase Invoice date: 2012-08-06 customer: first_name: Dorothy family_name: Gale
Utiliser des espaces et non des tabulations. |
2. Utilisation
Un serveur d’intégration continue peut permettre de :
-
Faire les opérations type git (pull, checkout, push)
-
Compiler du code source ⇒ build
-
Créer des archives
-
Déployer du code sur une machine de test
-
Exécuter une suite de tests (Junit, Audit de code source, test IHM, …)
-
Notifier des résultats (mail, RSS)
-
etc.
3. Services connus
4. Pour notre environnement (GitLab)
4.1. Processus type
-
Créer à la racine du projet un fichier
.gitlab-ci.yml
-
Y décrire ce que l’on souhaite réaliser
-
"Pousser" dans le dépôt GitLab ses modifications
-
Contrôler les résultats
4.2. Exemple (MPA2016-1B2)
image: node:4.2.2 (1) all_tests: script: (2) - npm install express --save - node ./myapp.js
1 | Nom du "runner" |
2 | Instructions à réaliser sur la machine |
Résultat avec le fichier .gitlab-ci.yml
précédent :

Avec un fichier .gitlab-ci.yml
plus conforme (exécution de tests) :
- npm install express --save - node ./specs/start.js ./specs/async.spec.js

test_async: stage: test script: - npm install - node ./specs/start.js ./specs/async.spec.js
4.3. Exemple HelloWorld
-
Code java
-
Petit
main
de test (pas unitaire) -
Compilation manuelle
-
Build ant
-
Améliorations
-
Tests
-
Eclipse
-
Intégration continue
4.3.1. Code java
Consultez le tutoriel ant : https://ant.apache.org/manual/tutorial-HelloWorldWithAnt.html |
package org.jmb;
public class HelloWorld
{
private String name = "";
public String getName()
{
return name;
}
public String getMessage()
{
if (name == "")
{
return "Hello!";
}
else
{
return "Hello " + name + "!";
}
}
public void setName(String name)
{
this.name = name;
}
}
4.3.2. Petit main
de test (pas unitaire)
package org.jmb;
public class Main {
public static void main(String[] args) {
HelloWorld h = new HelloWorld();
h.setName("JMB");
System.out.println(h.getMessage());
}
}
4.3.3. Compilation manuelle
$ javac -sourcepath src -d bin/ src/Main.java
$ ls bin
HelloWorld.class Main.class
$ java -cp bin Main
Hello JMB!
4.3.4. Build ant
<project>
<target name="clean">
<delete dir="bin"/>
</target>
<target name="build">
<mkdir dir="bin"/>
<javac srcdir="src" destdir="bin"/>
</target>
<target name="jar">
<mkdir dir="bin/jar"/>
<jar destfile="bin/jar/HelloWorld.jar" basedir="bin">
<manifest>
<attribute name="Main-Class" value="Main"/>
</manifest>
</jar>
</target>
<target name="run">
<java jar="bin/jar/HelloWorld.jar" fork="true"/>
</target>
</project>
$ ant clean build jar
$ ant run
Buildfile: /Users/bruel/HelloWorld/build.xml
run:
[java] Hello JMB!
BUILD SUCCESSFUL
Total time: 0 seconds
4.3.5. Améliorations
<project name="HelloWorld" basedir="." default="main">
<property name="src.dir" value="src"/>
<property name="build.dir" value="bin"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="Main"/>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<target name="run" depends="jar">
<java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
</project>
$ ant
Buildfile: /Users/bruel/HelloWorld/build.xml
clean:
[delete] Deleting directory /Users/bruel/HelloWorld/bin
compile:
[mkdir] Created dir: /Users/bruel/HelloWorld/bin/classes
[javac] Compiling 2 source files to /Users/bruel/HelloWorld/bin/classes
jar:
[mkdir] Created dir: /Users/bruel/HelloWorld/bin/jar
[jar] Building jar: /Users/bruel/HelloWorld/bin/jar/HelloWorld.jar
run:
[java] Hello JMB!
main:
BUILD SUCCESSFUL
Total time: 1 second
4.3.6. Tests
package org.jmb;
public class TestHelloWorld extends junit.framework.TestCase {
public void testNothing() {
}
public void testWillAlwaysFail() {
fail("An error message");
}
}
<project name="HelloWorld" basedir="." default="main">
<property name="src.dir" value="src"/>
<property name="build.dir" value="bin"/>
<property name="classes.dir" value="${build.dir}/classes"/>
<property name="jar.dir" value="${build.dir}/jar"/>
<property name="main-class" value="Main"/>
<property name="lib.dir" value="lib"/>
<path id="classpath">
<fileset dir="${lib.dir}" includes="**/*.jar"/>
</path>
<target name="clean">
<delete dir="${build.dir}"/>
</target>
<target name="compile">
<mkdir dir="${classes.dir}"/>
<javac srcdir="${src.dir}" destdir="${classes.dir}" classpathref="classpath"/>
</target>
<target name="jar" depends="compile">
<mkdir dir="${jar.dir}"/>
<jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
<manifest>
<attribute name="Main-Class" value="${main-class}"/>
</manifest>
</jar>
</target>
<path id="application" location="${jar.dir}/${ant.project.name}.jar"/>
<target name="run" depends="jar">
<java fork="true" classname="${main-class}">
<classpath>
<path refid="classpath"/>
<path refid="application"/>
<path location="${jar.dir}/${ant.project.name}.jar"/>
</classpath>
</java>
</target>
<target name="junit" depends="jar">
<junit printsummary="yes">
<classpath>
<path refid="classpath"/>
<path refid="application"/>
</classpath>
<batchtest fork="yes">
<fileset dir="${src.dir}" includes="TestHelloWorld.java"/>
</batchtest>
</junit>
</target>
<target name="clean-build" depends="clean,jar"/>
<target name="main" depends="clean,run"/>
</project>
$ ant junit
Buildfile: /Users/bruel/HelloWorld/build.xml
compile:
jar:
junit:
[junit] Running TestHelloWorld
[junit] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0,003 sec
[junit] Test TestHelloWorld FAILED
BUILD SUCCESSFUL
Total time: 1 second
package org.jmb;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class TestHelloWorldReal {
private HelloWorld h;
@Before
public void setUp() throws Exception
{
h = new HelloWorld();
}
@Test
public void testHelloEmpty()
{
assertEquals(h.getName(),"");
assertEquals(h.getMessage(),"Hello!");
}
@Test
public void testHelloWorld()
{
h.setName("World");
assertEquals(h.getName(),"World");
assertEquals(h.getMessage(),"Hello World!");
}
}

Ne pas hésiter à utiliser le plugin infinitest. ![]() Figure 7. Utilisation d’infinitest sous eclipse
|
4.3.7. Eclipse

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- WARNING: Eclipse auto-generated file.
Any modifications will be overwritten.
To include a user specific buildfile here, simply create one in the same
directory with the processing instruction <?eclipse.ant.import?>
as the first entry and export the buildfile again. --><project basedir="." default="build" name="HelloWorld">
<property environment="env"/>
<property name="junit.output.dir" value="junit"/>
<property name="debuglevel" value="source,lines,vars"/>
<property name="target" value="1.8"/>
<property name="source" value="1.8"/>
<path id="JUnit 4.libraryclasspath">
<pathelement location="../../../.p2/pool/plugins/org.junit_4.12.0.v201504281640/junit.jar"/>
<pathelement location="../../../.p2/pool/plugins/org.hamcrest.core_1.3.0.v201303031735.jar"/>
</path>
<path id="HelloWorld.classpath">
<pathelement location="bin"/>
<path refid="JUnit 4.libraryclasspath"/>
</path>
<target name="init">
<mkdir dir="bin"/>
<copy includeemptydirs="false" todir="bin">
<fileset dir="src">
<exclude name="**/*.launch"/>
<exclude name="**/*.java"/>
</fileset>
</copy>
</target>
<target name="clean">
<delete dir="bin"/>
</target>
<target depends="clean" name="cleanall"/>
<target depends="build-subprojects,build-project" name="build"/>
<target name="build-subprojects"/>
<target depends="init" name="build-project">
<echo message="${ant.project.name}: ${ant.file}"/>
<javac debug="true" debuglevel="${debuglevel}" destdir="bin" includeantruntime="false" source="${source}" target="${target}">
<src path="src"/>
<classpath refid="HelloWorld.classpath"/>
</javac>
</target>
<target description="Build all projects which reference this project. Useful to propagate changes." name="build-refprojects"/>
<target name="Main">
<java classname="org.jmb.Main" failonerror="true" fork="yes">
<classpath refid="HelloWorld.classpath"/>
</java>
</target>
<target name="TestHelloWorld">
<mkdir dir="${junit.output.dir}"/>
<junit fork="yes" printsummary="withOutAndErr">
<formatter type="xml"/>
<test name="org.jmb.TestHelloWorld" todir="${junit.output.dir}"/>
<jvmarg line="-ea"/>
<classpath refid="HelloWorld.classpath"/>
</junit>
</target>
<target name="junitreport">
<junitreport todir="${junit.output.dir}">
<fileset dir="${junit.output.dir}">
<include name="TEST-*.xml"/>
</fileset>
<report format="frames" todir="${junit.output.dir}"/>
</junitreport>
</target>
</project>

Autre avantage de junit, la génération de pages web (répertoire junit
, ouvrir index.html
).

4.3.8. Integration Continue
image: openjdk:8
before_script:
- apt-get update
- apt-get -y install ant
stages:
- build
- test
ci_build:
stage: build
script:
- cd HelloWorld
- ant clean
- ant build
ci_test:
stage: test
script:
- cd HelloWorld
- ant clean build TestHelloWorld
Il faut modifier éventuellement le build.xml , souvent trop lié à Eclipse.
|
5. Divers
5.1. Quand on ne veux pas lancer l’IC
git commit -m "Blabla... [ci skip]"
5.2. Pour vérifier la syntaxe de son fichier YAML
Possibilité de tester son fichier gitlab-ci.yml
:

6. Liens utiles
- Le site de référence
- Un tuto en français sur l’IC sous GitLab (merci @npm_kader)
-
https://www.grafikart.fr/tutoriels/divers/gitlab-ci-docker-808