Merge pull request #508 from mabbas85/feature/trigger_when_MR_merged_or_closed

Trigger build when merge request has been merged or closed
This commit is contained in:
milena 2017-04-18 11:09:51 +02:00 committed by GitHub
commit 0986edd9b3
9 changed files with 155 additions and 47 deletions

View File

@ -17,7 +17,7 @@
# Introduction
This plugin allows GitLab to trigger builds in Jenkins after code is pushed and/or after a merge request is created and report build status back to GitLab.
This plugin allows GitLab to trigger builds in Jenkins after code is pushed and/or after a merge request is created and/or after an existing merge request was merged/closed, and report build status back to GitLab.
# Seeking maintainers
@ -125,7 +125,7 @@ node {
* Select *Build when a change is pushed to GitLab*
* Make a note of the *GitLab CI Service URL* appearing on the same line with *Build when a change is
pushed to GitLab*. You will later use this URL to define a GitLab web hook.
* Use the check boxes to trigger builds on *Push Events* and/or *Merge Request Events*
* Use the check boxes to trigger builds on *Push Events* and/or *Created Merge Request Events* and/or *Accepted Merge Request Events* and/or *Closed Merge Request Events*
* Optionally use *Rebuild open Merge Requests* to enable re-building open merge requests after a
push to the source branch
* If you selected *Rebuild open Merge Requests* other than *None*, check *Comments*, and specify the
@ -267,6 +267,9 @@ These include:
* gitlabMergeRequestDescription
* gitlabMergeRequestId
* gitlabMergeRequestIid
* gitMergeRequestState
* gitMergedByUser
* gitMergeRequestAssignee
* gitlabMergeRequestLastCommit
* gitlabTargetBranch
* gitlabTargetRepoName

View File

@ -74,6 +74,8 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
private boolean triggerOnPush = true;
private boolean triggerOnMergeRequest = true;
private boolean triggerOnAcceptedMergeRequest = false;
private boolean triggerOnClosedMergeRequest = false;
private final TriggerOpenMergeRequest triggerOpenMergeRequestOnPush;
private boolean triggerOnNoteRequest = true;
private final String noteRegex;
@ -102,14 +104,17 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
@DataBoundConstructor
@GeneratePojoBuilder(intoPackage = "*.builder.generated", withFactoryMethod = "*")
public GitLabPushTrigger(boolean triggerOnPush, boolean triggerOnMergeRequest, TriggerOpenMergeRequest triggerOpenMergeRequestOnPush,
boolean triggerOnNoteRequest, String noteRegex, boolean skipWorkInProgressMergeRequest, boolean ciSkip,
public GitLabPushTrigger(boolean triggerOnPush, boolean triggerOnMergeRequest, boolean triggerOnAcceptedMergeRequest, boolean triggerOnClosedMergeRequest,
TriggerOpenMergeRequest triggerOpenMergeRequestOnPush, boolean triggerOnNoteRequest, String noteRegex,
boolean skipWorkInProgressMergeRequest, boolean ciSkip,
boolean setBuildDescription, boolean addNoteOnMergeRequest, boolean addCiMessage, boolean addVoteOnMergeRequest,
boolean acceptMergeRequestOnSuccess, BranchFilterType branchFilterType,
String includeBranchesSpec, String excludeBranchesSpec, String targetBranchRegex,
MergeRequestLabelFilterConfig mergeRequestLabelFilterConfig, String secretToken) {
this.triggerOnPush = triggerOnPush;
this.triggerOnMergeRequest = triggerOnMergeRequest;
this.triggerOnAcceptedMergeRequest = triggerOnAcceptedMergeRequest;
this.triggerOnClosedMergeRequest = triggerOnClosedMergeRequest;
this.triggerOnNoteRequest = triggerOnNoteRequest;
this.noteRegex = noteRegex;
this.triggerOpenMergeRequestOnPush = triggerOpenMergeRequestOnPush;
@ -252,7 +257,9 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
}
private void initializeTriggerHandler() {
mergeRequestHookTriggerHandler = newMergeRequestHookTriggerHandler(triggerOnMergeRequest, triggerOpenMergeRequestOnPush, skipWorkInProgressMergeRequest);
mergeRequestHookTriggerHandler = newMergeRequestHookTriggerHandler(triggerOnMergeRequest,
triggerOnAcceptedMergeRequest, triggerOnClosedMergeRequest, triggerOpenMergeRequestOnPush,
skipWorkInProgressMergeRequest);
noteHookTriggerHandler = newNoteHookTriggerHandler(triggerOnNoteRequest, noteRegex);
pushHookTriggerHandler = newPushHookTriggerHandler(triggerOnPush, triggerOpenMergeRequestOnPush, skipWorkInProgressMergeRequest);
}

View File

@ -36,6 +36,9 @@ public final class CauseData {
private final String mergeRequestDescription;
private final Integer mergeRequestId;
private final Integer mergeRequestIid;
private final String mergeRequestState;
private final String mergedByUser;
private final String mergeRequestAssignee;
private final String targetBranch;
private final String targetRepoName;
private final String targetNamespace;
@ -54,7 +57,7 @@ public final class CauseData {
String sourceRepoSshUrl, String sourceRepoHttpUrl, String mergeRequestTitle, String mergeRequestDescription, Integer mergeRequestId,
Integer mergeRequestIid, String targetBranch, String targetRepoName, String targetNamespace, String targetRepoSshUrl,
String targetRepoHttpUrl, String triggeredByUser, String before, String after, String lastCommit, String targetProjectUrl,
String triggerPhrase) {
String triggerPhrase, String mergeRequestState, String mergedByUser, String mergeRequestAssignee) {
this.actionType = checkNotNull(actionType, "actionType must not be null.");
this.sourceProjectId = checkNotNull(sourceProjectId, "sourceProjectId must not be null.");
this.targetProjectId = checkNotNull(targetProjectId, "targetProjectId must not be null.");
@ -72,6 +75,9 @@ public final class CauseData {
this.mergeRequestDescription = mergeRequestDescription == null ? "" : mergeRequestDescription;
this.mergeRequestId = mergeRequestId;
this.mergeRequestIid = mergeRequestIid;
this.mergeRequestState = mergeRequestState == null ? "" : mergeRequestState;
this.mergedByUser = mergedByUser == null ? "" : mergedByUser;
this.mergeRequestAssignee = mergeRequestAssignee == null ? "" : mergeRequestAssignee;
this.targetBranch = checkNotNull(targetBranch, "targetBranch must not be null.");
this.targetRepoName = checkNotNull(targetRepoName, "targetRepoName must not be null.");
this.targetNamespace = checkNotNull(targetNamespace, "targetNamespace must not be null.");
@ -103,6 +109,9 @@ public final class CauseData {
variables.put("gitlabMergeRequestId", mergeRequestId == null ? "" : mergeRequestId.toString());
variables.put("gitlabMergeRequestIid", mergeRequestIid == null ? "" : mergeRequestIid.toString());
variables.put("gitlabMergeRequestLastCommit", lastCommit);
variables.pufIfNotNull("gitlabMergeRequestState", mergeRequestState);
variables.pufIfNotNull("gitlabMergedByUser", mergedByUser);
variables.pufIfNotNull("gitlabMergeRequestAssignee", mergeRequestAssignee);
variables.put("gitlabTargetBranch", targetBranch);
variables.put("gitlabTargetRepoName", targetRepoName);
variables.put("gitlabTargetNamespace", targetNamespace);
@ -226,7 +235,19 @@ public final class CauseData {
return actionType.getShortDescription(this);
}
@Override
public String getMergeRequestState() {
return mergeRequestState;
}
public String getMergedByUser() {
return mergedByUser;
}
public String getMergeRequestAssignee() {
return mergeRequestAssignee;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
@ -253,6 +274,9 @@ public final class CauseData {
.append(mergeRequestDescription, causeData.mergeRequestDescription)
.append(mergeRequestId, causeData.mergeRequestId)
.append(mergeRequestIid, causeData.mergeRequestIid)
.append(mergeRequestState, causeData.mergeRequestState)
.append(mergedByUser, causeData.mergedByUser)
.append(mergeRequestAssignee, causeData.mergeRequestAssignee)
.append(targetBranch, causeData.targetBranch)
.append(targetRepoName, causeData.targetRepoName)
.append(targetNamespace, causeData.targetNamespace)
@ -286,6 +310,9 @@ public final class CauseData {
.append(mergeRequestDescription)
.append(mergeRequestId)
.append(mergeRequestIid)
.append(mergeRequestState)
.append(mergedByUser)
.append(mergeRequestAssignee)
.append(targetBranch)
.append(targetRepoName)
.append(targetNamespace)
@ -319,6 +346,9 @@ public final class CauseData {
.append("mergeRequestDescription", mergeRequestDescription)
.append("mergeRequestId", mergeRequestId)
.append("mergeRequestIid", mergeRequestIid)
.append("mergeRequestState", mergeRequestState)
.append("mergedByUser", mergedByUser)
.append("mergeRequestAssignee", mergeRequestAssignee)
.append("targetBranch", targetBranch)
.append("targetRepoName", targetRepoName)
.append("targetNamespace", targetNamespace)

View File

@ -13,6 +13,7 @@ import org.apache.commons.lang.builder.ToStringBuilder;
public class MergeRequestHook extends WebHook {
private User user;
private User assignee;
private Project project;
private MergeRequestObjectAttributes objectAttributes;
@ -40,7 +41,15 @@ public class MergeRequestHook extends WebHook {
this.objectAttributes = objectAttributes;
}
@Override
public User getAssignee() {
return assignee;
}
public void setAssignee(User assignee) {
this.assignee = assignee;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
@ -51,6 +60,7 @@ public class MergeRequestHook extends WebHook {
MergeRequestHook that = (MergeRequestHook) o;
return new EqualsBuilder()
.append(user, that.user)
.append(assignee, that.assignee)
.append(project, that.project)
.append(objectAttributes, that.objectAttributes)
.isEquals();
@ -60,6 +70,7 @@ public class MergeRequestHook extends WebHook {
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(user)
.append(assignee)
.append(project)
.append(objectAttributes)
.toHashCode();
@ -69,6 +80,7 @@ public class MergeRequestHook extends WebHook {
public String toString() {
return new ToStringBuilder(this)
.append("user", user)
.append("assignee", assignee)
.append("project", project)
.append("objectAttributes", objectAttributes)
.toString();

View File

@ -14,22 +14,33 @@ public final class MergeRequestHookTriggerHandlerFactory {
private MergeRequestHookTriggerHandlerFactory() {}
public static MergeRequestHookTriggerHandler newMergeRequestHookTriggerHandler(boolean triggerOnMergeRequest,
boolean triggerOnAcceptedMergeRequest,
boolean triggerOnClosedMergeRequest,
TriggerOpenMergeRequest triggerOpenMergeRequest,
boolean skipWorkInProgressMergeRequest) {
if (triggerOnMergeRequest || triggerOpenMergeRequest != TriggerOpenMergeRequest.never) {
return new MergeRequestHookTriggerHandlerImpl(retrieveAllowedStates(triggerOnMergeRequest, triggerOpenMergeRequest),
if (triggerOnMergeRequest || triggerOnAcceptedMergeRequest || triggerOnClosedMergeRequest || triggerOpenMergeRequest != TriggerOpenMergeRequest.never) {
return new MergeRequestHookTriggerHandlerImpl(retrieveAllowedStates(triggerOnMergeRequest, triggerOnAcceptedMergeRequest, triggerOnClosedMergeRequest, triggerOpenMergeRequest),
skipWorkInProgressMergeRequest);
} else {
return new NopMergeRequestHookTriggerHandler();
}
}
private static List<State> retrieveAllowedStates(boolean triggerOnMergeRequest, TriggerOpenMergeRequest triggerOpenMergeRequest) {
private static List<State> retrieveAllowedStates(boolean triggerOnMergeRequest,
boolean triggerOnAcceptedMergeRequest,
boolean triggerOnClosedMergeRequest,
TriggerOpenMergeRequest triggerOpenMergeRequest) {
List<State> result = new ArrayList<>();
if (triggerOnMergeRequest) {
result.add(State.opened);
result.add(State.reopened);
}
if (triggerOnAcceptedMergeRequest) {
result.add(State.merged);
}
if (triggerOnClosedMergeRequest) {
result.add(State.closed);
}
if (triggerOpenMergeRequest != TriggerOpenMergeRequest.never) {
result.add(State.updated);
}

View File

@ -87,6 +87,9 @@ class MergeRequestHookTriggerHandlerImpl extends AbstractWebHookTriggerHandler<M
.withMergeRequestDescription(hook.getObjectAttributes().getDescription())
.withMergeRequestId(hook.getObjectAttributes().getId())
.withMergeRequestIid(hook.getObjectAttributes().getIid())
.withMergeRequestState(hook.getObjectAttributes().getState().toString())
.withMergedByUser(hook.getUser() == null ? null : hook.getUser().getUsername())
.withMergeRequestAssignee(hook.getAssignee() == null ? null : hook.getAssignee().getUsername())
.withTargetBranch(hook.getObjectAttributes().getTargetBranch())
.withTargetRepoName(hook.getObjectAttributes().getTarget().getName())
.withTargetNamespace(hook.getObjectAttributes().getTarget().getNamespace())

View File

@ -60,6 +60,9 @@ class PushHookTriggerHandlerImpl extends AbstractWebHookTriggerHandler<PushHook>
.withMergeRequestDescription("")
.withMergeRequestId(null)
.withMergeRequestIid(null)
.withMergeRequestState(null)
.withMergedByUser("")
.withMergeRequestAssignee("")
.withTargetBranch(getTargetBranch(hook))
.withTargetRepoName("")
.withTargetNamespace("")

View File

@ -6,9 +6,15 @@
<f:entry title="Push Events" field="triggerOnPush">
<f:checkbox default="true"/>
</f:entry>
<f:entry title="Merge Request Events" field="triggerOnMergeRequest">
<f:entry title="Opened Merge Request Events" field="triggerOnMergeRequest">
<f:checkbox default="true"/>
</f:entry>
<f:entry title="Accepted Merge Request Events" field="triggerOnAcceptedMergeRequest">
<f:checkbox default="false"/>
</f:entry>
<f:entry title="Closed Merge Request Events" field="triggerOnClosedMergeRequest">
<f:checkbox default="false"/>
</f:entry>
<f:entry title="Rebuild open Merge Requests" field="triggerOpenMergeRequestOnPush">
<f:select/>
</f:entry>

View File

@ -14,7 +14,6 @@ import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@ -47,13 +46,6 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Rule
public TemporaryFolder tmp = new TemporaryFolder();
private MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler;
@Before
public void setup() {
mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false);
}
@Test
public void mergeRequest_ciSkip() throws IOException, InterruptedException {
final OneShotEvent buildTriggered = new OneShotEvent();
@ -66,6 +58,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
}
});
project.setQuietPeriod(0);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false);
mergeRequestHookTriggerHandler.handle(project, mergeRequestHook()
.withObjectAttributes(mergeRequestObjectAttributes().withDescription("[ci-skip]").build())
.build(), true, BranchFilterFactory.newBranchFilter(branchFilterConfig().build(BranchFilterType.All)),
@ -77,6 +70,45 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_build() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.opened);
assertThat(buildTriggered.isSignaled(), is(true));
}
@Test
public void mergeRequest_build_when_accepted() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.merged), false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.merged);
assertThat(buildTriggered.isSignaled(), is(true));
}
@Test
public void mergeRequest_build_when_closed() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.closed), false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.closed);
assertThat(buildTriggered.isSignaled(), is(true));
}
@Test
public void mergeRequest_do_not_build_when_accepted() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.updated), false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.merged);
assertThat(buildTriggered.isSignaled(), is(false));
}
@Test
public void mergeRequest_do_not_build_when_closed() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.updated), false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.closed);
assertThat(buildTriggered.isSignaled(), is(false));
}
private OneShotEvent doHandle(MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler, State state) throws GitAPIException, IOException, InterruptedException {
Git.init().setDirectory(tmp.getRoot()).call();
tmp.newFile("test");
Git git = Git.open(tmp.getRoot());
@ -98,40 +130,41 @@ public class MergeRequestHookTriggerHandlerImplTest {
project.setQuietPeriod(0);
mergeRequestHookTriggerHandler.handle(project, mergeRequestHook()
.withObjectAttributes(mergeRequestObjectAttributes()
.withTargetBranch("refs/heads/" + git.nameRev().add(head).call().get(head))
.withState(State.opened)
.withIid(1)
.withTitle("test")
.withTargetProjectId(1)
.withSourceProjectId(1)
.withSourceBranch("feature")
.withTargetBranch("master")
.withLastCommit(commit().withAuthor(user().withName("test").build()).withId(commit.getName()).build())
.withSource(project()
.withName("test")
.withNamespace("test-namespace")
.withHomepage("https://gitlab.org/test")
.withUrl("git@gitlab.org:test.git")
.withSshUrl("git@gitlab.org:test.git")
.withHttpUrl("https://gitlab.org/test.git")
.build())
.withTarget(project()
.withName("test")
.withNamespace("test-namespace")
.withHomepage("https://gitlab.org/test")
.withUrl("git@gitlab.org:test.git")
.withSshUrl("git@gitlab.org:test.git")
.withHttpUrl("https://gitlab.org/test.git")
.build())
.withTargetBranch("refs/heads/" + git.nameRev().add(head).call().get(head))
.withState(state)
.withIid(1)
.withTitle("test")
.withTargetProjectId(1)
.withSourceProjectId(1)
.withSourceBranch("feature")
.withTargetBranch("master")
.withLastCommit(commit().withAuthor(user().withName("test").build()).withId(commit.getName()).build())
.withSource(project()
.withName("test")
.withNamespace("test-namespace")
.withHomepage("https://gitlab.org/test")
.withUrl("git@gitlab.org:test.git")
.withSshUrl("git@gitlab.org:test.git")
.withHttpUrl("https://gitlab.org/test.git")
.build())
.withTarget(project()
.withName("test")
.withNamespace("test-namespace")
.withHomepage("https://gitlab.org/test")
.withUrl("git@gitlab.org:test.git")
.withSshUrl("git@gitlab.org:test.git")
.withHttpUrl("https://gitlab.org/test.git")
.build())
.build())
.withProject(project()
.withWebUrl("https://gitlab.org/test.git")
.build()
)
.build(), true, BranchFilterFactory.newBranchFilter(branchFilterConfig().build(BranchFilterType.All)),
newMergeRequestLabelFilter(null));
newMergeRequestLabelFilter(null));
buildTriggered.block(10000);
assertThat(buildTriggered.isSignaled(), is(true));
return buildTriggered;
}
}