In Jenkins, Is there a way to execute a pipeline when the source branch of a PR is updated? - jenkins

This is my Jenkinsfile:
pipeline {
agent { docker { image 'node:10.16' } }
stages {
stage('PR To Dev') {
when {
changeRequest target: 'dev'
}
steps {
sh 'npm install'
sh 'npm run lint'
}
}
}
}
I'm trying to run linting upon every PR made (on Github). This pipeline works and runs as intended when I make the initial PR to the dev branch. However, subsequent commits to the open PR are ignored by Jenkins, which defeats the usefulness of the initial lint check. How can I configure Jenkins to lint upon any updates to a branch that has an open PR to the dev (or any arbitrary) branch?

Achieving this goal is possible. It depends greatly on the plugin that you are using to integrate GitHub with Jenkins, and how you configure GitHub to use Jenkins' webhooks.
On the GitHub end, you can configure to trigger the webhook on different events. Default config is Push events (to any branch, whether on PR or not), All events (these can have many false positives), and the option to Select individual events (find your right balance between events coverage and false positives)
On the Jenkins end, some plugins will offer more customization options to discard unnecessary triggers, for example to avoid triggering a project on PR updates of the title or description (instead of code), etc.
I, personally, use the Generic Webhook Plugin on the Jenkins end and then I analyze the json of the webhook to determine whether to run a job or not

Related

Jenkins simple pipeline not triggered by github push

