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 | 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 extends GhprbExtensionType>... types) {
21 | List list = getExtensions();
22 | filterExtensions(list, types);
23 | return list;
24 | }
25 |
26 | private static void filterExtensions(List descriptors, Class extends GhprbExtensionType>... types) {
27 | List predicates = new ArrayList(types.length);
28 | for (Class extends GhprbExtensionType> 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 extends Action> 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 |
--------------------------------------------------------------------------------