Add Notifier for accepting a MR on success
This commit is contained in:
parent
1cae833f60
commit
f7bea1ae87
|
@ -6,6 +6,7 @@ import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
|
|||
import com.dabsquared.gitlabjenkins.gitlab.hook.model.MergeRequestHook;
|
||||
import com.dabsquared.gitlabjenkins.gitlab.hook.model.NoteHook;
|
||||
import com.dabsquared.gitlabjenkins.gitlab.hook.model.PushHook;
|
||||
import com.dabsquared.gitlabjenkins.publisher.GitLabAcceptMergeRequestPublisher;
|
||||
import com.dabsquared.gitlabjenkins.publisher.GitLabCommitStatusPublisher;
|
||||
import com.dabsquared.gitlabjenkins.publisher.GitLabMessagePublisher;
|
||||
import com.dabsquared.gitlabjenkins.publisher.GitLabVotePublisher;
|
||||
|
@ -81,7 +82,7 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
|
|||
private transient PushHookTriggerHandler pushHookTriggerHandler;
|
||||
private transient MergeRequestHookTriggerHandler mergeRequestHookTriggerHandler;
|
||||
private transient NoteHookTriggerHandler noteHookTriggerHandler;
|
||||
private boolean acceptMergeRequestOnSuccess = false;
|
||||
private transient boolean acceptMergeRequestOnSuccess;
|
||||
|
||||
|
||||
@DataBoundConstructor
|
||||
|
@ -149,6 +150,9 @@ public class GitLabPushTrigger extends Trigger<Job<?, ?>> {
|
|||
if (trigger.addVoteOnMergeRequest) {
|
||||
project.getPublishersList().add(new GitLabVotePublisher());
|
||||
}
|
||||
if (trigger.acceptMergeRequestOnSuccess) {
|
||||
project.getPublishersList().add(new GitLabAcceptMergeRequestPublisher());
|
||||
}
|
||||
project.save();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -94,8 +94,8 @@ public interface GitLabApi {
|
|||
|
||||
@PUT
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/projects/{projectId}/merge_request/{mergeRequestId}/merge")
|
||||
void acceptMergeRequest(@PathParam("projectId") String projectId,
|
||||
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/merge")
|
||||
void acceptMergeRequest(@PathParam("projectId") Integer projectId,
|
||||
@PathParam("mergeRequestId") Integer mergeRequestId,
|
||||
@QueryParam("merge_commit_message") String mergeCommitMessage,
|
||||
@QueryParam("should_remove_source_branch") boolean shouldRemoveSourceBranch);
|
||||
|
@ -107,13 +107,6 @@ public interface GitLabApi {
|
|||
@PathParam("mergeRequestId") Integer mergeRequestId,
|
||||
@QueryParam("body") String body);
|
||||
|
||||
@POST
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/projects/{projectId}/merge_requests/{mergeRequestId}/notes")
|
||||
void createMergeRequestNote(@PathParam("projectId") String projectId,
|
||||
@PathParam("mergeRequestId") Integer mergeRequestId,
|
||||
@QueryParam("body") String body);
|
||||
|
||||
@GET
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Path("/projects/{projectId}/merge_requests")
|
||||
|
|
|
@ -1,59 +0,0 @@
|
|||
package com.dabsquared.gitlabjenkins.listener;
|
||||
|
||||
import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
|
||||
import com.dabsquared.gitlabjenkins.cause.CauseData;
|
||||
import com.dabsquared.gitlabjenkins.cause.GitLabWebHookCause;
|
||||
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
|
||||
import hudson.Extension;
|
||||
import hudson.model.Result;
|
||||
import hudson.model.Run;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.model.listeners.RunListener;
|
||||
import jenkins.model.Jenkins;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
|
||||
import static com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty.getClient;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
@Extension
|
||||
public class GitLabMergeRequestRunListener extends RunListener<Run<?, ?>> {
|
||||
@Override
|
||||
public void onCompleted(Run<?, ?> build, @Nonnull TaskListener listener) {
|
||||
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob(build.getParent());
|
||||
GitLabWebHookCause cause = build.getCause(GitLabWebHookCause.class);
|
||||
|
||||
if (trigger != null && cause != null && (cause.getData().getActionType() == CauseData.ActionType.MERGE || cause.getData().getActionType() == CauseData.ActionType.NOTE)) {
|
||||
String buildUrl = getBuildUrl(build);
|
||||
Result buildResult = build.getResult();
|
||||
Integer projectId = cause.getData().getTargetProjectId();
|
||||
Integer mergeRequestId = cause.getData().getMergeRequestId();
|
||||
if (buildResult == Result.SUCCESS) {
|
||||
acceptMergeRequestIfNecessary(build, trigger, listener, projectId.toString(), mergeRequestId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String getBuildUrl(Run<?, ?> build) {
|
||||
return Jenkins.getInstance().getRootUrl() + build.getUrl();
|
||||
}
|
||||
|
||||
private void acceptMergeRequestIfNecessary(Run<?, ?> build, GitLabPushTrigger trigger, TaskListener listener, String projectId, Integer mergeRequestId) {
|
||||
if (trigger.getAcceptMergeRequestOnSuccess()) {
|
||||
try {
|
||||
GitLabApi client = getClient(build);
|
||||
if (client == null) {
|
||||
listener.getLogger().println("No GitLab connection configured");
|
||||
} else {
|
||||
client.acceptMergeRequest(projectId, mergeRequestId, "Merge Request accepted by jenkins build success", false);
|
||||
}
|
||||
} catch (WebApplicationException | ProcessingException e) {
|
||||
listener.getLogger().printf("Failed to accept merge request: %s", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package com.dabsquared.gitlabjenkins.publisher;
|
||||
|
||||
import com.dabsquared.gitlabjenkins.gitlab.api.GitLabApi;
|
||||
import hudson.Extension;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Result;
|
||||
import hudson.model.Run;
|
||||
import hudson.model.TaskListener;
|
||||
import hudson.tasks.BuildStepDescriptor;
|
||||
import hudson.tasks.BuildStepMonitor;
|
||||
import hudson.tasks.Publisher;
|
||||
import org.kohsuke.stapler.DataBoundConstructor;
|
||||
|
||||
import javax.ws.rs.ProcessingException;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
public class GitLabAcceptMergeRequestPublisher extends MergeRequestNotifier {
|
||||
private static final Logger LOGGER = Logger.getLogger(GitLabAcceptMergeRequestPublisher.class.getName());
|
||||
|
||||
@DataBoundConstructor
|
||||
public GitLabAcceptMergeRequestPublisher() { }
|
||||
|
||||
public BuildStepMonitor getRequiredMonitorService() {
|
||||
return BuildStepMonitor.NONE;
|
||||
}
|
||||
|
||||
@Extension
|
||||
public static class DescriptorImpl extends BuildStepDescriptor<Publisher> {
|
||||
|
||||
@Override
|
||||
public boolean isApplicable(Class<? extends AbstractProject> aClass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Messages.GitLabAcceptMergeRequestPublisher_DisplayName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void perform(Run<?, ?> build, TaskListener listener, GitLabApi client, Integer projectId, Integer mergeRequestId) {
|
||||
try {
|
||||
if (build.getResult() == Result.SUCCESS) {
|
||||
client.acceptMergeRequest(projectId, mergeRequestId, "Merge Request accepted by jenkins build success", false);
|
||||
}
|
||||
} catch (WebApplicationException | ProcessingException e) {
|
||||
listener.getLogger().printf("Failed to accept merge request for project '%s': %s%n", projectId, e.getMessage());
|
||||
LOGGER.log(Level.SEVERE, String.format("Failed to accept merge request for project '%s'", projectId), e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,3 +2,4 @@ GitLabCommitStatusPublisher.DisplayName=Publish build status to GitLab commit (G
|
|||
name.required=Build name required.
|
||||
GitLabMessagePublisher.DisplayName=Add note with build status on GitLab merge requests
|
||||
GitLabVotePublisher.DisplayName=Add vote for build status on GitLab merge requests
|
||||
GitLabAcceptMergeRequestPublisher.DisplayName=Accept GitLab merge request on success
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
package com.dabsquared.gitlabjenkins.publisher;
|
||||
|
||||
import com.cloudbees.plugins.credentials.CredentialsProvider;
|
||||
import com.cloudbees.plugins.credentials.CredentialsScope;
|
||||
import com.cloudbees.plugins.credentials.CredentialsStore;
|
||||
import com.cloudbees.plugins.credentials.SystemCredentialsProvider;
|
||||
import com.cloudbees.plugins.credentials.domains.Domain;
|
||||
import com.dabsquared.gitlabjenkins.connection.GitLabConnection;
|
||||
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionConfig;
|
||||
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.BuildListener;
|
||||
import hudson.model.Result;
|
||||
import hudson.model.StreamBuildListener;
|
||||
import hudson.plugins.git.util.BuildData;
|
||||
import hudson.util.Secret;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.jenkinsci.plugins.plaincredentials.impl.StringCredentialsImpl;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.jvnet.hudson.test.JenkinsRule;
|
||||
import org.mockserver.client.server.MockServerClient;
|
||||
import org.mockserver.junit.MockServerRule;
|
||||
import org.mockserver.model.HttpRequest;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.mockserver.model.HttpRequest.request;
|
||||
import static org.mockserver.model.HttpResponse.response;
|
||||
|
||||
/**
|
||||
* @author Nikolay Ustinov
|
||||
*/
|
||||
public class GitLabAcceptMergeRequestPublisherTest {
|
||||
|
||||
private static final String GIT_LAB_CONNECTION = "GitLab";
|
||||
private static final String API_TOKEN = "secret";
|
||||
|
||||
@ClassRule
|
||||
public static MockServerRule mockServer = new MockServerRule(new Object());
|
||||
|
||||
@ClassRule
|
||||
public static JenkinsRule jenkins = new JenkinsRule();
|
||||
|
||||
private MockServerClient mockServerClient;
|
||||
private BuildListener listener;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupConnection() throws IOException {
|
||||
GitLabConnectionConfig connectionConfig = jenkins.get(GitLabConnectionConfig.class);
|
||||
String apiTokenId = "apiTokenId";
|
||||
for (CredentialsStore credentialsStore : CredentialsProvider.lookupStores(Jenkins.getInstance())) {
|
||||
if (credentialsStore instanceof SystemCredentialsProvider.StoreImpl) {
|
||||
List<Domain> domains = credentialsStore.getDomains();
|
||||
credentialsStore.addCredentials(domains.get(0),
|
||||
new StringCredentialsImpl(CredentialsScope.SYSTEM, apiTokenId, "GitLab API Token", Secret.fromString(API_TOKEN)));
|
||||
}
|
||||
}
|
||||
connectionConfig.addConnection(new GitLabConnection(GIT_LAB_CONNECTION, "http://localhost:" + mockServer.getPort() + "/gitlab", apiTokenId, false, 10, 10));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
listener = new StreamBuildListener(jenkins.createTaskListener().getLogger(), Charset.defaultCharset());
|
||||
mockServerClient = new MockServerClient("localhost", mockServer.getPort());
|
||||
}
|
||||
|
||||
@After
|
||||
public void cleanup() {
|
||||
mockServerClient.reset();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void success() throws IOException, InterruptedException {
|
||||
Integer buildNumber = 1;
|
||||
Integer projectId = 3;
|
||||
Integer mergeRequestId = 1;
|
||||
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.SUCCESS, buildNumber);
|
||||
|
||||
HttpRequest[] requests = new HttpRequest[] {
|
||||
prepareAcceptMergeRequestWithSuccessResponse(projectId, mergeRequestId)
|
||||
};
|
||||
|
||||
GitLabAcceptMergeRequestPublisher publisher = spy(new GitLabAcceptMergeRequestPublisher());
|
||||
doReturn(projectId).when(publisher).getProjectId(build);
|
||||
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
|
||||
publisher.perform(build, null, listener);
|
||||
|
||||
mockServerClient.verify(requests);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failed() throws IOException, InterruptedException {
|
||||
Integer buildNumber = 1;
|
||||
Integer projectId = 3;
|
||||
Integer mergeRequestId = 1;
|
||||
AbstractBuild build = mockBuild("/build/123", GIT_LAB_CONNECTION, Result.FAILURE, buildNumber);
|
||||
|
||||
GitLabAcceptMergeRequestPublisher publisher = spy(new GitLabAcceptMergeRequestPublisher());
|
||||
doReturn(projectId).when(publisher).getProjectId(build);
|
||||
doReturn(mergeRequestId).when(publisher).getMergeRequestId(build);
|
||||
publisher.perform(build, null, listener);
|
||||
|
||||
mockServerClient.verifyZeroInteractions();
|
||||
}
|
||||
|
||||
private HttpRequest prepareAcceptMergeRequestWithSuccessResponse(Integer projectId, Integer mergeRequestId) throws UnsupportedEncodingException {
|
||||
HttpRequest updateCommitStatus = prepareAcceptMergeRequest(projectId, mergeRequestId);
|
||||
mockServerClient.when(updateCommitStatus).respond(response().withStatusCode(200));
|
||||
return updateCommitStatus;
|
||||
}
|
||||
|
||||
private HttpRequest prepareAcceptMergeRequest(Integer projectId, Integer mergeRequestId) throws UnsupportedEncodingException {
|
||||
return request()
|
||||
.withPath("/gitlab/api/v3/projects/" + projectId + "/merge_requests/" + mergeRequestId + "/merge")
|
||||
.withMethod("PUT")
|
||||
.withHeader("PRIVATE-TOKEN", "secret")
|
||||
.withQueryStringParameter("merge_commit_message", "Merge Request accepted by jenkins build success")
|
||||
.withQueryStringParameter("should_remove_source_branch", "false");
|
||||
}
|
||||
|
||||
private AbstractBuild mockBuild(String buildUrl, String gitLabConnection, Result result, Integer buildNumber, String... remoteUrls) {
|
||||
AbstractBuild build = mock(AbstractBuild.class);
|
||||
BuildData buildData = mock(BuildData.class);
|
||||
when(buildData.getRemoteUrls()).thenReturn(new HashSet<>(Arrays.asList(remoteUrls)));
|
||||
when(build.getAction(BuildData.class)).thenReturn(buildData);
|
||||
when(build.getResult()).thenReturn(result);
|
||||
when(build.getUrl()).thenReturn(buildUrl);
|
||||
when(build.getResult()).thenReturn(result);
|
||||
when(build.getUrl()).thenReturn(buildUrl);
|
||||
when(build.getNumber()).thenReturn(buildNumber);
|
||||
|
||||
AbstractProject<?, ?> project = mock(AbstractProject.class);
|
||||
when(project.getProperty(GitLabConnectionProperty.class)).thenReturn(new GitLabConnectionProperty(gitLabConnection));
|
||||
when(build.getProject()).thenReturn(project);
|
||||
return build;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue