40

I'm trying to use Jenkins file for all our builds in Jenkins, and I have following problem. We basically have 3 kind of builds:

  • pull-request build - it will be merged to master after code review, and if build works
  • manual pull-request build - a build that does the same as above, but can be triggered manually by the user (e.g. in case we have some unstable test)
  • an initial continuous deliver pipeline - this will build the code, deploy to repository, install artifacts from repository on the target server and start the application there

How should I contain all of the above builds into a single Jenkinsfile. Right now the only idea I have is to make a giant if that will check which branch it is and will do the steps.

So I have two questions:

1. Is that appropriate way to do it in Jenkinsfile?

  1. How to get the name of currently executing branch in multi-branch job type?

For reference, here's my current Jenkinsfile:

def servers = ['server1', 'server2']

def version = "1.0.0-${env.BUILD_ID}"

stage 'Build, UT, IT'
node {
    checkout scm
    env.PATH = "${tool 'Maven'}/bin:${env.PATH}"
    withEnv(["PATH+MAVEN=${tool 'Maven'}/bin"]) {
        sh "mvn -e org.codehaus.mojo:versions-maven-plugin:2.1:set -DnewVersion=$version -DgenerateBackupPoms=false"
        sh 'mvn -e clean deploy'
        sh 'mvn -e scm:tag'
    }
}


def nodes = [:]
for (int i = 0; i < servers.size(); i++) {
    def server = servers.get(i)
    nodes["$server"] = {
        stage "Deploy to INT ($server)"
        node {
            sshagent(['SOME-ID']) {
                sh """
                ssh ${server}.example.com <<END
                hostname
                /apps/stop.sh
                yum  -y update-to my-app.noarch
                /apps/start.sh
                END""".stripIndent()
            }
        }
    }
}

parallel nodes

EDIT: removed opinion based question

Krzysztof Krasoń
  • 26,515
  • 16
  • 89
  • 115
  • Possible duplicate of [Jenkins Multibranch pipeline: What is the branch name variable?](http://stackoverflow.com/questions/32789619/jenkins-multibranch-pipeline-what-is-the-branch-name-variable) – Krzysztof Krasoń May 07 '16 at 06:17

7 Answers7

29

You can add If statement for multiple stages if you want to skip multiple stages according to the branch as in:

if(env.BRANCH_NAME == 'master'){
     stage("Upload"){
        // Artifact repository upload steps here
        }
     stage("Deploy"){
        // Deploy steps here
       }
     }

or, you can add it to individual stage as in:

stage("Deploy"){
  if(env.BRANCH_NAME == 'master'){
   // Deploy steps here
  }
}
Sukhman Sandhu
  • 544
  • 6
  • 5
  • I wish that Scripted and Declarative Pipelines could both use the Conditional Options like `when{}` and such. I still find it kind of hard ATM to read their documentation. – FilBot3 Jun 14 '18 at 13:10
  • 4
    For me the use of `stage("Deploy") { if(...) {` results in `Not a valid stage section definition: "if(env.BRANCH_NAME == 'master') {` – CodingWithSpike Mar 05 '19 at 16:53
  • Yeah, this doesn't work for me either. Not enough examples of pipeline to figure this stuff out. – stricq May 14 '19 at 04:11
  • 2
    @CodingWithSpike sounds like you are probably using a declarative pipeline instead of scripted. I assume you have figured this out by now, but try using `stage(...){ when { expression { BRANCH_NAME == 'master' } } steps { ... } }`. – Spencer D Sep 16 '19 at 18:44
9

Using this post, this worked for me:

        stage('...') {
            when {
                expression { env.BRANCH_NAME == 'master' }
            }
            steps {
                ...
            }
        }

Mickaël
  • 3,763
  • 5
  • 26
  • 32
7

1) I don't know if it is appropriate, but if it resolves your problem, I think is appropriate enough.

2) In order to know the name of the branch you can use BRANCH_NAME variable, its name is taken from the branch name.

${env.BRANCH_NAME}

Here is the answer: Jenkins Multibranch pipeline: What is the branch name variable?

Community
  • 1
  • 1
3

We followed the model used by fabric8 for builds, tweaking it as we needed, where the Jenkinsfile is used to define the branch and deployment handling logic, and a release.groovy file for build logic.

