Skip to main content
Version: 10.3.2

Create pipeline

Create pipelines for reusable libraries

CI/CD diagram

Add Maven settings

To add the required Maven settings, do the following:

  1. In the root pom.xml file, add distribution management settings.

    <distributionManagement>
    <repository>
    <id>nexus</id>
    <url>https://YOUR_HOST/repository/REPO_NAME</url>
    </repository>
    <snapshotRepository>
    <id>nexus</id>
    <name>Snapshots</name>
    <url>https://YOUR_HOST/repository/ANOTHER_REPO_NAME</url>
    </snapshotRepository>
    </distributionManagement>
  2. Configure Sonar.

    note

    It's recommended to create a convention for naming projects for Sonar. For example, SOME_PREFIX.${project.groupId}:${project.artifactId} or ${project.groupId}:${project.artifactId}.

  3. Configure Sonar properties in pom.xml.

    <!-- sonar preferences -->
    <sonar.host.url>SONAR_URL</sonar.host.url>
    <sonar.projectKey>${project.groupId}:${project.artifactId}</sonar.projectKey>
    <sonar.language>java</sonar.language>
    <sonar.java.source>8</sonar.java.source>
    <sonar.issuesReport.html.enable>true</sonar.issuesReport.html.enable>
    <sonar.issuesReport.console.enable>true</sonar.issuesReport.console.enable>
    <sonar.sourceEncoding>UTF-8</sonar.sourceEncoding>
    <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
    <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
  4. Add a plugin for code coverage, for example:

    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
    <execution>
    <id>prepare-agent</id>
    <goals>
    <goal>prepare-agent</goal>
    </goals>
    </execution>
    <execution>
    <id>report</id>
    <goals>
    <goal>report</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
  5. Add a plugin for Sonar, for example:

    <plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.8.0.2131</version>
    </plugin>
  6. Check that you’ve configured the Surefire plugin to run your unit tests:

    Click to view JUnit 4.7 sample
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <dependencies>
    <dependency>
    <groupId>org.apache.maven.surefire</groupId>
    <artifactId>surefire-junit47</artifactId>
    <version>3.0.0-M5</version>
    </dependency>
    </dependencies>
    </plugin>
    Click to view JUnit 5.1.1 sample
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <dependencies>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.1.1</version>
    </dependency>
    </dependencies>
    </plugin>

Add build pipeline script to project

To add a script for the build pipeline to your project, do the following:

  1. Create a file for the build pipeline. For example, in your root directory, create the /jenkins directory, and inside it, add the build.groovy file.

  2. In the created build.groovy file, add the following content.

    Remember to replace TODO placeholders and change the script implementation in the environment section.

    Click to view sample Jenkins file
    pipeline {
    agent any

    environment {
    CONFIGURED_JDK = "java8" //name for configured in jenkins JDK
    CONFIGURED_JDK_FOR_SONAR = "java11"
    //name for configured in jenkins JDK, can be different if Sonar failed when use jdk8 for result submission
    CONFIGURED_MAVEN = "maven3" //name for configured in jenkins maven
    CONFIGURED_MAVEN_SETTINGS = "my-maven-settings" //id for the file in jenkins with settings.xml for maven

    EMAIL_TO = "TODO"

    EMAIL_FROM = "TODO"

    GIT_USER_NAME = "TODO"
    GIT_USER_EMAIL = "TODO"

    MAIN_GIT_BRANCH = "master"
    }

    options {
    buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '60'))
    skipDefaultCheckout true
    }

    stages {
    stage('prepare and checkout') {
    steps {
    // these changes for Git config are required only if your Git URL use https://
    sh 'git config --global credential.helper cache'
    sh 'git config --global push.default simple'
    sh 'git config --global user.name "${GIT_USER_NAME}"'
    sh 'git config --global user.email "${GIT_USER_EMAIL}"'
    checkout scm
    }
    }

    stage('Build') {
    steps {
    withMaven(
    jdk: env.CONFIGURED_JDK,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    def pomFile = readMavenPom file: 'pom.xml'
    env.MAVEN_VERSION = pomFile.getVersion()

    String command = env.BRANCH_NAME ==~ /(${env.MAIN_GIT_BRANCH}$)/ ? "deploy" : "install"
    sh "mvn -T 1C clean ${command}"
    }
    }
    }
    }

    stage('Release') {
    when {
    branch env.MAIN_GIT_BRANCH
    expression { return !env.MAVEN_VERSION.contains("-SNAPSHOT") }
    }
    steps {
    script {
    echo env.MAVEN_VERSION
    sh "git tag ${env.MAVEN_VERSION}"
    sh "git push --tags"

    String[] versions = env.MAVEN_VERSION.split('\\.')
    int lastVersion = Integer.valueOf(versions[versions.length - 1])
    versions[versions.length - 1] = ++lastVersion
    String newVersion = versions.join('.') + "-SNAPSHOT"

    withMaven(
    jdk: env.CONFIGURED_JDK,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    sh "mvn versions:set -DnewVersion=${newVersion} versions:update-child-modules versions:commit"
    }
    }
    sh "git add -A && git commit -am 'Change pom version to next SNAPSHOT'"
    sh "git push origin ${env.BRANCH_NAME}"
    }
    }
    }
    stage('Sonar') {
    when {
    branch env.MAIN_GIT_BRANCH
    }
    steps {
    withMaven(
    jdk: env.CONFIGURED_JDK_FOR_SONAR,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    sh "mvn sonar:sonar"
    }
    }
    }
    }
    }

    post {
    always {
    notifyEmail()
    }
    }
    }

    def notifyEmail() {
    mail body: "Build result: ${currentBuild.currentResult}. Job '${env.JOB_NAME}' build #${env.BUILD_NUMBER}. Branch: '${env.BRANCH_NAME}'\n More info at: ${env.BUILD_URL}",
    from: env.EMAIL_FROM,
    subject: "Jenkins build: ${currentBuild.currentResult}. Job '${env.JOB_NAME}'",
    to: env.EMAIL_TO
    }

