How to set GitHub commit status with Jenkinsfile NOT using a pull request builder
Asked Answered
D

5

24

We have Jenkins 2 set to build every push to GitHub, and we do not use the Pull Request builder (although commits that are part of a pull request obviously will get built, as well). The GitHub Integration Plugin says that it only works with the pull request builder, so this won't work for us.

I've also tried the github-notify plugin, but it seems not to work for our case (possibly because the repository is private and/or owned as part of an Organizaiton, rather than an individual user). I have tried letting it infer settings as well as manually specifying credentialsId, account, repo, and of course status arguments, all with no luck.

Here's an abbreviated version of my Jenkinsfile at the moment:

pipeline {
    agent { label "centos7" }

    stages {
        stage("github => pending") {
            steps {
                githubNotify status: "PENDING", credentialsId: "my-credentials-id", account: "my-account", repo: "my-repo"
            }
        }
        stage("build") {
            ...
        }
    }

    post {
        success {
            githubNotify status: "SUCCESS", credentialsId: "my-credentials-id", account: "my-account", repo: "my-repo"
        }
        failure {
            githubNotify status: "FAILURE", credentialsId: "my-credentials-id", account: "my-account", repo: "my-repo"
        }
    }
}

When I run the build, I get the following:

java.lang.IllegalArgumentException: The suplied credentials are invalid to login
    at org.jenkinsci.plugins.pipeline.githubstatusnotification.GitHubStatusNotificationStep.getGitHubIfValid(GitHubStatusNotificationStep.java:234)
    at org.jenkinsci.plugins.pipeline.githubstatusnotification.GitHubStatusNotificationStep.getRepoIfValid(GitHubStatusNotificationStep.java:239)
    at org.jenkinsci.plugins.pipeline.githubstatusnotification.GitHubStatusNotificationStep.access$100(GitHubStatusNotificationStep.java:75)
    at org.jenkinsci.plugins.pipeline.githubstatusnotification.GitHubStatusNotificationStep$Execution.run(GitHubStatusNotificationStep.java:344)
    at org.jenkinsci.plugins.pipeline.githubstatusnotification.GitHubStatusNotificationStep$Execution.run(GitHubStatusNotificationStep.java:326)
    at org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution$1$1.call(AbstractSynchronousNonBlockingStepExecution.java:47)
    at hudson.security.ACL.impersonate(ACL.java:221)
    at org.jenkinsci.plugins.workflow.steps.AbstractSynchronousNonBlockingStepExecution$1.run(AbstractSynchronousNonBlockingStepExecution.java:44)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

I've tested the credentials both through Jenkins (in the Configure System area) and manually in a browser—the username and password are correct, and have read/write access to the repository in question.

Daddylonglegs answered 4/4, 2017 at 18:17 Comment(4)
Are you able to manually set a status with those credentials through the GitHub API?Torruella
@Torruella yes -- the credentials are correct (the username matches the password, etc) and have full read/write access to the project in question.Daddylonglegs
I'm still not clear on whether or not you were able to perform the action via the API. If you were, why not just bypass the plugin and make your own calls to GitHub via the API?Torruella
Yes I can -- I just used the credentials to log in with Python. I can do various "test credentials" things in the Configure System section of the Jenkins UI; I cant get the githubNotify plugin to work, as described here.Daddylonglegs
W
28

Per the Jenkins GitHub plugin's own example:

void setBuildStatus(String message, String state) {
  step([
      $class: "GitHubCommitStatusSetter",
      reposSource: [$class: "ManuallyEnteredRepositorySource", url: "https://github.com/my-org/my-repo"],
      contextSource: [$class: "ManuallyEnteredCommitContextSource", context: "ci/jenkins/build-status"],
      errorHandlers: [[$class: "ChangingBuildStatusErrorHandler", result: "UNSTABLE"]],
      statusResultSource: [ $class: "ConditionalStatusResultSource", results: [[$class: "AnyBuildResult", message: message, state: state]] ]
  ]);
}

... 

pipeline {
  stages {
     ...
  }
  post {
    success {
        setBuildStatus("Build succeeded", "SUCCESS");
    }
    failure {
        setBuildStatus("Build failed", "FAILURE");
    }
  }
}

No superfluous plugins necessary. So long as you have the GitHub plugin installed and correctly configured, you shouldn't even need to do the above, it should happen automatically. We aren't using the Pull Request builder either but are instead using the Jenkins Multibranch Pipeline. We're merely using the above snippet for additional status granularity in our PR's.

