Search code examples
jenkinssvnjenkins-pipeline

Make Jenkins with declarative pipeline work with SVN trunk as well as branches


I want to set up a Jenkins job with a declarative pipeline from a Jenkinsfile, using Subversion as the SCM, which should

  • do a scheduled SCM poll to detect changes in the trunk
  • be manually startable to build the trunk or any branch of choice

So I've set up the Jenkins job with the List Subversion tags (and more) parameter that collects existing SVN branches from an SVN url and lets the user select one. The selected value is stored in a variable, for instance $svnBranch, and I defined 'trunk' as its default value.

Then this variable is used to build the resulting SCM url, for example

svn+ssh://svn.mydomain.org/Reponame/projectname/$svnBranch/componentname

Now here's the problem:

This setup does work as long as the job ist started manually. But if it is started by the cron schedule, Jenkins keeps detecting SCM changes each and every time and always starts a new build. The SCN polling log shows

Workspace doesn't contain Reponame/projectname/$svnBranch/componentname. Need a new build.

So the problem is obviously caused by Jenkins not resolving the variable when polling the SCM for changes. To verify this assumption, I changed the job to use a fixed string variable, and the same happened again.

I was wondering if it possible to solve the problem by moving the polling and checkout logic to the Jenkinsfile. The idea would be to always poll the trunk, but checkout and build based on $svnBranch, but I'm unsure how to do this. Is it possible to define different SCM urls for polling and checkouts? According to my research, all checkout urls in a Jenkinsfile would automatically be used for polling, so how to accomplish that?

Any other working solution would be welcome, too.

Note that there's a similar question Jenkins Pipeline - SVN polling that stumbled upon the same issue, but no solution that would fit my scenario.

Also note that there is an issue reported at JENKINS-10628: SCM build trigger not working correctly with variables in SVN URL that describes my problem, but it's said to be resolved with a new version of the Subversion plugin since 2015. I've updated to the latest version 2.16.0, yet it did not resolve the problem.


Solution

  • This is the solution I've found (I'm open to better ones - for example, I'm not happy with configuring the same SCM url in two different places):

    First, in the Jenkins job, under Pipeline from SCM, I configured the trunk url that contains no variable. This url will be used to poll for changes in trunk.

    svn+ssh://svn.mydomain.org/Reponame/projectname/trunk/componentname
    

    Second, I created thid function to replace the "trunk" part by the branch name:

    def call(Map param = [ : ]) {
        if ( param.branch == null ) {
            return param.trunkUrl                                     
        }
        url = param.trunkUrl.replaceAll('/trunk(?=/|$)', '/'+param.branch)  // replaces /trunk if followed by / or if at end of url
        return url
    }
    

    I have moved this function to a shared library, so I can use it from any pipeline.

    This is then used to derive the checkout url with the svnBranch being selected in the user interface:

        environment {
            // Set actual checkout url, because SVN_URL_1 will always contain the fixed url of the trunk used for polling
            checkoutUrl = composeSvnUrl(trunkUrl: env.SVN_URL_1, branch: env.svnBranch)
        }
    

    Finally, I added a checkout stage (as the first stage) to my Jenkinsfile:

            stage('Checkout') {
                steps {
                    /* Checkout for actual build (may be different if started manually) */
                    checkout(
                        poll: false, changelog: false, // = do not use this for polling 
                        scm: [
                            $class: 'SubversionSCM', 
                            quietOperation: false,
                            additionalCredentials: [], 
                            excludedCommitMessages: '', 
                            excludedRegions: '', 
                            excludedRevprop: '', 
                            excludedUsers: '', 
                            filterChangelog: false, 
                            ignoreDirPropChanges: false, 
                            includedRegions: '', 
                            locations: [[
                                credentialsId: 'id.from.jenkins.credentials', 
                                depthOption: 'infinity',
                                ignoreExternalsOption: true, 
                                local: '.', 
                                remote: checkoutUrl, 
                            workspaceUpdater: [$class: 'CheckoutUpdater']               
                        ]
                         
                    )
                    
                }
            }
    

    The important parts are:

    poll: false, changelog: false means that Jenkins should not use these checkout information for polling. As mentioned on Pipeline: SCM Step - checkout: Check out from version control at the very bottom of the page:

    If 'Include in polling' is disabled and 'Include in changelog' is disabled, then when polling occurs, changes that are detected from this repository will be ignored.

    workspaceUpdater: [$class: 'CheckoutUpdater'] is probably important, too, as this will wipe the workspace before checking out again.