├── Jenkinsfile ├── src ├── main │ ├── resources │ │ └── org │ │ │ └── jenkinsci │ │ │ └── plugins │ │ │ └── ghprb │ │ │ ├── GhprbGitHubAuth │ │ │ ├── help-serverAPIUrl.html │ │ │ ├── help-jenkinsUrl.html │ │ │ ├── help-credentialsId.html │ │ │ ├── help-secret.html │ │ │ ├── help-description.html │ │ │ └── config.groovy │ │ │ ├── GhprbPullRequestMerge │ │ │ ├── help-disallowOwnCode.html │ │ │ ├── help-onlyAdminsMerge.html │ │ │ ├── help-mergeComment.html │ │ │ ├── help-allowMergeWithoutTriggerPhrase.html │ │ │ ├── global.jelly │ │ │ └── config.jelly │ │ │ ├── GhprbTrigger │ │ │ ├── help-unstableAs.html │ │ │ ├── help-retestPhrase.html │ │ │ ├── help-okToTestPhrase.html │ │ │ ├── help-skipBuildPhrase.html │ │ │ ├── help-whitelistPhrase.html │ │ │ ├── help-permitAll.html │ │ │ ├── help-requestForTestingPhrase.html │ │ │ ├── help-buildDescTemplate.html │ │ │ ├── help-blackListCommitAuthor.html │ │ │ ├── help-allowMembersOfWhitelistedOrgsAsAdmin.html │ │ │ ├── help-onlyTriggerPhrase.html │ │ │ ├── help-blackListTargetBranches.html │ │ │ ├── help-triggerPhrase.html │ │ │ ├── help-whiteListTargetBranches.html │ │ │ ├── help-useComments.html │ │ │ ├── help-publishedURL.html │ │ │ ├── help-excludedRegions.html │ │ │ ├── help-includedRegions.html │ │ │ ├── help-useGitHubHooks.html │ │ │ ├── global.groovy │ │ │ └── config.groovy │ │ │ ├── upstream │ │ │ └── GhprbUpstreamStatus │ │ │ │ ├── help-showMatrixStatus.html │ │ │ │ ├── help-startedStatus.html │ │ │ │ ├── help-triggeredStatus.html │ │ │ │ ├── help-completedStatus.html │ │ │ │ ├── help-statusUrl.html │ │ │ │ ├── help-commitStatusContext.html │ │ │ │ ├── help.html │ │ │ │ └── config.jelly │ │ │ └── extensions │ │ │ ├── comments │ │ │ ├── GhprbCommentFile │ │ │ │ ├── help-commentFilePath.html │ │ │ │ └── config.jelly │ │ │ ├── GhprbBuildResultMessage │ │ │ │ ├── help-result.html │ │ │ │ ├── help-message.html │ │ │ │ └── config.jelly │ │ │ ├── GhprbPublishJenkinsUrl │ │ │ │ ├── help-publishedURL.html │ │ │ │ └── config.jelly │ │ │ ├── GhprbBuildStatus │ │ │ │ ├── help-messages.html │ │ │ │ └── config.jelly │ │ │ └── GhprbBuildLog │ │ │ │ ├── config.jelly │ │ │ │ └── help-logExceprtLines.html │ │ │ ├── status │ │ │ └── GhprbSimpleStatus │ │ │ │ ├── help-completedStatus.html │ │ │ │ ├── help-statusUrl.html │ │ │ │ ├── help-commitStatusContext.html │ │ │ │ ├── help-startedStatus.html │ │ │ │ ├── help-triggeredStatus.html │ │ │ │ └── config.jelly │ │ │ └── builds │ │ │ └── GhprbCancelBuildsOnUpdate │ │ │ └── config.jelly │ └── java │ │ └── org │ │ └── jenkinsci │ │ └── plugins │ │ └── ghprb │ │ ├── extensions │ │ ├── GhprbExtensionType.java │ │ ├── GhprbGlobalDefault.java │ │ ├── GhprbGlobalExtension.java │ │ ├── GhprbProjectExtension.java │ │ ├── GhprbExtension.java │ │ ├── GhprbCommentAppender.java │ │ ├── GhprbBuildStep.java │ │ ├── GhprbCommitStatus.java │ │ ├── GhprbCommitStatusException.java │ │ ├── status │ │ │ ├── GhprbSimpleStatusDescriptor.java │ │ │ └── GhprbNoCommitStatus.java │ │ ├── comments │ │ │ ├── GhprbBuildStatus.java │ │ │ ├── GhprbPublishJenkinsUrl.java │ │ │ ├── GhprbBuildLog.java │ │ │ ├── GhprbBuildResultMessage.java │ │ │ └── GhprbCommentFile.java │ │ ├── GhprbExtensionDescriptor.java │ │ └── build │ │ │ └── GhprbCancelBuildsOnUpdate.java │ │ ├── manager │ │ ├── impl │ │ │ ├── GhprbDefaultBuildManager.java │ │ │ ├── downstreambuilds │ │ │ │ └── BuildFlowBuildManager.java │ │ │ └── GhprbBaseBuildManager.java │ │ ├── GhprbBuildManager.java │ │ ├── configuration │ │ │ └── JobConfiguration.java │ │ └── factory │ │ │ └── GhprbBuildManagerFactoryUtil.java │ │ ├── jobdsl │ │ ├── GhprbCommentFilePathContext.java │ │ ├── GhprbCancelBuildsOnUpdateContext.java │ │ ├── GhprbBuildStatusContext.java │ │ ├── GhprbSimpleStatusContext.java │ │ ├── GhprbUpstreamStatusContext.java │ │ ├── GhprbExtensionContext.java │ │ ├── GhprbPullRequestMergeContext.java │ │ ├── GhprbContextExtensionPoint.java │ │ └── GhprbTriggerContext.java │ │ ├── GhprbBranch.java │ │ ├── HttpConnectorWithJenkinsProxy.java │ │ ├── GhprbBuildListener.java │ │ ├── GhprbCrumbExclusion.java │ │ ├── GhprbGitHub.java │ │ ├── GhprbTokenMacro.java │ │ ├── GhprbParametersAction.java │ │ ├── GhprbTriggerBackwardsCompatible.java │ │ ├── GhprbCause.java │ │ ├── upstream │ │ ├── GhprbUpstreamStatusListener.java │ │ └── GhprbUpstreamStatus.java │ │ └── GhprbBuilds.java └── test │ └── java │ └── org │ └── jenkinsci │ └── plugins │ └── ghprb │ ├── rules │ └── JenkinsRuleWithBuildFlow.java │ ├── manager │ ├── configuration │ │ └── JobConfigurationTest.java │ ├── factory │ │ └── GhprbBuildManagerFactoryUtilTest.java │ └── impl │ │ ├── GhprbDefaultBuildManagerTest.java │ │ └── downstreambuilds │ │ └── BuildFlowBuildManagerTest.java │ ├── extensions │ ├── build │ │ └── GhprbCancelBuildsOnUpdateTest.java │ └── status │ │ └── GhprbSimpleStatusTest.java │ ├── GeneralTest.java │ ├── GhprbBuildsTest.java │ ├── GhprbITBaseTestCase.java │ ├── GhprbIT.java │ ├── GhprbRootActionTest.java │ └── GhprbTriggerTest.java ├── FAQ.md ├── .gitignore ├── STYLE_GUIDE.md ├── LICENSE ├── CONTRIBUTING.md ├── AUTHORS.md ├── RELEASE.md └── checkstyle.xml /Jenkinsfile: -------------------------------------------------------------------------------- 1 | buildPlugin() 2 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/help-serverAPIUrl.html: -------------------------------------------------------------------------------- 1 |
2 | The API url for a GitHub instance. 3 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/help-disallowOwnCode.html: -------------------------------------------------------------------------------- 1 |
2 | Disallow a user to merge their own code. 3 |
4 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/help-jenkinsUrl.html: -------------------------------------------------------------------------------- 1 |
2 | Use this to override the Jenkins URL that GitHub should call. 3 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/help-credentialsId.html: -------------------------------------------------------------------------------- 1 |
2 | The global credentials that should be associated with the API url. 3 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/help-onlyAdminsMerge.html: -------------------------------------------------------------------------------- 1 |
2 | Only allow admin users to trigger a pull request merge. 3 |
4 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbExtensionType.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | public interface GhprbExtensionType { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbGlobalDefault.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | public interface GhprbGlobalDefault { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/help-secret.html: -------------------------------------------------------------------------------- 1 |
2 | When not blank, then any web hooks sent from GitHub will be checked with this secret. 3 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/help-mergeComment.html: -------------------------------------------------------------------------------- 1 |
2 | Comment that should show up when the merge command is sent to GitHub. 3 |
4 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/help-description.html: -------------------------------------------------------------------------------- 1 |
2 | A short description used in the job configuration page to tie the auth settings to each job. 3 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-unstableAs.html: -------------------------------------------------------------------------------- 1 |
2 | You can select how will be handled Unstable builds when reporting back to 3 | GitHub. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-showMatrixStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Does not post commit statuses for the children jobs for a Matrix job 3 |
4 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-retestPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | This expression is used for matching phrase which will start build of a Pull 3 | Request. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-okToTestPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | This expression is used for matching phrase which will allow building of a Pull 3 | Request. 4 |
5 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbGlobalExtension.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | public interface GhprbGlobalExtension extends GhprbExtensionType { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-skipBuildPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | When filled, adding this phrase to the pull request title or body will not trigger a build. 3 |
4 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbProjectExtension.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | public interface GhprbProjectExtension extends GhprbExtensionType { 4 | 5 | } 6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-whitelistPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | This expression is used for matching phrase which will add author of a Pull 3 | Request to whitelist. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbCommentFile/help-commentFilePath.html: -------------------------------------------------------------------------------- 1 |
2 | Extends the standard build comment message on github with a custom message file. 3 |
4 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-permitAll.html: -------------------------------------------------------------------------------- 1 |
2 | This is dangerous!!! With this option enabled everyone can run his own 3 | code on your machine! 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-requestForTestingPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | This phrase will be send as a comment when new a Pull Request is created by a 3 | user who is not in whitelist. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-startedStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status for when a build is started. 3 | If the field is left blank then the default value is used instead. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-triggeredStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status for when a build is triggered. 3 | If the field is left blank then the default value is used instead. 4 |
-------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | Frequently Asked Questions 2 | ===== 3 | 4 | **Q: Startup seems slow. What can I do?** 5 | 6 | A: A poolsize is set for 5 threads by default. This can be overridden with a 7 | JVM arg: `-Dorg.jenkinsci.plugins.ghprb.GhprbTrigger.poolSize=5` 8 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-buildDescTemplate.html: -------------------------------------------------------------------------------- 1 |
2 | Set the build description. The default is: <a title=\"$title\" href=\"$url\">PR #$pullId</a>: 3 | $abbrTitle 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildResultMessage/help-result.html: -------------------------------------------------------------------------------- 1 |
2 | Choose either SUCCESS, ERROR, or FAILURE for when the message is appended to the 3 | comment that is added to the pull request. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-completedStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status on the commit for when a build is completed. 3 | If the field is left blank then the default value is used instead. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-statusUrl.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom url instead of the Jenkins job url. 3 | If the desired url should be blank, use "--none--" to alert the trigger to use a blank url. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-blackListCommitAuthor.html: -------------------------------------------------------------------------------- 1 |
2 | When filled, pull request commits from this user(s) will not trigger a build. 3 | (Note: this can be overridden by job-specific configuration.) 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/help-completedStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status on the commit for when a build is completed. 3 | If the field is left blank then the default value is used instead. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/help-statusUrl.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom url instead of the Jenkins job url. 3 | If the desired url should be blank, use "--none--" to alert the trigger to use a blank url. 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-allowMembersOfWhitelistedOrgsAsAdmin.html: -------------------------------------------------------------------------------- 1 |
2 | Use this option to allow members of whitelisted organisations to behave like admins, 3 | i.e. whitelist users and trigger pull request testing. 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-onlyTriggerPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | When checked, only commenting the trigger phrase in the pull request will trigger a build. 3 | All other methods of triggering a pull request build are disabled. 4 |
5 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbExtension.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import hudson.model.AbstractDescribableImpl; 4 | 5 | public abstract class GhprbExtension extends AbstractDescribableImpl { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-blackListTargetBranches.html: -------------------------------------------------------------------------------- 1 |
2 | Adding branches to this blacklist allows you to prevent pull requests for specific branches.
3 | Supports regular expressions (e.g. 'master', 'feature-.*'). 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-triggerPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | When filled, commenting this phrase in the pull request will trigger a build. 3 | Matches case insensitively and supports regular expressions (e.g. .*(re)?run tests.*). 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbPublishJenkinsUrl/help-publishedURL.html: -------------------------------------------------------------------------------- 1 |
2 | Sometimes Jenkins is set up behind a firewall and normal access is restricted. 3 | This URL will be added to a comment appended to the Pull Request. 4 |
-------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | work/ 3 | 4 | # 5 | # Eclipse metadata. 6 | # 7 | .project 8 | .classpath 9 | .settings/ 10 | 11 | # 12 | # Eclipse and Maven build results 13 | # 14 | bin/ 15 | 16 | # IntelliJ metadata. 17 | *.iml 18 | *.ipr 19 | *.iws 20 | .idea/ 21 | 22 | # OS X 23 | .DS_Store -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/help-allowMergeWithoutTriggerPhrase.html: -------------------------------------------------------------------------------- 1 |
2 | Allow merging even if the trigger phrase is not present. 3 | This can be used with the permitAll ("Build every pull request automatically without asking (Dangerous!).") 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/help-commitStatusContext.html: -------------------------------------------------------------------------------- 1 |
2 | Context: 3 | A string label to differentiate this status from the status of other systems. Default: "default" 4 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help-commitStatusContext.html: -------------------------------------------------------------------------------- 1 |
2 | Context: 3 | A string label to differentiate this status from the status of other systems. Default: "default" 4 |
5 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-whiteListTargetBranches.html: -------------------------------------------------------------------------------- 1 |
2 | Adding branches to this whitelist allows you to selectively test pull requests destined for these branches 3 | only.
4 | Supports regular expressions (e.g. 'master', 'feature-.*'). 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbCommentFile/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /STYLE_GUIDE.md: -------------------------------------------------------------------------------- 1 | # Style Guide 2 | 3 | This project uses CheckStyle to validate the style of the code. 4 | 5 | Its automatically executed within the build process and 6 | will fail the build if it detects violations. 7 | 8 | You can import the `checkstyle.xml` into your IDE to ease using 9 | the guide. 10 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-useComments.html: -------------------------------------------------------------------------------- 1 |
2 | When checked, GitHub Pull Request Builder will try to add comment 3 | with results if updating commit status fails. This can happen when user 4 | doesn't have push rights for your repository. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbPublishJenkinsUrl/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildStatus/help-messages.html: -------------------------------------------------------------------------------- 1 |
2 | Global: Sets a default for each job, if the job setup has no entries then this is used.
3 |
4 | Add a text message to the comment posted to the pull request on build completion. 5 |
-------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbCommentAppender.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import hudson.model.Run; 4 | import hudson.model.TaskListener; 5 | 6 | public interface GhprbCommentAppender { 7 | String postBuildComment(Run build, TaskListener listener); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/builds/GhprbCancelBuildsOnUpdate/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/help-startedStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status for when a build is started. 3 | If the field is left blank then the default value is used instead. 4 | If no status updates should be made when a build is started, use "--none--" to alert the trigger. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/help-triggeredStatus.html: -------------------------------------------------------------------------------- 1 |
2 | Use a custom status for when a build is triggered. 3 | If the field is left blank then the default value is used instead. 4 | If no status updates should be made when a build is triggered, use "--none--" to alert the trigger. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildResultMessage/help-message.html: -------------------------------------------------------------------------------- 1 |
2 | The message that is appended to a comment when a build finishes with the desired build status. 3 | If no status updates should be made when a build finishes with the indicated 4 | build status, use "--none--" to alert the trigger. 5 |
6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildLog/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildLog/help-logExceprtLines.html: -------------------------------------------------------------------------------- 1 |
2 | Global: Sets the default number of lines to append to build status comments.
3 |
4 | Job: Sets the actual number of lines that are included from the build status. 5 | These lines are appended to a comment appended to the pull request when the build completes. 6 |
-------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbBuildStep.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import hudson.model.Action; 4 | import hudson.model.Job; 5 | import org.jenkinsci.plugins.ghprb.GhprbCause; 6 | 7 | public interface GhprbBuildStep extends Action { 8 | String BUILD_STEP = "GhprbBuildStep"; 9 | 10 | void onScheduleBuild(Job project, GhprbCause cause); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/impl/GhprbDefaultBuildManager.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.impl; 2 | 3 | import hudson.model.Run; 4 | 5 | /** 6 | * @author mdelapenya (Manuel de la Peña) 7 | */ 8 | public class GhprbDefaultBuildManager extends GhprbBaseBuildManager { 9 | 10 | public GhprbDefaultBuildManager(Run build) { 11 | super(build); 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildStatus/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildResultMessage/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 |
12 |
-------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-publishedURL.html: -------------------------------------------------------------------------------- 1 |
2 | If you don't want to see any comments, leave this field empty.
3 | When filled this plugin will send comment after build is finished.
4 | If your builds are done on private Jenkins instance (eg. behind firewall) 5 | and results are published on public Jenkins instance you can fill the public 6 | Jenkins url and when a build is finished it will post comment with link to 7 | the public Jenkins. 8 |
9 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbCommentFilePathContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | 5 | class GhprbCommentFilePathContext implements Context { 6 | private String commentFilePath; 7 | 8 | public String getCommentFilePath() { 9 | return commentFilePath; 10 | } 11 | 12 | /** 13 | * sets the path to the comment file 14 | */ 15 | public void commentFilePath(String commentFilePath) { 16 | this.commentFilePath = commentFilePath; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbCancelBuildsOnUpdateContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | 5 | class GhprbCancelBuildsOnUpdateContext implements Context { 6 | private Boolean overrideGlobal; 7 | 8 | public Boolean getOverrideGlobal() { 9 | return overrideGlobal; 10 | } 11 | 12 | /** 13 | * sets the overrideGlobal value 14 | */ 15 | public void overrideGlobal(Boolean overrideGlobal) { 16 | this.overrideGlobal = overrideGlobal; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/help.html: -------------------------------------------------------------------------------- 1 |
2 | Allows you to set a custom context and message on a pull request pulled using GHPRB. 3 |
This will add the context and message to the pull request found in the upstream job NOT on any pull requests 4 | taken on this job 5 |
To add a custom context and message on any pull requests on THIS job see the GHPRB section in the Build 6 | Triggers section 7 |
8 |
IMPORTANT: 9 |
This will only work if you configure a upstream job that uses the Github Pull Request Builder Plugin 10 |
11 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/global.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-excludedRegions.html: -------------------------------------------------------------------------------- 1 |
2 | Each exclusion uses regular expression pattern matching, and must be separated by a new line. 3 |
4 |
 5 |         myapp/src/main/web/.*\.html
 6 |         myapp/src/main/web/.*\.jpeg
 7 |         myapp/src/main/web/.*\.gif
 8 |     
9 |
10 | The example above illustrates that if only html/jpeg/gif files have been committed to 11 | the GitHub repository a build will not occur. 12 |
13 | More information on regular expressions can be found 14 | here. 15 |
16 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/rules/JenkinsRuleWithBuildFlow.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.rules; 2 | 3 | import com.cloudbees.plugins.flow.BuildFlow; 4 | import org.jvnet.hudson.test.JenkinsRule; 5 | 6 | import java.io.IOException; 7 | 8 | /** 9 | * @author mdelapenya (Manuel de la Peña) 10 | */ 11 | public class JenkinsRuleWithBuildFlow extends JenkinsRule { 12 | 13 | public BuildFlow createBuildFlowProject() throws IOException { 14 | return createBuildFlowProject(createUniqueProjectName()); 15 | } 16 | 17 | public BuildFlow createBuildFlowProject(String name) throws IOException { 18 | return jenkins.createProject(BuildFlow.class, name); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-includedRegions.html: -------------------------------------------------------------------------------- 1 |
2 | Each inclusion uses regular expression pattern matching, and must be separated by a new line. 3 | An empty list implies that everything is included. 4 |
5 |
 6 |         myapp/src/main/web/.*\.html
 7 |         myapp/src/main/web/.*\.jpeg
 8 |         myapp/src/main/web/.*\.gif
 9 |     
10 |
11 | The example above illustrates that a build will only occur, if html/jpeg/gif files 12 | have been committed to the GitHub repository. Exclusions take precedence over 13 | inclusions, if there is an overlap between included and excluded regions. 14 |
15 | More information on regular expressions can be found 16 | here. 17 |
18 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbCommitStatus.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import hudson.model.Job; 4 | import hudson.model.Run; 5 | import hudson.model.TaskListener; 6 | import org.kohsuke.github.GHRepository; 7 | 8 | public interface GhprbCommitStatus { 9 | void onEnvironmentSetup(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException; 10 | 11 | void onBuildTriggered(Job project, String commitSha, boolean isMergeable, int prId, GHRepository ghRepository) 12 | throws GhprbCommitStatusException; 13 | 14 | void onBuildStart(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException; 15 | 16 | void onBuildComplete(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException; 17 | } 18 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbPullRequestMerge/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/manager/configuration/JobConfigurationTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.configuration; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.fest.assertions.Assertions.assertThat; 6 | 7 | /** 8 | * @author mdelapenya (Manuel de la Peña) 9 | */ 10 | public class JobConfigurationTest { 11 | 12 | @Test 13 | public void shouldNotPrintStackTrace() { 14 | JobConfiguration jobConfiguration = JobConfiguration.builder().printStackTrace(false).build(); 15 | 16 | assertThat(jobConfiguration).isNotNull(); 17 | assertThat(jobConfiguration.printStackTrace()).isFalse(); 18 | } 19 | 20 | @Test 21 | public void shouldPrintStackTrace() { 22 | JobConfiguration jobConfiguration = JobConfiguration.builder().printStackTrace(true).build(); 23 | 24 | assertThat(jobConfiguration).isNotNull(); 25 | assertThat(jobConfiguration.printStackTrace()).isTrue(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/GhprbBuildManager.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager; 2 | 3 | import java.util.Iterator; 4 | 5 | /** 6 | * @author mdelapenya (Manuel de la Peña) 7 | */ 8 | public interface GhprbBuildManager { 9 | 10 | /** 11 | * Calculate the build URL of a build 12 | * 13 | * @param publishedURL the public jenkins url 14 | * @return the build URL 15 | */ 16 | String calculateBuildUrl(String publishedURL); 17 | 18 | /** 19 | * Returns downstream builds as an iterator 20 | * 21 | * @return the iterator 22 | */ 23 | Iterator downstreamProjects(); 24 | 25 | /** 26 | * Print tests result of a build in one line. 27 | * 28 | * @return the tests result 29 | */ 30 | String getOneLineTestResults(); 31 | 32 | /** 33 | * Print tests result of a build 34 | * 35 | * @return the tests result 36 | */ 37 | String getTestResults(); 38 | } 39 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbBranch.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import org.kohsuke.stapler.DataBoundConstructor; 7 | 8 | /** 9 | * @author Ray Sennewald 10 | * @author David Wang 11 | */ 12 | 13 | public class GhprbBranch extends AbstractDescribableImpl { 14 | private String branch; 15 | 16 | public String getBranch() { 17 | return branch; 18 | } 19 | 20 | public boolean matches(String s) { 21 | return s.matches(branch); 22 | } 23 | 24 | @DataBoundConstructor 25 | public GhprbBranch(String branch) { 26 | this.branch = branch.trim(); 27 | } 28 | 29 | @Extension 30 | public static class DescriptorImpl extends Descriptor { 31 | @Override 32 | public String getDisplayName() { 33 | return "Branch"; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/HttpConnectorWithJenkinsProxy.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.ProxyConfiguration; 4 | import org.kohsuke.github.HttpConnector; 5 | 6 | import java.io.IOException; 7 | import java.net.HttpURLConnection; 8 | import java.net.URL; 9 | 10 | public class HttpConnectorWithJenkinsProxy implements HttpConnector { 11 | 12 | private static final int DEFAULT_CONNECT_TIMEOUT = 10000; 13 | 14 | private static final int DEFAULT_READ_TIMEOUT = 10000; 15 | 16 | public HttpURLConnection connect(URL url) throws IOException { 17 | HttpURLConnection con = (HttpURLConnection) ProxyConfiguration.open(url); 18 | 19 | // Set default timeouts in case there are none 20 | if (con.getConnectTimeout() == 0) { 21 | con.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT); 22 | } 23 | if (con.getReadTimeout() == 0) { 24 | con.setReadTimeout(DEFAULT_READ_TIMEOUT); 25 | } 26 | return con; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, JBoss by Red Hat 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbBuildListener.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Run; 5 | import hudson.model.TaskListener; 6 | import hudson.model.listeners.RunListener; 7 | 8 | import javax.annotation.Nonnull; 9 | 10 | /** 11 | * @author janinko 12 | */ 13 | @Extension 14 | public class GhprbBuildListener extends RunListener> { 15 | 16 | @Override 17 | public void onStarted(Run build, TaskListener listener) { 18 | GhprbTrigger trigger = Ghprb.extractTrigger(build); 19 | if (trigger != null && trigger.getBuilds() != null) { 20 | trigger.getBuilds().onStarted(build, listener); 21 | } 22 | } 23 | 24 | @Override 25 | public void onCompleted(Run build, @Nonnull TaskListener listener) { 26 | GhprbTrigger trigger = Ghprb.extractTrigger(build); 27 | if (trigger != null && trigger.getBuilds() != null) { 28 | trigger.getBuilds().onCompleted(build, listener); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbCommitStatusException.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import org.kohsuke.github.GHCommitState; 4 | 5 | import java.io.IOException; 6 | 7 | public class GhprbCommitStatusException extends Exception { 8 | private static final long serialVersionUID = 6220095323686649609L; 9 | 10 | private final IOException exception; 11 | 12 | private final String message; 13 | 14 | private final GHCommitState state; 15 | 16 | private final int id; 17 | 18 | public GhprbCommitStatusException(IOException exception, GHCommitState state, String message, int id) { 19 | this.exception = exception; 20 | this.state = state; 21 | this.message = message; 22 | this.id = id; 23 | } 24 | 25 | public IOException getException() { 26 | return exception; 27 | } 28 | 29 | public String getMessage() { 30 | return message; 31 | } 32 | 33 | public GHCommitState getState() { 34 | return state; 35 | } 36 | 37 | public int getId() { 38 | return id; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbCrumbExclusion.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.Extension; 4 | import hudson.security.csrf.CrumbExclusion; 5 | 6 | import javax.servlet.FilterChain; 7 | import javax.servlet.ServletException; 8 | import javax.servlet.http.HttpServletRequest; 9 | import javax.servlet.http.HttpServletResponse; 10 | import java.io.IOException; 11 | 12 | /** 13 | * Excludes {@link GhprbRootAction} from the CSRF protection. 14 | * 15 | * @since 1.28 16 | */ 17 | @Extension 18 | public class GhprbCrumbExclusion extends CrumbExclusion { 19 | 20 | @Override 21 | public boolean process(HttpServletRequest req, HttpServletResponse resp, FilterChain chain) 22 | throws IOException, ServletException { 23 | final String pathInfo = req.getPathInfo(); 24 | if (pathInfo != null && pathInfo.startsWith(getExclusionPath())) { 25 | chain.doFilter(req, resp); 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | public String getExclusionPath() { 32 | return "/" + GhprbRootAction.URL + "/"; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbBuildStatusContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 5 | import org.kohsuke.github.GHCommitState; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | class GhprbBuildStatusContext implements Context { 11 | private List completedStatus = new ArrayList(); 12 | 13 | /** 14 | * Use a custom status for when a build is completed. Can be called multiple times to set messages for different 15 | * build results. Valid build results are {@code 'SUCCESS'}, {@code 'FAILURE'}, and {@code 'ERROR'}. 16 | */ 17 | void completedStatus(String buildResult, String message) { 18 | completedStatus.add(new GhprbBuildResultMessage( 19 | GHCommitState.valueOf(buildResult), 20 | message 21 | )); 22 | } 23 | 24 | public List getCompletedStatus() { 25 | return completedStatus; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatusDescriptor.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.status; 2 | 3 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 4 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 5 | import java.util.List; 6 | 7 | public abstract class GhprbSimpleStatusDescriptor extends GhprbExtensionDescriptor { 8 | 9 | public abstract String getDisplayName(); 10 | 11 | public abstract String getTriggeredStatusDefault(GhprbSimpleStatus local); 12 | 13 | public abstract String getStatusUrlDefault(GhprbSimpleStatus local); 14 | 15 | public abstract String getStartedStatusDefault(GhprbSimpleStatus local); 16 | 17 | public abstract Boolean getAddTestResultsDefault(GhprbSimpleStatus local); 18 | 19 | public abstract List getCompletedStatusDefault(GhprbSimpleStatus local); 20 | 21 | public abstract String getCommitStatusContextDefault(GhprbSimpleStatus local); 22 | 23 | public abstract Boolean getShowMatrixStatusDefault(GhprbSimpleStatus local); 24 | 25 | public abstract boolean addIfMissing(); 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/configuration/JobConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.configuration; 2 | 3 | /** 4 | * @author Miguel Pastor 5 | */ 6 | public final class JobConfiguration { 7 | 8 | public static PrintStackTrace builder() { 9 | return new JobConfigurationBuilder(); 10 | } 11 | 12 | private boolean printStacktrace; 13 | 14 | private JobConfiguration() { 15 | } 16 | 17 | public boolean printStackTrace() { 18 | return this.printStacktrace; 19 | } 20 | 21 | public interface PrintStackTrace { 22 | Build printStackTrace(boolean print); 23 | } 24 | 25 | public interface Build { 26 | JobConfiguration build(); 27 | } 28 | 29 | public static final class JobConfigurationBuilder implements Build, PrintStackTrace { 30 | 31 | private JobConfiguration jobConfiguration = new JobConfiguration(); 32 | 33 | public JobConfiguration build() { 34 | return jobConfiguration; 35 | } 36 | 37 | public Build printStackTrace(boolean print) { 38 | jobConfiguration.printStacktrace = print; 39 | return this; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/help-useGitHubHooks.html: -------------------------------------------------------------------------------- 1 |
2 | Checking this option will disable regular polling (cron) for changes in GitHub 3 | and will try to create a GitHub hook. Creating a GitHub hook requires that the user 4 | which is specified in the GitHub Pull Request Builder configuration has 5 | admin rights to the specified repository.
6 | By default, GitHub hooks created this way will have SSL certificate verification enabled. 7 | To disable SSL certificate verification in these GitHub hooks, start Jenkins with system 8 | property org.jenkinsci.plugins.ghprb.GhprbRepository.webhook.insecure set 9 | to true.
10 | If you want to create a hook manually set it for event types: 11 | issue_comment, pull_request 12 | and url < your jenkins server url >/ghprbhook/. The url should be composed of your full jenkins server url plus 13 | the ending /ghprbhook/ path. If your jenkins server is just a domain like https://jenkins.yourcompany.com then the url would look like 14 | https://jenkins.yourcompany.com/ghprbhook/. If your jenkins server is accessed at a path off of a domain like https://yourcompany.com/jenkins 15 | then the url would look like https://yourcompany.com/jenkins/ghprbhook/
. 16 | Your Jenkins server must be accessible from internet. 17 |
18 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatus/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/extensions/build/GhprbCancelBuildsOnUpdateTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.build; 2 | 3 | import hudson.model.FreeStyleProject; 4 | import org.jenkinsci.plugins.ghprb.GhprbITBaseTestCase; 5 | import org.junit.Before; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.jvnet.hudson.test.JenkinsRule; 10 | import org.mockito.runners.MockitoJUnitRunner; 11 | 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | import static org.mockito.BDDMockito.given; 16 | 17 | @RunWith(MockitoJUnitRunner.class) 18 | public class GhprbCancelBuildsOnUpdateTest extends GhprbITBaseTestCase { 19 | 20 | @Rule 21 | public JenkinsRule jenkinsRule = new JenkinsRule(); 22 | 23 | private FreeStyleProject project; 24 | 25 | private GhprbCancelBuildsOnUpdate gcbou; 26 | 27 | @Before 28 | public void setUp() throws Exception { 29 | project = jenkinsRule.getInstance().createProject(FreeStyleProject.class, "FSPRJ"); 30 | 31 | Map config = new HashMap<>(1); 32 | 33 | super.beforeTest(config, null, project); 34 | 35 | given(ghprbPullRequest.getPullRequestAuthor()).willReturn(ghUser); 36 | 37 | gcbou = new GhprbCancelBuildsOnUpdate(false); 38 | } 39 | 40 | @Test 41 | public void testCancelCurrentBuilds() { 42 | builds.build(ghprbPullRequest, ghUser, ""); 43 | gcbou.cancelCurrentBuilds(project, 1); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbGitHub.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import org.kohsuke.github.GHOrganization; 4 | import org.kohsuke.github.GHUser; 5 | 6 | import java.io.IOException; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | /** 11 | * @author janinko 12 | */ 13 | public class GhprbGitHub { 14 | private static final Logger LOGGER = Logger.getLogger(GhprbGitHub.class.getName()); 15 | 16 | private final GhprbTrigger trigger; 17 | 18 | public GhprbGitHub(GhprbTrigger trigger) { 19 | this.trigger = trigger; 20 | } 21 | 22 | public boolean isUserMemberOfOrganization(String organisation, GHUser member) { 23 | boolean orgHasMember = false; 24 | try { 25 | GHOrganization org = trigger.getGitHub().getOrganization(organisation); 26 | orgHasMember = org.hasMember(member); 27 | LOGGER.log(Level.FINE, "org.hasMember(member)? user:{0} org: {1} == {2}", 28 | new Object[] {member.getLogin(), organisation, orgHasMember ? "yes" : "no"}); 29 | 30 | } catch (IOException ex) { 31 | LOGGER.log(Level.SEVERE, null, ex); 32 | return false; 33 | } 34 | return orgHasMember; 35 | } 36 | 37 | public String getBotUserLogin() { 38 | try { 39 | return trigger.getGitHub().getMyself().getLogin(); 40 | } catch (IOException ex) { 41 | LOGGER.log(Level.SEVERE, null, ex); 42 | return null; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/factory/GhprbBuildManagerFactoryUtil.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.factory; 2 | 3 | import com.cloudbees.plugins.flow.FlowRun; 4 | import hudson.model.Run; 5 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 6 | import org.jenkinsci.plugins.ghprb.manager.configuration.JobConfiguration; 7 | import org.jenkinsci.plugins.ghprb.manager.impl.GhprbDefaultBuildManager; 8 | import org.jenkinsci.plugins.ghprb.manager.impl.downstreambuilds.BuildFlowBuildManager; 9 | 10 | /** 11 | * @author mdelapenya (Manuel de la Peña) 12 | */ 13 | public final class GhprbBuildManagerFactoryUtil { 14 | 15 | private GhprbBuildManagerFactoryUtil() { 16 | } 17 | 18 | /** 19 | * Gets an instance of a library that is able to calculate build urls depending of build type. 20 | *

21 | * If the class representing the build type is not present on the classloader then default implementation is returned. 22 | * 23 | * @param build the job from Jenkins 24 | * @return a buildManager 25 | */ 26 | public static GhprbBuildManager getBuildManager(Run build) { 27 | JobConfiguration jobConfiguration = JobConfiguration.builder().printStackTrace(false).build(); 28 | 29 | return getBuildManager(build, jobConfiguration); 30 | } 31 | 32 | public static GhprbBuildManager getBuildManager(Run build, JobConfiguration jobConfiguration) { 33 | try { 34 | if (build instanceof FlowRun) { 35 | return new BuildFlowBuildManager(build, jobConfiguration); 36 | } 37 | } catch (NoClassDefFoundError ncdfe) { 38 | } 39 | 40 | return new GhprbDefaultBuildManager(build); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus/config.jelly: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/manager/factory/GhprbBuildManagerFactoryUtilTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.factory; 2 | 3 | import com.cloudbees.plugins.flow.BuildFlow; 4 | import com.cloudbees.plugins.flow.FlowRun; 5 | import hudson.matrix.MatrixBuild; 6 | import hudson.matrix.MatrixProject; 7 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 8 | import org.jenkinsci.plugins.ghprb.manager.impl.GhprbDefaultBuildManager; 9 | import org.jenkinsci.plugins.ghprb.manager.impl.downstreambuilds.BuildFlowBuildManager; 10 | import org.jenkinsci.plugins.ghprb.rules.JenkinsRuleWithBuildFlow; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | 14 | import static org.fest.assertions.Assertions.assertThat; 15 | 16 | /** 17 | * @author mdelapenya (Manuel de la Peña) 18 | */ 19 | public class GhprbBuildManagerFactoryUtilTest { 20 | 21 | @Rule 22 | public JenkinsRuleWithBuildFlow jenkinsRule = new JenkinsRuleWithBuildFlow(); 23 | 24 | @Test 25 | public void shouldReturnDefaultManager() throws Exception { 26 | // GIVEN 27 | MatrixProject project = jenkinsRule.getInstance().createProject(MatrixProject.class, "PRJ"); 28 | 29 | GhprbBuildManager buildManager = GhprbBuildManagerFactoryUtil.getBuildManager(new MatrixBuild(project)); 30 | 31 | // THEN 32 | assertThat(buildManager).isInstanceOf(GhprbDefaultBuildManager.class); 33 | } 34 | 35 | @Test 36 | public void shouldReturnBuildFlowManager() throws Exception { 37 | // GIVEN 38 | BuildFlow buildFlowProject = jenkinsRule.createBuildFlowProject("BFPRJ"); 39 | 40 | GhprbBuildManager buildManager = GhprbBuildManagerFactoryUtil.getBuildManager(new FlowRun(buildFlowProject)); 41 | 42 | // THEN 43 | assertThat(buildManager).isInstanceOf(BuildFlowBuildManager.class); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbTokenMacro.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import com.google.common.collect.ListMultimap; 4 | import hudson.Extension; 5 | import hudson.model.AbstractBuild; 6 | import hudson.model.TaskListener; 7 | import org.jenkinsci.plugins.tokenmacro.DataBoundTokenMacro; 8 | import org.jenkinsci.plugins.tokenmacro.MacroEvaluationException; 9 | 10 | import java.io.IOException; 11 | import java.util.Map; 12 | 13 | /** 14 | * {@code PR_Name} token that expands to the PR Name. {@code PR_User} token that expands to the PR Opener's email. 15 | * 16 | * @author Josh Caldwell 17 | */ 18 | @Extension(optional = true) 19 | public class GhprbTokenMacro extends DataBoundTokenMacro { 20 | @Override 21 | public boolean acceptsMacroName(String macroName) { 22 | return macroName.equals("PR_Title") || macroName.equals("PR_Email"); 23 | } 24 | 25 | @Override 26 | public String evaluate( 27 | AbstractBuild abstractBuild, 28 | TaskListener taskListener, 29 | String s 30 | ) throws MacroEvaluationException, IOException, InterruptedException { 31 | return null; 32 | } 33 | 34 | @Override 35 | public String evaluate( 36 | AbstractBuild context, 37 | TaskListener listener, 38 | String macroName, 39 | Map arguments, 40 | ListMultimap argumentMultimap 41 | ) throws MacroEvaluationException, IOException, InterruptedException { 42 | GhprbCause cause = (GhprbCause) context.getCause(GhprbCause.class); 43 | if (cause == null) { 44 | return ""; 45 | } 46 | 47 | if (macroName.equals("PR_Title")) { 48 | return cause.getTitle(); 49 | } else if (macroName.equals("PR_Email")) { 50 | return cause.getAuthorEmail(); 51 | } else { 52 | return ""; 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/status/GhprbNoCommitStatus.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.status; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Job; 5 | import hudson.model.Run; 6 | import hudson.model.TaskListener; 7 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommitStatus; 8 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommitStatusException; 9 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; 12 | import org.kohsuke.github.GHRepository; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | 15 | public class GhprbNoCommitStatus extends GhprbExtension implements GhprbCommitStatus, GhprbProjectExtension { 16 | @Extension 17 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 18 | 19 | @DataBoundConstructor 20 | public GhprbNoCommitStatus() { 21 | 22 | } 23 | 24 | public void onBuildStart(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException { 25 | 26 | } 27 | 28 | public void onBuildComplete(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException { 29 | 30 | } 31 | 32 | public void onEnvironmentSetup(Run build, TaskListener listener, GHRepository repo) throws GhprbCommitStatusException { 33 | 34 | } 35 | 36 | public void onBuildTriggered( 37 | Job project, 38 | String commitSha, 39 | boolean isMergeable, 40 | int prId, 41 | GHRepository ghRepository 42 | ) throws GhprbCommitStatusException { 43 | 44 | } 45 | 46 | @Override 47 | public DescriptorImpl getDescriptor() { 48 | return DESCRIPTOR; 49 | } 50 | 51 | public static final class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbProjectExtension { 52 | 53 | @Override 54 | public String getDisplayName() { 55 | return "Do not update commit status"; 56 | } 57 | 58 | } 59 | 60 | 61 | } 62 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ====== 3 | 4 | This document describes contribution and development for this plugin. 5 | 6 | What we expect of each other 7 | ----- 8 | 9 | #### Contributors 10 | * Pull requests should describe the problem and how it's being solved 11 | * Pull requests with code should include tests (positive, negative, etc) for the conditions being introduced 12 | * Pull requests should call out any new requirements as part of the change 13 | * CHANGELOG additions would be appreciated 14 | * Contributors are still human; things will go wrong & that's ok 15 | * When things go wrong, contributors should be willing to help solve them if their changes were invovled. 16 | 17 | #### Maintainers 18 | * We promise to give some response in a timely manner (even if it's "I can't help right now") 19 | * When a change needs to be reverted, or a regression is introduced, we promise to contact the original contributor 20 | * If we need to refactor a pull request, we'll work hard to maintain the original contributor's commits 21 | * We are still human; things will go wrong & that's ok 22 | * When things go wrong, we will help solve them 23 | 24 | 25 | Things to help you develop 26 | ---- 27 | 28 | #### Starter 29 | * As always, you may find the Jenkins development wiki docs to be helpful. For starters, here's [Plugin Development](https://wiki.jenkins-ci.org/display/JENKINS/Plugin+tutorial) 30 | * Much of the plugin relies on the Jenkins [Github-API](https://github.com/kohsuke/github-api) library. Reading through that can give you a good perspective on what ghprb can be capable of doing. 31 | 32 | #### Tips 33 | * There are many interactions with GitHub as part of the plugin, and we currently don't have a test harness that can stand in place of the GitHub API or cloning. If running an instance on localhost (for example, using a docker image or using hpi:run in an IDE), you can use ngrok, as described on [GitHub docs](https://developer.github.com/webhooks/configuring/#using-ngrok). That can be helpful for catching webhooks and directing them to the localhost instance. (Remember you're opening your localhost to the world, and we assume you understand the risks and the local firewall/port restrictions.) 34 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GeneralTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import org.jenkinsci.plugins.ghprb.extensions.build.GhprbCancelBuildsOnUpdate; 4 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildLog; 5 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 6 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildStatus; 7 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbCommentFile; 8 | import org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.mockito.runners.MockitoJUnitRunner; 12 | 13 | import java.util.List; 14 | 15 | import static org.fest.assertions.Assertions.assertThat; 16 | 17 | @RunWith(MockitoJUnitRunner.class) 18 | public class GeneralTest { 19 | 20 | private void checkClassForGetters(Class clazz) { 21 | List errors = GhprbTestUtil.checkClassForGetters(clazz); 22 | assertThat(errors).isEmpty(); 23 | } 24 | 25 | @Test 26 | public void checkTriggerForGetters() { 27 | checkClassForGetters(GhprbTrigger.class); 28 | } 29 | 30 | @Test 31 | public void checkTriggerDescriptorForGetters() { 32 | checkClassForGetters(GhprbTrigger.DescriptorImpl.class); 33 | } 34 | 35 | @Test 36 | public void checkPullRequestMergeForGetters() { 37 | checkClassForGetters(GhprbPullRequestMerge.class); 38 | } 39 | 40 | @Test 41 | public void checkBuildLogForGetters() { 42 | checkClassForGetters(GhprbBuildLog.class); 43 | } 44 | 45 | @Test 46 | public void checkBuildResultMessageForGetters() { 47 | checkClassForGetters(GhprbBuildResultMessage.class); 48 | } 49 | 50 | @Test 51 | public void checkBuildStatusForGetters() { 52 | checkClassForGetters(GhprbBuildStatus.class); 53 | } 54 | 55 | @Test 56 | public void checkCommentFileForGetters() { 57 | checkClassForGetters(GhprbCommentFile.class); 58 | } 59 | 60 | @Test 61 | public void checkSimpleStatusForGetters() { 62 | checkClassForGetters(GhprbSimpleStatus.class); 63 | } 64 | 65 | @Test 66 | public void checkCancelBuildsOnUpdateForGetters() { 67 | checkClassForGetters(GhprbCancelBuildsOnUpdate.class); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildStatus.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.comments; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Run; 5 | import hudson.model.TaskListener; 6 | import org.jenkinsci.plugins.ghprb.Ghprb; 7 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 8 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 9 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalExtension; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; 12 | import org.kohsuke.stapler.DataBoundConstructor; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | public class GhprbBuildStatus extends GhprbExtension implements GhprbCommentAppender, GhprbGlobalExtension, GhprbProjectExtension { 18 | 19 | @Extension 20 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 21 | 22 | private final List messages; 23 | 24 | @DataBoundConstructor 25 | public GhprbBuildStatus(List messages) { 26 | this.messages = messages; 27 | } 28 | 29 | public List getMessages() { 30 | return messages == null ? new ArrayList(0) : messages; 31 | } 32 | 33 | public String postBuildComment(Run build, TaskListener listener) { 34 | StringBuilder msg = new StringBuilder(); 35 | 36 | List messages = getDescriptor().getMessagesDefault(this); 37 | 38 | for (GhprbBuildResultMessage message : messages) { 39 | msg.append(message.postBuildComment(build, listener)); 40 | } 41 | 42 | return msg.toString(); 43 | } 44 | 45 | @Override 46 | public DescriptorImpl getDescriptor() { 47 | return DESCRIPTOR; 48 | } 49 | 50 | public static class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbGlobalExtension, GhprbProjectExtension { 51 | 52 | @Override 53 | public String getDisplayName() { 54 | return "Build Status Messages"; 55 | } 56 | 57 | public List getMessagesDefault(GhprbBuildStatus local) { 58 | return Ghprb.getDefaultValue(local, GhprbBuildStatus.class, "getMessages"); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbParametersAction.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.EnvVars; 4 | import hudson.Extension; 5 | import hudson.model.EnvironmentContributor; 6 | import hudson.model.ParameterValue; 7 | import hudson.model.ParametersAction; 8 | import hudson.model.Run; 9 | import hudson.model.TaskListener; 10 | import org.kohsuke.accmod.Restricted; 11 | import org.kohsuke.accmod.restrictions.NoExternalUse; 12 | 13 | import javax.annotation.Nonnull; 14 | import java.io.IOException; 15 | import java.util.Arrays; 16 | import java.util.Collections; 17 | import java.util.List; 18 | 19 | 20 | @Restricted(NoExternalUse.class) 21 | public class GhprbParametersAction extends ParametersAction { 22 | 23 | private List parameters; 24 | 25 | public GhprbParametersAction(List parameters) { 26 | super(parameters); 27 | this.parameters = parameters; 28 | } 29 | 30 | public GhprbParametersAction(ParameterValue... parameters) { 31 | this(Arrays.asList(parameters)); 32 | } 33 | 34 | @Override 35 | public List getParameters() { 36 | return Collections.unmodifiableList(parameters); 37 | } 38 | 39 | @Override 40 | public ParameterValue getParameter(String name) { 41 | for (ParameterValue parameter : parameters) { 42 | if (parameter != null && parameter.getName().equals(name)) { 43 | return parameter; 44 | } 45 | } 46 | 47 | return null; 48 | } 49 | 50 | @Extension 51 | public static final class GhprbAdditionalParameterEnvironmentContributor extends EnvironmentContributor { 52 | 53 | // See SECURITY-170 54 | 55 | @Override 56 | @SuppressWarnings("rawtypes") 57 | public void buildEnvironmentFor(@Nonnull Run run, 58 | @Nonnull EnvVars envs, 59 | @Nonnull TaskListener listener) throws IOException, InterruptedException { 60 | 61 | GhprbParametersAction gpa = run.getAction(GhprbParametersAction.class); 62 | if (gpa != null) { 63 | for (ParameterValue p : gpa.getParameters()) { 64 | envs.put(p.getName(), String.valueOf(p.getValue())); 65 | } 66 | } 67 | super.buildEnvironmentFor(run, envs, listener); 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /AUTHORS.md: -------------------------------------------------------------------------------- 1 | - Aaron Boushley 2 | - Aaron France 3 | - Adamos Loizou 4 | - Adrian Bridgett 5 | - Albert Lee 6 | - Alex Rodionov 7 | - Alex Rønne Petersen 8 | - Alexander Bezrodniy 9 | - Alexander Kamyanskiy 10 | - Alexander Köplinger 11 | - Andrei Dulvac 12 | - Andrew Coulton 13 | - Andrey Baidarov 14 | - Antoine Duprat 15 | - Anton Chikin 16 | - Arcadiy Ivanov 17 | - AtkinsChang 18 | - Ben Patterson 19 | - Benjamin Hipple 20 | - Björn Häuser 21 | - Brandon Turner 22 | - C Nelson 23 | - Callum 24 | - Carl-Magnus Björkell 25 | - Chris Christensen 26 | - Chris Smouse 27 | - Dan Dumont 28 | - Daniel Spilker 29 | - David Tanner 30 | - Dirk Thomas 31 | - Dmitriy Rozhkov 32 | - Ed Hartwell Goose 33 | - Emanuele Zattin 34 | - Eric Tam 35 | - Federico Fissore 36 | - Felix Belzunce Arcos 37 | - Félix Belzunce Arcos 38 | - Frank Lee 39 | - Fredric Silberberg 40 | - George Hartzell 41 | - Greg Fogelberg 42 | - Guillaume Jacquet 43 | - Herman Radtke 44 | - Honza Brázdil 45 | - Jeff Grafton 46 | - Jesse Glick 47 | - Jo Shields 48 | - John Kelly 49 | - Jon San Miguel 50 | - Jonathan Lebon 51 | - Jose Molina 52 | - Josh Caldwell 53 | - Juan Sebastian Cadena 54 | - Justin Downing 55 | - Justin Leitgeb 56 | - Kazunori Kajihiro 57 | - Ken Dreyer 58 | - Kevin Falcone 59 | - Kevin Suwala 60 | - Kohsuke Kawaguchi 61 | - Kyle Havlovitz 62 | - Manuel de la Peña 63 | - Mark Herhold 64 | - Mark Janssen 65 | - Martijn de Vos 66 | - Mason Malone 67 | - Mathias Leppich 68 | - Matt Farmer 69 | - Matt Mitchell 70 | - Matthias Mailänder 71 | - Memphiz 72 | - Michael Vorburger 73 | - Miguel Angel Pastor Olivar 74 | - Nick Zeljkovic 75 | - Nicolas De Loof 76 | - Nikolay Bryskin 77 | - Nirav Shah 78 | - OHTAKE Tomohiro 79 | - Oliver Gondža 80 | - Patrice Bouillet 81 | - Patrick Butkiewicz 82 | - Pedro Algarvio 83 | - Pierre Dal-Pra 84 | - Radzislaw Galler 85 | - Ray Sennewald 86 | - Robin Neatherway 87 | - Rodrigo Campos 88 | - Ryan Taylor 89 | - Sang Jun Lee 90 | - Sebastien Launay 91 | - Shannon Carey 92 | - Szymon Acedański 93 | - Tamas Flamich 94 | - Thomas Heute 95 | - Timothy Lusk 96 | - Todd Tomkinson 97 | - Travis Johnson 98 | - Trevor Baker 99 | - Tugdual Saunier 100 | - Udi Schneider 101 | - Valdis Rigdon 102 | - Vaughn Dice 103 | - Vojtech Juranek 104 | - ZiLing Kang 105 | - aha-oretama 106 | - alex-bezrodniy 107 | - amritp55 108 | - christ66 109 | - codemercenary 110 | - itIGO 111 | - navyasruti 112 | - pawel-baster 113 | - ray.sennewald@gmail.com 114 | - rehevkor5 115 | - rsennewald 116 | - rholder 117 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/manager/impl/GhprbDefaultBuildManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.impl; 2 | 3 | import hudson.matrix.MatrixBuild; 4 | import hudson.matrix.MatrixProject; 5 | import org.jenkinsci.plugins.ghprb.GhprbITBaseTestCase; 6 | import org.jenkinsci.plugins.ghprb.GhprbTestUtil; 7 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 8 | import org.jenkinsci.plugins.ghprb.manager.factory.GhprbBuildManagerFactoryUtil; 9 | import org.junit.Before; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | import org.jvnet.hudson.test.JenkinsRule; 14 | import org.mockito.runners.MockitoJUnitRunner; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | import static org.fest.assertions.Assertions.assertThat; 20 | import static org.mockito.BDDMockito.given; 21 | 22 | /** 23 | * @author mdelapenya (Manuel de la Peña) 24 | */ 25 | @RunWith(MockitoJUnitRunner.class) 26 | public class GhprbDefaultBuildManagerTest extends GhprbITBaseTestCase { 27 | 28 | @Rule 29 | public JenkinsRule jenkinsRule = new JenkinsRule(); 30 | 31 | private MatrixProject project; 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | // GhprbTestUtil.mockGithubUserPage(); 36 | project = jenkinsRule.getInstance().createProject(MatrixProject.class, "MTXPRJ"); 37 | 38 | Map config = new HashMap<>(1); 39 | 40 | config.put("publishedURL", "defaultPublishedURL"); 41 | super.beforeTest(config, null, project); 42 | } 43 | 44 | @Test 45 | public void shouldCalculateUrlFromDefault() throws Exception { 46 | 47 | // GIVEN 48 | givenThatGhprbHasBeenTriggeredForAMatrixProject(); 49 | 50 | // THEN 51 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 52 | 53 | MatrixBuild matrixBuild = project.getBuilds().getFirstBuild(); 54 | 55 | GhprbBuildManager buildManager = GhprbBuildManagerFactoryUtil.getBuildManager(matrixBuild); 56 | 57 | assertThat(buildManager).isInstanceOf(GhprbDefaultBuildManager.class); 58 | 59 | assertThat(buildManager.calculateBuildUrl("defaultPublishedURL")).isEqualTo("defaultPublishedURL/" + matrixBuild.getUrl()); 60 | } 61 | 62 | private void givenThatGhprbHasBeenTriggeredForAMatrixProject() throws Exception { 63 | given(commitPointer.getSha()).willReturn("sha"); 64 | 65 | given(ghPullRequest.getNumber()).willReturn(1); 66 | 67 | given(ghRepository.getPullRequest(1)).willReturn(ghPullRequest); 68 | 69 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 70 | 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbSimpleStatusContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 5 | import org.kohsuke.github.GHCommitState; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | class GhprbSimpleStatusContext implements Context { 11 | Boolean showMatrixStatus; 12 | 13 | String context; 14 | 15 | String triggeredStatus; 16 | 17 | String startedStatus; 18 | 19 | String statusUrl; 20 | 21 | Boolean addTestResults; 22 | 23 | List completedStatus = new ArrayList(); 24 | 25 | /** 26 | * A boolean to indicate whether we only want to post commit statuses on the Matrix parent job 27 | */ 28 | void showMatrixStatus(Boolean showMatrixStatus) { 29 | this.showMatrixStatus = showMatrixStatus; 30 | } 31 | 32 | /** 33 | * A boolean to indicate whether we only want to post commit statuses on the Matrix parent job 34 | */ 35 | void showMatrixStatus() { 36 | this.showMatrixStatus = true; 37 | } 38 | 39 | /** 40 | * A string label to differentiate this status from the status of other systems. 41 | */ 42 | void context(String context) { 43 | this.context = context; 44 | } 45 | 46 | /** 47 | * Use a custom status for when a build is triggered. 48 | */ 49 | void triggeredStatus(String triggeredStatus) { 50 | this.triggeredStatus = triggeredStatus; 51 | } 52 | 53 | /** 54 | * Use a custom status for when a build is started. 55 | */ 56 | void startedStatus(String startedStatus) { 57 | this.startedStatus = startedStatus; 58 | } 59 | 60 | /** 61 | * Use a custom URL instead of the job default. 62 | */ 63 | void statusUrl(String statusUrl) { 64 | this.statusUrl = statusUrl; 65 | } 66 | 67 | /** 68 | * Add the test results as one line if available 69 | */ 70 | void addTestResults(Boolean addTestResults) { 71 | this.addTestResults = addTestResults; 72 | } 73 | 74 | /** 75 | * Use a custom status for when a build is completed. Can be called multiple times to set messages for different 76 | * build results. Valid build results are {@code 'SUCCESS'}, {@code 'FAILURE'}, and {@code 'ERROR'}. 77 | */ 78 | void completedStatus(String buildResult, String message) { 79 | completedStatus.add(new GhprbBuildResultMessage( 80 | GHCommitState.valueOf(buildResult), 81 | message 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbUpstreamStatusContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 5 | import org.kohsuke.github.GHCommitState; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | class GhprbUpstreamStatusContext implements Context { 11 | Boolean showMatrixStatus = false; 12 | 13 | String context; 14 | 15 | String triggeredStatus; 16 | 17 | String startedStatus; 18 | 19 | String statusUrl; 20 | 21 | Boolean addTestResults; 22 | 23 | List completedStatus = new ArrayList(); 24 | 25 | /** 26 | * A boolean to indicate whether we only want to post commit statuses on the Matrix parent job 27 | */ 28 | void showMatrixStatus(Boolean showMatrixStatus) { 29 | this.showMatrixStatus = showMatrixStatus; 30 | } 31 | 32 | /** 33 | * A boolean to indicate whether we only want to post commit statuses on the Matrix parent job 34 | */ 35 | void showMatrixStatus() { 36 | this.showMatrixStatus = true; 37 | } 38 | 39 | /** 40 | * A string label to differentiate this status from the status of other systems. 41 | */ 42 | void context(String context) { 43 | this.context = context; 44 | } 45 | 46 | /** 47 | * Use a custom status for when a build is triggered. 48 | */ 49 | void triggeredStatus(String triggeredStatus) { 50 | this.triggeredStatus = triggeredStatus; 51 | } 52 | 53 | /** 54 | * Use a custom status for when a build is started. 55 | */ 56 | void startedStatus(String startedStatus) { 57 | this.startedStatus = startedStatus; 58 | } 59 | 60 | /** 61 | * Use a custom URL instead of the job default. 62 | */ 63 | void statusUrl(String statusUrl) { 64 | this.statusUrl = statusUrl; 65 | } 66 | 67 | /** 68 | * Add the test results as one line if available 69 | */ 70 | void addTestResults(Boolean addTestResults) { 71 | this.addTestResults = addTestResults; 72 | } 73 | 74 | /** 75 | * Use a custom status for when a build is completed. Can be called multiple times to set messages for different 76 | * build results. Valid build results are {@code 'SUCCESS'}, {@code 'FAILURE'}, and {@code 'ERROR'}. 77 | */ 78 | void completedStatus(String buildResult, String message) { 79 | completedStatus.add(new GhprbBuildResultMessage( 80 | GHCommitState.valueOf(buildResult), 81 | message 82 | )); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbExtensionContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | import javaposse.jobdsl.plugin.ContextExtensionPoint; 5 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 6 | import org.jenkinsci.plugins.ghprb.extensions.build.GhprbCancelBuildsOnUpdate; 7 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildStatus; 8 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbCommentFile; 9 | import org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus; 10 | 11 | import java.util.ArrayList; 12 | import java.util.List; 13 | 14 | class GhprbExtensionContext implements Context { 15 | private List extensions = new ArrayList(); 16 | 17 | /** 18 | * Updates the commit status during the build. 19 | */ 20 | void commitStatus(Runnable closure) { 21 | GhprbSimpleStatusContext context = new GhprbSimpleStatusContext(); 22 | ContextExtensionPoint.executeInContext(closure, context); 23 | 24 | extensions.add(new GhprbSimpleStatus( 25 | context.showMatrixStatus, 26 | context.context, 27 | context.statusUrl, 28 | context.triggeredStatus, 29 | context.startedStatus, 30 | context.addTestResults, 31 | context.completedStatus 32 | )); 33 | } 34 | 35 | /** 36 | * Adds build result messages 37 | */ 38 | void buildStatus(Runnable closure) { 39 | GhprbBuildStatusContext context = new GhprbBuildStatusContext(); 40 | ContextExtensionPoint.executeInContext(closure, context); 41 | 42 | extensions.add(new GhprbBuildStatus(context.getCompletedStatus())); 43 | } 44 | 45 | /** 46 | * Adds comment file path handling 47 | */ 48 | void commentFilePath(Runnable closure) { 49 | GhprbCommentFilePathContext context = new GhprbCommentFilePathContext(); 50 | ContextExtensionPoint.executeInContext(closure, context); 51 | 52 | extensions.add(new GhprbCommentFile(context.getCommentFilePath())); 53 | } 54 | 55 | /** 56 | * Overrides global settings for cancelling builds when a PR was updated 57 | */ 58 | void cancelBuildsOnUpdate(Runnable closure) { 59 | GhprbCancelBuildsOnUpdateContext context = new GhprbCancelBuildsOnUpdateContext(); 60 | ContextExtensionPoint.executeInContext(closure, context); 61 | 62 | extensions.add(new GhprbCancelBuildsOnUpdate(context.getOverrideGlobal())); 63 | } 64 | 65 | public List getExtensions() { 66 | return extensions; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/GhprbExtensionDescriptor.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions; 2 | 3 | import hudson.DescriptorExtensionList; 4 | import hudson.model.Descriptor; 5 | import jenkins.model.Jenkins; 6 | import org.apache.commons.collections.Predicate; 7 | import org.apache.commons.collections.PredicateUtils; 8 | import org.apache.commons.collections.functors.InstanceofPredicate; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | 14 | @SuppressWarnings("unchecked") 15 | public abstract class GhprbExtensionDescriptor extends Descriptor { 16 | public boolean isApplicable(Class type) { 17 | return true; 18 | } 19 | 20 | public static List getExtensions(Class... types) { 21 | List list = getExtensions(); 22 | filterExtensions(list, types); 23 | return list; 24 | } 25 | 26 | private static void filterExtensions(List descriptors, Class... types) { 27 | List predicates = new ArrayList(types.length); 28 | for (Class type : types) { 29 | predicates.add(InstanceofPredicate.getInstance(type)); 30 | 31 | } 32 | Predicate anyPredicate = PredicateUtils.anyPredicate(predicates); 33 | Iterator iter = descriptors.iterator(); 34 | while (iter.hasNext()) { 35 | GhprbExtensionDescriptor descriptor = iter.next(); 36 | if (!anyPredicate.evaluate(descriptor)) { 37 | iter.remove(); 38 | } 39 | } 40 | } 41 | 42 | private static DescriptorExtensionList getExtensionList() { 43 | return Jenkins.getInstance().getDescriptorList(GhprbExtension.class); 44 | } 45 | 46 | /** 47 | * Don't mutate the list from Jenkins, they will persist; 48 | * 49 | * @return list of extensions 50 | */ 51 | private static List getExtensions() { 52 | List list = new ArrayList(); 53 | list.addAll(getExtensionList()); 54 | return list; 55 | } 56 | 57 | public static List allProject() { 58 | List list = getExtensions(); 59 | filterExtensions(list, GhprbProjectExtension.class); 60 | return list; 61 | } 62 | 63 | public static List allGlobal() { 64 | List list = getExtensions(); 65 | filterExtensions(list, GhprbGlobalExtension.class); 66 | return list; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/global.groovy: -------------------------------------------------------------------------------- 1 | j = namespace("jelly:core") 2 | f = namespace("/lib/form") 3 | 4 | 5 | f.section(title: descriptor.displayName) { 6 | f.entry(field: "githubAuth", title: _("GitHub Auth")) { 7 | f.repeatableProperty(field: "githubAuth", default: descriptor.getGithubAuth()) 8 | } 9 | f.entry(field: "manageWebhooks", title: _("Auto-manage webhooks")) { 10 | f.checkbox(default: true) 11 | } 12 | f.entry(field: "useComments", title: _("Use comments to report results when updating commit status fails")) { 13 | f.checkbox() 14 | } 15 | f.entry(field: "useDetailedComments", title: _("Use comments to report intermediate phases: triggered et al")) { 16 | f.checkbox() 17 | } 18 | f.entry(field: "adminlist", title: _("Admin list")) { 19 | f.textarea() 20 | } 21 | f.advanced() { 22 | f.entry(field: "unstableAs", title: _("Mark Unstable build in github as")) { 23 | f.select() 24 | } 25 | f.entry(field: "autoCloseFailedPullRequests", title: _("Close failed pull request automatically?")) { 26 | f.checkbox() 27 | } 28 | f.entry(field: "displayBuildErrorsOnDownstreamBuilds", title: _("Display build errors on downstream builds?")) { 29 | f.checkbox() 30 | } 31 | f.entry(field: "requestForTestingPhrase", title: _("Request for testing phrase")) { 32 | f.textarea(default: "Can one of the admins verify this patch?") 33 | } 34 | f.entry(field: "whitelistPhrase", title: _("Add to white list phrase")) { 35 | f.textbox(default: ".*add\\W+to\\W+whitelist.*") 36 | } 37 | f.entry(field: "okToTestPhrase", title: _("Accept to test phrase")) { 38 | f.textbox(default: ".*ok\\W+to\\W+test.*") 39 | } 40 | f.entry(field: "retestPhrase", title: _("Test phrase")) { 41 | f.textbox(default: ".*test\\W+this\\W+please.*") 42 | } 43 | f.entry(field: "skipBuildPhrase", title: _("Skip build phrase")) { 44 | f.textbox(default: ".*\\[skip\\W+ci\\].*") 45 | } 46 | f.entry(field: "cron", title: _("Crontab line"), help: "/descriptor/hudson.triggers.TimerTrigger/help/spec") { 47 | f.textbox(default: "H/5 * * * *", checkUrl: "'descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=' + encodeURIComponent(this.value)") 48 | } 49 | f.entry(field: "blackListCommitAuthor", title: _("Blacklist commit authors")) { 50 | f.textbox(default: "") 51 | } 52 | f.entry(field: "blackListLabels", title: _("List of GitHub labels for which the build should not be triggered.")) { 53 | f.textarea() 54 | } 55 | f.entry(field: "whiteListLabels", title: _("List of GitHub labels for which the build should only be triggered. (Leave blank for 'any')")) { 56 | f.textarea() 57 | } 58 | } 59 | f.entry(title: _("Application Setup")) { 60 | f.hetero_list(items: descriptor.extensions, name: "extensions", oneEach: "true", hasHeader: "true", descriptors: descriptor.getGlobalExtensionDescriptors()) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbPublishJenkinsUrl.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.comments; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Result; 5 | import hudson.model.Run; 6 | import hudson.model.TaskListener; 7 | import org.jenkinsci.plugins.ghprb.Ghprb; 8 | import org.jenkinsci.plugins.ghprb.GhprbTrigger; 9 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 12 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalExtension; 13 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 14 | import org.jenkinsci.plugins.ghprb.manager.configuration.JobConfiguration; 15 | import org.jenkinsci.plugins.ghprb.manager.factory.GhprbBuildManagerFactoryUtil; 16 | import org.kohsuke.stapler.DataBoundConstructor; 17 | 18 | public class GhprbPublishJenkinsUrl extends GhprbExtension implements GhprbCommentAppender, GhprbGlobalExtension { 19 | 20 | @Extension 21 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 22 | 23 | private final String publishedURL; 24 | 25 | @DataBoundConstructor 26 | public GhprbPublishJenkinsUrl(String publishedURL) { 27 | this.publishedURL = publishedURL; 28 | } 29 | 30 | public String getPublishedURL() { 31 | return publishedURL; 32 | } 33 | 34 | public String postBuildComment(Run build, TaskListener listener) { 35 | return "\nRefer to this link for build results (access rights to CI server needed): \n" 36 | + generateCustomizedMessage(build) + "\n"; 37 | } 38 | 39 | public boolean addIfMissing() { 40 | return false; 41 | } 42 | 43 | private String generateCustomizedMessage(Run build) { 44 | GhprbTrigger trigger = Ghprb.extractTrigger(build); 45 | if (trigger == null) { 46 | return ""; 47 | } 48 | JobConfiguration jobConfiguration = JobConfiguration.builder() 49 | .printStackTrace(trigger.getDisplayBuildErrorsOnDownstreamBuilds()).build(); 50 | 51 | GhprbBuildManager buildManager = GhprbBuildManagerFactoryUtil.getBuildManager(build, jobConfiguration); 52 | 53 | StringBuilder sb = new StringBuilder(); 54 | 55 | sb.append(buildManager.calculateBuildUrl(publishedURL)); 56 | 57 | if (build.getResult() != Result.SUCCESS) { 58 | sb.append(buildManager.getTestResults()); 59 | } 60 | 61 | return sb.toString(); 62 | } 63 | 64 | @Override 65 | public DescriptorImpl getDescriptor() { 66 | return DESCRIPTOR; 67 | } 68 | 69 | public static final class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbGlobalExtension { 70 | @Override 71 | public String getDisplayName() { 72 | return "Add link to Jenkins"; 73 | } 74 | 75 | public boolean addIfMissing() { 76 | return false; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GhprbBuildsTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.model.Build; 4 | import hudson.model.TaskListener; 5 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildStatus; 6 | import org.junit.Before; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.jvnet.hudson.test.JenkinsRule; 11 | import org.kohsuke.github.GHCommitState; 12 | import org.mockito.Mock; 13 | import org.mockito.Mockito; 14 | import org.mockito.runners.MockitoJUnitRunner; 15 | 16 | import java.io.PrintStream; 17 | 18 | import static org.mockito.BDDMockito.given; 19 | import static org.mockito.Matchers.anyInt; 20 | import static org.mockito.Matchers.anyString; 21 | import static org.mockito.Mockito.doNothing; 22 | import static org.mockito.Mockito.never; 23 | import static org.mockito.Mockito.times; 24 | import static org.mockito.Mockito.verify; 25 | 26 | 27 | /** 28 | * Unit test for {@link org.jenkinsci.plugins.ghprb.GhprbBuilds}. 29 | */ 30 | @RunWith(MockitoJUnitRunner.class) 31 | public class GhprbBuildsTest { 32 | 33 | @Mock 34 | private GhprbRepository repo; 35 | 36 | @Mock 37 | private GhprbCause cause; 38 | 39 | @Mock 40 | private GhprbBuildStatus appender; 41 | 42 | @Mock 43 | private TaskListener listener; 44 | 45 | @Mock 46 | private Build build; 47 | 48 | @Mock 49 | private PrintStream stream; 50 | 51 | @Rule 52 | public JenkinsRule jenkinsRule = new JenkinsRule(); 53 | 54 | private GhprbTrigger trigger; 55 | 56 | private GHCommitState state = GHCommitState.SUCCESS; 57 | 58 | @Before 59 | public void setup() throws Exception { 60 | // Mock trigger and add a mocked appender. 61 | trigger = GhprbTestUtil.getTrigger(); 62 | trigger.getExtensions().add(appender); 63 | 64 | // Mocks for GhprbRepository 65 | doNothing().when(repo).addComment(anyInt(), anyString()); 66 | 67 | // Mock out the logger. 68 | given(listener.getLogger()).willReturn(stream); 69 | doNothing().when(stream).println(anyString()); 70 | } 71 | 72 | @Test 73 | public void testCommentOnBuildResultWithSkip() { 74 | String testMessage = "--none--"; 75 | given(appender.postBuildComment(build, listener)).willReturn(testMessage); 76 | 77 | // WHEN 78 | GhprbBuilds builds = new GhprbBuilds(trigger, repo); 79 | builds.commentOnBuildResult(build, listener, state, cause); 80 | 81 | // THEN 82 | verify(repo, never()).addComment(Mockito.anyInt(), anyString()); 83 | } 84 | 85 | @Test 86 | public void testCommentOnBuildResultNoSkip() { 87 | String testMessage = "test"; 88 | given(appender.postBuildComment(build, listener)).willReturn(testMessage); 89 | 90 | // WHEN 91 | GhprbBuilds builds = new GhprbBuilds(trigger, repo); 92 | builds.commentOnBuildResult(build, listener, state, cause); 93 | 94 | // THEN 95 | verify(repo, times(1)).addComment(cause.getPullID(), testMessage, build, listener); 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbPullRequestMergeContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | 5 | public class GhprbPullRequestMergeContext implements Context { 6 | String mergeComment; 7 | 8 | boolean onlyAdminsMerge; 9 | 10 | boolean disallowOwnCode; 11 | 12 | boolean failOnNonMerge; 13 | 14 | boolean deleteOnMerge; 15 | 16 | boolean allowMergeWithoutTriggerPhrase; 17 | 18 | /** 19 | * @param mergeComment Sets a comment that should show up when the merge command is sent to GitHub. 20 | */ 21 | public void mergeComment(String mergeComment) { 22 | this.mergeComment = mergeComment; 23 | } 24 | 25 | /** 26 | * @param onlyAdminsMerge Allows only admin users to trigger a pull request merge. Defaults to {@code false}. 27 | */ 28 | public void onlyAdminsMerge(boolean onlyAdminsMerge) { 29 | this.onlyAdminsMerge = onlyAdminsMerge; 30 | } 31 | 32 | /** 33 | * Allows only admin users to trigger a pull request merge. Defaults to {@code false}. 34 | */ 35 | public void onlyAdminsMerge() { 36 | onlyAdminsMerge(true); 37 | } 38 | 39 | /** 40 | * @param disallowOwnCode Disallows a user to merge their own code. Defaults to {@code false}. 41 | */ 42 | public void disallowOwnCode(boolean disallowOwnCode) { 43 | this.disallowOwnCode = disallowOwnCode; 44 | } 45 | 46 | /** 47 | * Disallows a user to merge their own code. Defaults to {@code false}. 48 | */ 49 | public void disallowOwnCode() { 50 | disallowOwnCode(true); 51 | } 52 | 53 | /** 54 | * @param failOnNonMerge Fails the build if the pull request can't be merged. Defaults to {@code false}. 55 | */ 56 | public void failOnNonMerge(boolean failOnNonMerge) { 57 | this.failOnNonMerge = failOnNonMerge; 58 | } 59 | 60 | /** 61 | * Fails the build if the pull request can't be merged. Defaults to {@code false}. 62 | */ 63 | public void failOnNonMerge() { 64 | failOnNonMerge(true); 65 | } 66 | 67 | /** 68 | * @param deleteOnMerge Deletes the branch after a successful merge. Defaults to {@code false}. 69 | */ 70 | public void deleteOnMerge(boolean deleteOnMerge) { 71 | this.deleteOnMerge = deleteOnMerge; 72 | } 73 | 74 | /** 75 | * Deletes the branch after a successful merge. Defaults to {@code false}. 76 | */ 77 | public void deleteOnMerge() { 78 | deleteOnMerge(true); 79 | } 80 | 81 | /** 82 | * Allows merging the PR even if the trigger phrase was not present. Defaults to {@code false} 83 | * 84 | * @param allowMergeWithoutTriggerPhrase Allow the merge to happen without the trigger phrase 85 | */ 86 | public void allowMergeWithoutTriggerPhrase(boolean allowMergeWithoutTriggerPhrase) { 87 | this.allowMergeWithoutTriggerPhrase = allowMergeWithoutTriggerPhrase; 88 | } 89 | 90 | /** 91 | * Allows merging the PR even if the trigger phrase was not present. Defaults to {@code false} 92 | */ 93 | public void allowMergeWithoutTriggerPhrase() { 94 | allowMergeWithoutTriggerPhrase(false); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildLog.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.comments; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Run; 5 | import hudson.model.TaskListener; 6 | import org.jenkinsci.plugins.ghprb.Ghprb; 7 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 8 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 9 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalExtension; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; 12 | import org.kohsuke.github.GHCommitState; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | 15 | import java.io.IOException; 16 | import java.util.List; 17 | 18 | public class GhprbBuildLog extends GhprbExtension implements GhprbCommentAppender, GhprbProjectExtension, GhprbGlobalExtension { 19 | 20 | @Extension 21 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 22 | 23 | private final Integer logExcerptLines; 24 | 25 | @DataBoundConstructor 26 | public GhprbBuildLog(Integer logExcerptLines) { 27 | this.logExcerptLines = logExcerptLines; 28 | } 29 | 30 | public Integer getLogExcerptLines() { 31 | return logExcerptLines == null ? Integer.valueOf(0) : logExcerptLines; 32 | } 33 | 34 | public String postBuildComment(Run build, TaskListener listener) { 35 | 36 | StringBuilder msg = new StringBuilder(); 37 | GHCommitState state = Ghprb.getState(build); 38 | 39 | int numLines = getDescriptor().getLogExcerptLinesDefault(this); 40 | 41 | if (state != GHCommitState.SUCCESS && numLines > 0) { 42 | // on failure, append an excerpt of the build log 43 | try { 44 | // wrap log in "code" markdown 45 | msg.append("\n\n**Build Log**\n*last ").append(numLines).append(" lines*\n"); 46 | msg.append("\n ```\n"); 47 | List log = build.getLog(numLines); 48 | for (String line : log) { 49 | msg.append(line).append('\n'); 50 | } 51 | msg.append("```\n"); 52 | } catch (IOException ex) { 53 | listener.getLogger().println("Can't add log excerpt to commit comments"); 54 | ex.printStackTrace(listener.getLogger()); 55 | } 56 | } 57 | return msg.toString(); 58 | } 59 | 60 | public boolean ignorePublishedUrl() { 61 | return false; 62 | } 63 | 64 | @Override 65 | public DescriptorImpl getDescriptor() { 66 | return DESCRIPTOR; 67 | } 68 | 69 | 70 | public static final class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbGlobalExtension { 71 | 72 | @Override 73 | public String getDisplayName() { 74 | return "Append portion of build log"; 75 | } 76 | 77 | public Integer getLogExcerptLinesDefault(GhprbBuildLog local) { 78 | Integer lines = Ghprb.getDefaultValue(local, GhprbBuildLog.class, "getLogExcerptLines"); 79 | if (lines == null) { 80 | lines = 0; 81 | } 82 | return lines; 83 | } 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbBuildResultMessage.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.comments; 2 | 3 | import hudson.Extension; 4 | import hudson.model.AbstractDescribableImpl; 5 | import hudson.model.Descriptor; 6 | import hudson.model.Run; 7 | import hudson.model.TaskListener; 8 | import hudson.util.ListBoxModel; 9 | import org.apache.commons.lang.StringUtils; 10 | import org.jenkinsci.plugins.ghprb.Ghprb; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 12 | import org.kohsuke.github.GHCommitState; 13 | import org.kohsuke.stapler.DataBoundConstructor; 14 | import org.kohsuke.stapler.QueryParameter; 15 | 16 | public class GhprbBuildResultMessage extends AbstractDescribableImpl implements GhprbCommentAppender { 17 | 18 | @Extension 19 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 20 | 21 | private final String message; 22 | 23 | private final GHCommitState result; 24 | 25 | @DataBoundConstructor 26 | public GhprbBuildResultMessage(GHCommitState result, String message) { 27 | this.result = result; 28 | this.message = message; 29 | } 30 | 31 | public String getMessage() { 32 | return message; 33 | } 34 | 35 | public GHCommitState getResult() { 36 | return result; 37 | } 38 | 39 | public String postBuildComment(Run build, TaskListener listener) { 40 | StringBuilder msg = new StringBuilder(); 41 | 42 | GHCommitState state = Ghprb.getState(build); 43 | String buildMessage = null; 44 | if (state == result) { 45 | buildMessage = message; 46 | if (StringUtils.isEmpty(buildMessage)) { 47 | return ""; // will use default 48 | } else if (buildMessage.equals("--none--")) { 49 | return buildMessage; // will skip update 50 | } 51 | String message = Ghprb.replaceMacros(build, listener, buildMessage); 52 | // Only Append the build's custom message if it has been set. 53 | if (!StringUtils.isEmpty(message)) { 54 | // When the msg is not empty, append a newline first, to seperate it from the rest of the String 55 | if (msg.length() > 0) { 56 | msg.append("\n"); 57 | } 58 | msg.append(message); 59 | msg.append("\n"); 60 | } 61 | } 62 | return msg.toString(); 63 | } 64 | 65 | @Override 66 | public DescriptorImpl getDescriptor() { 67 | return DESCRIPTOR; 68 | } 69 | 70 | public static class DescriptorImpl extends Descriptor { 71 | public boolean isApplicable(Class type) { 72 | return true; 73 | } 74 | 75 | @Override 76 | public String getDisplayName() { 77 | return "Add message on Build Status"; 78 | } 79 | 80 | public ListBoxModel doFillResultItems(@QueryParameter String result) { 81 | ListBoxModel items = new ListBoxModel(); 82 | GHCommitState[] results = new GHCommitState[] {GHCommitState.SUCCESS, GHCommitState.ERROR, GHCommitState.FAILURE}; 83 | for (GHCommitState nextResult : results) { 84 | 85 | items.add(nextResult.toString(), nextResult.toString()); 86 | if (result.equals(nextResult.toString())) { 87 | items.get(items.size() - 1).selected = true; 88 | } 89 | } 90 | 91 | return items; 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbContextExtensionPoint.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import antlr.ANTLRException; 4 | import com.google.common.base.Joiner; 5 | import hudson.Extension; 6 | import javaposse.jobdsl.dsl.helpers.publisher.PublisherContext; 7 | import javaposse.jobdsl.dsl.helpers.triggers.TriggerContext; 8 | import javaposse.jobdsl.dsl.helpers.wrapper.WrapperContext; 9 | import javaposse.jobdsl.plugin.ContextExtensionPoint; 10 | import javaposse.jobdsl.plugin.DslExtensionMethod; 11 | import org.jenkinsci.plugins.ghprb.GhprbPullRequestMerge; 12 | import org.jenkinsci.plugins.ghprb.GhprbTrigger; 13 | import org.jenkinsci.plugins.ghprb.upstream.GhprbUpstreamStatus; 14 | 15 | 16 | @Extension(optional = true) 17 | public class GhprbContextExtensionPoint extends ContextExtensionPoint { 18 | @DslExtensionMethod(context = TriggerContext.class) 19 | public Object githubPullRequest(Runnable closure) throws ANTLRException { 20 | GhprbTriggerContext context = new GhprbTriggerContext(); 21 | executeInContext(closure, context); 22 | return new GhprbTrigger( 23 | Joiner.on("\n").join(context.admins), 24 | Joiner.on("\n").join(context.userWhitelist), 25 | Joiner.on("\n").join(context.orgWhitelist), 26 | context.cron, 27 | context.triggerPhrase, 28 | context.onlyTriggerPhrase, 29 | context.useGitHubHooks, 30 | context.permitAll, 31 | context.autoCloseFailedPullRequests, 32 | context.displayBuildErrorsOnDownstreamBuilds, 33 | null, 34 | context.skipBuildPhrase, 35 | context.blackListCommitAuthor, 36 | context.whiteListTargetBranches, 37 | context.blackListTargetBranches, 38 | context.allowMembersOfWhitelistedOrgsAsAdmin, 39 | null, 40 | null, 41 | null, 42 | null, 43 | context.buildDescriptionTemplate, 44 | Joiner.on("\n").join(context.blackListLabels), 45 | Joiner.on("\n").join(context.whiteListLabels), 46 | context.extensionContext.getExtensions(), 47 | context.includedRegions, 48 | context.excludedRegions 49 | ); 50 | } 51 | 52 | @DslExtensionMethod(context = PublisherContext.class) 53 | public Object mergeGithubPullRequest(Runnable closure) { 54 | GhprbPullRequestMergeContext context = new GhprbPullRequestMergeContext(); 55 | executeInContext(closure, context); 56 | 57 | return new GhprbPullRequestMerge( 58 | context.mergeComment, 59 | context.onlyAdminsMerge, 60 | context.disallowOwnCode, 61 | context.failOnNonMerge, 62 | context.deleteOnMerge, 63 | context.allowMergeWithoutTriggerPhrase); 64 | } 65 | 66 | @DslExtensionMethod(context = WrapperContext.class) 67 | public Object downstreamCommitStatus(Runnable closure) { 68 | GhprbUpstreamStatusContext context = new GhprbUpstreamStatusContext(); 69 | executeInContext(closure, context); 70 | 71 | return new GhprbUpstreamStatus( 72 | context.showMatrixStatus, 73 | context.context, 74 | context.statusUrl, 75 | context.triggeredStatus, 76 | context.startedStatus, 77 | context.addTestResults, 78 | context.completedStatus 79 | ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbGitHubAuth/config.groovy: -------------------------------------------------------------------------------- 1 | f = namespace(lib.FormTagLib) 2 | c = namespace(lib.CredentialsTagLib) 3 | 4 | f.entry(title: _("GitHub Server API URL"), field: "serverAPIUrl") { 5 | f.textbox() 6 | } 7 | 8 | f.entry(title: _("Jenkins URL override"), field: "jenkinsUrl") { 9 | f.textbox() 10 | } 11 | 12 | f.entry(title: _("Shared secret"), field: "secret") { 13 | f.password() 14 | } 15 | 16 | f.entry(title: _("Credentials"), field: "credentialsId") { 17 | c.select(onchange = """{ 18 | var self = this.targetElement ? this.targetElement : this; 19 | var r = findPreviousFormItem(self,'serverAPIUrl','credentialsId'); 20 | r.onchange(r); 21 | self = null; 22 | r = null; 23 | }""" /* workaround for JENKINS-19124 */) 24 | } 25 | 26 | f.advanced(title: _("Test Credentials")) { 27 | f.optionalBlock(title: _("Test basic connection to GitHub")) { 28 | f.entry() { 29 | f.validateButton(title: _("Connect to API"), progress: _("Connecting..."), with: "serverAPIUrl,credentialsId", method: "testGithubAccess") 30 | } 31 | } 32 | 33 | f.entry(title: _("Repository owner/name"), field: "repo") { 34 | f.textbox() 35 | } 36 | f.optionalBlock(title: _("Test Permissions to a Repository")) { 37 | f.entry() { 38 | f.validateButton(title: _("Check repo permissions"), progress: _("Checking..."), with: "serverAPIUrl,credentialsId,repo", method: "checkRepoAccess") 39 | } 40 | } 41 | f.optionalBlock(title: _("Test adding comment to Pull Request")) { 42 | f.entry(title: _("Issue ID"), field: "issueId") { 43 | f.number() 44 | } 45 | f.entry(title: _("Comment to post"), field: "message1") { 46 | f.textbox() 47 | } 48 | f.validateButton(title: _("Comment to issue"), progress: _("Commenting..."), with: "serverAPIUrl,credentialsId,repo,issueId,message1", method: "testComment") 49 | } 50 | f.optionalBlock(title: _("Test updating commit status")) { 51 | f.entry(title: _("Commit SHA"), field: "sha1") { 52 | f.textbox() 53 | } 54 | f.entry(title: _("Commit State"), field: "state") { 55 | f.select() 56 | } 57 | f.entry(title: _("Status url"), field: "url") { 58 | f.textbox() 59 | } 60 | f.entry(title: _("Message to post"), field: "message2") { 61 | f.textbox() 62 | } 63 | f.entry(title: _("Context for the status"), field: "context") { 64 | f.textbox() 65 | } 66 | f.validateButton(title: _("Update status"), progress: _("Updating..."), with: "serverAPIUrl,credentialsId,repo,sha1,state,url,message2,context", method: "testUpdateStatus") 67 | } 68 | } 69 | 70 | f.advanced(title: _("Create API Token")) { 71 | f.entry(title: _("Create API Token")) { 72 | f.entry(title: _("Username temp"), field: "username") { 73 | f.textbox() 74 | } 75 | f.entry(title: _("Password temp"), field: "password") { 76 | f.password() 77 | } 78 | f.validateButton(title: _("Create Token"), progress: _("Creating..."), with: "serverAPIUrl,credentialsId,username,password", method: "createApiToken") 79 | } 80 | } 81 | 82 | f.entry(field: "description", title: _("Description")) { 83 | f.textbox() 84 | } 85 | 86 | f.advanced(title: _("Auth ID")) { 87 | f.entry(field: instance != null ? null : 'id', title: _("ID")) { 88 | f.textbox(name: "_.id", value: instance != null ? instance.id : null, readonly: instance != null ? 'readonly' : null) 89 | } 90 | } 91 | 92 | f.entry { 93 | div(align: "right") { 94 | input(type: "button", value: _("Delete Server"), class: "repeatable-delete") 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /RELEASE.md: -------------------------------------------------------------------------------- 1 | # Release process 2 | 3 | This document is for maintainers to release versions of the GHPRB plugin. 4 | 5 | The following release process is a little intense. However, it serves as a high 6 | quality assurance test which users will appreciate when they go to upgrade their 7 | own Jenkins and nothing breaks from GHPRB. 8 | 9 | - [Before releasing](#before-releasing) 10 | - [Steps to release](#steps-to-release) 11 | 12 | # Before releasing 13 | 14 | Summary: 15 | 16 | 1. Setup Jenkins with GHPRB. 17 | 2. Track `$JENKINS_HOME` configuration with git. 18 | 3. Upgrade the plugin and check for any differences in `$JENKINS_HOME` which 19 | would negatively affect users. The configs should not break and Jenkins 20 | should cleanly migrate existing configurations. That is, configured settings 21 | and jobs should be able to upgrade. 22 | 23 | ### 1. Setup Jenkins with GHPRB 24 | 25 | Test that the plugin properly upgrades configuration without breaking existing 26 | configuration. Provision Jenkins LTS by downloading `jenkins.war`. Start 27 | Jenkins with the following command. 28 | 29 | ``` 30 | export JENKINS_HOME="./my_jenkins_home" 31 | java -jar jenkins.war 32 | ``` 33 | 34 | Install the GHPRB plugin from the Jenkins update center. Configure a dummy job 35 | with GHPRB configured both globally and in the job. 36 | 37 | ### 2. Track `JENKINS_HOME` with Git 38 | 39 | ref: https://github.com/github/gitignore/pull/1763 40 | 41 | ``` 42 | cd my_jenkins_home/ 43 | curl -Lo .gitignore https://raw.githubusercontent.com/samrocketman/gitignore/jenkins-gitignore/JENKINS_HOME.gitignore 44 | git init 45 | git add -A 46 | git commit -m 'initial commit' 47 | ``` 48 | 49 | ### 3. Upgrade the plugin 50 | 51 | Build the latest development `master` branch for the GHPRB plugin. It should 52 | create `target/ghprb.hpi`. In the Jenkins web UI you can upgrade the plugin by 53 | visiting the following: 54 | 55 | - Jenkins > Manage Jenkins > Plugin Manager > Advanced > Upload plugin 56 | 57 | Upload your built `ghprb.hpi`. Restart Jenkins by visiting 58 | `http://localhost:8080/restart`. After Jenkins has finished restarted, visit 59 | the `$JENKINS_HOME` and view any changed configuration. 60 | 61 | ``` 62 | cd my_jenkins_home/ 63 | git status 64 | git diff 65 | ``` 66 | 67 | If the migrated XML config looks OK and the jobs and settings you configured 68 | still work, then proceed to a release. 69 | 70 | # Steps to release 71 | 72 | This outlines the maintainers steps to release the Jenkins GitHub Pull Request 73 | Builder Plugin. Follow the Jenkins documentation for [making a new 74 | release][plugin-release]. 75 | 76 | - [ ] Configure your credentials in `~/.m2/settings.xml`. (outlined in [making a 77 | new release][plugin-release] doc) 78 | - [ ] Create a new issue to track the release and give it the label `maintainer 79 | communication`. 80 | - [ ] Create a release branch. `git checkout origin/master -b prepare_release` 81 | - [ ] Update the release notes in `CHANGELOG.md`. 82 | - [ ] Open a pull request from `prepare_release` branch to `master` branch. 83 | Merge it. 84 | - [ ] Fetch the latest `master`. 85 | - [ ] Clean the workspace `git clean -xfd`. 86 | - [ ] Execute the release plugin. 87 | 88 | ``` 89 | mvn release:prepare release:perform 90 | ``` 91 | 92 | - [ ] Wait for the plugin to be released into the Jenkins Update Center. It 93 | takes roughly 8 hours for a release. 94 | - [ ] Successfully perform an upgrade from the last stable plugin release to the 95 | current release. 96 | 97 | See also the [release section of hosting plugins][plugin-release]. 98 | 99 | 100 | [plugin-release]: https://wiki.jenkins.io/display/JENKINS/Hosting+Plugins#HostingPlugins-Releasingtojenkins-ci.org 101 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/comments/GhprbCommentFile.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.comments; 2 | 3 | import hudson.Extension; 4 | import hudson.FilePath; 5 | import hudson.model.Build; 6 | import hudson.model.Run; 7 | import hudson.model.TaskListener; 8 | import org.apache.commons.io.FileUtils; 9 | import org.jenkinsci.plugins.ghprb.Ghprb; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 11 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 12 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 13 | import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; 14 | import org.kohsuke.stapler.DataBoundConstructor; 15 | 16 | import java.io.File; 17 | import java.io.IOException; 18 | 19 | public class GhprbCommentFile extends GhprbExtension implements GhprbCommentAppender, GhprbProjectExtension { 20 | @Extension 21 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 22 | 23 | private final String commentFilePath; 24 | 25 | @DataBoundConstructor 26 | public GhprbCommentFile(String commentFilePath) { 27 | this.commentFilePath = commentFilePath; 28 | } 29 | 30 | public String getCommentFilePath() { 31 | return commentFilePath != null ? commentFilePath : ""; 32 | } 33 | 34 | public boolean ignorePublishedUrl() { 35 | return false; 36 | } 37 | 38 | public String postBuildComment(Run build, TaskListener listener) { 39 | StringBuilder msg = new StringBuilder(); 40 | if (commentFilePath != null && !commentFilePath.isEmpty()) { 41 | String scriptFilePathResolved = Ghprb.replaceMacros(build, listener, commentFilePath); 42 | 43 | try { 44 | String content = null; 45 | if (build instanceof Build) { 46 | final FilePath workspace = ((Build) build).getWorkspace(); 47 | final FilePath path = workspace.child(scriptFilePathResolved); 48 | 49 | if (path.exists()) { 50 | content = path.readToString(); 51 | } else { 52 | listener.getLogger().println( 53 | "Didn't find comment file in workspace at " + path.absolutize().getRemote() 54 | + ", falling back to file operations on master." 55 | ); 56 | } 57 | } 58 | 59 | if (content == null) { 60 | content = FileUtils.readFileToString(new File(scriptFilePathResolved)); 61 | } 62 | 63 | if (content.length() > 0) { 64 | msg.append(content); 65 | } 66 | 67 | } catch (IOException e) { 68 | msg.append("\n!!! Couldn't read commit file !!!\n"); 69 | listener.getLogger().println("Couldn't read comment file at " + scriptFilePathResolved); 70 | e.printStackTrace(listener.getLogger()); 71 | } catch (InterruptedException e) { 72 | Thread.currentThread().interrupt(); // set interrupt flag 73 | msg.append("\n!!! Couldn't read commit file !!!\n"); 74 | listener.getLogger().println("Reading comment file at " + scriptFilePathResolved + " was interrupted"); 75 | e.printStackTrace(listener.getLogger()); 76 | } 77 | } 78 | return msg.toString(); 79 | } 80 | 81 | @Override 82 | public DescriptorImpl getDescriptor() { 83 | return DESCRIPTOR; 84 | } 85 | 86 | public static final class DescriptorImpl extends GhprbExtensionDescriptor implements GhprbProjectExtension { 87 | @Override 88 | public String getDisplayName() { 89 | return "Comment File"; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbTriggerBackwardsCompatible.java: -------------------------------------------------------------------------------- 1 | // CHECKSTYLE:OFF 2 | package org.jenkinsci.plugins.ghprb; 3 | 4 | import antlr.ANTLRException; 5 | import hudson.model.Job; 6 | import hudson.triggers.Trigger; 7 | import hudson.util.DescribableList; 8 | import org.apache.commons.lang.StringUtils; 9 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 10 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 11 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 12 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildStatus; 13 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbCommentFile; 14 | import org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus; 15 | import org.kohsuke.github.GHCommitState; 16 | 17 | import java.util.ArrayList; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | public abstract class GhprbTriggerBackwardsCompatible extends Trigger> { 22 | 23 | public abstract DescribableList getExtensions(); 24 | 25 | protected final static int LATEST_VERSION = 3; 26 | 27 | protected Integer configVersion; 28 | 29 | public GhprbTriggerBackwardsCompatible(String cron) throws ANTLRException { 30 | super(cron); 31 | } 32 | 33 | 34 | @Deprecated 35 | protected transient String commentFilePath; 36 | 37 | @Deprecated 38 | protected transient String msgSuccess; 39 | 40 | @Deprecated 41 | protected transient String msgFailure; 42 | 43 | @Deprecated 44 | protected transient String commitStatusContext; 45 | 46 | @Deprecated 47 | protected transient GhprbGitHubAuth gitHubApiAuth; 48 | 49 | @Deprecated 50 | protected transient String project; 51 | 52 | @Deprecated 53 | protected transient Job _project; 54 | 55 | @Deprecated 56 | protected transient Map pullRequests; 57 | 58 | 59 | protected void convertPropertiesToExtensions() { 60 | if (configVersion == null) { 61 | configVersion = 0; 62 | } 63 | 64 | checkCommentsFile(); 65 | checkBuildStatusMessages(); 66 | checkCommitStatusContext(); 67 | 68 | configVersion = LATEST_VERSION; 69 | } 70 | 71 | private void checkBuildStatusMessages() { 72 | if (!StringUtils.isEmpty(msgFailure) || !StringUtils.isEmpty(msgSuccess)) { 73 | List messages = new ArrayList(2); 74 | if (!StringUtils.isEmpty(msgFailure)) { 75 | messages.add(new GhprbBuildResultMessage(GHCommitState.FAILURE, msgFailure)); 76 | msgFailure = null; 77 | } 78 | if (!StringUtils.isEmpty(msgSuccess)) { 79 | messages.add(new GhprbBuildResultMessage(GHCommitState.SUCCESS, msgSuccess)); 80 | msgSuccess = null; 81 | } 82 | addIfMissing(new GhprbBuildStatus(messages)); 83 | } 84 | } 85 | 86 | private void checkCommentsFile() { 87 | if (!StringUtils.isEmpty(commentFilePath)) { 88 | GhprbCommentFile comments = new GhprbCommentFile(commentFilePath); 89 | addIfMissing(comments); 90 | commentFilePath = null; 91 | } 92 | } 93 | 94 | private void checkCommitStatusContext() { 95 | if (configVersion < 1) { 96 | GhprbSimpleStatus status = new GhprbSimpleStatus(commitStatusContext); 97 | addIfMissing(status); 98 | } 99 | } 100 | 101 | protected void addIfMissing(GhprbExtension ext) { 102 | if (getExtensions().get(ext.getClass()) == null) { 103 | getExtensions().add(ext); 104 | } 105 | } 106 | 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/manager/impl/downstreambuilds/BuildFlowBuildManagerTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.impl.downstreambuilds; 2 | 3 | import com.cloudbees.plugins.flow.BuildFlow; 4 | import com.cloudbees.plugins.flow.FlowRun; 5 | import com.cloudbees.plugins.flow.JobInvocation; 6 | import org.jenkinsci.plugins.ghprb.GhprbITBaseTestCase; 7 | import org.jenkinsci.plugins.ghprb.GhprbTestUtil; 8 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 9 | import org.jenkinsci.plugins.ghprb.manager.factory.GhprbBuildManagerFactoryUtil; 10 | import org.jenkinsci.plugins.ghprb.rules.JenkinsRuleWithBuildFlow; 11 | import org.junit.Before; 12 | import org.junit.Rule; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.mockito.runners.MockitoJUnitRunner; 16 | 17 | import java.util.Iterator; 18 | 19 | import static org.fest.assertions.Assertions.assertThat; 20 | import static org.mockito.BDDMockito.given; 21 | 22 | /** 23 | * @author mdelapenya (Manuel de la Peña) 24 | */ 25 | @RunWith(MockitoJUnitRunner.class) 26 | public class BuildFlowBuildManagerTest extends GhprbITBaseTestCase { 27 | 28 | @Rule 29 | public JenkinsRuleWithBuildFlow jenkinsRule = new JenkinsRuleWithBuildFlow(); 30 | 31 | private BuildFlow buildFlowProject; 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | 36 | buildFlowProject = jenkinsRule.createBuildFlowProject(); 37 | 38 | jenkinsRule.createFreeStyleProject("downstreamProject1"); 39 | jenkinsRule.createFreeStyleProject("downstreamProject2"); 40 | jenkinsRule.createFreeStyleProject("downstreamProject3"); 41 | 42 | StringBuilder dsl = new StringBuilder(); 43 | 44 | dsl.append("parallel ("); 45 | dsl.append(" { build(\"downstreamProject1\") },"); 46 | dsl.append(" { build(\"downstreamProject2\") }"); 47 | dsl.append(")"); 48 | dsl.append("{ build(\"downstreamProject3\") }"); 49 | 50 | buildFlowProject.setDsl(dsl.toString()); 51 | 52 | given(ghPullRequest.getNumber()).willReturn(1); 53 | given(ghRepository.getPullRequest(1)).willReturn(ghPullRequest); 54 | 55 | super.beforeTest(null, null, buildFlowProject); 56 | } 57 | 58 | @Test 59 | public void shouldCalculateUrlWithDownstreamBuilds() throws Exception { 60 | // GIVEN 61 | GhprbTestUtil.triggerRunAndWait(10, trigger, buildFlowProject); 62 | 63 | // THEN 64 | assertThat(buildFlowProject.getBuilds().toArray().length).isEqualTo(1); 65 | 66 | FlowRun flowRun = buildFlowProject.getBuilds().getFirstBuild(); 67 | 68 | GhprbBuildManager buildManager = GhprbBuildManagerFactoryUtil.getBuildManager(flowRun); 69 | 70 | assertThat(buildManager).isInstanceOf(BuildFlowBuildManager.class); 71 | 72 | Iterator iterator = buildManager.downstreamProjects(); 73 | 74 | StringBuilder expectedUrl = new StringBuilder(); 75 | 76 | int count = 0; 77 | 78 | while (iterator.hasNext()) { 79 | Object downstreamBuild = iterator.next(); 80 | 81 | assertThat(downstreamBuild).isInstanceOf(JobInvocation.class); 82 | 83 | JobInvocation jobInvocation = (JobInvocation) downstreamBuild; 84 | 85 | String jobInvocationBuildUrl = jobInvocation.getBuildUrl(); 86 | 87 | expectedUrl.append("\n"); 90 | expectedUrl.append(jobInvocationBuildUrl); 91 | expectedUrl.append(""); 92 | 93 | count++; 94 | } 95 | 96 | assertThat(count).isEqualTo(4); 97 | 98 | assertThat(buildManager.calculateBuildUrl(null)).isEqualTo(expectedUrl.toString()); 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/impl/downstreambuilds/BuildFlowBuildManager.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.impl.downstreambuilds; 2 | 3 | import com.cloudbees.plugins.flow.FlowRun; 4 | import com.cloudbees.plugins.flow.JobInvocation; 5 | import hudson.model.Run; 6 | import hudson.tasks.test.AggregatedTestResultAction; 7 | import org.jenkinsci.plugins.ghprb.manager.configuration.JobConfiguration; 8 | import org.jenkinsci.plugins.ghprb.manager.impl.GhprbBaseBuildManager; 9 | import org.jgrapht.DirectedGraph; 10 | 11 | import java.util.Iterator; 12 | import java.util.logging.Level; 13 | import java.util.logging.Logger; 14 | 15 | /** 16 | * @author mdelapenya (Manuel de la Peña) 17 | */ 18 | public class BuildFlowBuildManager extends GhprbBaseBuildManager { 19 | 20 | private static final Logger LOGGER = Logger.getLogger(BuildFlowBuildManager.class.getName()); 21 | 22 | public BuildFlowBuildManager(Run build) { 23 | super(build); 24 | } 25 | 26 | public BuildFlowBuildManager(Run build, JobConfiguration jobConfiguration) { 27 | super(build, jobConfiguration); 28 | } 29 | 30 | /** 31 | * Calculate the build URL of a build of BuildFlow type, traversing its downstream builds graph 32 | * 33 | * @return the build URL of a BuildFlow build, with all its downstream builds 34 | */ 35 | @SuppressWarnings("unchecked") 36 | @Override 37 | public String calculateBuildUrl(String publishedURL) { 38 | Iterator iterator = (Iterator) downstreamProjects(); 39 | 40 | StringBuilder sb = new StringBuilder(); 41 | 42 | while (iterator.hasNext()) { 43 | JobInvocation jobInvocation = iterator.next(); 44 | 45 | sb.append("\n"); 46 | sb.append(""); 49 | sb.append(jobInvocation.getBuildUrl()); 50 | sb.append(""); 51 | } 52 | 53 | return sb.toString(); 54 | } 55 | 56 | /** 57 | * Return a downstream iterator of a build of default type. This will be overriden by specific build types. 58 | * 59 | * @return the downstream builds as an iterator 60 | */ 61 | @Override 62 | public Iterator downstreamProjects() { 63 | FlowRun flowRun = (FlowRun) build; 64 | 65 | DirectedGraph directedGraph = flowRun.getJobsGraph(); 66 | 67 | return directedGraph.vertexSet().iterator(); 68 | } 69 | 70 | /** 71 | * Return the tests results of a build of default type. This will be overriden by specific build types. 72 | * 73 | * @return the tests result of a build of default type 74 | */ 75 | @SuppressWarnings("unchecked") 76 | @Override 77 | public String getTestResults() { 78 | Iterator iterator = (Iterator) downstreamProjects(); 79 | 80 | StringBuilder sb = new StringBuilder(); 81 | 82 | while (iterator.hasNext()) { 83 | JobInvocation jobInvocation = iterator.next(); 84 | 85 | try { 86 | Run build = (Run) jobInvocation.getBuild(); 87 | 88 | AggregatedTestResultAction testResultAction = build.getAction(AggregatedTestResultAction.class); 89 | 90 | if (testResultAction != null) { 91 | sb.append("\n"); 92 | sb.append(jobInvocation.getBuildUrl()); 93 | sb.append("\n"); 94 | sb.append(getAggregatedTestResults(build)); 95 | } 96 | } catch (Exception e) { 97 | LOGGER.log(Level.SEVERE, "Job execution has failed", e); 98 | } 99 | } 100 | 101 | return sb.toString(); 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/resources/org/jenkinsci/plugins/ghprb/GhprbTrigger/config.groovy: -------------------------------------------------------------------------------- 1 | j = namespace("jelly:core") 2 | f = namespace("/lib/form") 3 | 4 | f.entry(field: "gitHubAuthId", title:_("GitHub API credentials")) { 5 | f.select() 6 | } 7 | 8 | f.entry(field: "adminlist", title: _("Admin list")) { 9 | f.textarea(default: descriptor.adminlist) 10 | } 11 | f.entry(field: "useGitHubHooks", title: "Use github hooks for build triggering") { 12 | f.checkbox() 13 | } 14 | f.advanced() { 15 | f.entry(field: "triggerPhrase", title: _("Trigger phrase")) { 16 | f.textbox() 17 | } 18 | f.entry(field: "onlyTriggerPhrase", title: "Only use trigger phrase for build triggering") { 19 | f.checkbox() 20 | } 21 | f.entry(field: "autoCloseFailedPullRequests", title: _("Close failed pull request automatically?")) { 22 | f.checkbox(default: descriptor.autoCloseFailedPullRequests) 23 | } 24 | f.entry(field: "skipBuildPhrase", title: _("Skip build phrase")) { 25 | f.textbox(default: descriptor.skipBuildPhrase) 26 | } 27 | f.entry(field: "displayBuildErrorsOnDownstreamBuilds", title: _("Display build errors on downstream builds?")) { 28 | f.checkbox(default: descriptor.displayBuildErrorsOnDownstreamBuilds) 29 | } 30 | f.entry(field: "cron", title: _("Crontab line"), help: "/descriptor/hudson.triggers.TimerTrigger/help/spec") { 31 | f.textbox(default: descriptor.cron, checkUrl: "'descriptorByName/hudson.triggers.TimerTrigger/checkSpec?value=' + encodeURIComponent(this.value)") 32 | } 33 | f.entry(field: "whitelist", title: _("White list")) { 34 | f.textarea() 35 | } 36 | f.entry(field: "orgslist", title: _("List of organizations. Their members will be whitelisted.")) { 37 | f.textarea() 38 | } 39 | f.entry(field: "blackListLabels", title: _("List of GitHub labels for which the build should not be triggered.")) { 40 | f.textarea() 41 | } 42 | f.entry(field: "whiteListLabels", title: _("List of GitHub labels for which the build should only be triggered. (Leave blank for 'any')")) { 43 | f.textarea() 44 | } 45 | f.entry(field: "allowMembersOfWhitelistedOrgsAsAdmin", title: "Allow members of whitelisted organizations as admins") { 46 | f.checkbox() 47 | } 48 | f.entry(field: "permitAll", title: "Build every pull request automatically without asking (Dangerous!).") { 49 | f.checkbox() 50 | } 51 | f.entry(field: "buildDescTemplate", title: _("Build description template")) { 52 | f.textarea() 53 | } 54 | f.entry(field: "blackListCommitAuthor", title: _("Blacklist commit authors")) { 55 | f.textbox(default: descriptor.blackListCommitAuthor) 56 | } 57 | f.entry(field: "whiteListTargetBranches", title: _("Whitelist Target Branches:")) { 58 | f.repeatable(field: "whiteListTargetBranches", minimum: "1", add: "Add Branch") { 59 | table(width: "100%") { 60 | f.entry(field: "branch") { 61 | f.textbox() 62 | } 63 | f.entry(title: "") { 64 | div(align: "right") { 65 | f.repeatableDeleteButton(value: "Delete Branch") 66 | } 67 | } 68 | } 69 | } 70 | } 71 | f.entry(field: "blackListTargetBranches", title: _("Blacklist Target Branches:")) { 72 | f.repeatable(field: "blackListTargetBranches", minimum: "1", add: "Add Branch") { 73 | table(width: "100%") { 74 | f.entry(field: "branch") { 75 | f.textbox() 76 | } 77 | f.entry(title: "") { 78 | div(align: "right") { 79 | f.repeatableDeleteButton(value: "Delete Branch") 80 | } 81 | } 82 | } 83 | } 84 | } 85 | f.entry(field: "includedRegions", title: _("Included regions")) { 86 | f.textarea() 87 | } 88 | f.entry(field: "excludedRegions", title: _("Excluded regions")) { 89 | f.textarea() 90 | } 91 | } 92 | f.advanced(title: _("Trigger Setup")) { 93 | f.entry(title: _("Trigger Setup")) { 94 | f.hetero_list(items: instance == null ? null : instance.extensions, 95 | name: "extensions", oneEach: "true", hasHeader: "true", descriptors: descriptor.getExtensionDescriptors()) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GhprbITBaseTestCase.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import com.coravy.hudson.plugins.github.GithubProjectProperty; 4 | import hudson.model.AbstractProject; 5 | import hudson.model.Run; 6 | import hudson.model.TaskListener; 7 | import hudson.plugins.git.GitSCM; 8 | import org.joda.time.DateTime; 9 | import org.kohsuke.github.GHCommitPointer; 10 | import org.kohsuke.github.GHIssueState; 11 | import org.kohsuke.github.GHPullRequest; 12 | import org.kohsuke.github.GHRateLimit; 13 | import org.kohsuke.github.GHRepository; 14 | import org.kohsuke.github.GHUser; 15 | import org.kohsuke.github.GitHub; 16 | import org.mockito.Mock; 17 | import org.mockito.Mockito; 18 | 19 | import java.util.Map; 20 | 21 | import static com.google.common.collect.Lists.newArrayList; 22 | import static org.mockito.BDDMockito.given; 23 | import static org.mockito.Matchers.any; 24 | import static org.mockito.Matchers.anyString; 25 | import static org.mockito.Matchers.eq; 26 | 27 | /** 28 | * @author mdelapenya (Manuel de la Peña) 29 | */ 30 | public abstract class GhprbITBaseTestCase { 31 | 32 | @Mock 33 | protected GHCommitPointer commitPointer; 34 | 35 | @Mock 36 | protected GHPullRequest ghPullRequest; 37 | 38 | @Mock 39 | protected GhprbGitHub ghprbGitHub; 40 | 41 | @Mock 42 | protected GHRepository ghRepository; 43 | 44 | @Mock 45 | protected GHUser ghUser; 46 | 47 | @Mock 48 | protected Ghprb helper; 49 | 50 | @Mock 51 | protected GhprbPullRequest ghprbPullRequest; 52 | 53 | protected GhprbBuilds builds; 54 | 55 | protected GhprbTrigger trigger; 56 | 57 | protected GHRateLimit ghRateLimit = new GHRateLimit(); 58 | 59 | protected void beforeTest( 60 | Map globalConfig, 61 | Map triggerConfig, 62 | AbstractProject project 63 | ) throws Exception { 64 | project.addProperty(new GithubProjectProperty("https://github.com/user/dropwizard")); 65 | GhprbTestUtil.setupGhprbTriggerDescriptor(globalConfig); 66 | 67 | trigger = GhprbTestUtil.getTrigger(triggerConfig); 68 | 69 | GitHub gitHub = trigger.getGitHub(); 70 | 71 | given(gitHub.getRepository(anyString())).willReturn(ghRepository); 72 | 73 | given(ghPullRequest.getHead()).willReturn(commitPointer); 74 | given(ghPullRequest.getUser()).willReturn(ghUser); 75 | 76 | given(commitPointer.getRef()).willReturn("ref"); 77 | given(commitPointer.getSha()).willReturn("sha"); 78 | 79 | given(ghRepository.getName()).willReturn("dropwizard"); 80 | 81 | GhprbTestUtil.mockPR(ghPullRequest, commitPointer, new DateTime(), new DateTime().plusDays(1)); 82 | 83 | given(ghRepository.getPullRequests(eq(GHIssueState.OPEN))) 84 | .willReturn(newArrayList(ghPullRequest)) 85 | .willReturn(newArrayList(ghPullRequest)); 86 | given(ghRepository.getPullRequest(Mockito.anyInt())).willReturn(ghPullRequest); 87 | 88 | given(ghUser.getEmail()).willReturn("email@email.com"); 89 | given(ghUser.getLogin()).willReturn("user"); 90 | 91 | ghRateLimit.remaining = GhprbTestUtil.INITIAL_RATE_LIMIT; 92 | 93 | GhprbTestUtil.mockCommitList(ghPullRequest); 94 | 95 | GhprbRepository repo = Mockito.spy(new GhprbRepository("user/dropwizard", trigger)); 96 | Mockito.doReturn(ghRepository).when(repo).getGitHubRepo(); 97 | Mockito.doNothing().when(repo).addComment(Mockito.anyInt(), Mockito.anyString(), any(Run.class), any(TaskListener.class)); 98 | Mockito.doReturn(ghprbPullRequest).when(repo).getPullRequest(Mockito.anyInt()); 99 | 100 | Mockito.doReturn(repo).when(trigger).getRepository(); 101 | 102 | builds = new GhprbBuilds(trigger, repo); 103 | 104 | // Creating spy on ghprb, configuring repo 105 | given(helper.getGitHub()).willReturn(ghprbGitHub); 106 | given(helper.getTrigger()).willReturn(trigger); 107 | given(helper.isWhitelisted(ghUser)).willReturn(true); 108 | given(helper.getBuilds()).willReturn(builds); 109 | 110 | Mockito.doCallRealMethod().when(trigger).run(); 111 | 112 | 113 | // Configuring and adding Ghprb trigger 114 | project.addTrigger(trigger); 115 | 116 | // Configuring Git SCM 117 | GitSCM scm = GhprbTestUtil.provideGitSCM(); 118 | project.setScm(scm); 119 | 120 | trigger.start(project, true); 121 | trigger.setHelper(helper); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbCause.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.model.Cause; 4 | import org.apache.commons.lang.StringUtils; 5 | import org.kohsuke.github.GHUser; 6 | import org.kohsuke.github.GitUser; 7 | 8 | import java.net.URL; 9 | 10 | /** 11 | * @author Honza Brázdil 12 | */ 13 | public class GhprbCause extends Cause { 14 | 15 | public static final int MAX_WIDTH = 30; 16 | 17 | private final String commit; 18 | 19 | private final int pullID; 20 | 21 | private final boolean merged; 22 | 23 | private final String targetBranch; 24 | 25 | private final String sourceBranch; 26 | 27 | private final String authorEmail; 28 | 29 | private final String title; 30 | 31 | private final URL url; 32 | 33 | private final transient GHUser triggerSender; 34 | 35 | private final String commentBody; 36 | 37 | private final transient GitUser commitAuthor; 38 | 39 | private final transient GHUser pullRequestAuthor; 40 | 41 | private final String description; 42 | 43 | private final String authorRepoGitUrl; 44 | 45 | private final String repoName; 46 | 47 | private final String credentialsId; 48 | 49 | public GhprbCause(String commit, 50 | int pullID, 51 | boolean merged, 52 | String targetBranch, 53 | String sourceBranch, 54 | String authorEmail, 55 | String title, 56 | URL url, 57 | GHUser triggerSender, 58 | String commentBody, 59 | GitUser commitAuthor, 60 | GHUser pullRequestAuthor, 61 | String description, 62 | String authorRepoGitUrl, 63 | String repoName, 64 | String credentialsId) { 65 | 66 | this.commit = commit; 67 | this.pullID = pullID; 68 | this.merged = merged; 69 | this.targetBranch = targetBranch; 70 | this.sourceBranch = sourceBranch; 71 | this.authorEmail = authorEmail; 72 | this.title = title; 73 | this.url = url; 74 | this.description = description; 75 | 76 | this.triggerSender = triggerSender; 77 | this.commentBody = commentBody; 78 | this.commitAuthor = commitAuthor; 79 | this.pullRequestAuthor = pullRequestAuthor; 80 | this.authorRepoGitUrl = authorRepoGitUrl; 81 | this.repoName = repoName; 82 | this.credentialsId = credentialsId; 83 | } 84 | 85 | @Override 86 | public String getShortDescription() { 87 | return "GitHub pull request #" + pullID + " of commit " + commit + (merged ? ", no merge conflicts." : ", has merge conflicts."); 88 | } 89 | 90 | public String getCommit() { 91 | return commit; 92 | } 93 | 94 | public boolean isMerged() { 95 | return merged; 96 | } 97 | 98 | public int getPullID() { 99 | return pullID; 100 | } 101 | 102 | public String getTargetBranch() { 103 | return targetBranch; 104 | } 105 | 106 | public String getSourceBranch() { 107 | return sourceBranch; 108 | } 109 | 110 | public String getAuthorEmail() { 111 | return authorEmail; 112 | } 113 | 114 | public URL getUrl() { 115 | return url; 116 | } 117 | 118 | public GHUser getTriggerSender() { 119 | return triggerSender; 120 | } 121 | 122 | public String getCommentBody() { 123 | return commentBody; 124 | } 125 | 126 | public GHUser getPullRequestAuthor() { 127 | return pullRequestAuthor; 128 | } 129 | 130 | /** 131 | * @return the title of the cause, not null. 132 | */ 133 | public String getTitle() { 134 | return title != null ? title : ""; 135 | } 136 | 137 | /** 138 | * @return at most the first 30 characters of the title 139 | */ 140 | public String getAbbreviatedTitle() { 141 | return StringUtils.abbreviate(getTitle(), MAX_WIDTH); 142 | } 143 | 144 | public GitUser getCommitAuthor() { 145 | return commitAuthor; 146 | } 147 | 148 | public String getDescription() { 149 | return description; 150 | } 151 | 152 | public String getAuthorRepoGitUrl() { 153 | return authorRepoGitUrl; 154 | } 155 | 156 | public String getRepositoryName() { 157 | return repoName; 158 | } 159 | 160 | public String getCredentialsId() { 161 | return credentialsId; 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/extensions/status/GhprbSimpleStatusTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.status; 2 | 3 | import org.jenkinsci.plugins.ghprb.GhprbPullRequest; 4 | import org.jenkinsci.plugins.ghprb.GhprbTestUtil; 5 | import org.jenkinsci.plugins.ghprb.GhprbTrigger; 6 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.jvnet.hudson.test.JenkinsRule; 12 | import org.kohsuke.github.GHCommitState; 13 | import org.kohsuke.github.GHRepository; 14 | import org.mockito.Mock; 15 | import org.mockito.runners.MockitoJUnitRunner; 16 | 17 | import java.util.ArrayList; 18 | 19 | import static org.mockito.BDDMockito.given; 20 | import static org.mockito.Matchers.eq; 21 | import static org.mockito.Matchers.isNull; 22 | import static org.mockito.Mockito.spy; 23 | import static org.mockito.Mockito.verify; 24 | import static org.mockito.Mockito.verifyNoMoreInteractions; 25 | 26 | @RunWith(MockitoJUnitRunner.class) 27 | public class GhprbSimpleStatusTest extends org.jenkinsci.plugins.ghprb.extensions.GhprbExtension { 28 | 29 | @Rule 30 | public JenkinsRule jenkinsRule = new JenkinsRule(); 31 | 32 | @Mock 33 | private GHRepository ghRepository; 34 | 35 | @Mock 36 | private GhprbPullRequest ghprbPullRequest; 37 | 38 | private GhprbTrigger trigger; 39 | 40 | @Before 41 | public void setUp() throws Exception { 42 | trigger = GhprbTestUtil.getTrigger(null); 43 | } 44 | 45 | @Test 46 | public void testMergedMessage() throws Exception { 47 | String mergedMessage = "Build triggered for merge commit."; 48 | given(ghprbPullRequest.getHead()).willReturn("sha"); 49 | given(ghprbPullRequest.isMergeable()).willReturn(true); 50 | 51 | GhprbSimpleStatus status = spy(new GhprbSimpleStatus("default")); 52 | status.onBuildTriggered(trigger.getActualProject(), "sha", true, 1, ghRepository); 53 | 54 | verify(ghRepository).createCommitStatus(eq("sha"), eq(GHCommitState.PENDING), eq(""), eq(mergedMessage), eq("default")); 55 | verifyNoMoreInteractions(ghRepository); 56 | 57 | verifyNoMoreInteractions(ghprbPullRequest); 58 | } 59 | 60 | @Test 61 | public void testMergeConflictMessage() throws Exception { 62 | String mergedMessage = "Build triggered for original commit."; 63 | given(ghprbPullRequest.getHead()).willReturn("sha"); 64 | given(ghprbPullRequest.isMergeable()).willReturn(false); 65 | 66 | GhprbSimpleStatus status = spy(new GhprbSimpleStatus("default")); 67 | status.onBuildTriggered(trigger.getActualProject(), "sha", false, 1, ghRepository); 68 | 69 | verify(ghRepository).createCommitStatus(eq("sha"), eq(GHCommitState.PENDING), eq(""), eq(mergedMessage), eq("default")); 70 | verifyNoMoreInteractions(ghRepository); 71 | 72 | verifyNoMoreInteractions(ghprbPullRequest); 73 | } 74 | 75 | @Test 76 | public void testDoesNotSendEmptyContext() throws Exception { 77 | String mergedMessage = "Build triggered for original commit."; 78 | given(ghprbPullRequest.getHead()).willReturn("sha"); 79 | given(ghprbPullRequest.isMergeable()).willReturn(false); 80 | 81 | GhprbSimpleStatus status = spy(new GhprbSimpleStatus("")); 82 | status.onBuildTriggered(trigger.getActualProject(), "sha", false, 1, ghRepository); 83 | 84 | verify(ghRepository).createCommitStatus(eq("sha"), eq(GHCommitState.PENDING), eq(""), eq(mergedMessage), isNull(String.class)); 85 | verifyNoMoreInteractions(ghRepository); 86 | 87 | verifyNoMoreInteractions(ghprbPullRequest); 88 | } 89 | 90 | @Test 91 | public void testUseDefaultContext() throws Exception { 92 | String mergedMessage = "Build triggered for original commit."; 93 | String statusUrl = "http://someserver.com"; 94 | String context = "testing context"; 95 | given(ghprbPullRequest.getHead()).willReturn("sha"); 96 | given(ghprbPullRequest.isMergeable()).willReturn(false); 97 | 98 | GhprbSimpleStatus globalStatus = 99 | new GhprbSimpleStatus(true, context, statusUrl, "test1", "test2", false, new ArrayList(0)); 100 | GhprbTrigger.getDscp().getExtensions().add(globalStatus); 101 | 102 | GhprbSimpleStatus status = new GhprbSimpleStatus(""); 103 | GhprbSimpleStatus statusSpy = spy(status); 104 | 105 | statusSpy.onBuildTriggered(trigger.getActualProject(), "sha", false, 1, ghRepository); 106 | verify(ghRepository).createCommitStatus(eq("sha"), eq(GHCommitState.PENDING), eq(""), eq(mergedMessage), isNull(String.class)); 107 | 108 | verifyNoMoreInteractions(ghRepository); 109 | 110 | verifyNoMoreInteractions(ghprbPullRequest); 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatusListener.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.upstream; 2 | 3 | import hudson.Extension; 4 | import hudson.Launcher; 5 | import hudson.model.AbstractBuild; 6 | import hudson.model.BuildListener; 7 | import hudson.model.Environment; 8 | import hudson.model.TaskListener; 9 | import hudson.model.listeners.RunListener; 10 | import org.jenkinsci.plugins.ghprb.Ghprb; 11 | import org.jenkinsci.plugins.ghprb.GhprbGitHubAuth; 12 | import org.jenkinsci.plugins.ghprb.GhprbTrigger; 13 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommitStatusException; 14 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 15 | import org.jenkinsci.plugins.ghprb.extensions.status.GhprbSimpleStatus; 16 | import org.kohsuke.github.GHCommitState; 17 | import org.kohsuke.github.GHRepository; 18 | import org.kohsuke.github.GitHub; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Map; 23 | import java.util.logging.Level; 24 | import java.util.logging.Logger; 25 | 26 | /** 27 | * @author Kevin Suwala 28 | * This class is responsible for sending the custom status and message on 29 | * downstream jobs that have the option configured. 30 | */ 31 | @Extension 32 | public class GhprbUpstreamStatusListener extends RunListener> { 33 | 34 | private static final Logger LOGGER = Logger.getLogger(GhprbUpstreamStatusListener.class.getName()); 35 | 36 | private static final int INITIAL_CAPACITY = 5; 37 | 38 | private GHRepository repo; 39 | 40 | // Gets all the custom env vars needed to send information to GitHub 41 | private Map returnEnvironmentVars(AbstractBuild build, TaskListener listener) { 42 | Map envVars = Ghprb.getEnvVars(build, listener); 43 | 44 | if (!envVars.containsKey("ghprbUpstreamStatus")) { 45 | return null; 46 | } 47 | 48 | GhprbGitHubAuth auth = GhprbTrigger.getDscp() 49 | .getGitHubAuth(envVars.get("ghprbCredentialsId")); 50 | 51 | try { 52 | GitHub gh = auth.getConnection(build.getProject()); 53 | repo = gh.getRepository(envVars.get("ghprbGhRepository")); 54 | return envVars; 55 | } catch (Exception e) { 56 | LOGGER.log(Level.SEVERE, "Unable to connect to GitHub repo", e); 57 | return null; 58 | } 59 | 60 | } 61 | 62 | private GhprbSimpleStatus returnGhprbSimpleStatus(Map envVars) { 63 | List statusMessages = new ArrayList(INITIAL_CAPACITY); 64 | 65 | for (GHCommitState state : GHCommitState.values()) { 66 | String envVar = String.format("ghprb%sMessage", state.name()); 67 | String message = envVars.get(envVar); 68 | statusMessages.add(new GhprbBuildResultMessage(state, message)); 69 | } 70 | 71 | return new GhprbSimpleStatus( 72 | Boolean.valueOf(envVars.get("ghprbShowMatrixStatus")), 73 | envVars.get("ghprbCommitStatusContext"), 74 | envVars.get("ghprbStatusUrl"), 75 | envVars.get("ghprbTriggeredStatus"), 76 | envVars.get("ghprbStartedStatus"), 77 | Boolean.valueOf(envVars.get("ghprbAddTestResults")), 78 | statusMessages 79 | ); 80 | } 81 | 82 | // Sets the status as pending when the job starts and then calls the createCommitStatus method to send it to GitHub 83 | @Override 84 | public Environment setUpEnvironment(@SuppressWarnings("rawtypes") AbstractBuild build, Launcher launcher, BuildListener listener) { 85 | Map envVars = returnEnvironmentVars(build, listener); 86 | if (envVars != null) { 87 | LOGGER.log(Level.FINE, "Job: " + build.getFullDisplayName() + " Attempting to send GitHub commit status"); 88 | 89 | try { 90 | returnGhprbSimpleStatus(envVars).onEnvironmentSetup(build, listener, repo); 91 | } catch (GhprbCommitStatusException e) { 92 | e.printStackTrace(); 93 | } 94 | } 95 | 96 | return new Environment() { 97 | }; 98 | } 99 | 100 | @Override 101 | public void onStarted(AbstractBuild build, TaskListener listener) { 102 | Map envVars = returnEnvironmentVars(build, listener); 103 | if (envVars == null) { 104 | return; 105 | } 106 | 107 | try { 108 | returnGhprbSimpleStatus(envVars).onBuildStart(build, listener, repo); 109 | } catch (GhprbCommitStatusException e) { 110 | e.printStackTrace(); 111 | } 112 | } 113 | 114 | // Sets the status to the build result when the job is done, and then calls the createCommitStatus method to send it to GitHub 115 | @Override 116 | public void onCompleted(AbstractBuild build, TaskListener listener) { 117 | Map envVars = returnEnvironmentVars(build, listener); 118 | if (envVars == null) { 119 | return; 120 | } 121 | 122 | try { 123 | returnGhprbSimpleStatus(envVars).onBuildComplete(build, listener, repo); 124 | } catch (GhprbCommitStatusException e) { 125 | e.printStackTrace(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/extensions/build/GhprbCancelBuildsOnUpdate.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.extensions.build; 2 | 3 | import hudson.Extension; 4 | import hudson.model.Cause; 5 | import hudson.model.Job; 6 | import hudson.model.Queue; 7 | import hudson.model.Result; 8 | import hudson.model.Run; 9 | import hudson.util.RunList; 10 | import jenkins.model.Jenkins; 11 | import org.jenkinsci.plugins.ghprb.Ghprb; 12 | import org.jenkinsci.plugins.ghprb.GhprbCause; 13 | import org.jenkinsci.plugins.ghprb.extensions.GhprbBuildStep; 14 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 15 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtensionDescriptor; 16 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalDefault; 17 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalExtension; 18 | import org.jenkinsci.plugins.ghprb.extensions.GhprbProjectExtension; 19 | import org.kohsuke.stapler.DataBoundConstructor; 20 | 21 | import java.util.List; 22 | import java.util.logging.Level; 23 | import java.util.logging.Logger; 24 | 25 | public class GhprbCancelBuildsOnUpdate extends GhprbExtension implements 26 | GhprbBuildStep, GhprbGlobalExtension, GhprbProjectExtension, GhprbGlobalDefault { 27 | 28 | private static final Logger LOGGER = Logger.getLogger(GhprbCancelBuildsOnUpdate.class.getName()); 29 | 30 | @Extension 31 | public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); 32 | 33 | private final Boolean overrideGlobal; 34 | 35 | @DataBoundConstructor 36 | public GhprbCancelBuildsOnUpdate(Boolean overrideGlobal) { 37 | this.overrideGlobal = overrideGlobal == null ? Boolean.valueOf(false) : overrideGlobal; 38 | } 39 | 40 | public Boolean getOverrideGlobal() { 41 | return overrideGlobal == null ? Boolean.valueOf(false) : overrideGlobal; 42 | } 43 | 44 | protected void cancelCurrentBuilds(Job project, 45 | Integer prId) { 46 | if (getOverrideGlobal()) { 47 | return; 48 | } 49 | 50 | LOGGER.log( 51 | Level.FINER, 52 | "New build scheduled for " + project.getName() + " on PR # " + prId 53 | + ", checking for queued items to cancel." 54 | ); 55 | 56 | Queue queue = Jenkins.getInstance().getQueue(); 57 | Queue.Item item = project.getQueueItem(); 58 | if (item != null) { 59 | List queueItems = queue.getItems(item.task); 60 | for (Queue.Item queueItem : queueItems) { 61 | GhprbCause qcause = null; 62 | 63 | for (Cause cause : queueItem.getCauses()) { 64 | if (cause instanceof GhprbCause) { 65 | qcause = (GhprbCause) cause; 66 | } 67 | } 68 | if (qcause != null && qcause.getPullID() == prId) { 69 | try { 70 | LOGGER.log( 71 | Level.FINER, 72 | "Cancelling queued build of " + project.getName() + " for PR # " 73 | + qcause.getPullID() + ", checking for queued items to cancel." 74 | ); 75 | queue.cancel(queueItem); 76 | } catch (Exception e) { 77 | LOGGER.log(Level.SEVERE, "Unable to cancel queued build", e); 78 | } 79 | } 80 | } 81 | } 82 | 83 | LOGGER.log(Level.FINER, "New build scheduled for " + project.getName() + " on PR # " + prId); 84 | RunList runs = project.getBuilds(); 85 | for (Run run : runs) { 86 | if (!run.isBuilding() && !run.hasntStartedYet()) { 87 | break; 88 | } 89 | GhprbCause cause = Ghprb.getCause(run); 90 | if (cause == null) { 91 | continue; 92 | } 93 | if (cause.getPullID() == prId) { 94 | try { 95 | LOGGER.log( 96 | Level.FINER, 97 | "Cancelling running build #" + run.getNumber() + " of " 98 | + project.getName() + " for PR # " + cause.getPullID() 99 | ); 100 | run.addAction(this); 101 | run.getExecutor().interrupt(Result.ABORTED); 102 | } catch (Exception e) { 103 | LOGGER.log(Level.SEVERE, "Error while trying to interrupt build!", e); 104 | } 105 | } 106 | } 107 | 108 | } 109 | 110 | public void onScheduleBuild(Job project, GhprbCause cause) { 111 | if (project == null || cause == null) { 112 | return; 113 | } 114 | if (project.isBuilding() || project.isInQueue()) { 115 | cancelCurrentBuilds(project, cause.getPullID()); 116 | } 117 | } 118 | 119 | public String getIconFileName() { 120 | return null; 121 | } 122 | 123 | public String getDisplayName() { 124 | return "Cancel Build on Pull Request Update"; 125 | } 126 | 127 | public String getUrlName() { 128 | return null; 129 | } 130 | 131 | @Override 132 | public DescriptorImpl getDescriptor() { 133 | return DESCRIPTOR; 134 | } 135 | 136 | public static final class DescriptorImpl extends GhprbExtensionDescriptor 137 | implements GhprbGlobalExtension, GhprbProjectExtension { 138 | 139 | @Override 140 | public String getDisplayName() { 141 | return "Cancel build on update"; 142 | } 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/upstream/GhprbUpstreamStatus.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.upstream; 2 | 3 | import hudson.Extension; 4 | import hudson.Launcher; 5 | import hudson.model.AbstractBuild; 6 | import hudson.model.BuildListener; 7 | import hudson.model.Descriptor; 8 | import hudson.tasks.BuildWrapper; 9 | import org.jenkinsci.plugins.ghprb.extensions.comments.GhprbBuildResultMessage; 10 | import org.kohsuke.github.GHCommitState; 11 | import org.kohsuke.stapler.DataBoundConstructor; 12 | 13 | import java.io.IOException; 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | import java.util.Map.Entry; 19 | 20 | /** 21 | * @author Kevin Suwala 22 | */ 23 | public class GhprbUpstreamStatus extends BuildWrapper { 24 | 25 | private static final int INITIAL_CAPACITY = 5; 26 | 27 | private final Boolean showMatrixStatus; 28 | 29 | private final String commitStatusContext; 30 | 31 | private final String triggeredStatus; 32 | 33 | private final String startedStatus; 34 | 35 | private final String statusUrl; 36 | 37 | private final Boolean addTestResults; 38 | 39 | private final List completedStatus; 40 | 41 | // sets the context and message as env vars so that they are available in the Listener class 42 | @Override 43 | public void makeBuildVariables(@SuppressWarnings("rawtypes") AbstractBuild build, Map variables) { 44 | variables.put("ghprbShowMatrixStatus", Boolean.toString(getShowMatrixStatus())); 45 | variables.put("ghprbUpstreamStatus", "true"); 46 | variables.put("ghprbCommitStatusContext", getCommitStatusContext()); 47 | variables.put("ghprbTriggeredStatus", getTriggeredStatus()); 48 | variables.put("ghprbStartedStatus", getStartedStatus()); 49 | variables.put("ghprbStatusUrl", getStatusUrl()); 50 | variables.put("ghprbAddTestResults", Boolean.toString(getAddTestResults())); 51 | 52 | Map statusMessages = new HashMap(INITIAL_CAPACITY); 53 | 54 | for (GhprbBuildResultMessage message : getCompletedStatus()) { 55 | GHCommitState state = message.getResult(); 56 | StringBuilder sb; 57 | if (!statusMessages.containsKey(state)) { 58 | sb = new StringBuilder(); 59 | statusMessages.put(state, sb); 60 | } else { 61 | sb = statusMessages.get(state); 62 | sb.append("\n"); 63 | } 64 | sb.append(message.getMessage()); 65 | } 66 | 67 | for (Entry next : statusMessages.entrySet()) { 68 | String key = String.format("ghprb%sMessage", next.getKey().name()); 69 | variables.put(key, next.getValue().toString()); 70 | } 71 | } 72 | 73 | @Override 74 | @SuppressWarnings("unchecked") 75 | public BuildWrapper.Environment setUp(@SuppressWarnings("rawtypes") AbstractBuild build, Launcher launcher, BuildListener listener) 76 | throws IOException, InterruptedException { 77 | makeBuildVariables(build, build.getBuildVariables()); 78 | return new Environment() { 79 | }; 80 | } 81 | 82 | @Override 83 | @SuppressWarnings("unchecked") 84 | public void preCheckout(@SuppressWarnings("rawtypes") AbstractBuild build, Launcher launcher, BuildListener listener) 85 | throws IOException, InterruptedException { 86 | makeBuildVariables(build, build.getBuildVariables()); 87 | } 88 | 89 | @DataBoundConstructor 90 | public GhprbUpstreamStatus( 91 | Boolean showMatrixStatus, 92 | String commitStatusContext, 93 | String statusUrl, 94 | String triggeredStatus, 95 | String startedStatus, 96 | Boolean addTestResults, 97 | List completedStatus 98 | ) { 99 | this.showMatrixStatus = showMatrixStatus; 100 | this.statusUrl = statusUrl; 101 | this.commitStatusContext = commitStatusContext == null ? "" : commitStatusContext; 102 | this.triggeredStatus = triggeredStatus; 103 | this.startedStatus = startedStatus; 104 | this.addTestResults = addTestResults; 105 | this.completedStatus = completedStatus; 106 | } 107 | 108 | 109 | public String getStatusUrl() { 110 | return statusUrl == null ? "" : statusUrl; 111 | } 112 | 113 | public String getCommitStatusContext() { 114 | return commitStatusContext == null ? "" : commitStatusContext; 115 | } 116 | 117 | public String getStartedStatus() { 118 | return startedStatus == null ? "" : startedStatus; 119 | } 120 | 121 | public String getTriggeredStatus() { 122 | return triggeredStatus == null ? "" : triggeredStatus; 123 | } 124 | 125 | public Boolean getAddTestResults() { 126 | return addTestResults == null ? Boolean.valueOf(false) : addTestResults; 127 | } 128 | 129 | public Boolean getShowMatrixStatus() { 130 | return showMatrixStatus == null ? Boolean.valueOf(false) : showMatrixStatus; 131 | } 132 | 133 | 134 | public List getCompletedStatus() { 135 | return completedStatus == null ? new ArrayList(0) : completedStatus; 136 | } 137 | 138 | @Extension 139 | public static final class DescriptorImpl extends Descriptor { 140 | 141 | @Override 142 | public String getDisplayName() { 143 | return "Set GitHub commit status with custom context and message (Must configure upstream job using GHPRB trigger)"; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GhprbIT.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import com.google.common.collect.Lists; 4 | import hudson.model.Action; 5 | import hudson.model.FreeStyleProject; 6 | import hudson.model.ParameterValue; 7 | import hudson.model.Run; 8 | import net.sf.json.JSONObject; 9 | import org.joda.time.DateTime; 10 | import org.junit.Before; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | import org.jvnet.hudson.test.JenkinsRule; 15 | import org.kohsuke.github.GHCommitState; 16 | import org.kohsuke.github.GHIssueComment; 17 | import org.kohsuke.stapler.BindInterceptor; 18 | import org.kohsuke.stapler.RequestImpl; 19 | import org.mockito.Mock; 20 | import org.mockito.Mockito; 21 | import org.mockito.runners.MockitoJUnitRunner; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.Map; 26 | 27 | import static com.google.common.collect.Lists.newArrayList; 28 | import static org.fest.assertions.Assertions.assertThat; 29 | import static org.mockito.BDDMockito.given; 30 | import static org.mockito.Mockito.any; 31 | 32 | @RunWith(MockitoJUnitRunner.class) 33 | public class GhprbIT extends GhprbITBaseTestCase { 34 | 35 | @Rule 36 | public JenkinsRule jenkinsRule = new JenkinsRule(); 37 | 38 | @Mock 39 | private RequestImpl req; 40 | 41 | @Mock 42 | private GHIssueComment comment; 43 | 44 | private FreeStyleProject project; 45 | 46 | @Before 47 | public void setUp() throws Exception { 48 | req = Mockito.mock(RequestImpl.class); 49 | 50 | given(req.bindJSON(any(Class.class), any(JSONObject.class))).willCallRealMethod(); 51 | given(req.bindJSON(any(Class.class), any(Class.class), any(JSONObject.class))).willCallRealMethod(); 52 | given(req.setBindInterceptor(any(BindInterceptor.class))).willCallRealMethod(); 53 | given(req.setBindListener(any(BindInterceptor.class))).willCallRealMethod(); 54 | given(req.getBindInterceptor()).willReturn(BindInterceptor.NOOP); 55 | 56 | req.setBindListener(BindInterceptor.NOOP); 57 | req.setBindInterceptor(BindInterceptor.NOOP); 58 | req.setBindInterceptor(BindInterceptor.NOOP); 59 | project = jenkinsRule.createFreeStyleProject("PRJ"); 60 | super.beforeTest(null, null, project); 61 | } 62 | 63 | @Test 64 | public void shouldBuildTriggersOnNewPR() throws Exception { 65 | given(ghPullRequest.getNumber()).willReturn(1); 66 | 67 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 68 | 69 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 70 | } 71 | 72 | @Test 73 | public void shouldBuildTriggersOnUpdatingNewCommitsPR() throws Exception { 74 | // GIVEN 75 | given(commitPointer.getSha()).willReturn("sha").willReturn("newOne").willReturn("newOne"); 76 | given(ghPullRequest.getComments()).willReturn(Lists.newArrayList()); 77 | 78 | given(ghPullRequest.getNumber()).willReturn(2).willReturn(2).willReturn(3).willReturn(3); 79 | 80 | // Also verify that uniquely different builds do not get commingled 81 | project.setQuietPeriod(4); 82 | 83 | GhprbTestUtil.triggerRunsAtOnceThenWait(10, trigger, project); 84 | 85 | assertThat(project.getBuilds().toArray().length).isEqualTo(2); 86 | } 87 | 88 | 89 | @Test 90 | public void shouldBuildTriggersOnUpdatingRetestMessagePR() throws Exception { 91 | // GIVEN 92 | given(ghPullRequest.getCreatedAt()).willReturn(new DateTime().toDate()); 93 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 94 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 95 | 96 | given(comment.getBody()).willReturn("retest this please"); 97 | given(comment.getUpdatedAt()).willReturn(new DateTime().plusDays(1).toDate()); 98 | given(comment.getUser()).willReturn(ghUser); 99 | 100 | given(ghPullRequest.getComments()).willReturn(newArrayList(comment)); 101 | given(ghPullRequest.getNumber()).willReturn(5).willReturn(5); 102 | 103 | 104 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 105 | assertThat(project.getBuilds().toArray().length).isEqualTo(2); 106 | } 107 | 108 | @Test 109 | public void shouldNotBuildDisabledBuild() throws Exception { 110 | // GIVEN 111 | given(commitPointer.getSha()).willReturn("sha"); 112 | 113 | given(comment.getBody()).willReturn("retest this please"); 114 | given(comment.getUpdatedAt()).willReturn(new DateTime().plusDays(1).toDate()); 115 | given(comment.getUser()).willReturn(ghUser); 116 | given(ghPullRequest.getComments()).willReturn(newArrayList(comment)); 117 | given(ghPullRequest.getNumber()).willReturn(5); 118 | 119 | project.disable(); 120 | 121 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 122 | assertThat(project.getBuilds().toArray().length).isEqualTo(0); 123 | 124 | Mockito.verify(ghRepository, Mockito.times(0)) 125 | .createCommitStatus(any(String.class), any(GHCommitState.class), any(String.class), any(String.class)); 126 | } 127 | 128 | @Test 129 | public void shouldContainParamsWhenDone() throws Exception { 130 | // GIVEN 131 | // This test confirms env vars are populated. It only tests one env var 132 | // under the premise that if one is populated then all are populated. 133 | 134 | String canaryVar = "ghprbActualCommit"; 135 | 136 | given(ghPullRequest.getNumber()).willReturn(1); 137 | 138 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 139 | 140 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 141 | 142 | hudson.util.RunList builds = project.getBuilds(); 143 | Run build = builds.getLastBuild(); 144 | Map envVars = build.getEnvVars(); 145 | 146 | // Ensure that the var is contained in the environment 147 | assertThat(envVars.get(canaryVar)).isNotNull(); 148 | 149 | 150 | ArrayList paramsList = newArrayList(); 151 | List actions = build.getAllActions(); 152 | for (Action a : actions) { // SECURITY-170 153 | if (a instanceof GhprbParametersAction) { 154 | List parameterValues = ((GhprbParametersAction) a).getParameters(); 155 | for (ParameterValue pv : parameterValues) { 156 | paramsList.add(pv.getName()); 157 | } 158 | } 159 | } 160 | 161 | // Ensure that the var is contained in the parameters 162 | assertThat(paramsList).contains(canaryVar); 163 | 164 | } 165 | 166 | @Test 167 | public void triggerIsRemovedFromListWhenProjectChanges() { 168 | 169 | } 170 | 171 | } 172 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/manager/impl/GhprbBaseBuildManager.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.manager.impl; 2 | 3 | import hudson.model.Job; 4 | import hudson.model.Result; 5 | import hudson.model.Run; 6 | import hudson.tasks.junit.CaseResult; 7 | import hudson.tasks.junit.TestResult; 8 | import hudson.tasks.test.AbstractTestResultAction; 9 | import hudson.tasks.test.AggregatedTestResultAction; 10 | import hudson.tasks.test.AggregatedTestResultAction.ChildReport; 11 | import jenkins.model.Jenkins; 12 | import org.jenkinsci.plugins.ghprb.manager.GhprbBuildManager; 13 | import org.jenkinsci.plugins.ghprb.manager.configuration.JobConfiguration; 14 | 15 | import java.io.IOException; 16 | import java.util.ArrayList; 17 | import java.util.Iterator; 18 | import java.util.List; 19 | import java.util.logging.Level; 20 | import java.util.logging.Logger; 21 | 22 | /** 23 | * @author mdelapenya (Manuel de la Peña) 24 | */ 25 | public abstract class GhprbBaseBuildManager implements GhprbBuildManager { 26 | 27 | private static final Logger LOGGER = Logger.getLogger(GhprbBaseBuildManager.class.getName()); 28 | 29 | public GhprbBaseBuildManager(Run build) { 30 | this.build = build; 31 | this.jobConfiguration = buildDefaultConfiguration(); 32 | } 33 | 34 | public GhprbBaseBuildManager(Run build, JobConfiguration jobConfiguration) { 35 | this.build = build; 36 | this.jobConfiguration = jobConfiguration; 37 | } 38 | 39 | private JobConfiguration buildDefaultConfiguration() { 40 | return JobConfiguration.builder().printStackTrace(false).build(); 41 | } 42 | 43 | /** 44 | * Calculate the build URL of a build of default type. This will be overriden by specific build types. 45 | * 46 | * @return the build URL of a build of default type 47 | */ 48 | public String calculateBuildUrl(String publishedURL) { 49 | return publishedURL + "/" + build.getUrl(); 50 | } 51 | 52 | /** 53 | * Return a downstream iterator of a build of default type. This will be overriden by specific build types. 54 | *

55 | * If the receiver of the call has no child projects, it will return an iterator over itself 56 | * 57 | * @return the downstream builds as an iterator 58 | */ 59 | public Iterator downstreamProjects() { 60 | List> downstreamList = new ArrayList>(); 61 | 62 | downstreamList.add(build); 63 | 64 | return downstreamList.iterator(); 65 | } 66 | 67 | public JobConfiguration getJobConfiguration() { 68 | return this.jobConfiguration; 69 | } 70 | 71 | /** 72 | * Return the tests results of a build of default type. This will be overriden by specific build types. 73 | * 74 | * @return the tests result of a build of default type 75 | */ 76 | public String getTestResults() { 77 | return getAggregatedTestResults(build); 78 | } 79 | 80 | protected String getAggregatedTestResults(Run build) { 81 | AggregatedTestResultAction testResultAction = build.getAction(AggregatedTestResultAction.class); 82 | 83 | if (testResultAction == null) { 84 | return ""; 85 | } 86 | 87 | StringBuilder sb = new StringBuilder(); 88 | 89 | if (build.getResult() != Result.UNSTABLE) { 90 | sb.append("

Build result: "); 91 | sb.append(build.getResult().toString()); 92 | sb.append("

"); 93 | 94 | try { 95 | List buildLog = build.getLog(MAX_LINES_COUNT); 96 | 97 | for (String buildLogLine : buildLog) { 98 | sb.append(buildLogLine); 99 | } 100 | } catch (IOException ioe) { 101 | LOGGER.log(Level.WARNING, ioe.getMessage()); 102 | } 103 | 104 | return sb.toString(); 105 | } 106 | 107 | sb.append("

Failed Tests: "); 108 | sb.append(""); 109 | sb.append(testResultAction.getFailCount()); 110 | sb.append("

"); 111 | 112 | List childReports = testResultAction.getChildReports(); 113 | 114 | for (ChildReport report : childReports) { 115 | TestResult result = (TestResult) report.result; 116 | 117 | if (result.getFailCount() < 1) { 118 | continue; 119 | } 120 | 121 | Job project = (Job) report.child.getProject(); 122 | 123 | String baseUrl = Jenkins.getInstance().getRootUrl() + build.getUrl() + project.getShortUrl() + "testReport"; 124 | 125 | sb.append("

"); 126 | sb.append(""); 129 | sb.append(""); 132 | sb.append(project.getFullName()); 133 | sb.append(""); 134 | sb.append(": "); 135 | sb.append(""); 136 | sb.append(result.getFailCount()); 137 | sb.append("

"); 138 | 139 | sb.append(""); 165 | } 166 | 167 | return sb.toString(); 168 | } 169 | 170 | public String getOneLineTestResults() { 171 | 172 | AbstractTestResultAction testResultAction = build.getAction(AbstractTestResultAction.class); 173 | 174 | if (testResultAction == null) { 175 | return "No test results found."; 176 | } 177 | return String.format( 178 | "%d tests run, %d skipped, %d failed.", 179 | testResultAction.getTotalCount(), 180 | testResultAction.getSkipCount(), 181 | testResultAction.getFailCount() 182 | ); 183 | } 184 | 185 | protected Run build; 186 | 187 | private static final int MAX_LINES_COUNT = 25; 188 | 189 | private JobConfiguration jobConfiguration; 190 | 191 | } 192 | -------------------------------------------------------------------------------- /checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 32 | 33 | 34 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GhprbRootActionTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import com.coravy.hudson.plugins.github.GithubProjectProperty; 4 | import hudson.model.FreeStyleProject; 5 | import hudson.plugins.git.GitSCM; 6 | import org.joda.time.DateTime; 7 | import org.junit.Before; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | import org.junit.runner.RunWith; 11 | import org.jvnet.hudson.test.JenkinsRule; 12 | import org.kohsuke.github.GHCommitPointer; 13 | import org.kohsuke.github.GHEventPayload.IssueComment; 14 | import org.kohsuke.github.GHIssueComment; 15 | import org.kohsuke.github.GHPullRequest; 16 | import org.kohsuke.github.GHRepository; 17 | import org.kohsuke.github.GHUser; 18 | import org.kohsuke.github.GitHub; 19 | import org.kohsuke.stapler.StaplerRequest; 20 | import org.mockito.Mock; 21 | import org.mockito.Mockito; 22 | import org.mockito.runners.MockitoJUnitRunner; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.ByteArrayInputStream; 26 | import java.io.InputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.Reader; 29 | import java.io.StringReader; 30 | import java.net.URLEncoder; 31 | 32 | import static com.google.common.collect.Lists.newArrayList; 33 | import static org.fest.assertions.Assertions.assertThat; 34 | import static org.kohsuke.github.GHIssueState.OPEN; 35 | import static org.mockito.BDDMockito.given; 36 | import static org.mockito.Matchers.anyString; 37 | import static org.mockito.Matchers.eq; 38 | import static org.mockito.Mockito.doReturn; 39 | import static org.mockito.Mockito.spy; 40 | import static org.mockito.Mockito.times; 41 | import static org.mockito.Mockito.verify; 42 | 43 | @RunWith(MockitoJUnitRunner.class) 44 | public class GhprbRootActionTest { 45 | 46 | @Mock 47 | protected GHCommitPointer commitPointer; 48 | 49 | @Mock 50 | protected GHPullRequest ghPullRequest; 51 | 52 | @Mock 53 | protected GhprbGitHub ghprbGitHub; 54 | 55 | @Mock 56 | protected GHRepository ghRepository; 57 | 58 | @Mock 59 | protected GHUser ghUser; 60 | 61 | @Rule 62 | public JenkinsRule jenkinsRule = new JenkinsRule(); 63 | 64 | @Mock 65 | private StaplerRequest req; 66 | 67 | private BufferedReader br; 68 | 69 | private GhprbTrigger trigger; 70 | 71 | private final int prId = 1; 72 | 73 | @Before 74 | public void setup() throws Exception { 75 | trigger = GhprbTestUtil.getTrigger(); 76 | GitHub gitHub = trigger.getGitHub(); 77 | 78 | given(gitHub.getRepository(anyString())).willReturn(ghRepository); 79 | given(commitPointer.getRef()).willReturn("ref"); 80 | given(ghRepository.getName()).willReturn("dropwizard"); 81 | 82 | GhprbTestUtil.mockPR(ghPullRequest, commitPointer, new DateTime(), new DateTime().plusDays(1)); 83 | 84 | given(ghRepository.getPullRequests(eq(OPEN))).willReturn(newArrayList(ghPullRequest)).willReturn(newArrayList(ghPullRequest)); 85 | 86 | given(ghPullRequest.getUser()).willReturn(ghUser); 87 | given(ghUser.getEmail()).willReturn("email@email.com"); 88 | given(ghUser.getLogin()).willReturn("user"); 89 | given(ghUser.getName()).willReturn("User"); 90 | 91 | 92 | GhprbTestUtil.mockCommitList(ghPullRequest); 93 | } 94 | 95 | @Test 96 | public void testUrlEncoded() throws Exception { 97 | // GIVEN 98 | FreeStyleProject project = jenkinsRule.createFreeStyleProject("testUrlEncoded"); 99 | 100 | doReturn(project).when(trigger).getActualProject(); 101 | doReturn(true).when(trigger).getUseGitHubHooks(); 102 | 103 | given(commitPointer.getSha()).willReturn("sha1"); 104 | GhprbTestUtil.setupGhprbTriggerDescriptor(null); 105 | project.addProperty(new GithubProjectProperty("https://github.com/user/dropwizard")); 106 | given(ghPullRequest.getId()).willReturn((long) prId); 107 | given(ghPullRequest.getNumber()).willReturn(prId); 108 | given(ghRepository.getPullRequest(prId)).willReturn(ghPullRequest); 109 | Ghprb ghprb = spy(new Ghprb(trigger)); 110 | doReturn(ghprbGitHub).when(ghprb).getGitHub(); 111 | doReturn(true).when(ghprb).isAdmin(Mockito.any(GHUser.class)); 112 | 113 | trigger.start(project, true); 114 | trigger.setHelper(ghprb); 115 | 116 | project.addTrigger(trigger); 117 | GitSCM scm = GhprbTestUtil.provideGitSCM(); 118 | project.setScm(scm); 119 | 120 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 121 | 122 | assertThat(project.getBuilds().toArray().length).isEqualTo(0); 123 | 124 | BufferedReader br = new BufferedReader(new StringReader( 125 | "payload=" + URLEncoder.encode(GhprbTestUtil.PAYLOAD, "UTF-8"))); 126 | 127 | 128 | given(req.getContentType()).willReturn("application/x-www-form-urlencoded"); 129 | given(req.getParameter("payload")).willReturn(GhprbTestUtil.PAYLOAD); 130 | given(req.getHeader("X-GitHub-Event")).willReturn("issue_comment"); 131 | given(req.getReader()).willReturn(br); 132 | given(req.getCharacterEncoding()).willReturn("UTF-8"); 133 | 134 | 135 | StringReader brTest = new StringReader(GhprbTestUtil.PAYLOAD); 136 | 137 | IssueComment issueComment = spy(GitHub.connectAnonymously().parseEventPayload(brTest, IssueComment.class)); 138 | brTest.close(); 139 | 140 | GHIssueComment ghIssueComment = spy(issueComment.getComment()); 141 | 142 | Mockito.when(issueComment.getComment()).thenReturn(ghIssueComment); 143 | Mockito.doReturn(ghUser).when(ghIssueComment).getUser(); 144 | 145 | 146 | given(trigger.getGitHub().parseEventPayload(Mockito.any(Reader.class), Mockito.eq(IssueComment.class))).willReturn(issueComment); 147 | 148 | GhprbRootAction ra = new GhprbRootAction(); 149 | ra.doIndex(req, null); 150 | // handles race condition around starting and finishing builds. Give the system time 151 | // to finish indexing, create a build, queue it, and run it. 152 | int count = 0; 153 | while (count < 5 && project.getBuilds().toArray().length == 0) { 154 | GhprbTestUtil.waitForBuildsToFinish(project); 155 | Thread.sleep(50); 156 | count = count + 1; 157 | } 158 | 159 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 160 | } 161 | 162 | @Test 163 | public void disabledJobsDontBuild() throws Exception { 164 | // GIVEN 165 | FreeStyleProject project = jenkinsRule.createFreeStyleProject("disabledJobsDontBuild"); 166 | doReturn(project).when(trigger).getActualProject(); 167 | 168 | given(commitPointer.getSha()).willReturn("sha1"); 169 | GhprbTestUtil.setupGhprbTriggerDescriptor(null); 170 | project.addProperty(new GithubProjectProperty("https://github.com/user/dropwizard")); 171 | given(ghPullRequest.getId()).willReturn((long) prId); 172 | given(ghPullRequest.getNumber()).willReturn(prId); 173 | given(ghRepository.getPullRequest(prId)).willReturn(ghPullRequest); 174 | Ghprb ghprb = spy(new Ghprb(trigger)); 175 | doReturn(ghprbGitHub).when(ghprb).getGitHub(); 176 | trigger.start(project, true); 177 | trigger.setHelper(ghprb); 178 | 179 | project.addTrigger(trigger); 180 | GitSCM scm = GhprbTestUtil.provideGitSCM(); 181 | project.setScm(scm); 182 | 183 | GhprbTestUtil.triggerRunAndWait(10, trigger, project); 184 | 185 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 186 | 187 | project.disable(); 188 | 189 | BufferedReader br = new BufferedReader(new StringReader( 190 | "payload=" + URLEncoder.encode(GhprbTestUtil.PAYLOAD, "UTF-8"))); 191 | 192 | given(req.getContentType()).willReturn("application/x-www-form-urlencoded"); 193 | given(req.getParameter("payload")).willReturn(GhprbTestUtil.PAYLOAD); 194 | given(req.getHeader("X-GitHub-Event")).willReturn("issue_comment"); 195 | given(req.getReader()).willReturn(br); 196 | given(req.getCharacterEncoding()).willReturn("UTF-8"); 197 | 198 | GhprbRootAction ra = new GhprbRootAction(); 199 | ra.doIndex(req, null); 200 | GhprbTestUtil.waitForBuildsToFinish(project); 201 | 202 | assertThat(project.getBuilds().toArray().length).isEqualTo(1); 203 | } 204 | 205 | @Test 206 | public void testJson() throws Exception { 207 | given(req.getContentType()).willReturn("application/json"); 208 | given(req.getHeader("X-GitHub-Event")).willReturn("issue_comment"); 209 | 210 | // convert String into InputStream 211 | InputStream is = new ByteArrayInputStream(GhprbTestUtil.PAYLOAD.getBytes()); 212 | // read it with BufferedReader 213 | br = spy(new BufferedReader(new InputStreamReader(is))); 214 | 215 | given(req.getReader()).willReturn(br); 216 | 217 | GhprbRootAction ra = new GhprbRootAction(); 218 | ra.doIndex(req, null); 219 | 220 | verify(br, times(1)).close(); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/test/java/org/jenkinsci/plugins/ghprb/GhprbTriggerTest.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import hudson.util.FormValidation; 4 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 5 | import org.jenkinsci.plugins.ghprb.extensions.GhprbGlobalDefault; 6 | import org.jenkinsci.plugins.ghprb.extensions.build.GhprbCancelBuildsOnUpdate; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | import org.junit.rules.Timeout; 10 | import org.junit.runner.RunWith; 11 | import org.jvnet.hudson.test.JenkinsRule; 12 | import org.kohsuke.github.GHPullRequest; 13 | import org.kohsuke.github.GitUser; 14 | import org.mockito.Mock; 15 | import org.mockito.runners.MockitoJUnitRunner; 16 | 17 | import java.lang.reflect.Field; 18 | import java.lang.reflect.Method; 19 | import java.util.Arrays; 20 | import java.util.HashMap; 21 | import java.util.HashSet; 22 | import java.util.Map; 23 | import java.util.Map.Entry; 24 | import java.util.Set; 25 | import java.util.concurrent.TimeUnit; 26 | import java.util.regex.Pattern; 27 | 28 | import static org.fest.assertions.Assertions.assertThat; 29 | import static org.junit.Assert.assertEquals; 30 | import static org.mockito.BDDMockito.given; 31 | import static org.mockito.Mockito.mock; 32 | 33 | /** 34 | * Unit test for {@link org.jenkinsci.plugins.ghprb.GhprbTriggerTest}. 35 | */ 36 | @RunWith(MockitoJUnitRunner.class) 37 | public class GhprbTriggerTest { 38 | @Rule 39 | public JenkinsRule jenkinsRule = new JenkinsRule(); 40 | 41 | @Rule 42 | public Timeout timeout = new Timeout(1, TimeUnit.MINUTES); 43 | 44 | @Mock 45 | private GhprbPullRequest pr; 46 | 47 | @Mock 48 | private Ghprb helper; 49 | 50 | @Test 51 | public void testGlobalExtensions() throws Exception { 52 | GhprbTrigger.getDscp().getExtensions().add(new GhprbCancelBuildsOnUpdate(false)); 53 | 54 | GhprbTrigger trigger = GhprbTestUtil.getTrigger(); 55 | 56 | for (GhprbExtension ext : trigger.getDescriptor().getExtensions()) { 57 | if (ext instanceof GhprbGlobalDefault) { 58 | assertThat(trigger.getExtensions().contains(ext)); 59 | } 60 | } 61 | } 62 | 63 | @Test 64 | public void testCheckSkipBuild() throws Exception { 65 | GHPullRequest issue = mock(GHPullRequest.class); 66 | 67 | boolean skipBuild = false; 68 | boolean build = true; 69 | 70 | String nonMatch = "Some dumb comment\r\nThat shouldn't match"; 71 | String multiLine = "This is a multiline skip\r\n[skip ci]"; 72 | String justSkipCi = "skip ci"; 73 | String fullSkipCi = "[skip ci]"; 74 | 75 | Map> stringsToTest = new HashMap>(10); 76 | 77 | Map comment = new HashMap<>(5); 78 | comment.put(nonMatch, build); 79 | comment.put(multiLine, skipBuild); 80 | comment.put(justSkipCi, build); 81 | comment.put(fullSkipCi, skipBuild); 82 | stringsToTest.put(".*\\[skip\\W+ci\\].*", comment); 83 | 84 | comment = new HashMap<>(5); 85 | comment.put(nonMatch, build); 86 | comment.put(multiLine, build); 87 | comment.put(justSkipCi, build); 88 | comment.put(fullSkipCi, skipBuild); 89 | stringsToTest.put("\\[skip ci\\]", comment); 90 | 91 | comment = new HashMap<>(5); 92 | comment.put(nonMatch, build); 93 | comment.put(multiLine, skipBuild); 94 | comment.put(justSkipCi, skipBuild); 95 | comment.put(fullSkipCi, skipBuild); 96 | stringsToTest.put("\\[skip ci\\]\n.*\\[skip\\W+ci\\].*\nskip ci", comment); 97 | 98 | Method checkSkip = GhprbPullRequest.class.getDeclaredMethod("checkSkipBuild"); 99 | checkSkip.setAccessible(true); 100 | 101 | Field prField = GhprbPullRequest.class.getDeclaredField("pr"); 102 | prField.setAccessible(true); 103 | prField.set(pr, issue); 104 | 105 | Field shouldRun = GhprbPullRequest.class.getDeclaredField("shouldRun"); 106 | shouldRun.setAccessible(true); 107 | 108 | Field prHelper = GhprbPullRequest.class.getDeclaredField("helper"); 109 | prHelper.setAccessible(true); 110 | prHelper.set(pr, helper); 111 | 112 | for (Entry> skipMap : stringsToTest.entrySet()) { 113 | String skipPhrases = skipMap.getKey(); 114 | 115 | Set phrases = new HashSet(Arrays.asList(skipPhrases.split("[\\r\\n]+"))); 116 | 117 | given(helper.getSkipBuildPhrases()).willReturn(phrases); 118 | 119 | for (Entry skipResults : skipMap.getValue().entrySet()) { 120 | String nextComment = skipResults.getKey(); 121 | Boolean shouldBuild = skipResults.getValue(); 122 | 123 | given(issue.getBody()).willReturn(nextComment); 124 | String skipPhrase = ""; 125 | 126 | for (String skipBuildPhrase : phrases) { 127 | skipBuildPhrase = skipBuildPhrase.trim(); 128 | Pattern skipBuildPhrasePattern = Ghprb.compilePattern(skipBuildPhrase); 129 | 130 | if (skipBuildPhrasePattern.matcher(nextComment).matches()) { 131 | skipPhrase = skipBuildPhrase; 132 | break; 133 | } 134 | } 135 | 136 | given(helper.checkSkipBuildPhrase(issue)).willReturn(skipPhrase); 137 | 138 | shouldRun.set(pr, true); 139 | checkSkip.invoke(pr); 140 | String errorMessage = String.format( 141 | "Comment does %scontain skip phrase \n(\n%s\n)\n[\n%s\n]", 142 | shouldBuild ? "not " : "", 143 | nextComment, 144 | skipPhrases 145 | ); 146 | 147 | if (shouldBuild) { 148 | assertThat(skipPhrase).overridingErrorMessage(errorMessage).isEmpty(); 149 | } else { 150 | assertThat(skipPhrase).overridingErrorMessage(errorMessage).isNotEmpty(); 151 | } 152 | 153 | assertThat(shouldRun.get(pr)).overridingErrorMessage(errorMessage).isEqualTo(shouldBuild); 154 | } 155 | } 156 | } 157 | 158 | @Test 159 | public void testCheckSkipBuildCommitAuthor() throws Exception { 160 | GHPullRequest issue = mock(GHPullRequest.class); 161 | GitUser user = mock(GitUser.class); 162 | 163 | Field userNameField = GitUser.class.getDeclaredField("name"); 164 | userNameField.setAccessible(true); 165 | userNameField.set(user, "foo"); 166 | 167 | Method checkSkip = GhprbPullRequest.class.getDeclaredMethod("checkSkipBuild"); 168 | checkSkip.setAccessible(true); 169 | 170 | Field prField = GhprbPullRequest.class.getDeclaredField("pr"); 171 | prField.setAccessible(true); 172 | prField.set(pr, issue); 173 | 174 | Field commitAuthorField = GhprbPullRequest.class.getDeclaredField("commitAuthor"); 175 | commitAuthorField.setAccessible(true); 176 | commitAuthorField.set(pr, user); 177 | 178 | Field shouldRun = GhprbPullRequest.class.getDeclaredField("shouldRun"); 179 | shouldRun.setAccessible(true); 180 | 181 | Field prHelper = GhprbPullRequest.class.getDeclaredField("helper"); 182 | prHelper.setAccessible(true); 183 | prHelper.set(pr, helper); 184 | 185 | given(helper.getBlacklistedCommitAuthors()).willReturn(new HashSet(Arrays.asList("bot1", "bot2"))); 186 | given(helper.checkBlackListCommitAuthor(user.getName())).willReturn(null); 187 | shouldRun.set(pr, true); 188 | checkSkip.invoke(pr); 189 | assertThat(shouldRun.get(pr)).isEqualTo(true); 190 | 191 | given(helper.checkBlackListCommitAuthor(user.getName())).willReturn("bot2"); 192 | shouldRun.set(pr, true); 193 | checkSkip.invoke(pr); 194 | assertThat(shouldRun.get(pr)).isEqualTo(false); 195 | } 196 | 197 | @Test 198 | public void testDoCheckAdminListWithOnlyHyphens() throws Exception { 199 | GhprbTrigger trigger = GhprbTestUtil.getTrigger(); 200 | 201 | final FormValidation formValidation = trigger.getDescriptor().doCheckAdminlist("--"); 202 | assertEquals(FormValidation.Kind.ERROR, formValidation.kind); 203 | } 204 | 205 | @Test 206 | public void testDoCheckAdminListWithUnderscore() throws Exception { 207 | GhprbTrigger trigger = GhprbTestUtil.getTrigger(); 208 | 209 | StringBuilder stringBuilder = new StringBuilder(); 210 | for (int i = 0; i < 1000; i++) { 211 | stringBuilder.append("test"); 212 | } 213 | 214 | stringBuilder.append("test_"); 215 | 216 | final FormValidation formValidation = trigger.getDescriptor().doCheckAdminlist(stringBuilder.toString()); 217 | assertEquals(FormValidation.Kind.ERROR, formValidation.kind); 218 | } 219 | 220 | @Test 221 | public void testDoCheckAdminListWithValidName() throws Exception { 222 | GhprbTrigger trigger = GhprbTestUtil.getTrigger(); 223 | 224 | final FormValidation formValidation = trigger.getDescriptor().doCheckAdminlist("foo-bar"); 225 | assertEquals(FormValidation.Kind.OK, formValidation.kind); 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/jobdsl/GhprbTriggerContext.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb.jobdsl; 2 | 3 | import javaposse.jobdsl.dsl.Context; 4 | import javaposse.jobdsl.plugin.ContextExtensionPoint; 5 | import org.jenkinsci.plugins.ghprb.GhprbBranch; 6 | 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | class GhprbTriggerContext implements Context { 11 | List admins = new ArrayList(); 12 | 13 | List userWhitelist = new ArrayList(); 14 | 15 | List orgWhitelist = new ArrayList(); 16 | 17 | List whiteListTargetBranches = new ArrayList(); 18 | 19 | List blackListTargetBranches = new ArrayList(); 20 | 21 | List blackListLabels = new ArrayList(); 22 | 23 | List whiteListLabels = new ArrayList(); 24 | 25 | String cron = "H/5 * * * *"; 26 | 27 | String triggerPhrase; 28 | 29 | String skipBuildPhrase; 30 | 31 | String blackListCommitAuthor; 32 | 33 | boolean onlyTriggerPhrase; 34 | 35 | boolean useGitHubHooks; 36 | 37 | boolean permitAll; 38 | 39 | boolean autoCloseFailedPullRequests; 40 | 41 | boolean allowMembersOfWhitelistedOrgsAsAdmin; 42 | 43 | boolean displayBuildErrorsOnDownstreamBuilds; 44 | 45 | String buildDescriptionTemplate; 46 | 47 | String includedRegions; 48 | 49 | String excludedRegions; 50 | 51 | GhprbExtensionContext extensionContext = new GhprbExtensionContext(); 52 | 53 | /** 54 | * Adds admins for this job. 55 | */ 56 | public void admin(String admin) { 57 | admins.add(admin); 58 | } 59 | 60 | /** 61 | * Adds admins for this job. 62 | */ 63 | public void admins(Iterable admins) { 64 | for (String admin : admins) { 65 | admin(admin); 66 | } 67 | } 68 | 69 | /** 70 | * Adds whitelisted users for this job. 71 | */ 72 | public void userWhitelist(String user) { 73 | userWhitelist.add(user); 74 | } 75 | 76 | /** 77 | * Adds whitelisted users for this job. 78 | */ 79 | public void userWhitelist(Iterable users) { 80 | for (String user : users) { 81 | userWhitelist(user); 82 | } 83 | } 84 | 85 | /** 86 | * Adds organisation names whose members are considered whitelisted for this specific job. 87 | */ 88 | public void orgWhitelist(String organization) { 89 | orgWhitelist.add(organization); 90 | } 91 | 92 | /** 93 | * Adds organisation names whose members are considered whitelisted for this specific job. 94 | */ 95 | public void orgWhitelist(Iterable organizations) { 96 | for (String organization : organizations) { 97 | orgWhitelist(organization); 98 | } 99 | } 100 | 101 | /** 102 | * Add branch names whose they are considered whitelisted for this specific job 103 | */ 104 | public void whiteListTargetBranch(String branch) { 105 | whiteListTargetBranches.add(new GhprbBranch(branch)); 106 | } 107 | 108 | /** 109 | * Add branch names whose they are considered blacklisted for this specific job 110 | */ 111 | public void blackListTargetBranch(String branch) { 112 | blackListTargetBranches.add(new GhprbBranch(branch)); 113 | } 114 | 115 | /** 116 | * Add branch names whose they are considered whitelisted for this specific job 117 | */ 118 | public void whiteListTargetBranches(Iterable branches) { 119 | for (String branch : branches) { 120 | whiteListTargetBranches.add(new GhprbBranch(branch)); 121 | } 122 | } 123 | 124 | /** 125 | * Add branch names whose they are considered blacklisted for this specific job 126 | */ 127 | public void blackListTargetBranches(Iterable branches) { 128 | for (String branch : branches) { 129 | blackListTargetBranches.add(new GhprbBranch(branch)); 130 | } 131 | } 132 | 133 | /** 134 | * Set label lists which are considered whitelisted for this specific job 135 | */ 136 | private void whiteListLabel(String whiteListLabel) { 137 | whiteListLabels.add(whiteListLabel); 138 | } 139 | 140 | /** 141 | * Set label lists which are considered whitelisted for this specific job 142 | */ 143 | public void whiteListLabels(Iterable labels) { 144 | for (String label : labels) { 145 | whiteListLabel(label); 146 | } 147 | 148 | } 149 | 150 | /** 151 | * Set label lists which are considered blacklisted for this specific job 152 | */ 153 | private void blackListLabel(String label) { 154 | blackListLabels.add(label); 155 | } 156 | 157 | /** 158 | * Set label lists which are considered blacklisted for this specific job 159 | */ 160 | public void blackListLabels(Iterable labels) { 161 | for (String label : labels) { 162 | blackListLabel(label); 163 | } 164 | 165 | } 166 | 167 | /** 168 | * This schedules polling to GitHub for new changes in pull requests. 169 | */ 170 | public void cron(String cron) { 171 | this.cron = cron; 172 | } 173 | 174 | /** 175 | * When filled, commenting this phrase in the pull request will trigger a build. 176 | */ 177 | public void triggerPhrase(String triggerPhrase) { 178 | this.triggerPhrase = triggerPhrase; 179 | } 180 | 181 | /** 182 | * When filled, adding this phrase to the pull request title or body will skip the build. 183 | */ 184 | public void skipBuildPhrase(String skipBuildPhrase) { 185 | this.skipBuildPhrase = skipBuildPhrase; 186 | } 187 | 188 | /** 189 | * When filled, pull requests comits from this user will be skipped. 190 | */ 191 | public void blackListCommitAuthor(String blackListCommitAuthor) { 192 | this.blackListCommitAuthor = blackListCommitAuthor; 193 | } 194 | 195 | /** 196 | * When set, only commenting the trigger phrase in the pull request will trigger a build. 197 | */ 198 | public void onlyTriggerPhrase(boolean onlyTriggerPhrase) { 199 | this.onlyTriggerPhrase = onlyTriggerPhrase; 200 | } 201 | 202 | 203 | /** 204 | * When set, only commenting the trigger phrase in the pull request will trigger a build. 205 | */ 206 | public void onlyTriggerPhrase() { 207 | onlyTriggerPhrase(true); 208 | } 209 | 210 | /** 211 | * Checking this option will disable regular polling for changes in GitHub and will try to create a GitHub hook. 212 | */ 213 | public void useGitHubHooks(boolean useGitHubHooks) { 214 | this.useGitHubHooks = useGitHubHooks; 215 | } 216 | 217 | /** 218 | * Checking this option will disable regular polling for changes in GitHub and will try to create a GitHub hook. 219 | */ 220 | public void useGitHubHooks() { 221 | useGitHubHooks(true); 222 | } 223 | 224 | /** 225 | * Build every pull request automatically without asking. 226 | */ 227 | public void permitAll(boolean permitAll) { 228 | this.permitAll = permitAll; 229 | } 230 | 231 | /** 232 | * Build every pull request automatically without asking. 233 | */ 234 | public void permitAll() { 235 | permitAll(true); 236 | } 237 | 238 | /** 239 | * Close pull request automatically when the build fails. 240 | */ 241 | public void autoCloseFailedPullRequests(boolean autoCloseFailedPullRequests) { 242 | this.autoCloseFailedPullRequests = autoCloseFailedPullRequests; 243 | } 244 | 245 | /** 246 | * Close pull request automatically when the build fails. 247 | */ 248 | public void autoCloseFailedPullRequests() { 249 | autoCloseFailedPullRequests(true); 250 | } 251 | 252 | /** 253 | * Allows members of whitelisted organisations to behave like admins. 254 | */ 255 | public void allowMembersOfWhitelistedOrgsAsAdmin(boolean allowMembersOfWhitelistedOrgsAsAdmin) { 256 | this.allowMembersOfWhitelistedOrgsAsAdmin = allowMembersOfWhitelistedOrgsAsAdmin; 257 | } 258 | 259 | /** 260 | * Allows members of whitelisted organisations to behave like admins. 261 | */ 262 | public void allowMembersOfWhitelistedOrgsAsAdmin() { 263 | allowMembersOfWhitelistedOrgsAsAdmin(true); 264 | } 265 | 266 | /** 267 | * Allow this upstream job to get commit statuses from downstream builds 268 | */ 269 | public void displayBuildErrorsOnDownstreamBuilds(boolean displayBuildErrorsOnDownstreamBuilds) { 270 | this.displayBuildErrorsOnDownstreamBuilds = displayBuildErrorsOnDownstreamBuilds; 271 | } 272 | 273 | /** 274 | * Allow this upstream job to get commit statuses from downstream builds 275 | */ 276 | public void displayBuildErrorsOnDownstreamBuilds() { 277 | displayBuildErrorsOnDownstreamBuilds(true); 278 | } 279 | 280 | /** 281 | * When filled, changes the default build description template 282 | */ 283 | public void buildDescriptionTemplate(String template) { 284 | this.buildDescriptionTemplate = template; 285 | } 286 | 287 | /** 288 | * Adds additional trigger options. 289 | */ 290 | public void extensions(Runnable closure) { 291 | ContextExtensionPoint.executeInContext(closure, extensionContext); 292 | } 293 | 294 | public void includedRegions(String regions) { 295 | this.includedRegions = regions; 296 | } 297 | 298 | public void excludedRegions(String regions) { 299 | this.excludedRegions = regions; 300 | } 301 | 302 | public void includedRegions(Iterable regions) { 303 | String includedRegionsStr = ""; 304 | for (String region : regions) { 305 | includedRegionsStr += (region + "\n"); 306 | } 307 | includedRegions(includedRegionsStr); 308 | } 309 | 310 | public void excludedRegions(Iterable regions) { 311 | String excludedRegionsStr = ""; 312 | for (String region : regions) { 313 | excludedRegionsStr += (region + "\n"); 314 | } 315 | excludedRegions(excludedRegionsStr); 316 | } 317 | 318 | } 319 | -------------------------------------------------------------------------------- /src/main/java/org/jenkinsci/plugins/ghprb/GhprbBuilds.java: -------------------------------------------------------------------------------- 1 | package org.jenkinsci.plugins.ghprb; 2 | 3 | import com.google.common.annotations.VisibleForTesting; 4 | import hudson.Launcher; 5 | import hudson.Util; 6 | import hudson.model.Result; 7 | import hudson.model.Run; 8 | import hudson.model.TaskListener; 9 | import hudson.model.queue.QueueTaskFuture; 10 | import hudson.plugins.git.util.BuildData; 11 | import org.apache.commons.lang.StringUtils; 12 | import org.jenkinsci.plugins.ghprb.extensions.GhprbBuildStep; 13 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommentAppender; 14 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommitStatus; 15 | import org.jenkinsci.plugins.ghprb.extensions.GhprbCommitStatusException; 16 | import org.jenkinsci.plugins.ghprb.extensions.GhprbExtension; 17 | import org.kohsuke.github.GHCommitState; 18 | import org.kohsuke.github.GHIssueState; 19 | import org.kohsuke.github.GHPullRequest; 20 | import org.kohsuke.github.GHUser; 21 | 22 | import java.io.IOException; 23 | import java.io.PrintStream; 24 | import java.net.URL; 25 | import java.util.HashMap; 26 | import java.util.Map; 27 | import java.util.logging.Level; 28 | import java.util.logging.Logger; 29 | 30 | /** 31 | * @author janinko 32 | */ 33 | public class GhprbBuilds { 34 | 35 | private static final Logger LOGGER = Logger.getLogger(GhprbBuilds.class.getName()); 36 | 37 | private static final int WAIT_TIMEOUT = 1000; 38 | 39 | private static final int WAIT_COUNTER = 60; 40 | 41 | private final GhprbTrigger trigger; 42 | 43 | private final GhprbRepository repo; 44 | 45 | public GhprbBuilds(GhprbTrigger trigger, GhprbRepository repo) { 46 | this.trigger = trigger; 47 | this.repo = repo; 48 | } 49 | 50 | public void build(GhprbPullRequest pr, GHUser triggerSender, String commentBody) { 51 | 52 | URL url = null; 53 | GHUser prAuthor = null; 54 | 55 | try { 56 | url = pr.getUrl(); 57 | prAuthor = pr.getPullRequestAuthor(); 58 | } catch (IOException e) { 59 | LOGGER.log(Level.SEVERE, "Unable to get PR author or PR URL", e); 60 | } 61 | 62 | GhprbCause cause = new GhprbCause(pr.getHead(), 63 | pr.getId(), 64 | pr.isMergeable(), 65 | pr.getTarget(), 66 | pr.getSource(), 67 | pr.getAuthorEmail(), 68 | pr.getTitle(), 69 | url, 70 | triggerSender, 71 | commentBody, 72 | pr.getCommitAuthor(), 73 | prAuthor, 74 | pr.getDescription(), 75 | pr.getAuthorRepoGitUrl(), 76 | repo.getName(), 77 | trigger.getGitHubApiAuth().getCredentialsId()); 78 | 79 | for (GhprbExtension ext : Ghprb.getJobExtensions(trigger, GhprbCommitStatus.class)) { 80 | if (ext instanceof GhprbCommitStatus) { 81 | try { 82 | ((GhprbCommitStatus) ext).onBuildTriggered( 83 | trigger.getActualProject(), 84 | pr.getHead(), 85 | pr.isMergeable(), 86 | pr.getId(), 87 | repo.getGitHubRepo() 88 | ); 89 | } catch (GhprbCommitStatusException e) { 90 | repo.commentOnFailure(null, null, e); 91 | } 92 | } 93 | } 94 | QueueTaskFuture build = trigger.scheduleBuild(cause, repo); 95 | if (build == null) { 96 | LOGGER.log(Level.SEVERE, "Job did not start"); 97 | } 98 | } 99 | 100 | public void onStarted(Run build, TaskListener listener) { 101 | PrintStream logger = listener.getLogger(); 102 | GhprbCause c = Ghprb.getCause(build); 103 | if (c == null) { 104 | return; 105 | } 106 | 107 | GhprbTrigger trigger = Ghprb.extractTrigger(build); 108 | GhprbPullRequest pullRequest = trigger.getRepository().getPullRequest(c.getPullID()); 109 | pullRequest.setBuild(build); 110 | 111 | try { 112 | GHPullRequest pr = pullRequest.getPullRequest(true); 113 | int counter = 0; 114 | // If the PR is being resolved by GitHub then getMergeable will return null 115 | Boolean isMergeable = pr.getMergeable(); 116 | boolean isMerged = pr.isMerged(); 117 | while (isMergeable == null && !isMerged && counter++ < WAIT_COUNTER) { 118 | Thread.sleep(WAIT_TIMEOUT); 119 | isMergeable = pr.getMergeable(); 120 | isMerged = pr.isMerged(); 121 | } 122 | 123 | if (isMerged) { 124 | logger.println("PR has already been merged, builds using the merged sha1 will fail!!!"); 125 | } else if (isMergeable == null) { 126 | logger.println("PR merge status couldn't be retrieved, maybe GitHub hasn't settled yet"); 127 | } else if (isMergeable != c.isMerged()) { 128 | logger.println("!!! PR mergeability status has changed !!! "); 129 | if (isMergeable) { 130 | logger.println("PR now has NO merge conflicts"); 131 | } else if (!isMergeable) { 132 | logger.println("PR now has merge conflicts!"); 133 | } 134 | } 135 | 136 | } catch (Exception e) { 137 | logger.print("Unable to query GitHub for status of PullRequest"); 138 | e.printStackTrace(logger); 139 | } 140 | 141 | for (GhprbExtension ext : Ghprb.getJobExtensions(trigger, GhprbCommitStatus.class)) { 142 | if (ext instanceof GhprbCommitStatus) { 143 | try { 144 | ((GhprbCommitStatus) ext).onBuildStart(build, listener, repo.getGitHubRepo()); 145 | } catch (GhprbCommitStatusException e) { 146 | repo.commentOnFailure(build, listener, e); 147 | } 148 | } 149 | } 150 | 151 | try { 152 | String template = trigger.getBuildDescTemplate(); 153 | if (StringUtils.isEmpty(template)) { 154 | template = "PR #$pullId: $abbrTitle"; 155 | } 156 | Map vars = getVariables(c); 157 | template = Util.replaceMacro(template, vars); 158 | template = Ghprb.replaceMacros(build, listener, template); 159 | build.setDescription(template); 160 | } catch (IOException ex) { 161 | logger.print("Can't update build description"); 162 | ex.printStackTrace(logger); 163 | } 164 | } 165 | 166 | public Map getVariables(GhprbCause c) { 167 | Map vars = new HashMap(); 168 | vars.put("title", c.getTitle()); 169 | if (c.getUrl() != null) { 170 | vars.put("url", c.getUrl().toString()); 171 | } else { 172 | vars.put("url", ""); 173 | } 174 | vars.put("pullId", Integer.toString(c.getPullID())); 175 | vars.put("abbrTitle", c.getAbbreviatedTitle()); 176 | return vars; 177 | } 178 | 179 | public void onCompleted(Run build, TaskListener listener) { 180 | GhprbCause c = Ghprb.getCause(build); 181 | if (c == null) { 182 | return; 183 | } 184 | 185 | // remove the BuildData action that we may have added earlier to avoid 186 | // having two of them, and because the one we added isn't correct 187 | // @see GhprbTrigger 188 | for (BuildData data : build.getActions(BuildData.class)) { 189 | if (data.getLastBuiltRevision() != null && !data.getLastBuiltRevision().getSha1String().equals(c.getCommit())) { 190 | build.getActions().remove(data); 191 | break; 192 | } 193 | } 194 | 195 | if (build.getResult() == Result.ABORTED) { 196 | GhprbBuildStep abortAction = build.getAction(GhprbBuildStep.class); 197 | if (abortAction != null) { 198 | return; 199 | } 200 | } 201 | 202 | for (GhprbExtension ext : Ghprb.getJobExtensions(trigger, GhprbCommitStatus.class)) { 203 | if (ext instanceof GhprbCommitStatus) { 204 | try { 205 | ((GhprbCommitStatus) ext).onBuildComplete(build, listener, repo.getGitHubRepo()); 206 | } catch (GhprbCommitStatusException e) { 207 | repo.commentOnFailure(build, listener, e); 208 | } 209 | } 210 | } 211 | 212 | GHCommitState state; 213 | state = Ghprb.getState(build); 214 | 215 | commentOnBuildResult(build, listener, state, c); 216 | // close failed pull request automatically 217 | if (state == GHCommitState.FAILURE && trigger.getAutoCloseFailedPullRequests()) { 218 | closeFailedRequest(listener, c); 219 | } 220 | } 221 | 222 | private void closeFailedRequest(TaskListener listener, GhprbCause c) { 223 | try { 224 | GHPullRequest pr = repo.getActualPullRequest(c.getPullID()); 225 | 226 | if (pr.getState().equals(GHIssueState.OPEN)) { 227 | repo.closePullRequest(c.getPullID()); 228 | } 229 | } catch (IOException ex) { 230 | listener.getLogger().println("Can't close pull request"); 231 | ex.printStackTrace(listener.getLogger()); 232 | } 233 | } 234 | 235 | @VisibleForTesting 236 | void commentOnBuildResult(Run build, TaskListener listener, GHCommitState state, GhprbCause c) { 237 | StringBuilder msg = new StringBuilder(); 238 | 239 | for (GhprbExtension ext : Ghprb.getJobExtensions(trigger, GhprbCommentAppender.class)) { 240 | if (ext instanceof GhprbCommentAppender) { 241 | String cmt = ((GhprbCommentAppender) ext).postBuildComment(build, listener); 242 | if ("--none--".equals(cmt)) { 243 | return; 244 | } 245 | msg.append(cmt); 246 | } 247 | } 248 | 249 | if (msg.length() > 0) { 250 | listener.getLogger().println(msg); 251 | repo.addComment(c.getPullID(), msg.toString(), build, listener); 252 | } 253 | } 254 | 255 | public void onEnvironmentSetup(@SuppressWarnings("rawtypes") Run build, Launcher launcher, TaskListener listener) { 256 | GhprbCause c = Ghprb.getCause(build); 257 | if (c == null) { 258 | return; 259 | } 260 | 261 | LOGGER.log(Level.FINE, "Job: " + build.getFullDisplayName() + " Attempting to send GitHub commit status"); 262 | 263 | for (GhprbExtension ext : Ghprb.getJobExtensions(trigger, GhprbCommitStatus.class)) { 264 | if (ext instanceof GhprbCommitStatus) { 265 | try { 266 | ((GhprbCommitStatus) ext).onEnvironmentSetup(build, listener, repo.getGitHubRepo()); 267 | } catch (GhprbCommitStatusException e) { 268 | repo.commentOnFailure(build, listener, e); 269 | } 270 | } 271 | } 272 | } 273 | 274 | } 275 | --------------------------------------------------------------------------------