Create multibranch pipeline

To create a multibranch pipeline, see the instruction.

Create pipeline for use cases

Set up Git project

Create a Git project to store pipelines common to all scenarios (deploy and release) and the related meta-information. It helps you to avoid duplicate jobs for each case.

  1. Create a Git project.

  2. Create the components configuration file. For example, create components.yaml in the root directory. Use this path in the variable USE_CASES_CONFIG_YAML_PATH in use case Jenkins pipelines.

    Add meta information for the use cases to configure for CI/CD.

    See the following example for https:// Git links:

    useCases:
    uc_code1:
    git: "https://GIT_HOST/OWNER/USE_CASE.git"
    mainBranch: "master"
    owner: "owner_email@text.com"
    uc_code2:
    git: "https://GIT_HOST/OWNER/USE_CASE.git"
    mainBranch: "master"
    owner: "another_owner_email@text.com"
  3. Add a standard pipeline script for the deploy scenario. For example, create the /jenkins directory, and inside it, place the deploy.groovy file.

    Remember to replace TODO placeholders and change the script implementation in the environment section.

    Click to view sample Jenkins file
    pipeline {
    agent any

    environment {
    EMAIL_FROM = "TODO"

    GIT_CREDENTIALS_ID = "gitlab-id"

    USE_CASES_CONFIG_GIT_URL = "TODO"
    USE_CASES_CONFIG_MAIN_BRANCH = "main"
    USE_CASES_CONFIG_YAML_PATH = "components.yaml"
    USE_CASES_CONGIF_DEPLOY_SCRIPT_PATH = "scripts/deploy.sh"

    NEXUS_REPO_URL = "TODO"
    ASSET_BUNDLE_MODULE_SUFFIX = "bundle"

    CT_TEST_URL = "TODO"
    CT_USER_USER_ID = "TODO"
    CT_PROD_URL = "TODO"
    CT_PROD_USER_ID = "TODO"
    }

    options {
    buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '60'))
    disableConcurrentBuilds()
    }

    stages {
    stage('Check out use-cases config') {
    steps {
    dir('use-case-config') {
    checkout scm: [
    $class : 'GitSCM',
    branches : [[name: env.USE_CASES_CONFIG_MAIN_BRANCH]],
    extensions : [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: false, timeout: 30],
    [$class: 'LocalBranch', localBranch: "**"]],
    userRemoteConfigs: [[
    credentialsId: env.GIT_CREDENTIALS_ID,
    url : env.USE_CASES_CONFIG_GIT_URL
    ]]
    ]
    }
    }
    }

    stage('Update job parameters') {
    when {
    expression { return params.SKIP_RUN == null || params.SKIP_RUN }
    }
    steps {
    script {
    currentBuild.displayName = "Update job params"
    List<String> componentsList = new ArrayList<String>()

    def componentsYAML = readYaml(file: "use-case-config/$USE_CASES_CONFIG_YAML_PATH")
    componentsYAML.useCases.each { componentId, componentInfo -> componentsList.push(componentId) }

    properties([
    parameters([
    booleanParam(name: 'SKIP_RUN', description: 'Skips all stages. Used to update parameters in case of changes.', defaultValue: false),
    choice(name: 'USE_CASE', choices: componentsList.sort().join('\n')),
    choice(name: 'CT_ENVIRONMENT', choices: ['TEST', 'PROD'], description: 'Choose what CT to use'),
    stringParam(name: 'VERSION', description: 'What use case version to deploy')
    ])
    ])
    }
    }
    }

    stage('prepare and checkout') {
    when {
    expression { return !params.SKIP_RUN }
    }
    steps {

    script {
    def componentsYAML = readYaml(file: "use-case-config/$USE_CASES_CONFIG_YAML_PATH")
    componentsYAML.useCases.each { u, uInfo ->
    if (u == params.USE_CASE) {
    env.EMAIL_TO = uInfo.owner
    env.USE_CASE_GIT_URL = uInfo.git
    env.RELEASE_BRANCH = uInfo.mainBranch
    }
    }
    echo "$env.EMAIL_TO"
    echo "$env.USE_CASE_GIT_URL"
    echo "$env.RELEASE_BRANCH"
    }

    checkout scm: [
    $class : 'GitSCM',
    branches : [[name: env.RELEASE_BRANCH]],
    extensions : [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: false, timeout: 30],
    [$class: 'LocalBranch', localBranch: "**"]],
    userRemoteConfigs: [[
    credentialsId: env.GIT_CREDENTIALS_ID,
    url : env.USE_CASE_GIT_URL
    ]]
    ]
    }
    }

    stage('Deploy to CT') {
    when {
    expression { return !params.SKIP_RUN }
    }
    steps {
    script {
    currentBuild.displayName = "${params.USE_CASE} v.${env.VERSION} to ${CT_ENVIRONMENT}"

    env.CT_ENVIRONMENT_URL = params.CT_ENVIRONMENT == "PROD" ? env.CT_PROD_URL : env.CT_TEST_URL
    env.CT_USER_ID = params.CT_ENVIRONMENT == "PROD" ? env.CT_PROD_USER_ID : env.CT_TEST_USER_ID

    def pomFile = readMavenPom file: 'pom.xml'
    String groupId = pomFile.getGroupId()
    String module = pomFile.getModules().find { it.contains(env.ASSET_BUNDLE_MODULE_SUFFIX) }
    String version = env.VERSION
    String finalUrl = env.NEXUS_REPO_URL + groupId.replaceAll('\\.', '\\/') + "/" + module + "/" + version + "/" + module + "-" + version + ".zip"

    echo "downloading $finalUrl"
    sh " wget -O package.zip $finalUrl"

    sh "chmod 0750 ./use-case-config/$USE_CASES_CONGIF_DEPLOY_SCRIPT_PATH"

    withCredentials([usernamePassword(credentialsId: CT_USER_ID, usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
    sh './use-case-config/$USE_CASES_CONGIF_DEPLOY_SCRIPT_PATH package.zip $CT_ENVIRONMENT_URL $USERNAME $PASSWORD'
    }
    }
    }
    }
    }

    post {
    always {
    script {
    if (!params.SKIP_RUN) {
    notifyEmail()
    }
    }
    }
    }
    }

    def notifyEmail() {
    mail body: "Deploy result for ${params.USE_CASE} v.${env.VERSION} to ${CT_ENVIRONMENT} : ${currentBuild.currentResult}. Job '${env.JOB_NAME}' build #${env.BUILD_NUMBER}. \n More info at: ${env.BUILD_URL}",
    from: env.EMAIL_FROM,
    subject: "Deploy ${params.USE_CASE} v.${env.VERSION} to ${CT_ENVIRONMENT}: ${currentBuild.currentResult}",
    to: env.EMAIL_TO
    }
  4. Add a standard pipeline script for the release scenario. For example, create the /jenkins directory, and inside it, place the release.groovy file.

    In the environment section, replace TODO placeholders and change the script implementation.

    Click to view sample Jenkins file
    pipeline {
    agent any

    environment {
    CONFIGURED_JDK = "java8" //name for configured in jenkins JDK
    //name for configured in jenkins JDK, can be different if Sonar failed when use jdk8 for result submission
    CONFIGURED_MAVEN = "maven3" //name for configured in jenkins maven
    CONFIGURED_MAVEN_SETTINGS = "my-maven-settings" //id for the file in jenkins with settings.xml for maven

    EMAIL_FROM = "TODO"

    GIT_USER_NAME = "TODO"
    GIT_USER_EMAIL = "TODO"
    GIT_CREDENTIALS_ID="gitlab-id"

    USE_CASES_CONFIG_GIT_URL = "TODO"
    USE_CASES_CONFIG_MAIN_BRANCH = "main"
    USE_CASES_CONFIG_YAML_PATH = "components.yaml"
    }

    options {
    buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '60'))
    disableConcurrentBuilds()
    }

    stages {
    stage('Check out use-cases config') {
    steps {
    dir('use-case-config') {
    checkout scm: [
    $class : 'GitSCM',
    branches : [[name: env.USE_CASES_CONFIG_MAIN_BRANCH]],
    extensions : [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: false, timeout: 30],
    [$class: 'LocalBranch', localBranch: "**"]],
    userRemoteConfigs: [[
    credentialsId: env.GIT_CREDENTIALS_ID,
    url : env.USE_CASES_CONFIG_GIT_URL
    ]]
    ]
    }
    }
    }

    stage('Update job parameters') {
    when {
    expression { return params.SKIP_RUN == null || params.SKIP_RUN }
    }
    steps {
    script {
    currentBuild.displayName = "Update job params"
    List<String> componentsList = new ArrayList<String>()

    def componentsYAML = readYaml(file: "use-case-config/$USE_CASES_CONFIG_YAML_PATH")
    componentsYAML.useCases.each { componentId, componentInfo -> componentsList.push(componentId) }

    properties([
    parameters([
    booleanParam(name: 'SKIP_RUN', description: 'Skips all stages. Used to update parameters in case of changes.', defaultValue: false),
    choice(name: 'USE_CASE', choices: componentsList.sort().join('\n'))

    ])
    ])
    }
    }
    }

    stage('prepare and checkout') {
    when {
    expression { return !params.SKIP_RUN }
    }
    steps {

    sh 'git config --global credential.helper cache'
    sh 'git config --global push.default simple'
    sh 'git config --global user.name "${GIT_USER_NAME}"'
    sh 'git config --global user.email "${GIT_USER_EMAIL}"'

    script {
    def componentsYAML = readYaml(file: "use-case-config/$USE_CASES_CONFIG_YAML_PATH")
    componentsYAML.useCases.each { u, uInfo ->
    if (u == params.USE_CASE) {
    env.EMAIL_TO = uInfo.owner
    env.USE_CASE_GIT_URL = uInfo.git
    env.RELEASE_BRANCH = uInfo.mainBranch
    }
    }
    echo "$env.EMAIL_TO"
    echo "$env.USE_CASE_GIT_URL"
    echo "$env.RELEASE_BRANCH"

    sh "rm -r use-case-config"
    }

    checkout scm: [
    $class : 'GitSCM',
    branches : [[name: env.RELEASE_BRANCH]],
    extensions : [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: false, timeout: 30],
    [$class: 'LocalBranch', localBranch: "**"]],
    userRemoteConfigs: [[
    credentialsId: env.GIT_CREDENTIALS_ID,
    url : env.USE_CASE_GIT_URL
    ]]
    ]
    }
    }

    stage('Release') {
    when {
    expression { return !params.SKIP_RUN }
    }
    steps {
    script {
    def pomFile = readMavenPom file: 'pom.xml'
    env.RELEASE_VERSION = pomFile.getVersion()

    currentBuild.displayName = "${params.USE_CASE} v. ${env.RELEASE_VERSION}"

    echo "$env.RELEASE_VERSION"
    sh "git tag ${env.RELEASE_VERSION}"
    sh "git push --tags"

    String[] versions = env.RELEASE_VERSION.split('\\.')
    int lastVersion = Integer.valueOf(versions[versions.length - 1])
    versions[versions.length - 1] = ++lastVersion
    String newVersion = versions.join('.')

    withMaven(
    jdk: env.CONFIGURED_JDK,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    sh "mvn versions:set -DnewVersion=${newVersion} versions:update-child-modules versions:commit"
    }
    }
    sh "git add -A && git commit -am 'Change pom version to the next'"
    sh "git push origin ${env.RELEASE_BRANCH}"
    }
    }
    }
    }

    post {
    always {
    script {
    if (!params.SKIP_RUN) {
    notifyEmail()
    }
    }
    }
    }
    }

    def notifyEmail() {
    mail body: "Release result for ${params.USE_CASE} v${env.RELEASE_VERSION}: ${currentBuild.currentResult}. Job '${env.JOB_NAME}' build #${env.BUILD_NUMBER}. \n More info at: ${env.BUILD_URL}",
    from: env.EMAIL_FROM,
    subject: "Release for ${params.USE_CASE} v ${env.RELEASE_VERSION}: ${currentBuild.currentResult}",
    to: env.EMAIL_TO
    }
  5. Add a shell script for Asset Bundle deployment. For example, create the /scripts directory and place the deploy.sh file inside it.

    In the deploy Jenkins pipeline, use this path in USE_CASES_CONGIF_DEPLOY_SCRIPT_PATH.

    No need to make any changes to this file.

    #!/usr/bin/env bash

    bundleFile=$1
    resolutionStrategy=REPLACE
    statusAttemptLimit=300
    #delay is in seconds
    statusDelay=1

    hostname=$2
    username=$3
    password=$4

    timestamp() {
    date +%Y-%m-%d_%H-%M-%S-%3N
    }

    writeOutputToFile() {
    echo $1 >bundle-import-$2.json
    }

    if [[ -z "$bundleFile" ]]; then
    echo 'Please pass path to bundle as first parameter'
    exit 1
    fi

    if [[ -z "$hostname" ]]; then
    echo 'Please pass hostname as second parameter'
    exit 1
    fi

    if [[ -z "$username" ]]; then
    echo 'Please pass username as third parameter'
    exit 1
    fi

    if [[ -z "$password" ]]; then
    echo 'Please pass password as fourth parameter'
    exit 1
    fi

    if [ ! -f "$bundleFile" ]; then
    echo 'The given bundle file does not seem to exist (possible typo?)'
    exit 1
    fi

    #calculate checksum
    checksum=$(md5sum "$bundleFile" | cut -d ' ' -f1)
    #if absolute path with escaping (\\) is passed, need to cut '\'
    if [[ $checksum == \\* ]]; then
    checksum="${checksum:1}"
    fi

    cookie_file=$(mktemp --suffix=_bunle_import_cookie)

    #login
    loginResponse=$(
    curl -s --request POST \
    --url $hostname/workfusion/api/dologin \
    --header 'Accept: */*' \
    --header 'Cache-Control: no-cache' \
    --header 'Connection: keep-alive' \
    --header 'Content-Type: application/x-www-form-urlencoded' \
    --header 'accept-encoding: gzip, deflate' \
    --data "j_username=$username&j_password=$password" \
    --cookie-jar $cookie_file \
    --insecure
    )

    #extract token
    token=$(echo $loginResponse | grep -Po '"csrfToken":.*?[^\\]",' | grep -Po ':".*"' | sed 's/:"//g' | sed 's/"//g')
    if [[ -z "$token" ]]; then
    echo "Login failed"
    writeOutputToFile "$loginResponse" "$(timestamp)"
    exit 1
    fi
    echo 'Login successful'

    #call bundle import
    importResponse=$(
    curl --request POST \
    --url $hostname/workfusion/api/v1/bundle-import/ \
    --header 'Accept: */*' \
    --header 'Cache-Control: no-cache' \
    --header 'Connection: keep-alive' \
    --header "X-CSRF-TOKEN: $token" \
    --header 'accept-encoding: gzip, deflate' \
    --header 'cache-control: no-cache' \
    --form "bundleFile=@$bundleFile" \
    --form "checksum=$checksum;type=text/plain" \
    --form "conflictResolutions={\"conflictResolutionStrategy\": \"$resolutionStrategy\"};type=application/json" \
    --cookie $cookie_file \
    --insecure
    )

    #if verification failed, script is stopped
    uuid=$(echo $importResponse | grep -Po '"uuid":.*?[^\\]",' | grep -Po ':".*"' | sed 's/:"//g' | sed 's/"//g')
    validationStatus=$(echo $importResponse | grep -Po '"importStatus":.*?[^\\]",' | grep -Po ':".*"' | sed 's/:"//g' | sed 's/"//g')
    if [[ (-z "$uuid") || ("$validationStatus" != 'ACCEPTED') ]]; then
    currTimestamp=$(timestamp)
    echo "Import failed, see bundle-import-$currTimestamp.json for details"
    writeOutputToFile "$importResponse" "$currTimestamp"
    exit 1
    fi
    echo 'Asset Package accepted'

    #checking status in a cycle
    isFinished=0
    attempt=1
    while [ $isFinished -eq 0 ]; do
    #stop script if number of retries exceeded
    if [[ $attempt -ge $statusAttemptLimit ]]; then
    echo "Attempt limit ($statusAttemptLimit) is exceeded"
    writeOutputToFile "Attempt limit ($statusAttemptLimit) is exceeded" "$(timestamp)"
    exit 1
    fi
    sleep $statusDelay

    statusResponse=$(
    curl -s --request GET \
    --url $hostname/workfusion/api/v1/bundle-import/$uuid \
    --header 'Accept: */*' \
    --header 'Cache-Control: no-cache' \
    --header 'Connection: keep-alive' \
    --header "X-CSRF-TOKEN: $token" \
    --header 'accept-encoding: gzip, deflate' \
    --header 'cache-control: no-cache' \
    --cookie $cookie_file \
    --insecure
    )

    status=$(echo $statusResponse | grep -Po -m1 '{"status":.*?[^\\]",' | head -1 | grep -Po ':".*"' | sed 's/:"//g' | sed 's/"//g')
    if [ -z "$status" ]; then
    currTimestamp=$(timestamp)
    echo "Import failed, see bundle-import-$currTimestamp.json for details"
    writeOutputToFile "$status" "$currTimestamp"
    exit 1
    fi

    echo "Import in progress, attempt $attempt"
    ((attempt++))

    if [[ "$status" == 'SUCCEEDED' ]]; then
    isFinished=1
    echo "Import succeeded"
    fi
    if [[ "$status" == 'FAILED' ]]; then
    isFinished=1
    currTimestamp=$(timestamp)
    echo "Import failed, see bundle-import-$currTimestamp.json for details"
    writeOutputToFile "$statusResponse" "$currTimestamp"
    fi
    done
    exit

