Continuous Integration for Pull Requests with Jenkins and Bitbucket Server (Stash)

Continuous integration and pull requests are two important concepts for almost any development team. Besides many other benefits, ensuring code stability and quality, ease of collaboration with other developers and fast release cycles are some of the key aspects. This post describes a solution to combine and Jenkins for automated builds of pull requests and reporting back the status to Stash. Thereby we build not the last commit of the pull request but merge the pull request automatically into the target branch. This gives a much better view if the result of merging the pull request will compile and if other coding guidelines are followed, e.g. static code analysis checks.

[Update 1: 2015-08-02]

[Update 2: 2015-08-13]

  • Starting with version 1.18 of Pull Request Notifier for Stash the url for the pull request is now provided as a parameter (PULL_REQUEST_URL). The parameter can now be past directly and we don’t need to build the parameter manually.
  • In order to set the build description also when there is a problem with merging the source and target branch we now set the build parameter already before we clone the repository. For this the Pre SCM Buildstep Plugin is used.


Here the workflow we try to achieve:

  1. Users creates a topic branch (e.g. feature, bugfix).
  2. After completing his development work and pushing his changes to Stash the user creates a pull request.
  3. In order to approve a pull request we require at least one successful Jenkins build. Thereby we would like to get not only the build result of the code checked in for the pull request but get the build status after the code has been merged with the target branch.
  4. When a pull request is created/updated Jenkins shall be triggered automatically for real continuous integration.
  5. The source of the pull request shall be automatically merged with the target branch.
  6. Set the build description with the pull request ID and a link back to the Stash pull request.
  7. The build result shall be reported back to Stash.
  8. Only if the build was successful and the number of successful builds configured in Stash is reached the pull request shall be approved and merged.

Design Criteria

The following design criteria and features are addressed by this solution:

  • Re-use existing plugins as much as possible
  • Keep configuration to a minimum
  • Clear responsibility split between Stash (PR management and notification) and Jenkins (Job management and configuration)
  • Automatic merge into the correct target branch
  • Support for different Jenkins job configurations (target branches) in parallel
  • Different jobs for regular continuous integration and pull request pre-merge builds.


The following ingredients are necessary for above mentioned workflow (versions available at the time or writing):

Step-by-Step Configuration

Here are the steps necessary to achieve the above mentioned workflow.
  1. Install and configure Pull Request Notifier for Stash
  2. Configure Pull Request Configuration in Stash
  3. Install and configure Jenkins Git Plugin
  4. Install and configure Jenkins Stash Notifier Plugin
  5. Create/Update Jenkins job

Install and configure Pull Request Notifier for Stash

  1. Install the Pull Request Notifier for Stash add-on via the Universal Plugin Manager or manually by downloading from the Atlassian Marketplace.
  2. Choose Administration > Manage Add-ons > Pull Request Notifier > Configure to configure the plugin.
  3. Select the following triggers. They will trigger a new build when a pull request is opened, reopened, or either the target or source branch content has changed.
    • OPENED
  4. Enter Jenkins and proxy credentials as required
  5. Choose GET as HTTP method
  6. Enter http://localhost:8080/jenkins/git/notifyCommit?url=${PULL_REQUEST_TO_SSH_CLONE_URL}&branches=pr/${PULL_REQUEST_TO_BRANCH}&sha1=${PULL_REQUEST_FROM_HASH}&PULL_REQUEST_URL=${PULL_REQUEST_URL}&PULL_REQUEST_ID=${PULL_REQUEST_ID} as the URL to invoke. The trick here is the pr prefix in the branch name which is then used also in the branch name in the Jenkins job configuration. By this we can differentiate between regular commits to the target branch and the pull requests and can have two jobs in parallel. This notification will trigger automatically the correct builds using the Push Notification Support of the Jenkins Git Plugin based on the target branch and automatically merges the pull request with the target branch. Please adjust the Jenkins URL (http://localhost:8080/jenkins) to match with your environment.  jenkins_stash_pr_stash_trigger_configuration
  7. Save the trigger configuration with Save.

