Table of Contents
- Pipeline: GitHub
- License
- Prerequisites
- Credentials
- Triggers
- Global Variables
- Auxiliary Classes
- Examples
Pipeline: GitHub
The entry points for this plugin’s functionality are additional global variables, available to pipeline scripts when the plugin is enabled and the prerequisites are met.
License
MIT
Prerequisites
-
Jenkins running Java 8 or higher.
-
Projects/jobs must be automatically created by the GitHub Organization folder/project type.
See: GitHub Branch Source Plugin
Credentials
Currently all operations against GitHub will be performed using the builds GitHubSCMSource
credentials. These will typically be the Scan Credentials
you configured in your GitHub Organization.
However you can override this in a pipeline script by calling setCredentials(String userName, String password)
before any properties or methods are accessed/invoked on the pullRequest
global variable.
pullRequest.setCredentials('John.Smith', 'qwerty4321')
If you plan to use this plugin to add/modify/remove comments, labels, commit statuses etc. Please ensure that the required permissions are assigned to the token supplied in the credentials (Scan Credentials
or Manually
supplied).
Triggers
This plugin adds the following pipeline triggers
- issueCommentTrigger
- pullRequestReview
issueCommentTrigger
Requirements
- This trigger only works on Pull Requests, created by the GitHub Branch Source Plugin.
- Currently this trigger will only allow collaborators of the repository in question to trigger builds.
Limitations
The Pull Request's job/build must have run at least once for the trigger to be registered. If an initial run never takes place then the trigger won't be registered and cannot pickup on any comments made.
This should not be an issue in practice, because a requirement of using this plugin is that your jobs are setup automatically by the GitHub Branch Source Plugin, which will trigger an initial build when it is notified of a new Pull Request.
Considerations
This trigger would be of limited usefulness for people wishing to build public GitHub/Jenkins bots, using pipeline scripts. As there is no way to ensure that a Pull Request's Jenkinsfile
contains any triggers. Not to mention you would not want to trust just any Jenkinsfile
from a random Pull Request/non-collaborator.
This trigger is intended to be used inside enterprise organizations:
- Where all branches and forks just contain a token
Jenkinsfile
that delegates to the real pipeline script, using shared libraries. - Trust all their Pull Request authors.
Parameters
commentPattern
(Required) - A Java style regular expression
Usage
Scripted Pipeline:
properties([
pipelineTriggers([
issueCommentTrigger('.*test this please.*')
])
])
Declarative Pipeline:
pipeline {
triggers {
issueCommentTrigger('.*test this please.*')
}
}
Detecting whether a build was started by the trigger in a script:
Note that the following uses currentBuild.rawBuild
and should therefore only be done in a @NonCPS
context. See the workflow-cps-plugin Technical Design for more information.
def triggerCause = currentBuild.rawBuild.getCause(org.jenkinsci.plugins.pipeline.github.trigger.IssueCommentCause)
if (triggerCause) {
echo("Build was started by ${triggerCause.userLogin}, who wrote: " +
"\"${triggerCause.comment}\", which matches the " +
"\"${triggerCause.triggerPattern}\" trigger pattern.")
} else {
echo('Build was not started by a trigger')
}
Environment variables
The GitHub comment and author that triggered the build are exposed as environment variables (from version > 2.5).
GITHUB_COMMENT
GITHUB_COMMENT_AUTHOR
pullRequestReview
Parameters
reviewStates
(Optional) - A Java array of the PR review states you wish to trigger the build with. If not specified it will trigger for any review state. Possible states arepending, approved, changes_requested, commented, dismissed
Usage
Scripted Pipeline:
properties([
pipelineTriggers([
pullRequestReview(reviewStates: ['approved'])
])
])
Declarative Pipeline:
pipeline {
triggers {
pullRequestReview(reviewStates: ['approved'])
}
}
Detecting whether a build was started by the trigger in a script:
Note that the following uses currentBuild.rawBuild
and should therefore only be done in a @NonCPS
context. See the workflow-cps-plugin Technical Design for more information.
def triggerCause = currentBuild.rawBuild.getCause(org.jenkinsci.plugins.pipeline.github.trigger.PullRequestReviewCause)
if (triggerCause) {
echo("Build was started by ${triggerCause.userLogin}, who reviewed the PR: " +
"\"${triggerCause.state}\", which matches one of " +
"\"${triggerCause.reviewStates}\" trigger pattern.")
} else {
echo('Build was not started by a trigger')
}
Environment variables
The GitHub review comment and author that triggered the build are exposed as environment variables (from version > 2.8).
GITHUB_REVIEW_COMMENT
GITHUB_REVIEW_AUTHOR
GITHUB_REVIEW_STATE
Global Variables
repository
Coming soon!
pullRequest
Usage
Before you can use the pullRequest
global variable you must ensure you are actually in a Pull Request build job. The best way to do this is to check for the existence of the CHANGE_ID
environment variable.
Scripted Pipeline:
node {
stage('Build') {
try {
echo 'Hello World'
} catch (err) {
// CHANGE_ID is set only for pull requests, so it is safe to access the pullRequest global variable
if (env.CHANGE_ID) {
pullRequest.addLabel('Build Failed')
}
throw err
}
}
}
Declarative Pipeline:
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'Hello World'
}
}
}
post {
failure {
script {
// CHANGE_ID is set only for pull requests, so it is safe to access the pullRequest global variable
if (env.CHANGE_ID) {
pullRequest.addLabel('Build Failed')
}
}
}
}
}
Properties
Name | Type | Setter | Description |
---|---|---|---|
id | Integer |
false | |
state | String |
true | Valid values open or closed |
number | Integer |
false | |
url | String |
false | |
patchUrl | String |
false | |
diffUrl | String |
false | |
issueUrl | String |
false | |
title | String |
true | |
body | String |
true | |
locked | Boolean |
true | Accepts true , false or 'true' , 'false' |
milestone | Milestone |
true | Setter accepts int or Milestone class. |
head | String |
false | Revision (SHA) of the head commit of this pull request |
headRef | String |
false | Name of the branch this pull request is created for |
base | String |
true | Name of the base branch in the current repository this pull request targets |
files | Iterable<CommitFile> |
false | |
assignees | Iterable<String> |
true | Accepts a List<String> |
commits | Iterable<Commit> |
false | |
comments | Iterable<IssueComment> |
false | |
reviewComments | Iterable<ReviewComment> |
false | |
labels | Iterable<String> |
true | Accepts a List<String> |
statuses | Iterable<CommitStatus> |
false | |
requestedReviewers | Iterable<String> |
false | |
reviews | Iterable<Review> |
false | |
updatedAt | Date |
false | |
createdAt | Date |
false | |
createdBy | String |
false | |
closedAt | Date |
false | |
closedBy | String |
false | |
mergedAt | Date |
false | |
mergedBy | String |
false | |
commitCount | Integer |
false | |
commentCount | Integer |
false | |
additions | Integer |
false | |
deletions | Integer |
false | |
changedFiles | Integer |
false | |
merged | Boolean |
false | |
mergeable | Boolean |
false | |
mergeCommitSha | String |
false | |
mergeableState | String |
false | |
maintainerCanModify | Boolean |
true | Accepts true , false or 'true' , 'false' |
draft | Boolean |
false |
Methods
Merge
String merge([String commitTitle, String commitMessage, String sha, String mergeMethod])
Returns the merge's SHA/commit id.
Commit Status
CommitStatus createStatus(String status [, String context, String description, String targetUrl])
Labels
void addLabels(List labels)
void removeLabel(String label)
Assignees
void addAssignees(List assignees)
void removeAssignees(List assignees)
Reviews
void review(String event)
void review(String event, String body)
void review(String commitId, String event, String body)
Review Comments
ReviewComment reviewComment(String commitId, String path, int position, String body)
ReviewComment editReviewComment(long commentId, String body)
ReviewComment replyToReviewComment(long commentId, String body)
void deleteReviewComment(long commentId)
Pull Request Comments (Issue Comments)
IssueComment comment(String body)
IssueComment editComment(long commentId, String body)
void deleteComment(long commentId)
Requested Reviewers
Iterable getRequestedReviewers<>()
void createReviewRequests(List reviewers)
void createTeamReviewRequests(List teams)
Iterable getRequestedTeamReviewers<>()
void deleteReviewRequests(List reviewers)
void deleteTeamReviewRequests(List teams)
Delete Branch
void deleteBranch()
Misc
void setCredentials(String userName, String password)
Auxiliary Classes
CommitStatus
Properties
Name | Type | Setter | Description |
---|---|---|---|
id | String |
false | |
url | String |
false | |
state | String |
false | One of pending , success , failure or error |
context | String |
false | |
description | String |
false | |
targetUrl | String |
false | |
createdAt | Date |
false | |
updatedAt | Date |
false | |
creator | String |
false |
Methods
None.
Commit
Properties
Name | Type | Setter | Description |
---|---|---|---|
sha | String |
false | |
url | String |
false | |
author | String |
false | |
committer | String |
false | |
parents | Iterable<String> |
false | List of parent commit SHA's |
message | String |
false | |
commentCount | Integer |
false | |
comments | Iterable<ReviewComment> |
false | |
additions | Integer |
false | |
deletions | Integer |
false | |
totalChanges | Integer |
false | |
files | Iterable<CommitFile> |
false | List of files added, removed and or modified in this commit |
statuses | Iterable<CommitStatus> |
false | List of statuses associated with this commit |
Methods
Commit Status
CommitStatus createStatus(String status [, String context, String description, String targetUrl])
Review Comment
ReviewComment comment(String body [, String path, Integer position])
CommitFile
Properties
Name | Type | Setter | Description |
---|---|---|---|
sha | String |
false | |
filename | String |
false | |
status | String |
false | |
patch | String |
false | |
additions | Integer |
false | |
deletions | Integer |
false | |
changes | Integer |
false | |
rawUrl | String |
false | |
blobUrl | String |
false |
Methods
None.
IssueComment
Properties
Name | Type | Setter | Description |
---|---|---|---|
id | Integer |
false | |
url | String |
false | |
user | String |
false | |
body | String |
true | |
createdAt | Date |
false | |
updatedAt | Date |
false |
Methods
void delete()
ReviewComment
Properties
Name | Type | Setter | Description |
---|---|---|---|
id | Long |
false | |
url | String |
false | |
user | String |
false | |
createdAt | Date |
false | |
updatedAt | Date |
false | |
commitId | String |
false | |
originalCommitId | Long |
false | |
body | String |
true | |
path | String |
false | |
line | Integer |
false | This will always return null as the GitHub APIs no longer return line , use position instead. |
position | Integer |
false | |
originalPosition | Integer |
false | |
diffHunk | String |
false | |
pullRequestUrl | String |
false | |
pullRequestReviewId | Long |
false | |
inReplyToId | Long |
false |
Methods
void delete()
Review
Properties
Name | Type | Setter | Description |
---|---|---|---|
id | long |
false | |
user | String |
false | |
body | String |
false | |
commitId | String |
false | |
state | String |
One of APPROVED, PENDING, CHANGES_REQUESTED, DISMISSED, COMMENTED |
Methods
None.
Milestone
Properties
Name | Type | Setter | Description |
---|---|---|---|
number | Integer |
false | |
createdAt | Date |
false | |
dueOn | Date |
false | |
updatedAt | Date |
false | |
closedAt | Date |
false | |
closedIssues | Integer |
false | |
openIssues | Integer |
false | |
description | String |
false | |
state | String |
false | |
title | String |
false | |
url | String |
false | |
creator | String |
false |
Methods
None.
Examples
Pull Requests
Updating a Pull Request's title and body
pullRequest.title = 'Updated title'
pullRequest.body = pullRequest.body + '\nEdited by Pipeline'
Closing a Pull Request
pullRequest.status = 'closed'
Creating a Commit Status against the head of the Pull Request
pullRequest.createStatus(status: 'success',
context: 'continuous-integration/jenkins/pr-merge/tests',
description: 'All tests are passing',
targetUrl: "${env.JOB_URL}/testResults")
Locking and unlocking a Pull Request's conversation
if (pullRequest.locked) {
pullRequest.locked = false
}
Merging a Pull Request
if (pullRequest.mergeable) {
pullRequest.merge('merge commit message here')
}
// or
if (pullRequest.mergeable) {
pullRequest.merge(commitTitle: 'Make it so..', commitMessage: 'TO BOLDLY GO WHERE NO MAN HAS GONE BEFORE...', mergeMethod: 'squash')
}
Adding a label
pullRequest.addLabel('Build Passing')
Removing a label
pullRequest.removeLabel('Build Passing')
Replacing all labels
pullRequest.labels = ['Bug', 'Feature']
Adding an assignee
pullRequest.addAssignee('Spock')
Removing an assignee
pullRequest.removeAssignee('McCoy')
Replacing all assignees
pullRequest.assignees = ['Data', 'Scotty']
Listing all added/modified/removed files
for (commitFile in pullRequest.files) {
echo "SHA: ${commitFile.sha} File Name: ${commitFile.filename} Status: ${commitFile.status}"
}
Adding a review
pullRequest.review('APPROVE')
// or
pullRequest.review('REQUEST_CHANGES', 'Change is the essential process of all existence.')
// or
def commitId = 'SHA of the commit containing the change/file you wish to review' // if unspecified, defaults to the most recent commit
def event = 'REQUEST_CHANGES' // valid review events: APPROVE, REQUEST_CHANGES, COMMENT
def body = 'Change is the essential process of all existence.' // body is required when event equals REQUEST_CHANGES or COMMENT
pullRequest.review(commitId, event, body)
Adding a comment
def comment = pullRequest.comment('This PR is highly illogical..')
Editing a comment
pullRequest.editComment(comment.id, 'Live long and prosper.')
// or
comment.body = 'Live long and prosper.'
Deleting a comment
pullRequest.deleteComment(commentId)
// or
comment.delete()
Adding a review comment
def commitId = 'SHA of the commit containing the change/file you wish to review';
def path = 'src/main/java/Main.java'
def lineNumber = 5
def body = 'The review comment'
def comment = pullRequest.reviewComment(commitId, path, lineNumber, body)
Editing a review comment
pullRequest.editReviewComment(comment.id, 'Live long and prosper.')
// or
comment.body = 'Live long and prosper.'
Deleting a review comment
pullRequest.deleteReviewComment(comment.id)
// or
comment.delete()
Replying to a review comment
pullRequest.replyToReviewComment(comment.id, 'Khaaannnn!')
// or
comment.createReply('Khaaannnn!')
Listing a Pull Request's commits
for (commit in pullRequest.commits) {
echo "SHA: ${commit.sha}, Committer: ${commit.committer}, Commit Message: ${commit.message}"
}
Listing a Pull Request's comments
for (comment in pullRequest.comments) {
echo "Author: ${comment.user}, Comment: ${comment.body}"
}
Listing a Pull Request's review comments
for (reviewComment in pullRequest.reviewComments) {
echo "File: ${reviewComment.path}, Position: ${reviewComment.position}, Author: ${reviewComment.user}, Comment: ${reviewComment.body}"
}
Listing a commit's statuses
for (commit in pullRequest.commits) {
for (status in commit.statuses) {
echo "Commit: ${commit.sha}, State: ${status.state}, Context: ${status.context}, URL: ${status.targetUrl}"
}
}
Creating a Commit Status against arbitrary commits
for (commit in pullRequest.commits) {
commit.createStatus(status: 'pending')
}
Listing a Pull Request's current statuses
for (status in pullRequest.statuses) {
echo "Commit: ${pullRequest.head}, State: ${status.state}, Context: ${status.context}, URL: ${status.targetUrl}"
}
Listing a Pull Request's requested reviewers
for (requestedReviewer in pullRequest.requestedReviewers) {
echo "${requestedReviewer} was requested to review this Pull Request"
}
Listing a Pull Request's requested team reviewers
for (requestedTeamReviewer in pullRequest.requestedTeamReviewers) {
echo "${requestedTeamReviewer} was requested to review this Pull Request"
}
Listing a Pull Request's reviews
for (review in pullRequest.reviews) {
echo "${review.user} has a review in ${review.state} state for Pull Request. Review body: ${review.body}"
}
Requesting reviewers
pullRequest.createReviewRequests(['Spock', 'McCoy'])
Deleting requested reviewers
pullRequest.deleteReviewRequests(['McCoy'])
Requesting team reviewers
pullRequest.createTeamReviewRequests(['justice-league'])
Deleting requested team reviewers
pullRequest.deleteTeamReviewRequests(['justice-league'])
Deleting a branch of the pull request after Merging the pull request
pullRequest.merge(pullRequest.title)
pullRequest.deleteBranch()