Add new use case to Git project

To add a new use case to the Git project, open the file components.yaml for editing and add your use case data:

  • Add the use case code to be displayed on Jenkins UI and used in the build pipeline.

  • In the Git section, provide the URL to your Git project.

  • In owner, specify the email of the person who receives notifications about build results.

  • In mainBranch, specify the branch name where the job adds the tag. This is required for the release pipeline and optional for deploy.

Create build pipeline

Add Maven settings

To add the required Maven settings, do the following:

  1. In the root pom.xml file, add the <maven.deploy.skip>true</maven.deploy.skip> property.

  2. Add the <maven.deploy.skip>false</maven.deploy.skip> property to pom.xml for the module with Asset Bundle.

  3. Add the following setting to pom.xml for the module with Asset Bundle. Also, remove distributionManagement in all other pom.xml files.

    <distributionManagement>
    <repository>
    <id>nexus</id>
    <url>https://YOUR_HOST/repository/REPO_NAME</url>
    </repository>
    </distributionManagement>
  4. Configure Sonar.

    note

    It's recommended to create a convention for naming projects for Sonar. For example, SOME_PREFIX.${project.groupId}:${project.artifactId} or ${project.groupId}:${project.artifactId}.

  5. In pom.xml, configure the Sonar properties.

    <!-- sonar preferences -->
    <sonar.host.url>SONAR_URL</sonar.host.url>
    <sonar.projectKey>${project.groupId}:${project.artifactId}</sonar.projectKey>
    <sonar.language>java</sonar.language>
    <sonar.java.source>8</sonar.java.source>
    <sonar.issuesReport.html.enable>true</sonar.issuesReport.html.enable>
    <sonar.issuesReport.console.enable>true</sonar.issuesReport.console.enable>
    <sonar.sourceEncoding>UTF-8</sonar.sourceEncoding>
    <sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
    <sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
  6. Add a plugin for code coverage, for example:

    <plugin>
    <groupId>org.jacoco</groupId>
    <artifactId>jacoco-maven-plugin</artifactId>
    <version>0.8.5</version>
    <executions>
    <execution>
    <id>prepare-agent</id>
    <goals>
    <goal>prepare-agent</goal>
    </goals>
    </execution>
    <execution>
    <id>report</id>
    <goals>
    <goal>report</goal>
    </goals>
    </execution>
    </executions>
    </plugin>
  7. Add a plugin for Sonar, for example:

    <plugin>
    <groupId>org.sonarsource.scanner.maven</groupId>
    <artifactId>sonar-maven-plugin</artifactId>
    <version>3.8.0.2131</version>
    </plugin>
  8. Check that you’ve configured the Surefire plugin to run your unit tests:

    Click to view JUnit 4.7 sample
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <dependencies>
    <dependency>
    <groupId>org.apache.maven.surefire</groupId>
    <artifactId>surefire-junit47</artifactId>
    <version>3.0.0-M5</version>
    </dependency>
    </dependencies>
    </plugin>
    Click to view JUnit 5.1.1 sample
    <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>3.0.0-M5</version>
    <dependencies>
    <dependency>
    <groupId>org.junit.jupiter</groupId>
    <artifactId>junit-jupiter-engine</artifactId>
    <version>5.1.1</version>
    </dependency>
    </dependencies>
    </plugin>
  9. Add the <sonar.skip>true</sonar.skip> property to pom.xml of the Asset Bundle module to skip the Sonar scan for the module.