Configure Pull Request Configuration in Stash

  1. Choose in your Stash repository Settings > Pull requests and select the Requires a minimum of successful builds and enter the number of builds. Example: If you have  two builds for the pull request, e.g. one continuous integration and one static code analysis build and both should be successful as a prerequisite to merge the build request, enter 2 as the number of builds. jenkins_stash_pr_stash_pull_request_configuration

Install and configure Jenkins Git Plugin

  1. Install the Jenkins Git Plugin via Jenkins > Manage Jenkins > Manage Plugins
  2. Perform all standard configuration so that you can access Stash via SSH and can pull repositories from Stash.

Install and configure Jenkins Pre SCM Buildstep Plugin

  1. Install the Pre SCM Buildstep Plugin via Jenkins > Manage Jenkins > Manage Plugins
  2. Perform all standard configuration so that you can access Stash via SSH and can pull repositories from Stash.

Install and configure Jenkins Stash Notifier Plugin

  1. Install the Jenkins Stash Notifier Plugin via Jenkins > Manage Jenkins > Manage Plugin
  2. Choose Jenkins > Configure System > Stash Notifier and enter the data for your Stash installation. Jenkins Stash Notifier Configuration

Install and configure Jenkins Groovy Plugin

  1. Install the Jenkins Groovy Plugin via Jenkins > Manage Jenkins > Manage Plugins

Create Jenkins job to Build the Pull Request

  1. Add the Git configuration for your Jenkins job as follows
    • Select Git in the Source Code Management section
    • Update the Repository URL to point to your Stash installation.
    • Update Branch Specifier (blank for ‘any’) and Branch to merge to to the branch name which should be build and act as the target branch. If the target branch is master, then the values should be entered in the format as shown in the following picture. Please note the pr prefix in the Branch Specifier (blank for ‘any’) field, which we have used in the URL in the Pull Request Notification Plugin configuration in a previous step.jenkins_stash_pr_jenkins_job_git_configuration
  2. Set the build description with the pull request ID and a link back to the Stash pull request.
    • Select in the Build Environment section the Run buildstep before SCM runs and add an Execute system Groovy script step using the following script:
      def currentBuild = Thread.currentThread().executable
      def PULL_REQUEST_URL = build.buildVariableResolver.resolve('PULL_REQUEST_URL')
      def PULL_REQUEST_ID = build.buildVariableResolver.resolve('PULL_REQUEST_ID')
      def description = "<a href='$PULL_REQUEST_URL'>PR #$PULL_REQUEST_ID</a>"
    • This will set add pull request ID in format PR #ID, e.g. PR #1 with a link back to the Stash pull request. Here we use the new capabilities of the Jenkins Git Plugin to pass additional parameters in the commit notification, here PULL_REQUEST_ID and PULL_REQUEST_URL.
  3. Create/update the Stash notification configuration of your Jenkins job(s) as follows
    • Add in the Post-build Actions section the Notify Stash Instance post build action and configure. No further configuration is necessary as we configured the Stash parameters as system wide settings already in step Install and configure Jenkins Stash Notifier Plugin.jenkins_stash_pr_jenkins_job_stash_notifier_configuration
  4. Save job configuration with Save
  5. Optionally, if you have additional jobs which build the same repository/branch combination and which should not be build on pull request notifications, update the Git configuration in these jobs and add the Don’t trigger a build on commit notifications as an additional behaviour as shown on the following example. jenkins_stash_pr_jenkins_job_ignore_notification_configuration

Final Result

Congratulations, all configuration is done!

When everything is setup correctly and you have created your first pull request in Stash you should see a new build triggered in Jenkins along with the ID of the pull request in the build description.


Furthermore after the build has finished you should see the build status information updated on the pull request in Stash.


When you click on the 1 build link on the top right then you see more details on the build including a link to the respective Jenkins build.





63 thoughts on “Continuous Integration for Pull Requests with Jenkins and Bitbucket Server (Stash)

  1. What about using this with remote forked repostiories? I did the setup according to this website, but I was unable to get PR from forks to work. The jobs fails with unknown commitID (the master repo om the build machine doesn’t know the commit and I’d need to be trackign developers’ reposiroties, whcih is no go). Any ideas how to get this working with remote forks?

    • The solution currently works only in a setup where the source and target branch are in the same repository. Main reason for this is that the Jenkins Git plugin is used to trigger the correct build and that only one repository can be past as part of the notification. In order to trigger for changes both in the source and the target branch this would then require at least two jobs as the repositories for source and target branch are different. When source and target branch should be monitored also the “other” (the one which is not used for notification) needs to be passed so that Jenkins can do the merge. Currently the Jenkins Git plugin doesn’t allow to pass additional parameters on the commit notification, but I already submitted a PR to the Jenkins Git plugin to allow this in the future. With this it should be possible then in the future to trigger also PR builds when source and target branch are in different repositories. It nonetheless needs to be made sure, that Jenkins can reach both (source and target) repositories. So if you manage your forks also via Stash and the forks are accessible to Jenkins then it should be possible in the future. I try to do some test once the Jenkins Git plugin is released containing the necessary changes and post an update here.

  2. I’m probably missing something, but the “refs/heads/pr/{branch}” are not handled automagically by stash, are they?

    The thing is that I’m using generators, which end up failing, because pr/{branch} does not exist. I guess the generator should use {branch}, and only the generated job should use pr/{branch} ((( “use” is not the proper word, but maybe “have it in the configuration”, since I guess the job run will **use** the git hash that is passed in the notifyCommit URL and totally ignore the branch ref )))

    • The ref “refs/heads/pr/{branch}” actually doesn’t exists but is a necessary twist to have two builds in Jenkins.
      One build which works on the actual branch (after PR approval) and another build to build on the PR. Not sure what you mean with generators but in the generated jobs the “Branches to build” should contain the refspec in the format “refs/heads/pr/{branch}” to make this two builds working You can also use any other prefix then “pr” but it felt natural to use this one. For building the branch is indeed ignored and the git hash is used.

  3. Pingback: Set Jenkins Build Description With a System Groovy Script | Code Addiction


    I set the refspec for git SCM in the Jenkins job to:

    Branches to build:

    Poll SCM: H 0 1 1 0 (is an example, but this works very nice. This seems to modify the behaviour of the Git SCM plugin somehow, because this will accept the trigger URL above)

    This will work with branches on the main repository, but also pull requests from forks!! Thanks a lot for this awesome blog post.

  5. First, thanks for the great post! I have got a setup working as you have described 🙂

    Now I am wondering how I get at the parameters in Jenkins so I can differentiate between pull requests that are opened and rescoped and pull requests that are approved. So How do I get at the PULL_REQUEST_ACTION parameter?

    The use case is 2 seperate jenkins jobs.

    * One job reacts on the opening and updating of a pull request. It will merge to our integration branch, build and execute a “trimmed” down set of tests. It will not push merge back to repository.

    * Another job reacts on pull request approval. It will merge to our integration branch, execute a more robust set of tests and push the merge back to repository, if all goes well.

    • Nice to hear that I was able to help.

      The event type is available as the PULL_REQUEST_ACTION parameter.

      I see the following two options
      1. For each job type/event combination a configuration in the Stash plugin and add an additional parameter in trigger URL, e.g. ACTION=${PULL_REQUEST_ACTION}. In order to trigger the correct job type update the refspec in the git configuration and the URL and set the two different values, e.g. origin/pr_open/master and origin/pr_approved/master

      2. Similar to the first approach create for each job type/event combination a configuration in the Stash plugin and add an additional parameter in trigger URL, e.g. ACTION=${PULL_REQUEST_ACTION}. Instead of using different refspecs to trigger the correct Jenkins job, one can use only one Jenkins job type and differentiate between the types using the Conditional BuildStep Plugin to execute the trimmed version or full version based on the ACTION parameter. I haven’t tested this approach but it should work.

  6. Thank you so much. This was a great post and has helped me immensely!

    One problem I have run into is that a Pull Request that has a conflict (so it cannot be auto-merged) will cause problems for other Pull Request builds. I am not exactly sure why because I wipe/re-clone the repository before each job, but I get the message “ERROR: Branch not suitable for integration as it does not merge cleanly: Could not merge ” for branches that can be merge. (If I force the PR build to kick off again later, it will work just fine).

    I have seen others with the issue resolve it by setting the branch specifier to “refs/remotes//”, but I wasn’t sure how that could apply to Pull Requests specifically.

      • The jobs are using the “Wipe out repository & force clone” setting from the Jenkins GIT plugin, which seems to be clearing out the whole workspace as far as I can tell.

        However, your comment that we should make sure to only run one build at a time makes me wonder if I have made a different mistake. For reasons I can’t defend on the internet, the repository actually contains several independently-built products, so each pull request triggers several separate Jenkins jobs (each with its own repo clone). Many PRs only affect one or two of the products, so I added Groovy Script to check the sub-folder and exit (successfully) early if there are no changes. This means that we often have different builds running against different Pull Requests- though we never have the same build running twice. In picture form:

        Repository A
        |—-> Product 1
        |—-> Product 2
        |—-> Product 3

        At any time we could have the following jobs running:
        – Product 1 Build (for Pull Request AA-123)
        – Product 2 Build (for Pull Request BB-456)
        – Product 3 Build (for Pull Request CC-789)

        Though we would never have two builds of the same product going at once (for example Product 1 building twice).

        I would not have thought this would be an issue since each job has its own full clone of the repo, but the issue does seem to occur around the time that one product is building with an unmerge-able pull request.

  7. First of all, thank you: I’ve been despairing of coming up with a decent solution to this problem, and it looks like what you’ve put together here actually works!

    My only gripe is the tiniest of things:

    def description = “<a href=’$PULL_REQUEST_URL’>PR #$PULL_REQUEST_ID</a>”

    …doesn’t seem to work, as jenkins escapes the html and thus you end up with a literal “<a href=…” displayed as the build description. Is there some groovy or jenkins setting that has to be adjusted for this to work? (And does doing so open us up to XSS?)

  8. Do these instructions work anymore? I was not able to install stashNotifier because it said there were missing dependencies of plain-credentials (1.1) and ssh-agent (1.3) … I had newer versions installed. When I attempted to downgrade, my entire Jenkins system would not start and had to be re-installed.

    it’s also not clear what things like this mean: Perform all standard configuration so that you can access Stash via SSH and can pull repositories from Stash.

    You can’t use HTTP?

    • 1. If you have a specific problem with installing a plugin you should check on the plugin home page and file an issue on the plugin issue tracker.
      2. You need to configure the correct URL for you repo in Bitbucket Server. You can also use HTTP but from a performance point of view I advise to use ssh.

  9. Pingback: Manage the lifecycle of pull requests with Bitbucket, Jenkins and ElasticBox | ElasticBox

  10. Please note that there is a difference between Bitbucket and Bitbucket Servers. Although they share the name they are actually different products having also a different code base. My post is about Bitbucket Server.

  11. Christian, thanks for this great post and for your feedback. That’s the reason why we decided to include “(having Stash but not Bitbucket)” in that part. Anyway, every feedback an comment is always welcome and again, congrats!

  12. Great writeup! Our BitBucket Server and Jenkins setup recently broke due to incompatibilities introduced by a couple of updates so I decided to give your setup a shot. Unfortunately I can’t seem to get it to work. Jenkins doesn’t seem to see the pull requests from BitBucket Server. Do you have any suggestions for how I could troubleshoot this?

    We’re using Jenkins 2.8 with all the plugins at their latest version.

    • You can check in the Jenkins log if the you get the request from the Bitbucket server. If I remember correctly I think you should see some logs if you grep for “notifyCommit”. If you have Apache/Nginx fronting Jenkins then you can also check their log files.

    • Could it be related to the “lazy ref updates” in Bitbucket (described at We had to implement something that would do a get to the REST API for the pull-request/*/changes before the call to the jenkins /git/notifyCommit url.

      Without that, anytime we pushed new code to an already open pull request, jenkins would be triggered to poll for changes, but would never find changes until someone manually visited the pull request page in BitBucket.

      I’ve been looking for another solution to this problem, but we do not control the BitBucket server, so I can’t change any settings there, but if I can get enough ammunition, I might be able to convince those admins to make changes.

      Our setup is jenkins 1.642, Git plugin 3.0 & BitBucket 4.10.2 with Bitbucket Server Webhook to Jenkins (not sure of version) with Branch Options set to “build all: pull-requests/* develop”

  13. Hi

    Firstly just want to say thanks for this post. Its been instrumental in helping me get a build process setup that does pull requests.

    Only minor issue I’ve had is that I don’t seem to have values for the variables in the groovy script. Which means my build ends up with a description
    PR #null
    and a link to null as well.

    I’ve double checked the name of the parameter in the Bitbucket configuration and in the Jenkins configuration and it seems to be the same. Its just missing this value. Is there something else I need to configure to get this bit to work?

    Many thanks Again

    • Starting with the Git Plugin 2.5 parameters send via the notification are not automatically passed as parameters to the job for security reasons.

      In order to allow again to pass the parameter to the job an additional environment parameter needs to be set containing the name of the parameters to pass.

      More details can be found at

      Currently I don’t have an installation available with Git Plugin 2.5 so it would be great if you can configure your system as documented in the link with the additional environment parameters and leave a comments if this works.

  14. First of all thank you for this very handy article Christian! I am new to Jenkins and went with it because Bamboo is just way to expensive for us at the moment and I never thought that Jenkins can be integrated with Bitbucket at this level.
    Unfortunately I haven’t been able to solve one thing and since you seem to be active here I’d like to ask your help.
    It seems that the Groovy script can’t extract the PULL_REQUEST_URL and PULL_REQUEST_ID parameters from the build environment. I am using Jenkins 2.10 with version 2.5 of the Git Plugin. I can see in the webserver’s logs in front of Jenkins that the parameters are passed on and everything else is working fine, although the job’s description shows ‘null’ where those variables should’ve been extended.
    Do you have any suggestions on how should I proceed?

    • Starting with the Git Plugin 2.5 parameters send via the notification are not automatically passed as parameters to the job for security reasons.

      In order to allow again to pass the parameter to the job an additional environment parameter needs to be set containing the name of the parameters to pass.

      More details can be found at

      Currently I don’t have an installation available with Git Plugin 2.5 so it would be great if you can configure your system as documented in the link with the additional environment parameters and leave a comments if this works.

      • Hi Christian,

        thank you for pointing me in the right direction. Here’s what I did to make it work.

        I passed Java the ‘-Dhudson.plugins.git.GitStatus.safeParameters=’PULL_REQUEST_URL,PULL_REQUEST_ID” parameter, and configured the job as parameterized and added the above two as string parameters without any default values.

      • Glad to see that it works. Out of curiosity did you also tried without the additional job parameters but only specifying the hudson.plugins.git.GitStatus.safeParameters?

      • Yes I tried it. Jenkins rejects the parameters because they are not defined at the job level. I think that’s another layer of security.

  15. After some tinkering I now have a job that uses parameters in the build, mainly to have a job that takes one branch merges it to another and running tests etc on the result.

    To make it work in 2.5.0
    1. add the “-Dhudson.plugins.git.GitStatus.safeParameters=XXX,YYY,ZZZ” to the startup of jenkins (
    2. add String parameters to the build with name equal to the notifyCommit params. e.g XXX,YYY.ZZZ. No need for default values. If the job is not parameterized this way, I can’t get it to work…
    3. Use them as $XXX (or ${XXX}) in the build steps.

    Thanks for the work on this! It’s a really good feature of the notifyCommit handler.

    • I tried adding them in jenkins.xml and restarted jenkins service as I am using jenkins on windows machine. But still it dint help.

      -Xrs -Xmx256m -Dhudson.plugins.git.GitStatus.safeParameters=PULL_REQUEST_ID,PULL_REQUEST_URL -jar “%BASE%\jenkins.war” –httpPort=8080 –webroot=”%BASE%\war”

      • Hello Nidhi:

        The issues is that you are trying to append safeParamters to JENKINS_ARGS not JAVA_ARGS. Please remove -Dhudson.plugins.git.GitStatus.safeParameters=PULL_REQUEST_ID,PULL_REQUEST_URL from JENKINS_ARGS and add them to JAVA_ARGS.

        Hope that helps.

  16. Hi Christian,

    Thanks for the great article.. Really helpful…

    My use case is slightly different than explained, but helped in configuring most of it.

    I need to trigger a SONAR Build when a Pull request is created. for the same we have installed SONAR STASH plugin on SONARQube server and now with the this plugin trying to invoke the Jenkins job for that project.

    Queries :
    1. Is the URL mentioned in article suffice for SONAR as well?

    Geting 404 code. Not sure why, as Jenkins is up and running

    If not, can you please direct me to correct one.

    2. I was not sure, and created 2 configs on STASH/BitBucket . How to get hold of them and remove the incorrect one?

    Thanks in advance.

  17. Hello Christian,

    Thanks for the very detailed tutorial. I configured the Jenkins and Stash server as per the instructions, however I don’t see any job triggers on Jenkins when a PR is created on stash server.

    How can I debug the issue?


  18. Great article. Thanks a lot for putting the time to write this up.

    A couple of questions here:

    – I am using Sonar Qube Plugin & Sonar for Bitbucket to perform to display any sonar violations on pull requests in Bitbucket

    In order to decline the pull request if either fails, do I set the minimum number of successful builds required to 2?

  19. ISSUE: Bitbucket Push Notify Fails
    Git Plugin: Push notification from repository

    The Git Plugin accepts the build trigger only when “Build Triggers-> Poll SCM” is selected. No CRON schedule should be entered in the text area.

    ERROR: from HTTP GET:
    No git jobs using repository: ssh:// and branches:
    No Git consumers using SCM API plugin for: ssh://

    I found that the Jenkins (version 2.44) build trigger made by “Pull Request Notifier for Bitbucket” must have the “Poll SCM” feature enabled. This doesn’t appear in the directions. Please, add it.

  20. Hi,

    We have Jenkins version 2.7.1 and git plugin 3.1.0
    Our Jenkins running on Linux OS

    Still whenever we raised request in jenkins still showing PR #null;
    In Linux please can you help where to add those parameters.

    • The id is passed as parameter PULL_REQUEST_ID from Bitbucket using the Pull Request Notifier add-on, see step 6.
      You also need to make sure to configure all the parameters which shall be passed through, see my comment from 2016-06-27 AT 18:30 and the answer by Mate.

  21. This is our settings:URL to invoke this is we have.These are parameters.

    Please can you guide and we are not aware were these should be added-Dhudson.plugins.git.GitStatus.safeParameters=PULL_REQUEST_URL,PULL_REQUEST_ID,PULL_REQUEST_FROM_HASH,PULL_REQUEST_TO_BRANCH

    Do we need to change anything in build step.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s