Continous integration is, in computer sciences, a must-have. Jenkins help us to reach this objectives. But how to configure it using “Pipelines” ?
What are pipeline for ?
From Jenkins official documentation :
“Jenkins Pipeline (or simply “Pipeline” with a capital “P”) is a suite of plugins which supports implementing and integrating continuous delivery pipelines into Jenkins.
A continuous delivery (CD) pipeline is an automated expression of your process for getting software from version control right through to your users and customers. Every change to your software (committed in source control) goes through a complex process on its way to being released. This process involves building the software in a reliable and repeatable manner, as well as progressing the built software (called a “build”) through multiple stages of testing and deployment. “
With this specific process, you will be able to create a list of tasks to be executed.
How to start ?
A Jenkins Pipeline always start with the node function.
node {
echo "Hello World !"
}
This simple Pipeline will only print “Hello World !”.
Let”s go deeper : Git + Maven + Sonar
Before configuring Pipeline, you will have to configure your Jenkins environment.
Preparing Sonar environment
On your SonarQube interface, create a new user (named it jenkins). Connect to SonarQube with jenkins credentials and generate an new authentication token (accessible from the “My Account” menu).
The token will be displayed after setting a new token name and clicked on “Generate”. Copy it, you will need it to configure SonarQube in Jenkins.
Preparing git environment
On your Git interface, create a new user and affect it to your Git repository as developer.
Preparing Jenkins environment
Back to Jenkins. Go into “Manage Jenkins” then “Configure System”
Add a new SonarQube server. Check the “Enable injection of SonarQube server configuration as build environment variables” checkbox, set the name to “SonarQube”, the server URL to the /sonarqube endpoint, and the authentication token from the previously generated token. When it’s done, click on “Save”.
When it’s done, click on “Manage Jenkins” > “Managed files”. Add a new Maven configuration file matching with your environment, and name it “Maven Settings”, then click “Submit”.
When the maven file is configured, let’s configure Jenkins tools. Click on “Manage Jenkins” and “Global Tools Configuration”.
Configure the “Maven Configuration” part as the following:
Configure the “Git” part as the following:
Configure the “SonarQube Server” part as the following:
Configure the “Maven” part as the following:
When all is configured, click on “Save”. You are now ready to go with the Pipeline part !
Defining a new Jenkins Job
Create a new Jenkins Job. Named it “My Pipeline”, click on the Pipeline wizard, and click on “OK”.
In the “Build Triggers” part, you can configure when the job is launch. For my personal use, I will check the “Build periodically” check box, and set the “Schedule” part to “H H * * *” (one execution per day).
Then the Pipeline script. Set the “Definition” value to “Pipeline script” (I will write my script in Jenkins instead of a SCM file available on Git repository), and let’s go to the Pipeline !
Preparation stage
Firstly, let’s prepare our environment. Here is the preparation stage:
stage('Preparation') { // for display purposes
// Cleaning workspace
cleanWs()
// Get some code from Git
git branch: 'master',
credentialsId: 'e65361e0-6bc0-4de3-8041-d22b44e6d3b3',
url: 'http://git-server/gitlab/repository.git'
// Get the Maven tool.
env.PATH = "${tool 'Maven3'}/bin:${env.PATH}"
}
This stage will clean the Job workspace on each execution, and will checkout my “master” Git branch using specific credentials (based on credentials id) from a specific url. I will also add to the PATH environment my Maven informations (here, ‘Maven3’ must match the Maven tool name you set previously).
Second stage, compiling all my sources:
stage('Build') {
// Run the maven build
configFileProvider(
[configFile(fileId: 'MavenSettings', variable: 'MAVEN_SETTINGS')]) {
try {
sh 'mvn -s $MAVEN_SETTINGS clean package -Dmaven.test.skip=true'
} catch (err) {
echo "Error while compiling projects"
}
}
}
Here, I specify my configuration file using the “configFileProvider” function, fileId matching my “Managed Files” configuration.
Third stage, my SonarQube execution. Based on Maven, I will just invoke the “sonar:sonar” goal:
stage('SonarQube analysis') {
withSonarQubeEnv('SonarQube') {
// requires SonarQube Scanner for Maven 3.2+
configFileProvider([configFile(fileId: 'MavenSettings', variable: 'MAVEN_SETTINGS')]) {
try {
sh 'mvn -s $MAVEN_SETTINGS org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar'
} catch (err) {
echo "Error while processing sonar on projects"
}
}
}
}
Same as the “Build” stage, I specified my SonarQube installation using the “withSonarQubeEnv” function (“SonarQube” is my actual installation name), and I run my SonarQube analysis by calling sh ‘mvn -s $MAVEN_SETTINGS org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar’.
Next stage, the Quality Gate:
stage("Quality Gate"){
timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
Will wait 1 hour for SonarQube execution.
You can also add a “Result” stage, if you want to do something when all stages were executed well:
stage('Results') {
}
Your global Pipeline must look as the following:
node {
def mvnHome
stage('Preparation') { // for display purposes
// Cleaning workspace
cleanWs()
// Get some code from Git
git branch: 'master',
credentialsId: 'e65361e0-6bc0-4de3-8041-d22b44e6d3b3',
url: 'http://git-server/gitlab/repository.git'
// Get the Maven tool.
env.PATH = "${tool 'Maven3'}/bin:${env.PATH}"
}
stage('Build') {
// Run the maven build
configFileProvider(
[configFile(fileId: 'MavenSettings', variable: 'MAVEN_SETTINGS')]) {
try {
sh 'mvn -s $MAVEN_SETTINGS clean package -Dmaven.test.skip=true'
} catch (err) {
echo "Error while compiling projects"
}
}
}
stage('SonarQube analysis') {
withSonarQubeEnv('SonarQube Production Line') {
// requires SonarQube Scanner for Maven 3.2+
configFileProvider([configFile(fileId: 'MavenSettings', variable: 'MAVEN_SETTINGS')]) {
try {
sh 'mvn -s $MAVEN_SETTINGS org.sonarsource.scanner.maven:sonar-maven-plugin:3.2:sonar'
} catch (err) {
echo "Error while processing sonar on projects"
}
}
}
}
stage("Quality Gate"){
timeout(time: 1, unit: 'HOURS') { // Just in case something goes wrong, pipeline will be killed after a timeout
def qg = waitForQualityGate() // Reuse taskId previously collected by withSonarQubeEnv
if (qg.status != 'OK') {
error "Pipeline aborted due to quality gate failure: ${qg.status}"
}
}
}
stage('Results') {
}
}
And voila ! Your Pipeline is now ready to run ! A first execution will result to the following:
Your Pipeline execution is successfull, your report is available in your SonarQube application, and your compilation packages can be retrieved by accessing your last execution, clicking on “Pipeline Steps”, “Allocate node : Start” and “Workspace”.