Add build script to your project

To add the Build Pipeline script to your project, do the following:

  1. Create a file for the build pipeline. For example, in your root directory, create the /jenkins directory and create the build.groovy file inside it.

  2. In the created file, add the following content.

    In the environment section, replace TODO placeholders and change the script implementation.

    pipeline {
    agent any
    environment {
    CONFIGURED_JDK = "java8" //name for configured in jenkins JDK
    CONFIGURED_JDK_FOR_SONAR = "java11" //name for configured in jenkins JDK, can be different if new Sonar failed when use jdk8 for result submission
    CONFIGURED_MAVEN = "maven3" //name for configured in jenkins maven
    CONFIGURED_MAVEN_SETTINGS = "my-maven-settings" //id for the file in jenkins with settings.xml for maven
    EMAIL_FROM = "TODO"
    GIT_USER_NAME = "TODO"
    GIT_USER_EMAIL = "TODO"
    GIT_CREDENTIALS_ID = "TODO"
    USE_CASES_CONFIG_GIT_URL = "TODO"
    USE_CASES_CONFIG_MAIN_BRANCH = "main"
    USE_CASES_CONFIG_YAML_PATH = "components.yaml"
    USE_CASE_CODE = "TODO" // must be equal to code from env.USE_CASES_CONFIG_YAML_PATH file
    JOB_NAME_TO_DEPLOY_TO_CT = "use-cases/deploy-use-case"
    CT_ENVIRONMENT_CODE = "TEST"
    }
    options {
    buildDiscarder(logRotator(numToKeepStr: '10', daysToKeepStr: '60'))
    skipDefaultCheckout true
    }
    stages {
    stage('prepare and checkout') {
    steps {
    // these changes for Git config are required only if your Git URL use https://
    sh 'git config --global credential.helper cache'
    sh 'git config --global push.default simple'
    sh 'git config --global user.name "${GIT_USER_NAME}"'
    sh 'git config --global user.email "${GIT_USER_EMAIL}"'
    checkout scm
    }
    }
    stage('Read use case config') {
    steps {
    dir('use-case-config') {
    checkout scm: [
    $class : 'GitSCM',
    branches : [[name: env.USE_CASES_CONFIG_MAIN_BRANCH]],
    extensions : [[$class: 'CloneOption', depth: 1, noTags: false, reference: '', shallow: false, timeout: 30],
    [$class: 'LocalBranch', localBranch: "**"]],
    userRemoteConfigs: [[
    credentialsId: env.GIT_CREDENTIALS_ID,
    url : env.USE_CASES_CONFIG_GIT_URL
    ]]
    ]
    }
    script {
    def componentsYAML = readYaml(file: "use-case-config/$USE_CASES_CONFIG_YAML_PATH")
    componentsYAML.useCases.each { u, uInfo ->
    if (u == env.USE_CASE_CODE) {
    env.EMAIL_TO = uInfo.owner
    env.MAIN_GIT_BRANCH = uInfo.mainBranch
    }
    }
    echo "$env.EMAIL_TO"
    echo "$env.MAIN_GIT_BRANCH"

    sh "rm -r use-case-config"
    }
    }
    }
    stage('Set custom version') {
    when {
    not { branch env.MAIN_GIT_BRANCH }
    }
    steps {
    withMaven(
    jdk: env.CONFIGURED_JDK,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    def pomFile = readMavenPom file: 'pom.xml'
    env.MAVEN_VERSION = pomFile.getVersion()
    String newVersion = env.MAVEN_VERSION + "-" + env.BRANCH_NAME.substring(0, Math.min(10, env.BRANCH_NAME.length()))

    echo "setting version ${newVersion}"
    sh "mvn versions:set -DnewVersion=${newVersion} versions:update-child-modules versions:commit"
    }
    }
    }
    }
    stage('Build') {
    steps {
    withMaven(
    jdk: env.CONFIGURED_JDK,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    def pomFile = readMavenPom file: 'pom.xml'
    env.CURRENT_VERSION = pomFile.getVersion()

    sh "mvn -T 1C clean deploy"
    }
    }
    }
    }
    stage('Sonar') {
    when {
    branch env.MAIN_GIT_BRANCH
    }
    steps {
    withMaven(
    jdk: env.CONFIGURED_JDK_FOR_SONAR,
    maven: env.CONFIGURED_MAVEN,
    mavenSettingsConfig: env.CONFIGURED_MAVEN_SETTINGS,
    options: [
    openTasksPublisher(disabled: true),
    ]
    ) {
    script {
    sh "mvn sonar:sonar"
    }
    }
    }
    }
    stage('Deploy to CT') {
    when {
    branch env.MAIN_GIT_BRANCH
    }
    steps {
    build job: env.JOB_NAME_TO_DEPLOY_TO_CT,
    parameters: [booleanParam(name: 'SKIP_RUN', value: false),
    string(name: 'USE_CASE', value: env.USE_CASE_CODE),
    string(name: 'CT_ENVIRONMENT', value: env.CT_ENVIRONMENT_CODE),
    string(name: 'VERSION', value: env.CURRENT_VERSION)],
    propagate: true, wait: true
    }
    }
    }
    post {
    always {
    notifyEmail()
    }
    }
    }

    def notifyEmail() {
    mail body: "Build result: ${currentBuild.currentResult}. Job '${env.JOB_NAME}' build #${env.BUILD_NUMBER}. Branch: '${env.BRANCH_NAME}'\n More info at: ${env.BUILD_URL}",
    from: env.EMAIL_FROM,
    subject: "Jenkins build: ${currentBuild.currentResult}. Job '${env.JOB_NAME}'",
    to: env.EMAIL_TO
    }

