Skip to content

Jenkins Multibranch Pipeline Configuration

Overview

Purpose

Multibranch Pipelines allow Jenkins to automatically discover and build branches in a repository. This ensures that every branch with a Jenkinsfile is automatically built and tested.

Scope

Configuration of Multibranch Pipeline jobs in Jenkins using GitLab Branch Sources.

Audience

CORE, DEV

Prerequisites

  • Jenkins is installed and running (see Jenkins Installation).
  • Access to the GitLab instance: https://gitlab.quintessence.net.
  • A repository containing a Jenkinsfile.

Step-by-Step Configuration

Create a New Item

  1. Log in to the Jenkins GUI.
  2. Click on New Item in the left sidebar.
  3. Enter a name for the job (e.g., quintessence-pipeline-name).
  4. Select Multibranch Pipeline and click OK.

Branch Sources (GitLab)

  1. In the job configuration, scroll down to the Branch Sources section.
  2. Click Add source and select GitLab Project.
  3. Checkout Credentials: Select the appropriate credentials for GitLab access.
  4. Owner: Enter the group id.
  5. Projects: Select the specific project from the dropdown.
  6. Behaviors:
    • Discover branches: Usually set to "All branches".
    • Discover merge requests from origin: Optional, depending on workflow.

Build Configuration

  1. Scroll to the Build Configuration section.
  2. Mode: Select "by Jenkinsfile".
  3. Script Path: Ensure it points to the Jenkinsfile in your repository (default is Jenkinsfile).

Webhook Configuration

Since we use GitLab Branch Sources, triggers are handled via webhooks. Ensure that the GitLab project has a webhook configured pointing to your Jenkins instance to trigger builds on push or merge request events.

Example Jenkinsfile

Most projects use a declarative pipeline style. Below is the standard template used for our Maven-based projects:

pipeline {
    agent any

    options {
        disableConcurrentBuilds()
    }

    tools {
        maven "Maven 3.9"
        jdk "Java 8"
    }
    parameters {
        booleanParam(name: 'RELEASE', defaultValue: false, description: 'Run release build (true = direct mvn, false = withMaven)')
    }

    environment {
        MAVEN_OPTS = "-Xms512m -Xmx1024m -Dmaven.test.failure.ignore=false -XX:+AlwaysPreTouch -XX:+UseG1GC -XX:+ExplicitGCInvokesConcurrent -XX:+ParallelRefProcEnabled -XX:+UseStringDeduplication -XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=20 -XX:+UnlockDiagnosticVMOptions -XX:G1SummarizeRSetStatsPeriod=1"
        RELEASE = "${params.RELEASE}"
    }

    // Build trigger configuration
    triggers {
        snapshotDependencies()
    }

    stages {
        stage("Clean Workspace") {
            steps {
                cleanWs()
            }
        }
        stage("Checkout") {
            steps {
                checkout scm
            }
        }
        stage("Compile") {
            steps {
                script {
                    if (params.RELEASE) {
                        sh "mvn -U clean compile"
                    } else {
                        withMaven(maven: "Maven 3.9") {
                            sh "mvn -U clean compile"
                        }
                    }
                }
            }
        }
        stage("Test") {
            steps {
                script {
                    if (params.RELEASE) {
                        sh "mvn test"
                    } else {
                        withMaven(maven: "Maven 3.9") {
                            sh "mvn test"
                        }
                    }
                }
            }
        }
        stage('SonarQube Analysis') {
            steps {
                script {
                    def java17Home = tool name: 'Java 17', type: 'jdk'

                    withEnv(["JAVA_HOME=${java17Home}", "PATH=${java17Home}/bin:${env.PATH}"]) {
                        withSonarQubeEnv('sonarqube') {
                            if (params.RELEASE) {
                                sh "mvn sonar:sonar -Dsonar.login=${SONAR_AUTH_TOKEN}"
                            } else {
                                withMaven(maven: "Maven 3.9") {
                                    sh "mvn sonar:sonar -Dsonar.login=${SONAR_AUTH_TOKEN}"
                                }
                            }
                        }
                    }
                    sh 'rm -rf reports'
                }
            }
        }

        stage('Quality Gate') {
            steps {
                timeout(time: 1, unit: 'MINUTES') {
                    waitForQualityGate abortPipeline: true
                }
            }
        }
        stage("Deploy") {
            steps {
                script {
                    if (params.RELEASE) {
                        sh "mvn deploy -DskipTests -P nexus3"
                    } else {
                        withMaven(maven: "Maven 3.9") {
                            sh "mvn deploy -DskipTests -P nexus3"
                        }
                    }
                }
            }
        }
    }

    post {
        unstable {
            emailext(
                    subject: "Build Unstable: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                    body: """See <${env.BUILD_URL}display/redirect?page=changes>

Changes:
${getChanges()}

Stacktrace:
${currentBuild.rawBuild.getLog(100).join('\n')}
""",
                    recipientProviders: [
                            [$class: 'CulpritsRecipientProvider'],
                            [$class: 'RequesterRecipientProvider']
                    ]
            )
        }
        failure {
            emailext(
                    subject: "Build FAILED: ${env.JOB_NAME} #${env.BUILD_NUMBER}",
                    body: """See <${env.BUILD_URL}display/redirect?page=changes>

Changes:
${getChanges()}

Stacktrace:
${currentBuild.rawBuild.getLog(100).join('\n')}
""",
                    recipientProviders: [
                            [$class: 'CulpritsRecipientProvider'],
                            [$class: 'RequesterRecipientProvider']
                    ]
            )
        }
    }
}

def getChanges() {
    def changes = ""
    def changeSets = currentBuild.changeSets
    if (changeSets) {
        changeSets.each { changeSet ->
            changeSet.items.each { item ->
                changes += "[${item.author}] ${item.msg}\n"
            }
        }
    } else {
        changes = "No changes found."
    }
    return changes
}

Validation & Testing

  • Check the Scan Multibranch Pipeline Log to ensure the repository is being scanned correctly.
  • Verify that each branch is listed and has its own build history.

Troubleshooting

  • No branches found: Check credentials and repository URL.
  • Jenkinsfile not found: Ensure the Jenkinsfile is in the root of the branch or the Script Path is correct.
  • Webhook issues: Verify the GitLab webhook configuration pointing to https://jenkins.quintessence.de/project-name/.

Changelog

Date Author Message
2026-02-25 aresnikowa Merge remote-tracking branch 'origin/master'