Uploaded image for project: 'Jenkins'
  1. Jenkins
  2. JENKINS-45246

Project with "Branch Specifier" set to "master" not fired by notifyCommit

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Minor Minor
    • git-plugin
    • None

      I imagine this must be operator error, but I can't understand why one job is not firing on a "notifyCommit" call, but another one is.  I can see it happen (and not happen) while stepping through "GitStatus.JenkinsAbstractProjectListener.onNotifyCommit", but I don't understand why I'm seeing what I'm seeing.

      Background:

      I'm using Jenkins 2.46.2, and version 3.3.0 of the git plugin.

      I have a Bitbucket server, using the "Bitbucket Server Webhook to Jenkins". This fires when I merge a pull request branch to the master branch. What I want to have happen at that point is for my main Jenkins job, which I'll call "main" here, to fire and build the master branch.

      I also have Bitbucket configured with a "Pull Request Notification" that calls "buildWithParameters" to the "main" job, sending a parameter that specifies the branch to build.  This part works perfectly fine.

      The default value for the branch parameter in the "main" job is "master" (I use a custom parameter, not called "branch", to hold the branch name.  The job is a scripted pipeline job. If I do "Build with Parameters" from the Jenkins GUI and leave all the defaults, it will build the master branch.

      In the job definition, I use "Pipeline script from SCM". The "Repository URL" points to the same repo that my code is in.  In the "Branch Specifier", I put "master".

      What we see happen is that when the merge is done, the "main" job is not fired.  As an experiment, we created a copy of "main", called "main-2", where the only difference is that the "Branch Specifier" is blank, not "master".

      With that second job in place, when we merge a PR, then "main-2" is fired, but not "main".  However, if we run "main-2" with "Build with Parameters" from the GUI, it fails to find the Jenkinsfile, because it appears that it starts out attempting to build every single branch it can find, even though I wanted it to only build "master".  The fact that the "Branch Specifier" is blank points to this.

      I tried stepping through "hudson.plugins.git.GitStatus.JenkinsAbstractProjectListener.onNotifyCommit(String, URIish, String, List<ParameterValue>, String...)" and setting conditional breakpoints to see what it did for "main" and "main-2".  I sort of understand why "main-2" is firing in this scenario, because the "Branch Specifier" of "(blank)" will match anything.  However, I didn't understand why the "Branch Specifier" value of "master" in "main" wasn't matching for the merge to the "master" branch.

      What I discovered while stepping through the code is that the branch doesn't match because it's not comparing "master" to "master", but it's comparing "master" to "<pull request branch name>".  The BranchSpec coming from the trigger is reflecting the SOURCE branch, not the TARGET branch.  I imagine this is not surprising to you, as it was probably designed this way, but it leaves me wondering how I can possibly get done what I need to get done.

      I need the "main" job to fire and build the master branch when a merge to master occurs, and I also need to be able to run the "main" job interactively from the Jenkins GUI.  I now have this ugly workaround in place, where I simply have two almost identical copies of the job, one that gets fired for merges, and the other that I can run interactively, and is also fired for PRNs (I haven't tested "main-2" with the PRN, but I suppose it might work).

       

          [JENKINS-45246] Project with "Branch Specifier" set to "master" not fired by notifyCommit

          David Karr added a comment - - edited

          To provide some more detail, I'm debugging "hudson.plugins.git.GitStatus.JenkinsAbstractProjectListener.onNotifyCommit(String, URIish, String, List<ParameterValue>, String...)", which starts like this:

                  public List<ResponseContributor> onNotifyCommit(String origin, URIish uri, String sha1, List<ParameterValue> buildParameters, String... branches) {
                      if (LOGGER.isLoggable(Level.FINE)) {
                              LOGGER.fine("Received notification from " + StringUtils.defaultIfBlank(origin, "?")
                                      + " for uri = " + uri + " ; sha1 = " + sha1 + " ; branches = " + Arrays.toString(branches));
                      }
          

           In BitBucket, I merged my test pull request, and it hit the breakpoint in this method.  The log statement shown above printed the following:

          Received notification from xx.xx.xx.xx ⇒ http://xxxx/jenkins/git/notifyCommit for uri = ssh://git@....:7999/st_usl/oce_usl.git ; sha1 = 8329ef161deeb3a7b4eeb26f81b08ca2a4ce46a7 ; branches = [master]
          

          I then "resumed" the execution, and it hit a conditional breakpoint in the following code:

                          for (final Item project : Jenkins.getInstance().getAllItems()) {
                              SCMTriggerItem scmTriggerItem = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project);
                              if (scmTriggerItem == null) {
                                  continue;
                              }
                              SCMS: for (SCM scm : scmTriggerItem.getSCMs()) {
                                  if (!(scm instanceof GitSCM)) {
                                      continue;
                                  }
                                  GitSCM git = (GitSCM) scm;
          

          My conditional breakpoint is for:

          project.getDisplayName().contains("my-job-name")
          

          Both my original and "cloned" job (the clone has the blank "Branch Specifier") start with that name.

          I stepped past the last line in the "following code" block and then printed "git.getBranches()" in the display view.  It showed this:

          (java.util.Collections$SingletonList<E>) [*/another-debugging-attempt]
          

          The name "another-debugging-attempt" is the name of my pull request branch.  This is the critical clue that tells me that this isn't doing what I thought.

          I continued stepping through the code until I got to this block:

                                          OUT: for (BranchSpec branchSpec : git.getBranches()) {
                                              if (branchSpec.getName().contains("$")) {
                                                  ....
                                              } else {
                                                  for (String branch : branches) {
                                                      if (branchSpec.matches(repository.getName() + "/" + branch)) {
                                                          if (LOGGER.isLoggable(Level.FINE)) {
                                                              LOGGER.fine("Branch Spec " + branchSpec + " matches modified branch " + branch + " for " + project.getFullDisplayName() + ". ");
                                                          }
                                                          branchFound = true;
                                                          break OUT;
                                                      }
                                                  }
                                              }
          

          Note the iteration through "git.getBranches()", which I showed above just has a single entry, the name of the pull request branch. It then gets the BranchSpec for that branch, and then it tries to see if it matches the expression:

          repository.getName() + "/" + branch
          

          In this case, that value comes out to "origin/master".

          Again, the "branchSpec" has a name value of "*/another-debugging-attempt".  That does not match "origin/master", so the build for this job is not spawned.

          I then "resume" the execution and let it hit the conditional breakpoint again, which finds the other project that begins with the same "my-job-name" string, but which has the blank "Branch Specifier".

          For this project, the value of "git.getBranches()" shows this:

          (java.util.Collections$SingletonList<E>) [*/master]
          

          Notice the difference between this value and the similar one for the first project.  This one has "/master", but the other has "/another-debugging-attempt".  The ONLY difference between these two projects is the name (the second one has "-2" added to the name, and the "Branch Specifier" in "-2" is blank, whereas the value in the original job is "master".

          If I then let the debugger resume, the "matches" call above succeeds, because "/master" matches "/master", and the job is spawned.

          What I need and want is for this to match the first job, which had a Branch Specifier value of "master".

           

           

          David Karr added a comment - - edited To provide some more detail, I'm debugging "hudson.plugins.git.GitStatus.JenkinsAbstractProjectListener.onNotifyCommit(String, URIish, String, List<ParameterValue>, String...)", which starts like this:         public List<ResponseContributor> onNotifyCommit( String origin, URIish uri, String sha1, List<ParameterValue> buildParameters, String ... branches) {             if (LOGGER.isLoggable(Level.FINE)) {                     LOGGER.fine( "Received notification from " + StringUtils.defaultIfBlank(origin, "?" )                             + " for uri = " + uri + " ; sha1 = " + sha1 + " ; branches = " + Arrays.toString(branches));             }  In BitBucket, I merged my test pull request, and it hit the breakpoint in this method.  The log statement shown above printed the following: Received notification from xx.xx.xx.xx ⇒ http: //xxxx/jenkins/git/notifyCommit for uri = ssh://git@....:7999/st_usl/oce_usl.git ; sha1 = 8329ef161deeb3a7b4eeb26f81b08ca2a4ce46a7 ; branches = [master] I then "resumed" the execution, and it hit a conditional breakpoint in the following code:                 for ( final Item project : Jenkins.getInstance().getAllItems()) {                     SCMTriggerItem scmTriggerItem = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project);                     if (scmTriggerItem == null ) {                         continue ;                     }                     SCMS: for (SCM scm : scmTriggerItem.getSCMs()) {                         if (!(scm instanceof GitSCM)) {                             continue ;                         }                         GitSCM git = (GitSCM) scm; My conditional breakpoint is for: project.getDisplayName().contains( "my-job-name" ) Both my original and "cloned" job (the clone has the blank "Branch Specifier") start with that name. I stepped past the last line in the "following code" block and then printed "git.getBranches()" in the display view.  It showed this: (java.util.Collections$SingletonList<E>) [*/another-debugging-attempt] The name "another-debugging-attempt" is the name of my pull request branch.  This is the critical clue that tells me that this isn't doing what I thought. I continued stepping through the code until I got to this block:                                 OUT: for (BranchSpec branchSpec : git.getBranches()) {                                     if (branchSpec.getName().contains( "$" )) { ....                                     } else {                                         for ( String branch : branches) {                                             if (branchSpec.matches(repository.getName() + "/" + branch)) {                                                 if (LOGGER.isLoggable(Level.FINE)) {                                                     LOGGER.fine( "Branch Spec " + branchSpec + " matches modified branch " + branch + " for " + project.getFullDisplayName() + ". " );                                                 }                                                 branchFound = true ;                                                 break OUT;                                             }                                         }                                     } Note the iteration through "git.getBranches()", which I showed above just has a single entry, the name of the pull request branch. It then gets the BranchSpec for that branch, and then it tries to see if it matches the expression: repository.getName() + "/" + branch In this case, that value comes out to "origin/master". Again, the "branchSpec" has a name value of "*/another-debugging-attempt".  That does not match "origin/master", so the build for this job is not spawned. I then "resume" the execution and let it hit the conditional breakpoint again, which finds the other project that begins with the same "my-job-name" string, but which has the blank "Branch Specifier". For this project, the value of "git.getBranches()" shows this: (java.util.Collections$SingletonList<E>) [*/master] Notice the difference between this value and the similar one for the first project.  This one has " /master", but the other has " /another-debugging-attempt".  The ONLY difference between these two projects is the name (the second one has "-2" added to the name, and the "Branch Specifier" in "-2" is blank, whereas the value in the original job is "master". If I then let the debugger resume, the "matches" call above succeeds, because " /master" matches " /master", and the job is spawned. What I need and want is for this to match the first job, which had a Branch Specifier value of "master".    

            Unassigned Unassigned
            dkarr David Karr
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

              Created:
              Updated: