Continuous Delivery with VSTS and Jenkins: Triggering Builds Only on PR Merge

Continuous DeliveryDevOps

YOUR COSMETIC CARE STARTS HERE

Find the Best Cosmetic Hospitals

Trusted • Curated • Easy

Looking for the right place for a cosmetic procedure? Explore top cosmetic hospitals in one place and choose with confidence.

“Small steps lead to big changes — today is a perfect day to begin.”

Explore Cosmetic Hospitals Compare hospitals, services & options quickly.

✓ Shortlist providers • ✓ Review options • ✓ Take the next step with confidence

This article is based on a real-world integration challenge originally discussed on Stack Overflow, expanded here with practical solutions for DevOps engineers setting up CD pipelines with VSTS (now Azure DevOps) and Jenkins.

The Goal: Merge-Only Deployments

A common and sensible continuous delivery workflow looks like this:

  1. A developer branches off master, makes changes, and opens a pull request.
  2. A peer reviews and merges the PR into master.
  3. An automation system detects the merge and:
    1. Increments a version number stored in a file in the repository.
    2. Commits the version bump back to master.
    3. Runs the build.
    4. Deploys the artifact.

This is straightforward in concept, but wiring it up correctly between VSTS (now called Azure DevOps) and Jenkins introduces several non-obvious pitfalls.

The VSTS Service Hook Problem

VSTS provides Service Hooks that can trigger external systems like Jenkins. The three hooks most relevant to this workflow are:

  1. Build completed – fires when a VSTS build finishes.
  2. Code pushed – fires on any push to a repository.
  3. Pull request merge commit created – fires when activity occurs on a pull request.

The third option sounds ideal, but it does not fire only on merge. It fires whenever a new commit is pushed to the source branch of an open pull request as well. This means every intermediate commit a developer pushes while their PR is still open will trigger the Jenkins job—causing redundant and potentially broken deployments.

The “Code Pushed” Infinite Loop

Switching to the Code pushed hook seems like it could work if you filter by branch, but it introduces a different problem: the Jenkins pipeline itself commits back to master when it bumps the version number. That commit triggers the hook again, which kicks off another Jenkins run, creating an infinite loop of builds and deployments.

Diagram showing the infinite loop between VSTS Code Pushed hook and Jenkins version commit

The Jenkins Poll SCM Limitation

Jenkins has a built-in Poll SCM trigger that checks the repository on a cron-like schedule. However, when using it with a multi-branch or remotely-hosted repository, it can be unclear which repository and branch the job will poll without explicitly configuring the Git plugin settings in the job. Polling also introduces latency and does not cleanly distinguish between a version-bump commit (made by Jenkins itself) and a genuine developer merge.

Recommended Solutions

Option 1: Filter Triggers Using a Commit Message Convention

Tag the version-bump commit with a recognisable message such as [ci skip] or a custom prefix like [version bump]. In your Jenkins pipeline or job script, inspect the commit message at the start of the build and abort early if the trigger commit matches that pattern.

// Example Jenkinsfile guard
def commitMsg = sh(returnStdout: true, script: 'git log -1 --pretty=%B').trim()
if (commitMsg.contains('[version bump]')) {
    currentBuild.result = 'ABORTED'
    error('Skipping build triggered by version bump commit.')
}Code language: JavaScript (javascript)

This breaks the loop without requiring changes to the VSTS hook configuration.

Option 2: Use a Dedicated Service Account for Version Commits

Configure Jenkins to push version-bump commits using a dedicated service account or bot user. In the VSTS Code pushed Service Hook, set the filter to exclude pushes made by that account. This prevents Jenkins-originated commits from re-triggering the pipeline without requiring any in-pipeline logic.

Option 3: Use the VSTS REST API to Verify PR Status Before Proceeding

When the Pull request merge commit created hook fires, VSTS passes contextual payload data to Jenkins. In the Jenkins job, add an early step that calls the VSTS REST API to confirm the PR status is completed (i.e., actually merged) before proceeding with the build and deployment stages. If the PR is still active, the job exits cleanly without deploying.

Option 4: Move Version Bumping Out of the Jenkins Pipeline

Consider separating version management from the build trigger entirely. Options include:

  • Use a VSTS build task or release pipeline step to handle the version bump after the Jenkins build succeeds, not before.
  • Derive the version number from the Git tag or commit count at build time rather than writing it back to the repository, eliminating the need for a post-merge commit entirely.
  • Use a tool like Semantic Versioning with GitVersion or a similar utility that reads the version from Git history without modifying it.

Option 5: Replace Service Hooks with Jenkins Multibranch Pipeline and VSTS Git Plugin

The Jenkins Git plugin supports polling a specific VSTS Git repository and branch. In a Multibranch Pipeline job, configure the branch source to point at your VSTS repository and set the branch filter to master only. This ensures:

  • Only changes to master are acted upon.
  • Feature branches are ignored unless you explicitly add them.
  • You can combine polling with a short interval or a VSTS webhook pointing at the Jenkins Git endpoint to reduce latency.

To prevent version-bump commits from re-triggering, combine this with Option 1 (commit message filtering).

Recommended Architecture Summary

ApproachBreaks Loop?Merge-Only?Complexity
Commit message skip flagYesPartialLow
Bot/service account exclusion in hook filterYesPartialLow–Medium
VSTS REST API PR status check in JenkinsNo (mitigates)YesMedium
Eliminate version-bump commitYesYesMedium
Multibranch pipeline + commit filterYesYesMedium

Key Takeaways

  • The VSTS Pull request merge commit created hook fires on all PR-related commits, not only on the merge itself—do not rely on it alone to gate deployments.
  • Any pipeline that commits back to the monitored branch must include a mechanism to prevent retriggering.
  • Eliminating the version-bump commit (by deriving version from Git metadata) is the cleanest long-term solution and removes an entire class of trigger-loop problems.
  • VSTS is now called Azure DevOps; most of these concepts and hook types carry over to the modern platform.
0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x