Wind answered 7/11, 2017 at 16:0 Comment(11)
nice I tried many things to get the github-notify-plugin working including all suggestions from the other answers and comments. Your method worked in about 5 minutes :-DTeepee
Hello @James, How do you set up the "Pending" state when the pull request starts building? Thanks a lot.Eugenides
@Eugenides you'd simply call setBuildStatus immediately upon running your pipeline with a status of "PENDING". Google GHCommitState to see your options.Wind
@Wind "So long as you have the GitHub plugin installed and correctly configured, you shouldn't even need to do the above..." Is it really unnecessary if the plugin is 'properly configured'?Thanhthank
The hardcoded URL of the repository can be replaced with env.GIT_URL.Thanhthank
@Thanhthank by itself it will only report overall build pending/success/failure. But by using the snippet above, you can add granularity in addition to that. GitHub supports displaying multiple status checks, just use a different context for each.Wind
How to modify that code to use other credentials like a Github token with repo:status privilege only?Gaynellegayner
@Wind with regard to the PENDING state, I'm getting an error java.lang.NoSuchMethodError: No such DSL method 'setBuildStatus' found among steps when trying to set PENDING status from inside the first build step. Where exactly do you put it for this?Gallicanism
The example does not work for me with Github Enterprise because of permission issues. How can I provide credentials to GitHubCommitStatusSetter?Shadrach
@MichaelS it's been ages since I've used Jenkins but I believe your credentials are set in the plugin config, not anywhere here within the pipeline code.Wind
@James, that I am already doing, but GitHubCommitStatusSetter is not picking up the credentialsShadrach
A
5

First, make sure those credentials are global ones, not folder credentials.
The latter is not yet supported and would generate a similar error message: see JENKINS-42955 (still in review)

Second, if those credentials works in a browser but not through a DSL config file lie a jenkinsfile, that might be due to special characters in the name or password: see if you don't have to percent encode reserved characters.

Alixaliza answered 7/4, 2017 at 21:39 Comment(2)
The credentials are global credentials. The only special character is a hyphen -- otherwise all alphanumerical.Daddylonglegs
I had the same error and tried all of these without success. I'd recommend anyone use the Jenkins Github plugin following James's answerTeepee
N
4

If you do not want to bother with the specialized plugins, here is an alternative using curl:

post {
  success {
    withCredentials([usernamePassword(credentialsId: 'your_credentials_id', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
      sh 'curl -X POST --user $USERNAME:$PASSWORD --data  "{\\"state\\": \\"success\\"}" --url $GITHUB_API_URL/statuses/$GIT_COMMIT'
    }
  }
  failure {
    withCredentials([usernamePassword(credentialsId: 'your_credentials_id', usernameVariable: 'USERNAME', passwordVariable: 'PASSWORD')]) {
      sh 'curl -X POST --user $USERNAME:$PASSWORD --data  "{\\"state\\": \\"failure\\"}" --url $GITHUB_API_URL/statuses/$GIT_COMMIT'
    }
  }
}

Where the GITHUB_API_URL is usually constructed like so, for example in the environment directive:

environment {
   GITHUB_API_URL='https://api.github.com/repos/organization_name/repo_name'
}

The credentialsId can be created and obtained from Jenkins -> Credentials

Niko answered 22/8, 2019 at 15:48 Comment(0)
B
3

It didn't occur to me that value in account parameter must not match user in credentials. In account you must specify repository owner. And in credentialsId you may use any user with push access to the repository:

credentialsId: The id of the github's credentials to use, must be of type UsernameAndPassword. Make sure the credentials have write access, as stated by doc: Users with push access can create commit statuses for a given ref

account: The account that owns the repository

Bannerman answered 27/6, 2017 at 6:30 Comment(1)
Note that the docs go on to state (and I've tested) that either your account password or an access token can be used in the UsernameAndPassword credential.Stereophotography
S
3

A better example from the docs:

def getRepoURL() {
  sh "git config --get remote.origin.url > .git/remote-url"
  return readFile(".git/remote-url").trim()
}

def getCommitSha() {
  sh "git rev-parse HEAD > .git/current-commit"
  return readFile(".git/current-commit").trim()
}

def updateGithubCommitStatus(build) {
  // workaround https://issues.jenkins-ci.org/browse/JENKINS-38674
  repoUrl = getRepoURL()
  commitSha = getCommitSha()

  step([
    $class: 'GitHubCommitStatusSetter',
    reposSource: [$class: "ManuallyEnteredRepositorySource", url: repoUrl],
    commitShaSource: [$class: "ManuallyEnteredShaSource", sha: commitSha],
    errorHandlers: [[$class: 'ShallowAnyErrorHandler']],
    statusResultSource: [
      $class: 'ConditionalStatusResultSource',
      results: [
        [$class: 'BetterThanOrEqualBuildResult', result: 'SUCCESS', state: 'SUCCESS', message: build.description],
        [$class: 'BetterThanOrEqualBuildResult', result: 'FAILURE', state: 'FAILURE', message: build.description],
        [$class: 'AnyBuildResult', state: 'FAILURE', message: 'Loophole']
      ]
    ]
  ])
}
Sting answered 17/7, 2019 at 2:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.