Here's what our Jenkinsfile looks like for a pipeline that continuously deploys into DEV from master branch:

#!groovy
import com.terradatum.jenkins.workflow.*

node {

  wrap([$class: 'TimestamperBuildWrapper']) {
    checkout scm

    echo "branch: ${env.BRANCH_NAME}"
    def pipeline = load "${pwd()}/release.groovy"

    if (env.DEPLOY_ENV != null) {
      if (env.DEPLOY_ENV.trim() == 'STAGE') {
        setDisplayName(pipeline.staging() as Version)
      } else if (env.DEPLOY_ENV.trim() == 'PROD') {
        setDisplayName(pipeline.production() as Version)
      }
    } else if (env.BRANCH_NAME == 'master') {
      try {
        setDisplayName(pipeline.development() as Version)
      } catch (Exception e) {
        hipchatSend color: 'RED', failOnError: true, message: "<p>BUILD FAILED: </p><p>Check console output at <a href='${env.BUILD_URL}'>${env.JOB_NAME} [${env.BUILD_NUMBER}]</a></p><p><pre>${e.message}</pre></p>", notify: true, room: 'Aergo', v2enabled: false
        throw e; // rethrow so the build is considered failed
      }
    } else {
      setDisplayName(pipeline.other() as Version)
    }
  }
}

def setDisplayName(Version version) {
  if (version) {
    currentBuild.displayName = version.toString()
  }
}

Note: you can find the code for our global pipeline library here.

mkobit
  • 43,979
  • 12
  • 156
  • 150
rbellamy
  • 5,683
  • 6
  • 38
  • 48
  • I'll say, this was really helpful to me, but why use the release.groovy file instead of including the methods from release.groovy under vars/... and having them globally accessible without having to load release.groovy? I only ask because I'm about to go down a similar road where my 'root' Jenkinsfile contains only config for the pipeline, wrapped by a method from vars/... that defines the entire build, loading vars/... methods and global libs as needed. The goal here is to let end users write their own Jenkinsfiles by defining the necessary config, and calling their build type. – user797963 Jul 18 '17 at 04:09
  • Here's a link that better explains what I'm going for: https://jenkins.io/doc/book/pipeline/shared-libraries/#defining-a-more-structured-dsl. The Jenkinsfile would contain all necessary config in buildPlugin{}, and the buildPlugin.groovy file would contain multiple stages and steps to run the entire build (not just the trivial example in the link). – user797963 Jul 18 '17 at 04:12
  • You could certainly push the code from `release.groovy` into your global pipeline library, with the caveat that you'd want to avoid a situation where you have code that is specific to a single build in a library that is designed to service ALL builds. – rbellamy Jul 21 '17 at 15:44
0

for questions 2 you may be able to do

sh 'git branch > GIT_BRANCH' def gitBranch = readFile 'GIT_BRANCH'

since you're checking out from git

Tash Pemhiwa
  • 7,590
  • 4
  • 45
  • 49
0

Don't know if this what you want.. I prefer because it's look more structured.

Jenkinsfile

node {
    def rootDir = pwd()

    def branchName = ${env.BRANCH_NAME}

    // Workaround for pipeline (not multibranches pipeline)
    def branchName = getCurrentBranch()

    echo 'BRANCH.. ' + branchName
    load "${rootDir}@script/Jenkinsfile.${branchName}.Groovy"
}

def getCurrentBranch () {
    return sh (
        script: 'git rev-parse --abbrev-ref HEAD',
        returnStdout: true
    ).trim()
}

Jenkinsfile.mybranch.Groovy

echo 'mybranch'
// Pipeline code here
kitta
  • 1,723
  • 3
  • 23
  • 33
0

In my scenarium, I have needed run a stage Deploy Artifactory only if the branch was master(webhook Gitlab), otherwise I couldn't perform the deploy.

Below the code of my jenkinsfile:

stages {

    stage('Download'){

        when{
            environment name: 'gitlabSourceBranch', value: 'master'

        }

        steps{
            echo "### Deploy Artifactory ###"

            }       
    }

}

Renato Coutinho
  • 1,151
  • 10
  • 7