Créer un projet Jenkins Multibranch Pipeline pour Delphi (Seconde partie)

Dans la première partie, nous avons vu comment installer Jenkins et les extensions nécessaires pour compiler un projet Delphi. Nous allons maintenant voir comment créer un script Jenkinsfile de base.

Créer le fichier Jenkinsfile

Les pipelines s’appuient sur des scripts écris en Groovy, un langage de script faisant appel à Java à travers une sandbox.

Jenkins recherche dans chaque branche du dépôt un fichier Jenkinfile contenant un script Groovy définissant les étapes de la compilation.

Structure de base du script

Un script de pipeline commence par la ligne de commentaire #!groovy, ce qui permet aux éditeurs reconnaissant ce format de le coloriser automatiquement. Sous Windows, je n’ai pas trouvé de logiciel le gérant, même si la demande existe pour une intégration dans Notepad++.

Ensuite, le script doit contenir la balise pipeline {}. Elle indique à Jenkins que le script commence. Ensuite, on peut indiquer des options pour ce pipeline et surtout des « stages » qui seront en fait les étapes de notre compilation.

#!groovy
pipeline {
    agent any
    stages {
        stage('Compilation') {
        }
    } 
}

Dans cet exemple, on déclare un pipeline, qu’il peut utiliser n’importe quel agent déclaré dans Jenkins et qu’il n’y a qu’une grande étape, la compilation.

Compilation d’un projet Delphi

Pour pouvoir compiler Jenkins, nous allons créer une étape de compilation faisant appel à MSBuild.

Dans un pipeline Jenkins, les variables d’environnement de Windows ne sont pas prises en compte ! Il faut donc les préciser à Jenkins dans le script.

Pour cela, nous allons rajouter une section « environnement » à notre script et y déclarer toutes les variables d’environnement nécessaires à la compilation.

environment {
    BDS = "C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0"
    BDSINCLUDE = "C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\include"
    BDSCOMMONDIR = "C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0"
    FrameworkDir = "C:\\Windows\\Microsoft.NET\\Framework\\v3.5"
    FrameworkVersion= "v3.5"
    FrameworkSDKDir = ""
    PATH = "C:\\Program Files (x86)\\Embarcadero\\Studio\\18.0\\bin;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\18.0\\Bpl;C:\\Program Files (x86)\\Embarcadero\\Studio\\18.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\18.0\\Bpl\\Win64;C:\\Bitnami\\redmine-3.2.0-0\\ruby\\bin;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files\\ImageMagick-6.9.2-Q16;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0\\Bpl;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0\\Bpl\\Win64;C:\\Program Files\\Perl64\\site\\bin;C:\\Program Files\\Perl64\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\PROGRA~1\\ITM\\bin;C:\\PROGRA~1\\ITM\\InstallITM;C:\\PROGRA~1\\ITM\\TMAITM6;C:\\Program Files\\ITM\\bin;C:\\Program Files\\ITM\\InstallITM;C:\\Program Files\\ITM\\TMAITM6;C:\\Program Files\\Microsoft\\Web Platform Installer\\;C:\\Program Files\\TortoiseSVN\\bin;%USERPROFILE%\\.dnx\\bin;C:\\Program Files\\Microsoft DNX\\Dnvm\\;C:\\sonar-runner\\bin;C:\\Program Files (x86)\\Windows Kits\\10\\Windows Performance Toolkit\\;C:\\Program Files\\Java\\jre1.8.0_66\\bin;C:\\Program Files\\Git\\cmd;C:\\Program Files\\1E\\NomadBranch\\;C:\\Program Files\\VisualSVN Server\\bin;C:\\Program Files (x86)\\Resource Hacker\\;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\InterBase\\redist\\InterBaseXE7\\IDE_spoof;"
    LANGDIR = "FR"
    PLATFORM = ""
    PlatformSDK = ""
}

Comme vous pouvez le voir dans l’exemple ci-dessus, les chemins sont tous absolus, il n’est pas possible d’utiliser %PATH% par exemple pour simplement ajouter quelque chose au PATH existant. Ici, nous avons deux versions de Delphi installées, la 17 (Seatle) et la 18 (Berlin) d’où la présence des deux chemins.

Nous allons maintenant pouvoir appeler la compilation à proprement parler. Pour cela, nous allons déclarer une « steps » et faire appel à notre plugin MSBuild via un « script ».