Create multibranch pipeline

To create a multibranch pipeline, see the instruction.

Create deployment pipeline

The created pipeline is standard for all use cases. Choose the use case code and version you want to deploy. So, you need to create this job only once and then update the parameters.

Create Jenkins pipeline

See this instruction to create a simple pipeline. Use a common Git project as a source for the pipeline script.

Add new use case to the existing pipeline

To add a new deploy use case to the existing pipeline, do the following:

  1. Add the new case to the common Git project.

  2. Run the deploy job with the selected parameter SKIP_RUN. It's needed to update the list of use cases in this job and add new use cases to the list of parameters.

Create release pipeline

The created pipeline is standard for all use cases. Choose the use case code that you want to release. So you need to create this job only once and then update the parameters.

Create Jenkins pipeline

See this instruction to create simple pipeline. Use a common Git project as a source for the pipeline script.

Add new use case to the existing pipeline

To add a release use case to the existing pipeline, do the following:

  1. Add the new case to the common Git project.

  2. Run the release job with the selected SKIP_RUN parameter. It's essential to update the list of use cases in this job and add a new use case to the list of parameters.

Configure simple pipeline

To configure a pipeline in Jenkins, follow the steps below:

  1. Log in to Jenkins.

  2. Go to the folder where you plan to create a job.

  3. On the left menu bar, click New Item.

  4. Define the pipeline name and choose Pipeline. Apply convention for job names. For example, the name must contain maven artifactId.

  5. In the Pipeline section, select the Pipeline script from SCM option.

  6. Configure SCM:

    1. Choose Git.
    2. Insert Repository URL.
    3. Choose Credentials with access to the selected Git project.
    4. Specify the branch to use for the pipeline, for example, main or master.
  7. In the Script Path group, provide the path to the GROOVY file from your project, for example, jenkins/deploy.groovy.

  8. Click Save.

Configure multibranch pipeline

To configure a multibranch pipeline in Jenkins, do the following:

  1. Log in to Jenkins.

  2. Go to the folder where you plan to create a job.

  3. On the left menu bar, click New Item.

  4. Specify the pipeline name and choose Multibranch Pipeline. Apply convention for job names (for example, the name must contain maven artifactId).

  5. In the Branch Sources section, click Add Source and choose a Git server type, for example, GitLab project.

  6. Configure the Branch Source section:

    1. Specify Checkout Credentials.

    2. Specify Owner. The field is required to set up the other project's parameters.

    3. In the menu, choose your project. If there are no options, check that you have correctly configured fields Owner, Checkout Credentials, or Branch Source.

    4. In the Discover Branches Strategy box, set All branches.

    5. Click Add and select Check out to matching local branch.

  7. In the Build Configuration group, provide the path to the GROOVY file from your project, for example, jenkins/build.groovy.

  8. In the Pipeline Maven Configuration group, specify the settings:

    1. Turn on the option Override global maven configuration.
    2. Choose Provided settings.xml.
    3. Choose your custom maven settings, for example, MySettings.
  9. Click Save.

The job starts scanning all branches and launches the build for all branches that contain the build script in the specified location (Step 7).