Merge pull request #693 from ttsalonen/pending-build-status-for-pipelines

Pending status for pipelines and possibility to cancel pending builds on merge request updates
This commit is contained in:
Owen Mehegan 2018-05-26 11:39:51 -04:00 committed by GitHub
commit f73144fa9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 408 additions and 37 deletions

View File

@ -162,6 +162,11 @@
<artifactId>workflow-step-api</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins.workflow</groupId>
<artifactId>workflow-job</artifactId>
<version>1.15</version>
</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>credentials</artifactId>

View File

@ -100,6 +100,8 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
private String targetBranchRegex;
private MergeRequestLabelFilterConfig mergeRequestLabelFilterConfig;
private volatile Secret secretToken;
private String pendingBuildName;
private boolean cancelPendingBuildsOnUpdate;
private transient BranchFilter branchFilter;
private transient PushHookTriggerHandler pushHookTriggerHandler;
@ -120,8 +122,8 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
boolean setBuildDescription, boolean addNoteOnMergeRequest, boolean addCiMessage, boolean addVoteOnMergeRequest,
boolean acceptMergeRequestOnSuccess, BranchFilterType branchFilterType,
String includeBranchesSpec, String excludeBranchesSpec, String targetBranchRegex,
MergeRequestLabelFilterConfig mergeRequestLabelFilterConfig, String secretToken, boolean triggerOnPipelineEvent,
boolean triggerOnApprovedMergeRequest) {
MergeRequestLabelFilterConfig mergeRequestLabelFilterConfig, String secretToken, boolean triggerOnPipelineEvent,
boolean triggerOnApprovedMergeRequest, String pendingBuildName, boolean cancelPendingBuildsOnUpdate) {
this.triggerOnPush = triggerOnPush;
this.triggerOnMergeRequest = triggerOnMergeRequest;
this.triggerOnAcceptedMergeRequest = triggerOnAcceptedMergeRequest;
@ -144,6 +146,8 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
this.mergeRequestLabelFilterConfig = mergeRequestLabelFilterConfig;
this.secretToken = Secret.fromString(secretToken);
this.triggerOnApprovedMergeRequest = triggerOnApprovedMergeRequest;
this.pendingBuildName = pendingBuildName;
this.cancelPendingBuildsOnUpdate = cancelPendingBuildsOnUpdate;
initializeTriggerHandler();
initializeBranchFilter();
@ -273,6 +277,14 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
return secretToken == null ? null : secretToken.getPlainText();
}
public String getPendingBuildName() {
return pendingBuildName;
}
public boolean getCancelPendingBuildsOnUpdate() {
return this.cancelPendingBuildsOnUpdate;
}
@DataBoundSetter
public void setTriggerOnPush(boolean triggerOnPush) {
this.triggerOnPush = triggerOnPush;
@ -388,6 +400,16 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
this.triggerOnPipelineEvent = triggerOnPipelineEvent;
}
@DataBoundSetter
public void setPendingBuildName(String pendingBuildName) {
this.pendingBuildName = pendingBuildName;
}
@DataBoundSetter
public void setCancelPendingBuildsOnUpdate(boolean cancelPendingBuildsOnUpdate) {
this.cancelPendingBuildsOnUpdate = cancelPendingBuildsOnUpdate;
}
// executes when the Trigger receives a push request
public void onPost(final PushHook hook) {
if (branchFilter == null) {
@ -438,7 +460,7 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
private void initializeTriggerHandler() {
mergeRequestHookTriggerHandler = newMergeRequestHookTriggerHandler(triggerOnMergeRequest,
triggerOnAcceptedMergeRequest, triggerOnClosedMergeRequest, triggerOpenMergeRequestOnPush,
skipWorkInProgressMergeRequest, triggerOnApprovedMergeRequest);
skipWorkInProgressMergeRequest, triggerOnApprovedMergeRequest, cancelPendingBuildsOnUpdate);
noteHookTriggerHandler = newNoteHookTriggerHandler(triggerOnNoteRequest, noteRegex);
pushHookTriggerHandler = newPushHookTriggerHandler(triggerOnPush, triggerOpenMergeRequestOnPush, skipWorkInProgressMergeRequest);
pipelineTriggerHandler = newPipelineHookTriggerHandler(triggerOnPipelineEvent);

View File

@ -6,23 +6,22 @@ import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.WebHook;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
import com.dabsquared.gitlabjenkins.trigger.exception.NoRevisionToBuildException;
import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilter;
import com.dabsquared.gitlabjenkins.trigger.filter.MergeRequestLabelFilter;
import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.CauseAction;
import hudson.model.Job;
import hudson.plugins.git.GitSCM;
import hudson.plugins.git.RevisionParameterAction;
import hudson.scm.SCM;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import jenkins.triggers.SCMTriggerItem;
import net.karneim.pojobuilder.GeneratePojoBuilder;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.transport.URIish;
import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
@ -37,6 +36,7 @@ import java.util.logging.Logger;
public abstract class AbstractWebHookTriggerHandler<H extends WebHook> implements WebHookTriggerHandler<H> {
private static final Logger LOGGER = Logger.getLogger(AbstractWebHookTriggerHandler.class.getName());
protected PendingBuildsHandler pendingBuildsHandler = new PendingBuildsHandler();
@Override
public void handle(Job<?, ?> job, H hook, boolean ciSkip, BranchFilter branchFilter, MergeRequestLabelFilter mergeRequestLabelFilter) {
@ -48,6 +48,7 @@ public abstract class AbstractWebHookTriggerHandler<H extends WebHook> implement
String targetBranch = getTargetBranch(hook);
if (branchFilter.isBranchAllowed(targetBranch)) {
LOGGER.log(Level.INFO, "{0} triggered for {1}.", LoggerUtil.toArray(job.getFullName(), getTriggerType()));
cancelPendingBuildsIfNecessary(job, hook);
setCommitStatusPendingIfNecessary(job, hook);
scheduleBuild(job, createActions(job, hook));
} else {
@ -60,19 +61,17 @@ public abstract class AbstractWebHookTriggerHandler<H extends WebHook> implement
protected abstract boolean isCiSkip(H hook);
private void setCommitStatusPendingIfNecessary(Job<?, ?> job, H hook) {
if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) {
GitLabCommitStatusPublisher publisher =
(GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class);
String buildName = PendingBuildsHandler.resolvePendingBuildName(job);
if (StringUtils.isNotBlank(buildName)) {
GitLabClient client = job.getProperty(GitLabConnectionProperty.class).getClient();
BuildStatusUpdate buildStatusUpdate = retrieveBuildStatusUpdate(hook);
try {
if (client == null) {
LOGGER.log(Level.SEVERE, "No GitLab connection configured");
} else {
String targetUrl =
Jenkins.getInstance().getRootUrl() + job.getUrl() + job.getNextBuildNumber() + "/";
String targetUrl = DisplayURLProvider.get().getJobURL(job);
client.changeBuildStatus(buildStatusUpdate.getProjectId(), buildStatusUpdate.getSha(),
BuildState.pending, buildStatusUpdate.getRef(), publisher.getName(), targetUrl, BuildState.pending.name());
BuildState.pending, buildStatusUpdate.getRef(), buildName, targetUrl, BuildState.pending.name());
}
} catch (WebApplicationException | ProcessingException e) {
LOGGER.log(Level.SEVERE, "Failed to set build state to pending", e);
@ -94,6 +93,8 @@ public abstract class AbstractWebHookTriggerHandler<H extends WebHook> implement
return actions.toArray(new Action[actions.size()]);
}
protected void cancelPendingBuildsIfNecessary(Job<?, ?> job, H hook) {}
protected abstract CauseData retrieveCauseData(H hook);
protected abstract String getTargetBranch(H hook);

View File

@ -0,0 +1,96 @@
package com.dabsquared.gitlabjenkins.trigger.handler;
import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.cause.CauseData;
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import hudson.model.AbstractProject;
import hudson.model.Cause;
import hudson.model.Job;
import hudson.model.Queue;
import jenkins.model.Jenkins;
import org.apache.commons.lang.StringUtils;
import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import java.util.logging.Level;
import java.util.logging.Logger;
public class PendingBuildsHandler {
private static final Logger LOGGER = Logger.getLogger(PendingBuildsHandler.class.getName());
public void cancelPendingBuilds(Job<?, ?> job, Integer projectId, String branch) {
Queue queue = Jenkins.getInstance().getQueue();
for (Queue.Item item : queue.getItems()) {
if (!job.getName().equals(item.task.getName())) {
continue;
}
GitLabWebHookCause queueItemGitLabWebHookCause = getGitLabWebHookCauseData(item);
if (queueItemGitLabWebHookCause == null) {
continue;
}
CauseData queueItemCauseData = queueItemGitLabWebHookCause.getData();
if (!projectId.equals(queueItemCauseData.getSourceProjectId())) {
continue;
}
if (branch.equals(queueItemCauseData.getBranch())) {
cancel(item, queue, branch);
setCommitStatusCancelledIfNecessary(queueItemCauseData, job);
}
}
}
private GitLabWebHookCause getGitLabWebHookCauseData(Queue.Item item) {
for (Cause cause : item.getCauses()) {
if (cause instanceof GitLabWebHookCause) {
return (GitLabWebHookCause) cause;
}
}
return null;
}
private void cancel(Queue.Item item, Queue queue, String branch) {
try {
LOGGER.log(Level.INFO, "Cancelling job {0} for branch {1}", LoggerUtil.toArray(item.task.getName(), branch));
queue.cancel(item);
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Error cancelling queued build", e);
}
}
private void setCommitStatusCancelledIfNecessary(CauseData causeData, Job<?, ?> job) {
String buildName = resolvePendingBuildName(job);
if (StringUtils.isBlank(buildName)) {
return;
}
String targetUrl = DisplayURLProvider.get().getJobURL(job);
GitLabClient client = job.getProperty(GitLabConnectionProperty.class).getClient();
try {
client.changeBuildStatus(causeData.getSourceProjectId(), causeData.getLastCommit(), BuildState.canceled,
causeData.getSourceBranch(), buildName, targetUrl, BuildState.canceled.name());
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Failed to set build state to pending", e);
}
}
public static String resolvePendingBuildName(Job<?, ?> job) {
if (job instanceof AbstractProject) {
GitLabCommitStatusPublisher publisher =
(GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class);
if (publisher != null) {
return publisher.getName();
}
} else if (job instanceof WorkflowJob) {
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob(job);
if (trigger != null) {
return trigger.getPendingBuildName();
}
}
return null;
}
}

View File

@ -21,11 +21,12 @@ public final class MergeRequestHookTriggerHandlerFactory {
boolean triggerOnClosedMergeRequest,
TriggerOpenMergeRequest triggerOpenMergeRequest,
boolean skipWorkInProgressMergeRequest,
boolean triggerOnApprovedMergeRequest) {
boolean triggerOnApprovedMergeRequest,
boolean cancelPendingBuildsOnUpdate) {
if (triggerOnMergeRequest || triggerOnAcceptedMergeRequest || triggerOnClosedMergeRequest || triggerOpenMergeRequest != TriggerOpenMergeRequest.never || triggerOnApprovedMergeRequest) {
return new MergeRequestHookTriggerHandlerImpl(retrieveAllowedStates(triggerOnMergeRequest, triggerOnAcceptedMergeRequest, triggerOnClosedMergeRequest, triggerOpenMergeRequest),
retrieveAllowedActions(triggerOnApprovedMergeRequest),
skipWorkInProgressMergeRequest);
skipWorkInProgressMergeRequest, cancelPendingBuildsOnUpdate);
} else {
return new NopMergeRequestHookTriggerHandler();
}

View File

@ -12,6 +12,7 @@ import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilter;
import com.dabsquared.gitlabjenkins.trigger.filter.MergeRequestLabelFilter;
import com.dabsquared.gitlabjenkins.trigger.handler.AbstractWebHookTriggerHandler;
import com.dabsquared.gitlabjenkins.util.BuildUtil;
import com.dabsquared.gitlabjenkins.trigger.handler.PendingBuildsHandler;
import hudson.model.Job;
import hudson.model.Run;
import hudson.plugins.git.GitSCM;
@ -39,15 +40,17 @@ class MergeRequestHookTriggerHandlerImpl extends AbstractWebHookTriggerHandler<M
private final Collection<State> allowedStates;
private final boolean skipWorkInProgressMergeRequest;
private final Collection<Action> allowedActions;
private final boolean cancelPendingBuildsOnUpdate;
MergeRequestHookTriggerHandlerImpl(Collection<State> allowedStates, boolean skipWorkInProgressMergeRequest) {
this(allowedStates, EnumSet.allOf(Action.class),skipWorkInProgressMergeRequest);
MergeRequestHookTriggerHandlerImpl(Collection<State> allowedStates, boolean skipWorkInProgressMergeRequest, boolean cancelPendingBuildsOnUpdate) {
this(allowedStates, EnumSet.allOf(Action.class), skipWorkInProgressMergeRequest, cancelPendingBuildsOnUpdate);
}
MergeRequestHookTriggerHandlerImpl(Collection<State> allowedStates, Collection<Action> allowedActions, boolean skipWorkInProgressMergeRequest) {
MergeRequestHookTriggerHandlerImpl(Collection<State> allowedStates, Collection<Action> allowedActions, boolean skipWorkInProgressMergeRequest, boolean cancelPendingBuildsOnUpdate) {
this.allowedStates = allowedStates;
this.allowedActions = allowedActions;
this.skipWorkInProgressMergeRequest = skipWorkInProgressMergeRequest;
this.cancelPendingBuildsOnUpdate = cancelPendingBuildsOnUpdate;
}
@Override
@ -77,6 +80,17 @@ class MergeRequestHookTriggerHandlerImpl extends AbstractWebHookTriggerHandler<M
&& hook.getObjectAttributes().getDescription().contains("[ci-skip]");
}
@Override
protected void cancelPendingBuildsIfNecessary(Job<?, ?> job, MergeRequestHook hook) {
if (!this.cancelPendingBuildsOnUpdate) {
return;
}
if (!hook.getObjectAttributes().getAction().equals(Action.update)) {
return;
}
this.pendingBuildsHandler.cancelPendingBuilds(job, hook.getObjectAttributes().getSourceProjectId(), hook.getObjectAttributes().getSourceBranch());
}
@Override
protected String getTargetBranch(MergeRequestHook hook) {
return hook.getObjectAttributes() == null ? null : hook.getObjectAttributes().getTargetBranch();

View File

@ -12,20 +12,20 @@ import com.dabsquared.gitlabjenkins.gitlab.api.model.MergeRequest;
import com.dabsquared.gitlabjenkins.gitlab.api.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.PushHook;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.State;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilter;
import com.dabsquared.gitlabjenkins.trigger.filter.MergeRequestLabelFilter;
import com.dabsquared.gitlabjenkins.util.LoggerUtil;
import hudson.model.AbstractProject;
import com.dabsquared.gitlabjenkins.trigger.handler.PendingBuildsHandler;
import hudson.model.Action;
import hudson.model.CauseAction;
import hudson.model.Job;
import hudson.plugins.git.RevisionParameterAction;
import hudson.triggers.Trigger;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import jenkins.model.ParameterizedJobMixIn.ParameterizedJob;
import org.apache.commons.lang.StringUtils;
import org.eclipse.jgit.transport.URIish;
import org.jenkinsci.plugins.displayurlapi.DisplayURLProvider;
import javax.ws.rs.ProcessingException;
import javax.ws.rs.WebApplicationException;
@ -72,7 +72,7 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
}
}
}
} else {
LOGGER.log(Level.FINE, "Not a ParameterizedJob: {0}",LoggerUtil.toArray(job.getClass().getName()));
}
@ -114,7 +114,6 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
Project project = client.getProject(mergeRequest.getSourceProjectId().toString());
String commit = branch.getCommit().getId();
setCommitStatusPendingIfNecessary(job, mergeRequest.getSourceProjectId(), commit, branch.getName());
List<Action> actions = Arrays.<Action>asList(new CauseAction(new GitLabWebHookCause(retrieveCauseData(hook, project, mergeRequest, branch))),
new RevisionParameterAction(commit, retrieveUrIish(hook)));
scheduleBuild(job, actions.toArray(new Action[actions.size()]));
@ -153,13 +152,12 @@ class OpenMergeRequestPushHookTriggerHandler implements PushHookTriggerHandler {
}
private void setCommitStatusPendingIfNecessary(Job<?, ?> job, Integer projectId, String commit, String ref) {
if (job instanceof AbstractProject && ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class) != null) {
GitLabCommitStatusPublisher publisher =
(GitLabCommitStatusPublisher) ((AbstractProject) job).getPublishersList().get(GitLabCommitStatusPublisher.class);
String buildName = PendingBuildsHandler.resolvePendingBuildName(job);
if (StringUtils.isNotBlank(buildName)) {
GitLabClient client = job.getProperty(GitLabConnectionProperty.class).getClient();
try {
String targetUrl = Jenkins.getInstance().getRootUrl() + job.getUrl() + job.getNextBuildNumber() + "/";
client.changeBuildStatus(projectId, commit, BuildState.pending, ref, publisher.getName(), targetUrl, BuildState.pending.name());
String targetUrl = DisplayURLProvider.get().getJobURL(job);
client.changeBuildStatus(projectId, commit, BuildState.pending, ref, buildName, targetUrl, BuildState.pending.name());
} catch (WebApplicationException | ProcessingException e) {
LOGGER.log(Level.SEVERE, "Failed to set build state to pending", e);
}

View File

@ -42,6 +42,12 @@
<f:entry title="Build on successful pipeline events" field="triggerOnPipelineEvent">
<f:checkbox default="false"/>
</f:entry>
<f:entry title="Pending build name for pipeline" help="/plugin/gitlab-plugin/help/help-pendingBuildName.html">
<f:textbox field="pendingBuildName"/>
</f:entry>
<f:entry title="Cancel pending merge request builds on update" field="cancelPendingBuildsOnUpdate">
<f:checkbox default="false"/>
</f:entry>
<f:entry title="Allowed branches">
<table>

View File

@ -0,0 +1,8 @@
<div>
<div>
<p>Applicable only for pipelines.</p>
<p>When filled, a 'pending' build status with the given build name is published to Gitlab when the pipeline is
triggered. Further status updates should be defined in pipeline steps.</p>
<p>For other types of jobs the build name is taken from 'Publish build status to Gitlab commit' post-build action.</p>
</div>
</div>

View File

@ -0,0 +1,219 @@
package com.dabsquared.gitlabjenkins.trigger.handler;
import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabClient;
import com.dabsquared.gitlabjenkins.gitlab.api.model.BuildState;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.*;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.builder.generated.*;
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
import com.dabsquared.gitlabjenkins.trigger.filter.BranchFilterType;
import hudson.model.FreeStyleProject;
import hudson.model.ItemGroup;
import hudson.model.Project;
import hudson.model.Queue;
import org.jenkinsci.plugins.workflow.job.WorkflowJob;
import org.junit.After;
import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.jvnet.hudson.test.JenkinsRule;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import static com.dabsquared.gitlabjenkins.gitlab.hook.model.builder.generated.CommitBuilder.commit;
import static com.dabsquared.gitlabjenkins.gitlab.hook.model.builder.generated.MergeRequestObjectAttributesBuilder.mergeRequestObjectAttributes;
import static com.dabsquared.gitlabjenkins.gitlab.hook.model.builder.generated.UserBuilder.user;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class PendingBuildsHandlerTest {
private static final String GITLAB_BUILD_NAME = "Jenkins";
@ClassRule
public static JenkinsRule jenkins = new JenkinsRule();
@Mock
private GitLabClient gitLabClient;
@Mock
private GitLabConnectionProperty gitLabConnectionProperty;
@Before
public void init() {
when(gitLabConnectionProperty.getClient()).thenReturn(gitLabClient);
}
@After
public void clearQueue() {
Queue queue = jenkins.getInstance().getQueue();
for (Queue.Item item : queue.getItems()) {
queue.cancel(item);
}
}
@Test
public void projectCanBeConfiguredToSendPendingBuildStatusWhenTriggered() throws IOException {
Project project = freestyleProject("freestyleProject1", new GitLabCommitStatusPublisher(GITLAB_BUILD_NAME, false));
GitLabPushTrigger gitLabPushTrigger = gitLabPushTrigger(project);
gitLabPushTrigger.onPost(pushHook(1, "branch1", "commit1"));
verify(gitLabClient).changeBuildStatus(eq(1), eq("commit1"), eq(BuildState.pending), eq("branch1"), eq(GITLAB_BUILD_NAME),
contains("/freestyleProject1/"), eq(BuildState.pending.name()));
verifyNoMoreInteractions(gitLabClient);
}
@Test
public void workflowJobCanConfiguredToSendToPendingBuildStatusWhenTriggered() throws IOException {
WorkflowJob workflowJob = workflowJob();
GitLabPushTrigger gitLabPushTrigger = gitLabPushTrigger(workflowJob);
gitLabPushTrigger.setPendingBuildName(GITLAB_BUILD_NAME);
gitLabPushTrigger.onPost(mergeRequestHook(1, "branch1", "commit1"));
verify(gitLabClient).changeBuildStatus(eq(1), eq("commit1"), eq(BuildState.pending), eq("branch1"), eq(GITLAB_BUILD_NAME),
contains("/workflowJob/"), eq(BuildState.pending.name()));
verifyNoMoreInteractions(gitLabClient);
}
@Test
public void queuedMergeRequestBuildsCanBeCancelledOnMergeRequestUpdate() throws IOException {
Project project = freestyleProject("project1", new GitLabCommitStatusPublisher(GITLAB_BUILD_NAME, false));
GitLabPushTrigger gitLabPushTrigger = gitLabPushTrigger(project);
gitLabPushTrigger.setCancelPendingBuildsOnUpdate(true);
assertThat(jenkins.getInstance().getQueue().getItems().length, is(0));
gitLabPushTrigger.onPost(mergeRequestHook(1, "sourceBranch", "commit1")); // Will be cancelled
gitLabPushTrigger.onPost(mergeRequestHook(1, "sourceBranch", "commit2")); // Will be cancelled
gitLabPushTrigger.onPost(mergeRequestHook(1, "sourceBranch", "commit3"));
gitLabPushTrigger.onPost(mergeRequestHook(1, "anotherBranch", "commit4"));
gitLabPushTrigger.onPost(mergeRequestHook(2, "sourceBranch", "commit5"));
verify(gitLabClient).changeBuildStatus(eq(1), eq("commit1"), eq(BuildState.canceled), eq("sourceBranch"),
eq("Jenkins"), contains("project1"), eq(BuildState.canceled.name()));
verify(gitLabClient).changeBuildStatus(eq(1), eq("commit2"), eq(BuildState.canceled), eq("sourceBranch"),
eq("Jenkins"), contains("project1"), eq(BuildState.canceled.name()));
assertThat(jenkins.getInstance().getQueue().getItems().length, is(3));
}
private GitLabPushTrigger gitLabPushTrigger(Project project) throws IOException {
GitLabPushTrigger gitLabPushTrigger = gitLabPushTrigger();
project.addTrigger(gitLabPushTrigger);
gitLabPushTrigger.start(project,true);
return gitLabPushTrigger;
}
private GitLabPushTrigger gitLabPushTrigger(WorkflowJob workflowJob) {
GitLabPushTrigger gitLabPushTrigger = gitLabPushTrigger();
workflowJob.addTrigger(gitLabPushTrigger);
gitLabPushTrigger.start(workflowJob,true);
return gitLabPushTrigger;
}
private GitLabPushTrigger gitLabPushTrigger() {
GitLabPushTrigger gitLabPushTrigger = new GitLabPushTrigger();
gitLabPushTrigger.setTriggerOnPush(true);
gitLabPushTrigger.setTriggerOnMergeRequest(true);
gitLabPushTrigger.setPendingBuildName(GITLAB_BUILD_NAME);
gitLabPushTrigger.setBranchFilterType(BranchFilterType.NameBasedFilter);
gitLabPushTrigger.setBranchFilterName("");
return gitLabPushTrigger;
}
private MergeRequestHook mergeRequestHook(int projectId, String branch, String commitId) {
return MergeRequestHookBuilder.mergeRequestHook()
.withObjectAttributes(mergeRequestObjectAttributes()
.withAction(Action.update)
.withState(State.updated)
.withIid(1)
.withTitle("test")
.withTargetProjectId(1)
.withTargetBranch("targetBranch")
.withSourceBranch(branch)
.withSourceProjectId(projectId)
.withLastCommit(commit().withAuthor(user().withName("author").build()).withId(commitId).build())
.withSource(ProjectBuilder.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(ProjectBuilder.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(ProjectBuilder.project()
.withWebUrl("https://gitlab.org/test.git")
.build()
)
.build();
}
private PushHook pushHook(int projectId, String branch, String commitId) {
User user = new UserBuilder()
.withName("username")
.build();
Repository repository = new RepositoryBuilder()
.withName("repository")
.withGitSshUrl("sshUrl")
.withGitHttpUrl("httpUrl")
.build();
return new PushHookBuilder()
.withProjectId(projectId)
.withRef(branch)
.withAfter(commitId)
.withRepository(new Repository())
.withProject(ProjectBuilder.project().withNamespace("namespace").build())
.withCommits(Arrays.asList(CommitBuilder.commit().withId(commitId).withAuthor(user).build()))
.withRepository(repository)
.withObjectKind("push")
.withUserName("username")
.build();
}
private Project freestyleProject(String name, GitLabCommitStatusPublisher gitLabCommitStatusPublisher) throws IOException {
FreeStyleProject project = jenkins.createFreeStyleProject(name);
project.setQuietPeriod(5000);
project.getPublishersList().add(gitLabCommitStatusPublisher);
project.addProperty(gitLabConnectionProperty);
return project;
}
private WorkflowJob workflowJob() throws IOException {
ItemGroup itemGroup = mock(ItemGroup.class);
when(itemGroup.getFullName()).thenReturn("parent");
when(itemGroup.getUrlChildPrefix()).thenReturn("prefix");
WorkflowJob workflowJob = new WorkflowJob(itemGroup, "workflowJob");
when(itemGroup.getRootDirFor(workflowJob)).thenReturn(new File("work"));
workflowJob.addProperty(gitLabConnectionProperty);
workflowJob.setQuietPeriod(5000);
workflowJob.onCreatedFromScratch();
return workflowJob;
}
}

View File

@ -69,7 +69,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
}
});
project.setQuietPeriod(0);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false, false);
mergeRequestHookTriggerHandler.handle(project, mergeRequestHook()
.withObjectAttributes(mergeRequestObjectAttributes().withDescription("[ci-skip]").build())
.build(), true, BranchFilterFactory.newBranchFilter(branchFilterConfig().build(BranchFilterType.All)),
@ -81,7 +81,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_build() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.reopened), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.opened);
assertThat(buildTriggered.isSignaled(), is(true));
@ -89,7 +89,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_build_when_accepted() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.merged), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.merged), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.merged);
assertThat(buildTriggered.isSignaled(), is(true));
@ -97,7 +97,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_build_when_closed() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.closed), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.closed), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.closed);
assertThat(buildTriggered.isSignaled(), is(true));
@ -105,7 +105,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@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);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.updated), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.merged);
assertThat(buildTriggered.isSignaled(), is(false));
@ -113,7 +113,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@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);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(Arrays.asList(State.opened, State.updated), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, State.closed);
assertThat(buildTriggered.isSignaled(), is(false));
@ -121,7 +121,8 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_build_when_approved() throws IOException, InterruptedException, GitAPIException, ExecutionException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.approved), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.approved), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, Action.approved);
assertThat(buildTriggered.isSignaled(), is(true));
@ -129,7 +130,7 @@ public class MergeRequestHookTriggerHandlerImplTest {
@Test
public void mergeRequest_do_not_build_when_when_approved() throws Exception {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.update), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.update), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, defaultMergeRequestObjectAttributes().withState(State.opened).withAction(Action.approved));
assertThat(buildTriggered.isSignaled(), is (false));
@ -148,11 +149,11 @@ public class MergeRequestHookTriggerHandlerImplTest {
private void mergeRequest_build_only_when_approved(Action action)
throws GitAPIException, IOException, InterruptedException {
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.approved), false);
MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler = new MergeRequestHookTriggerHandlerImpl(EnumSet.allOf(State.class), EnumSet.of(Action.approved), false, false);
OneShotEvent buildTriggered = doHandle(mergeRequestHookTriggerHandler, action);
assertThat(buildTriggered.isSignaled(), is(false));
}
}
private OneShotEvent doHandle(MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler, Action action) throws GitAPIException, IOException, InterruptedException {
return doHandle(mergeRequestHookTriggerHandler, defaultMergeRequestObjectAttributes().withAction(action));