steps {
    script {
        def msbuildHome = tool 'MSBuild pour Delphi'
        bat "${msbuildHome}\\MSBuild.exe\ "${WORKSPACE}\\MonProjet\\Projet.groupproj\""
}

On commence par indiquer à Jenkins qu’on veut récupérer le chemin d’accès à l’outil MSBuild en l’appelant par le nom déclaré lors de la configuration.

Ensuite, on fait appel à la commande « bat » à laquelle on passe le chemin d’accès absolu à MSBuild et le chemin d’accès absolu à notre fichier .dproj. La variable ${WORSPACE} est renseignée par Jenkins et contient le chemin vers le répertoire de travail où va avoir lieu la compilation.

Si besoin, on peut ajouter des paramètres dans la ligne de commande pour indiquer quel plateforme utilisée ou le mode de compilation.

On obtient donc au final le script suivant :

#!groovy
pipeline {
    agent any
    stages {
        stage('Compilation') {
            environment {
                BDS = "C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0"
                BDSINCLUDE = "C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\include"
                BDSCOMMONDIR = "C:\sers\\Public\\Documents\\Embarcadero\\Studio\\17.0"
                FrameworkDir = "C:\\Windows\\Microsoft.NET\\Framework\\v3.5"
                FrameworkVersion= "v3.5"
                FrameworkSDKDir = ""
                PATH = "C:\\Program Files (x86)\\Embarcadero\\Studio\\18.0\\bin;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\18.0\\Bpl;C:\\Program Files (x86)\\Embarcadero\\Studio\\18.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\18.0\\Bpl\\Win64;C:\\Bitnami\\redmine-3.2.0-0\\ruby\\bin;C:\\ProgramData\\Oracle\\Java\\javapath;C:\\Program Files\\ImageMagick-6.9.2-Q16;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0\\Bpl;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0\\Bpl\\Win64;C:\\Program Files\\Perl64\\site\\bin;C:\\Program Files\\Perl64\\bin;C:\\Windows\\system32;C:\\Windows;C:\\Windows\\System32\\Wbem;C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\;C:\\PROGRA~1\\ITM\\bin;C:\\PROGRA~1\\ITM\\InstallITM;C:\\PROGRA~1\\ITM\\TMAITM6;C:\\Program Files\\ITM\\bin;C:\\Program Files\\ITM\\InstallITM;C:\\Program Files\\ITM\\TMAITM6;C:\\Program Files\\Microsoft\\Web Platform Installer\\;C:\\Program Files\\TortoiseSVN\\bin;%USERPROFILE%\\.dnx\\bin;C:\\Program Files\\Microsoft DNX\\Dnvm\\;C:\\sonar-runner\\bin;C:\\Program Files (x86)\\Windows Kits\\10\\Windows Performance Toolkit\\;C:\\Program Files\\Java\\jre1.8.0_66\\bin;C:\\Program Files\\Git\\cmd;C:\\Program Files\\1E\\NomadBranch\\;C:\\Program Files\\VisualSVN Server\\bin;C:\\Program Files (x86)\\Resource Hacker\\;C:\\Users\\Public\\Documents\\Embarcadero\\Studio\\17.0;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin;C:\\Program Files (x86)\\Embarcadero\\Studio\\17.0\\bin64;C:\\Users\\Public\\Documents\\Embarcadero\\InterBase\\redist\\InterBaseXE7\\IDE_spoof;"
                LANGDIR = "FR"
                PLATFORM = ""
                PlatformSDK = ""
                } 
            steps {
                script {
 def msbuildHome = tool 'MSBuild pour Delphi (release)'
 bat "${msbuildHome}\\MSBuild.exe \"${WORKSPACE}\\Composants\\TMSFMXPack\\TMSFMXPackDXE9.groupproj\""
 bat "${msbuildHome}\\MSBuild.exe \"${WORKSPACE}\\WLZ\\WLZcompression.dproj\""
 bat "${msbuildHome}\\MSBuild.exe \"${WORKSPACE}\\EPERMITLIB.XE8\\LIBTOTAL\\Total.dproj\""
 bat "${msbuildHome}\\MSBuild.exe \"${WORKSPACE}\\EPERMITLIB.XE8\\connection monitoring\\DSServerConnectionMonitoring\\DataSnapServerConnectionMonitoring.dproj\""
 bat "${msbuildHome}\\MSBuild.exe \"${WORKSPACE}\\EPERMIT.XE8\\EPERMIT.groupproj\""
                }
            }
        }
    }
}

Et voilà, vous n’avez plus qu’à nomer ce fichier « Jeninksfile » et à le placer à la racine du trunk ou de la branche, puis livrer !

Comme nous n’avons pas encore configuré la compilation automatique sur une livraison SVN, il nous faut lancer un scanne des branches manuellement. Pour cela, dans l’interface de Jenkins, cliquez sur votre projet Mutibranch Pipeline puis sur « Scan Multibranch Pipeline » dans le menu de gauche. Jenkins va alors détecter qu’une branche est à compiler et lancer la compilation.

Créer un projet Jenkins Multibranch Pipeline pour Delphi (Première partie)

Vu les difficultés que j’ai rencontrées lors de la mise en place d’un projet Multibranch Pipeline pour la plateforme de compilation Jenkins, voici une série d’article pour comprendre comment cela fonctionne et comment écrire un script Jenkinsfile.

Installer Jenkins sous Windows

Vu qu’il est question de Delphi, le serveur de compilation sera forcément sous Windows. Dans mon cas, l’installation a été faite sur un Windows 2012 R2 n’ayant pas accès à Internet, ce qui compliquait énormément les choses.

Installation de Delphi

Commencez par installer Delphi sur le serveur avec la licence adéquate. Attention, seule les versions de Delphi supportant MSBuild pourront être compilées par Jenkins. Les fichiers à transmettre à MSBuild sont les .dproj. Si vous n’en avez pas, c’est que votre version est trop ancienne.

Lancez au moins une fois Delphi pour qu’il crée son environnement complet, dont entre autres, les fichiers dans %appdata% et certaines clés de registre.

Installation de Jenkins

Commencez par télécharger Jenkins, la version LTS étant recommandée (Long Term Support). Ensuite, lancez simplement l’installation comme pour tout logiciel sous Windows.

Si votre serveur n’a pas accès à Internet, le mieux est encore de faire aussi l’installation sur un poste ayant accès qui vous servira pour installer les extensions que vous reporterez manuellement vers le serveur cible.

Au premier démarrage, Jenkins vous propose toute une série d’extensions par défaut, il est conseillé de les installer.

Une fois installé, Jenkins est présent dans la liste des services et lancé par le compte système. Ce compte ne peut pas accéder à certains fichiers de Delphi qui ne sont créés qu’au lancement de l’IDE (entre autre, le fichier EnvOptions.proj placé dans %appdata%\Embarcadero\BDS\18.0\ si vous utilisez Delphi Berlin).

Il faut donc modifier le compte utilisé pour démarrer le service Jenkins et utilisé celui qui a été utilisé pour installer Delphi, puis redémarrer le service.

Installation des extensions pour Jenkins

Si vous avez laissez Jenkins installer les extensions recommandés, une grande partie est déjà là.

Il ne manque donc que ce qui est spécifique à Delphi. Voici les extensions que j’ajoute personnellement :

  • MSBuild Plugin : obligatoire, c’est la base pour lancer la compilation de projets Delphi à l’heure actuel. Il existe une extension RAD Studio Plugin mais elle ne supporte pas encore les pipelines même si l’auteur a prévu de le faire.
  • Redmine plugin : pour permettre de lier notre projet à une application de gestion de projet.
  • SonarQube Plugin : pour analyser le code Delphi, je consacrerai un article à son utilisation avec Delphi.
  • NUnit plugin : pour effectuer des tests unitaires, malgré son nom, il supporte DUnit dans sa dernière version.
  • Blue Ocean beta : c’est la future interface de Jenkins, qui a l’avantage d’apporter avec elle une multitude de plugins nécessaires à l’utilisation des pipelines.
  • Blue Ocean Pipeline Editor : c’est un éditeur de script intégré qui devrait bientôt nous simplifier beaucoup la tâche !

Configuration de l’extension MSBuild

Allez dans « Administrer Jenkins » puis « Configuration globale des outils ». Cliquez ensuite sur « Ajouter MSBuild » et saisissez un nom pour définir votre outil.

Saisissez dans le premier champ, l’emplacement ou se trouve MSBuild, généralement C:\Windows\Microsoft.NET\Framework\v3.5.

Saisissez ensuite les paramètres :

  • /t:build indique que vous voulez faire une construction complète du projet (recommandé).
  • /p:platform=Win64 indique que vous souhaitez compiler en 64 bits pour Windows. Reportez-vous à l’aide de Delphi pour savoir quelle plateforme utiliser.
  • /p:config=Debug indique que la compilation se fera en mode Debug.

Dans mon cas, j’ai créé deux outils, un pour compiler en mode Debug, et un pour compiler en mode Release.

L’extension MSBuild n’étant pas encore compatible avec les pipelines, les paramètres ne sont pas correctement pris en compte. Il faudra donc les passer dans la ligne de commande dans le script.

Création du projet Multibranch Pipeline

Cliquez sur « Nouveau Item », saisissez un titre, sélectionnez Multibranch Pipeline puis validez.

Une page de configuration s’affiche. La seule partie réellement importante, est la section « Branch Sources ». Elle va vous permettre d’indiquer le serveur de sources vers lequel va pointer Jenkins. Dans mon exemple, c’est Subversion qui va être utilisé.

Notez que l’option « Déclencher les builds à distance (Par exemple, à partir de scripts) » n’est pas fonctionnelle et ne devrait pas s’afficher ici. C’est un bogue connu des développeurs de Jenkins et nous verrons plus loin comment faire pour que Jenkins construise automatiquement une branche quand une livraison de code à lieu.

Sauvegardez la configuration. Jenkins va aussitôt scanner le dépôt pour y trouver les branches et voir si elles contiennent un fichier Jenkinsfile. C’est la présence de ce fichier qui va indiquer si une banche doit être compilée. Et c’est l’écriture de ce fichier que nous allons voir ensuite.