I have created a jenkins pipeline job called "pipelinejob" with the below script:
pipeline {
agent any
stages {
stage ('Setup'){
steps{
//echo "${BRANCH_NAME}"
echo "${env.BRANCH_NAME}"
//echo "${GIT_BRANCH}"
echo "${env.GIT_BRANCH}"
}
}
}
}
Under General, I have selected "GitHub project" and inserted my company's github in the form:
https://github.mycompany.com/MYPROJECTNAME/MY_REPOSITORY_NAME/
Under Build Triggers, i have checked "GitHub hook trigger for GITScm polling
I have created a simple job called "simplejob" with same configuration as 1) and 2)
In my company's Github, i have created a webhook like "jenkins_url/jenkins/github-webhook/"
I commit a change in "mybranch" in "MY_REPOSITORY_NAME"
My simple job "simplejob" is triggered and built successfully
My pipeline job "pipelinejob" is not triggered
In Jenkins log i see the below:
Sep 12, 2019 2:42:45 PM INFO org.jenkinsci.plugins.github.webhook.subscriber.DefaultPushGHEventSubscriber$1 run
Poked simplejob
Nothing regarding my "pipelinejob".
Could you please point me to the right directions as to what to check next?
P.S. I have manually executed my "pipelinejob" successfully
I wasted two days of work on this, as none of the previous solutions worked for me. :-(
Eventually I found the solution on another forum:
The problem is that if you use a Jenkinsfile that is stored in GitHub, along with your project sources, then this trigger must be configured in the Jenkinsfile itself, not in the Jenkins or project configuration.
So add a triggers {} block like this to your Jenkinsfile:
pipeline {
agent any
triggers {
githubPush()
}
stages {
...
}
}
Then...
Push your Jenkinsfile into GitHub
Run one build manually, to let Jenkins know about your will to use this trigger.
You'll notice that the "GitHub hook trigger for GITScm polling" checkbox will be checked at last!
Restart Jenkins.
The next push should trigger an automated build at last!
On the left side-pane of your pipeline job, click GitHub Hook log. If it says 'Polling has not run yet', you will need to manually trigger the pipeline job once before Jenkins registers it to poke on receiving hooks.
Henceforth, the job should automatically trigger on GitHub push events.
I found an answer to this question with scripted pipeline file. We need to declare the Github push event trigger in Jenkins file as follows.
properties([pipelineTriggers([githubPush()])])
node {
git url: 'https://github.com/sebin-vincent/Treasure_Hunt.git',branch: 'master'
stage ('Compile Stage') {
echo "compiling"
echo "compilation completed"
}
stage ('Testing Stage') {
echo "testing completed"
echo "testing completed"
}
stage("Deploy") {
echo "deployment completed"
}
}
}
The declaration should be in the first line.
git url: The URL on which pipeline should be triggered.
branch: The branch on which pipeline should be triggered. When you specify the branch as master and make changes to other branches like develop or QA, that won't trigger the pipeline.
Hope this could help someone who comes here for an answer for the same problem with Jenkins scripted pipeline :-(.
The thing is whenever you create a pipeline job for git push which is to be triggered by github-webhook, first you need to build the pipeline job manually for one time. If it builds successfully, then Jenkins registers it to poke on receiving hooks. And from the next git push, your pipeline job will trigger automatically.
Note: Also make sure that the pipeline job built manually for the first time should be built successfully, otherwise Jenkins will not poke it. If it fails to build, you can never trigger the job again.

GitLab pipelines for merge requests in Jenkins

I'm working on a CI integration between GitLab and Jenkins where I want to do specific validation depending on the target branch of an opened merge request (MR).
To be more specific, we have a repository containing Flyway database migrations and we'd like to validate inside a Docker container whether the migrations can be applied successfully against different environments. The target environment can be deduced by the target branch of a merge request.
Now, my problem is this. I'd like to reject MRs that don't build successfully, but I couldn't find way of rejecting MRs, only commits.
The way our integration is set up is that Jenkins receives webhook notifications for both commit and MR events. So, when someone pushes a new commit to an MR, two Jenkins builds will be triggered — one for the whole MR and one for the commit. The MR build will fail, but the commit build will succeed and due to race conditions in how the builds are run, the MR pipeline in GitLab will be marked as successful although it shouldn't be.
Is there a way to mark merge requests as failed in a Jenkins-GitLab integration?
I believe this feature is called pipelines for merge requests in GitLab's built-in CI/CD support, but can it be simulated with Jenkins?
Here's a sketch of the Jenkinsfile I'm using:
def isMergeRequest() {
gitlabActionType == 'MERGE' && gitlabTargetBranch != gitlabSourceBranch
}
pipeline {
agent any
stages {
stage('Validate') {
when {
expression {
isMergeRequest()
}
}
steps {
gitlabCommitStatus('Validate') {
sh "jenkins/validate_mr.py $gitlabTargetBranch $gitlabSourceBranch"
}
}
}
}
}

How to "Scan Repository Now" from a Jenkinsfile

I can call another jenkins job using the build command. Is there a way I can tell another job to do a branch scan?
A multibranch pipeline job has a UI button "Scan Repository Now". When you press this button, it will do a checkout of the configured SCM repository and detect all the branches and create subjobs for each branch.
I have a multibranch pipeline job for which I have selected the "Suppress automatic SCM triggering" option because I only want it to run when I call it from another job. Because this option is selected, the multibranch pipeline doesn't automatically detect when new branches are added to the repository. (If I click "Scan Repository Now" in the UI it will detect them.)
Essentially I have a multibranch pipeline job and I want to call it from another multibranch pipeline job that uses the same git repository.
node {
if(env.BRANCH_NAME == "the-branch-I-want" && other_criteria) {
//scanScm "../my-other-multibranch-job" <--- scanScm is a fake command I made up
build "../my-other-multibranch-job/${env.BRANCH_NAME}"
I get an error on that build line, because the target multibranch pipeline job does not yet know that BRANCH_NAME exists. I need a way to trigger an SCM re-scan in the target job from this current job.
Similar to what you figured out yourself, I can contribute my optimization that actually waits until the scan has finished (but is subject to Script Security):
// Helper functions to trigger branch indexing for a certain multibranch project.
// The permissions that this needs are pretty evil.. but there's currently no other choice
//
// Required permissions:
// - method jenkins.model.Jenkins getItemByFullName java.lang.String
// - staticMethod jenkins.model.Jenkins getInstance
//
// See:
// https://github.com/jenkinsci/pipeline-build-step-plugin/blob/3ff14391fe27c8ee9ccea9ba1977131fe3b26dbe/src/main/java/org/jenkinsci/plugins/workflow/support/steps/build/BuildTriggerStepExecution.java#L66
// https://stackoverflow.com/questions/41579229/triggering-branch-indexing-on-multibranch-pipelines-jenkins-git
void scanMultiBranchAndWaitForJob(String multibranchProject, String branch) {
String job = "${multibranchProject}/${branch}"
// the `build` step does not support waiting for branch indexing (ComputedFolder job type),
// so we need some black magic to poll and wait until the expected job appears
build job: multibranchProject, wait: false
echo "Waiting for job '${job}' to appear..."
while (Jenkins.instance.getItemByFullName(job) == null || Jenkins.instance.getItemByFullName(job).isDisabled()) {
sleep 3
}
}
Ended up figuring this out shortly after posting the question. Calling build against the base multibranch pipeline job as opposed to a branch causes it to re-scan. The solution to my above snippet would have ended up looking something like...
node {
if(env.BRANCH_NAME == "the-branch-I-want" && other_criteria) {
build job: "../my-other-multibranch-job", wait: false, propagate: false // scan for branches
sleep 2 // scanning takes time
build "../my-other-multibranch-job/${env.BRANCH_NAME}"
The wait: false is important because otherwise you get "ERROR: Waiting for non-job items is not supported". The multibranch "parent" job is closer to a folder than a job, but it's a folder that supports the build command, and it does so by scanning the SCM.
But solving this just led to another problem, which is that with wait: false we have no way of knowing when the SCM Scan finished. If you have a large repository (or you're short on jenkins agents), the branch won't get discovered until after the second build command has already failed due to the branch not existing. You could bump the sleep time even higher, but that doesn't scale.
Fortunately, it turns out manually initiating the SCM scan isn't even needed if you have github webhooks set up for your jenkins. The branch will be discovered more-or-less instantly, so for my purposes this is solved another way. The reason I was running into it is we don't have webhooks set up in our dev jenkins, but once I move this code to prod it will work fine.
If you're trying to use JobDSL to set up multibranches calling multibranches and you don't have webhooks or something equivalent, the better path is probably to abandon multibranch for your second tier of jobs and use JobDSL to create folders and manage the branch jobs yourself.

Skipping stages in Jenkins pipeline on a Commit but not a PR

We are currently using the Jenkins/Groovy pipeline method for CI.
I'm trying to create a single pipeline for building packages and running unit tests on a branch (and let me know if this is bad practice).
The problem is on a commit I don't want to execute my test steps (due to the large number of commits and time to execute the full pipeline), but I still want the packaging stage to run for our manual testers to be able to pull and install on instances.
Is there any way to distinguish between a run for a PR vs a commit in the pipeline steps or in the job configuration?
Using the (assuming github, but theres bitbucket equiv as well)
https://wiki.jenkins.io/display/JENKINS/GitHub+Branch+Source+Plugin to discover Repositories to build branches and PR's will allow you to fall back on Jenkins ENV variables.
This allows simple if statement to determine if the build is for a branch, or for a PR, as PR's are built on a 'branch' of PR-n
Once a PR is open however, all commits would be built.
https://go.cloudbees.com/docs/cloudbees-documentation/cje-user-guide/index.html#github-branch-source
you can use the changeRequest built-in condition from Jenkins.
Executes the stage if the current build is for a "change request" (a.k.a. Pull Request on GitHub and Bitbucket, Merge Request on GitLab, Change in Gerrit, etc.). When no parameters are passed the stage runs on every change request, for example: when { changeRequest() }.
stage('Run only for pull requests to master branch or at the master branch') {
when {
anyOf {
branch 'master'
changeRequest target: 'master'
}
}
steps {
// this is a very long step for integration test that we don't want to execute often, but we need to execute before to merge to master
sh "${mvn} " +
"clean " +
"test-compile " +
"failsafe:integration-test failsafe:verify"
}
}

How to check if a pipeline is triggered from a pull request

I'm using Jenkins pipeline to build Pull Requests branches using the GitHub Organization plugin.
I can build them fine, but I want to avoid some steps (such as publishing the artifacts). Checking git's current branch gives me the PR's target since the PR branch is being merged into the target before the build is attempted.
How can I check if the build is being initiated from a PR vs than a regular branch build?
At least on Jenkins 2.16 env.BRANCH_NAME gives the source branch not the target branch. You could do something like:
if (env.BRANCH_NAME == "master") {
sh "./publish.sh"
}
Other env vars that could be useful are the CHANGE_* variables. E.g.,
CHANGE_AUTHOR='me'
CHANGE_ID='6'
CHANGE_TARGET='master'
CHANGE_TITLE='Update README.md'
CHANGE_URL='https://github.com/test-org/test-repo/pull/6'
For documentation on these and more: https://ci.eclipse.org/webtools/env-vars.html/
The env variable CHANGE_ID only exists when the build is triggered from a Pull Request check.
For a multibranch project corresponding to some kind of change request, this will be set to the change ID, such as a pull request number, if supported; else unset.
To specifically detect GitHub pull requests, this can be used:
script {
if (env.BRANCH_NAME == 'master') {
sh 'make'
} else if (env.BRANCH_NAME.startsWith('PR')) {
// do actions for pull request
} else {
// some other branch
}
}
Of course, if you expect to have branches starting with PR on your main repository this wouldn't be reliable. The nice thing about it is that script can be used also in post not just stages, which is useful since when is not allowed in post. If you don't care about that it's worth looking into the when directive. There is some documentation from Cloudbees and Jenkins with some examples.

Resources