12

I have a github repo called multibranch-test with two sub-directories Project1, Project2.

PS C:\Repos\multibranch-test> tree . Folder PATH listing for volume Windows Volume serial number is 2085-6D3D C:\REPOS\MULTIBRANCH-TEST ├───Project1 └───Project2

Each sub-directory has a Jenkinsfile and the code for that project.

In Jenkins I have two multibranch pipeline jobs - one for Project1 and one for Project2. In the configuration for Project1 I don't want a push notification or polling to build Project1 if a commit was pushed in sub-directory for Project2.

So in Project1 I have configured Additional Behaviours:

  • Advanced clone behaviours: Shallow clone is checked
  • Sparse checkout path is set to Project1#
  • Polling ignores commits in certain paths
    • Included Regions: Project1/*
    • Excluded Regions: *
  • Build Configuration: Script Path: Project1/Jenkinsfile

What is happening is if I push a commit to master in sub-directory Project2, both Project1 and Project2 jobs get built. I only want Project2 to build. Can someone point out what I'm doing wrong?

Jenkinsfiles for both Projects are similar and look like:

#!groovy
node  {
    stage ('checkout') {
        checkout scm
    }

    stage ('build') {
        dir ('Project1') {
            bat 'powershell -Command gci'
            bat 'powershell -Command gci env:'
            bat 'powershell -File .\\Project1.ps1'
        }
    }
Mark Allison
  • 6,838
  • 33
  • 102
  • 151
  • 1
    I am having the same issue. Surprising to see this has no solution yet. Very disappointing. – Ven Feb 16 '21 at 10:40
  • I think the syntax for specifying a dir is dirname/.* ... you show Project1/* ... but it seems to me that if you specify include for something that does not exist, then it should never fire. Also including * for exclude should cause it never to fire. But you say it fires for any change. IMO the jenkins feature is completely broken. – steve May 06 '21 at 13:06

3 Answers3

14

This has been a big hassle for us, but we were able to solve it with some workarounds.

We have a master Jenkins job that's triggered by a GitHub commit hook. It figures out what changed since the last commit, and then triggers other service-specific Jenkins jobs.

We have some other conventions we're using (like naming conventions for services, directories, and Jenkins jobs) that aren't specified here, but hopefully this will help someone out.

Here's a breakdown of each component in the solution:

  1. Jenkins jobs and corresponding Jenkinsfiles for each service in the monorepo.

C:\REPOS\MULTIBRANCH-TEST\Project1\Jenkinsfile (your build logic here) C:\REPOS\MULTIBRANCH-TEST\Project2\Jenkinsfile (your build logic here)

  1. A shell script, that gets a list of what changed since the last commit (adapted from this blog post).

C:\REPOS\MULTIBRANCH-TEST\change-sets.sh

    #!/usr/bin/env bash

    changeSets=(`git diff-tree --name-status HEAD`)
    for(( i=0; i<${#changeSets[@]}; i++))
    do
      if [ ${changeSets[$i]} == "M" ]
      then
        echo ${changeSets[$i+1]}
      fi
    done
  1. A Jenkins job that builds on GitHub commit hook

C:\REPOS\MULTIBRANCH-TEST\Jenkinsfile

    #!/usr/bin/env groovy

    pipeline {
        agent any

        stages {

            stage('Define Services to Build') {
                steps {
                    script {

                        def SERVICES_TO_BUILD = sh script:"./change-sets.sh", returnStdout: true
                        SERVICES_TO_BUILD.split("\n").each {
                            echo "Triggering build for ${it}"
                            try {
                                build job: "${it}", propagate: false, wait: false
                            } catch (ex) {
                                echo "Failed to trigger build for ${it}: ${ex.message}"
                            }

                        }
                    }
                }
            }
        }
    }
Garrett Clyde
  • 306
  • 3
  • 9
  • 1
    But this will not quite work for multibranch pipielines, right? Scenario: existing branch (=branch2) with ongoing work on "project2"; I create new branch (=branch1) to change only "project1" code. I commit changes to "project1" in branch1, push branch - multibranch pipeline for project1 triggered, discover new branch - builds. Others commit changes to branch2, multibranch pipeline for project2 triggered - build changes for branch2 and discovers new branch - branch1 - and builds it too. – mrzodiak Feb 12 '18 at 14:20
  • We were able to solve the scenario you're describing with the above solution. The multibranch pipeline runs `~\change-sets.sh` which decides what directories (projects) have changes since the last commit. It then fires off other non-multibranch, project-specific pipeline jobs that build each project. So, in your scenario, the multibranch job would see changes in branch1 and cause project1's Jenkins job to build. The subsequent branch2 commit, would see only changes in project2 since the last commit, so the multibranch pipeline job would only fire-off project2's Jenkins job. – Garrett Clyde Feb 12 '18 at 21:17
  • If your branch has more than 1 commit, then you'll want to change your `change-sets.sh` script to look like this: ```BRANCH=$(git symbolic-ref --short HEAD); changeSets=(`git diff-tree --name-status master $BRANCH`) ``` Otherwise, `git diff-tree` only looks at the difference between HEAD and it's parent commit. – Marco Oct 05 '20 at 12:26
  • 1
    Is this still the best answer as of 2021? – Adam Hughes Apr 14 '21 at 15:51
3

Default Jenkins behavior is that projects get rebuilt if their repo gets a commit, so your commit in repo generates event for both Jenkins projects and triggers both builds. Take a look on Jenkins docs: https://jenkins.io/doc/book/pipeline/

From Jenkins point of view it's hard to tell if change went into project 1 or 2 - what is immediately visible is "new commit to watched repo".

Simples solution would be to split repo into two separate repos one for each project. Since they are supposed to build separately it shouldn't be a problem.

stmi
  • 733
  • 4
  • 13
  • 2
    Thanks but I can't split the repo. – Mark Allison Jul 11 '17 at 17:53
  • I came here as well with the goal of taking existing repos and merging into 1. We have 20 repos for a single app and it's impossible to maintain, so keeping them seperate is a no-go for us as well. Just wanted to provide another usecase/persective – Adam Hughes Apr 14 '21 at 15:49
0

You could create a job for the whole repo, which looks at the changes the commits brought you and then trigger the corresponding Jenkinsfile of project 1 or 2 or both

genmad
  • 1
  • 4