Notify SCMSourceOwners about changes for GitLab Push Hooks (Fixes #298)

This commit is contained in:
Robin Müller 2016-07-09 16:52:25 +02:00
parent 1ea956f34c
commit d404a9dc4d
4 changed files with 79 additions and 29 deletions

View File

@ -20,6 +20,7 @@ import hudson.security.AccessDeniedException2;
import hudson.security.Permission;
import hudson.util.HttpResponses;
import jenkins.model.Jenkins;
import jenkins.scm.api.SCMSourceOwner;
import org.apache.commons.io.IOUtils;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
@ -46,20 +47,25 @@ public class ActionResolver {
public WebHookAction resolve(final String projectName, StaplerRequest request) {
Iterator<String> restOfPathParts = Splitter.on('/').omitEmptyStrings().split(request.getRestOfPath()).iterator();
Job<?, ?> project = resolveProject(projectName, restOfPathParts);
Item project = resolveProject(projectName, restOfPathParts);
if (project == null) {
throw HttpResponses.notFound();
}
return resolveAction(project, Joiner.on('/').join(restOfPathParts), request);
}
private WebHookAction resolveAction(Job<?, ?> project, String restOfPath, StaplerRequest request) {
private WebHookAction resolveAction(Item project, String restOfPath, StaplerRequest request) {
String method = request.getMethod();
if (method.equals("POST")) {
checkPermission(Item.BUILD);
return onPost(project, request);
} else if (method.equals("GET")) {
return onGet(project, restOfPath, request);
if (project instanceof Job<?, ?>) {
return onGet((Job<?, ?>) project, restOfPath, request);
} else {
LOGGER.log(Level.FINE, "GET is not supported for this project {0}", project.getName());
return new NoopAction();
}
}
LOGGER.log(Level.FINE, "Unsupported HTTP method: {0}", method);
return new NoopAction();
@ -94,7 +100,7 @@ public class ActionResolver {
}
}
private WebHookAction onPost(Job<?, ?> project, StaplerRequest request) {
private WebHookAction onPost(Item project, StaplerRequest request) {
String eventHeader = request.getHeader("X-Gitlab-Event");
if (eventHeader == null) {
LOGGER.log(Level.FINE, "Missing X-Gitlab-Event header");
@ -107,7 +113,7 @@ public class ActionResolver {
case "Tag Push Hook":
return new PushBuildAction(project, getRequestBody(request));
case "Note Hook":
return new NoteBuildAction(project, getRequestBody(request));
return new NoteBuildAction(project, getRequestBody(request));
default:
LOGGER.log(Level.FINE, "Unsupported X-Gitlab-Event header: {0}", eventHeader);
return new NoopAction();
@ -125,17 +131,17 @@ public class ActionResolver {
return requestBody;
}
private Job<?, ?> resolveProject(final String projectName, final Iterator<String> restOfPathParts) {
return ACLUtil.impersonate(ACL.SYSTEM, new ACLUtil.Function<Job<?, ?>>() {
public Job<?, ?> invoke() {
private Item resolveProject(final String projectName, final Iterator<String> restOfPathParts) {
return ACLUtil.impersonate(ACL.SYSTEM, new ACLUtil.Function<Item>() {
public Item invoke() {
final Jenkins jenkins = Jenkins.getInstance();
if (jenkins != null) {
Item item = jenkins.getItemByFullName(projectName);
while (item instanceof ItemGroup<?> && !(item instanceof Job<?, ?>) && restOfPathParts.hasNext()) {
while (item instanceof ItemGroup<?> && !(item instanceof Job<?, ?> || item instanceof SCMSourceOwner) && restOfPathParts.hasNext()) {
item = jenkins.getItem(restOfPathParts.next(), (ItemGroup<?>) item);
}
if (item instanceof Job<?, ?>) {
return (Job<?, ?>) item;
if (item instanceof Job<?, ?> || item instanceof SCMSourceOwner) {
return item;
}
}
LOGGER.log(Level.FINE, "No project found: {0}, {1}", toArray(projectName, Joiner.on('/').join(restOfPathParts)));
@ -149,7 +155,7 @@ public class ActionResolver {
try {
Jenkins.getInstance().checkPermission(permission);
} catch (AccessDeniedException2 e) {
throw HttpResponses.error(403, e.getMessage());
throw HttpResponses.errorWithoutStack(403, e.getMessage());
}
}
}

View File

@ -5,6 +5,7 @@ import com.dabsquared.gitlabjenkins.gitlab.hook.model.MergeRequestHook;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.MergeRequestObjectAttributes;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.Project;
import com.dabsquared.gitlabjenkins.util.JsonUtil;
import hudson.model.Item;
import hudson.model.Job;
import hudson.security.ACL;
import hudson.util.HttpResponses;
@ -20,10 +21,10 @@ import static com.dabsquared.gitlabjenkins.util.JsonUtil.toPrettyPrint;
public class MergeRequestBuildAction extends BuildWebHookAction {
private final static Logger LOGGER = Logger.getLogger(MergeRequestBuildAction.class.getName());
private Job<?, ?> project;
private Item project;
private MergeRequestHook mergeRequestHook;
public MergeRequestBuildAction(Job<?, ?> project, String json) {
public MergeRequestBuildAction(Item project, String json) {
LOGGER.log(Level.FINE, "MergeRequest: {0}", toPrettyPrint(json));
this.project = project;
this.mergeRequestHook = JsonUtil.read(json, MergeRequestHook.class);
@ -46,9 +47,12 @@ public class MergeRequestBuildAction extends BuildWebHookAction {
}
public void execute() {
if (!(project instanceof Job<?, ?>)) {
throw HttpResponses.errorWithoutStack(409, "Merge Request Hook is not supported for this project");
}
ACL.impersonate(ACL.SYSTEM, new Runnable() {
public void run() {
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob(project);
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob((Job<?, ?>) project);
if (trigger != null) {
trigger.onPost(mergeRequestHook);
}

View File

@ -4,6 +4,7 @@ import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.NoteHook;
import com.dabsquared.gitlabjenkins.util.JsonUtil;
import com.dabsquared.gitlabjenkins.webhook.WebHookAction;
import hudson.model.Item;
import hudson.model.Job;
import hudson.security.ACL;
import hudson.util.HttpResponses;
@ -20,19 +21,22 @@ import static com.dabsquared.gitlabjenkins.util.JsonUtil.toPrettyPrint;
public class NoteBuildAction implements WebHookAction {
private final static Logger LOGGER = Logger.getLogger(NoteBuildAction.class.getName());
private Job<?, ?> project;
private Item project;
private NoteHook noteHook;
public NoteBuildAction(Job<?, ?> project, String json) {
public NoteBuildAction(Item project, String json) {
LOGGER.log(Level.FINE, "Note: {0}", toPrettyPrint(json));
this.project = project;
this.noteHook = JsonUtil.read(json, NoteHook.class);
}
public void execute(StaplerResponse response) {
if (!(project instanceof Job<?, ?>)) {
throw HttpResponses.errorWithoutStack(409, "Note Hook is not supported for this project");
}
ACL.impersonate(ACL.SYSTEM, new Runnable() {
public void run() {
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob(project);
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob((Job<?, ?>) project);
if (trigger != null) {
trigger.onPost(noteHook);
}

View File

@ -4,19 +4,24 @@ import com.dabsquared.gitlabjenkins.GitLabPushTrigger;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.Project;
import com.dabsquared.gitlabjenkins.gitlab.hook.model.PushHook;
import com.dabsquared.gitlabjenkins.util.JsonUtil;
import com.dabsquared.gitlabjenkins.webhook.WebHookAction;
import hudson.model.Item;
import hudson.model.Job;
import hudson.security.ACL;
import hudson.util.HttpResponses;
import jenkins.plugins.git.GitSCMSource;
import jenkins.scm.api.SCMSource;
import jenkins.scm.api.SCMSourceOwner;
import org.apache.commons.lang.StringUtils;
import org.kohsuke.stapler.StaplerResponse;
import org.eclipse.jgit.transport.URIish;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import static com.dabsquared.gitlabjenkins.util.JsonUtil.toPrettyPrint;
import static com.dabsquared.gitlabjenkins.util.LoggerUtil.toArray;
/**
* @author Robin Müller
@ -24,11 +29,11 @@ import static com.dabsquared.gitlabjenkins.util.JsonUtil.toPrettyPrint;
public class PushBuildAction extends BuildWebHookAction {
private final static Logger LOGGER = Logger.getLogger(PushBuildAction.class.getName());
private final Job<?, ?> project;
private final Item project;
private PushHook pushHook;
public PushBuildAction(Job<?, ?> project, String json) {
public PushBuildAction(Item project, String json) {
LOGGER.log(Level.FINE, "Push: {0}", toPrettyPrint(json));
this.project = project;
this.pushHook = JsonUtil.read(json, PushHook.class);
@ -58,14 +63,45 @@ public class PushBuildAction extends BuildWebHookAction {
return;
}
ACL.impersonate(ACL.SYSTEM, new Runnable() {
public void run() {
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob(project);
if (trigger != null) {
trigger.onPost(pushHook);
if (project instanceof Job<?, ?>) {
ACL.impersonate(ACL.SYSTEM, new Runnable() {
public void run() {
GitLabPushTrigger trigger = GitLabPushTrigger.getFromJob((Job<?, ?>) project);
if (trigger != null) {
trigger.onPost(pushHook);
}
}
});
throw HttpResponses.ok();
}
if (project instanceof SCMSourceOwner) {
ACL.impersonate(ACL.SYSTEM, new SCMSourceOwnerNotifier());
throw HttpResponses.ok();
}
throw HttpResponses.errorWithoutStack(409, "Push Hook is not supported for this project");
}
private class SCMSourceOwnerNotifier implements Runnable {
public void run() {
for (SCMSource scmSource : ((SCMSourceOwner) project).getSCMSources()) {
if (scmSource instanceof GitSCMSource) {
GitSCMSource gitSCMSource = (GitSCMSource) scmSource;
try {
if (new URIish(gitSCMSource.getRemote()).equals(new URIish(gitSCMSource.getRemote()))) {
if (!gitSCMSource.isIgnoreOnPushNotifications()) {
LOGGER.log(Level.FINE, "Notify scmSourceOwner {0} about changes for {1}",
toArray(project.getName(), gitSCMSource.getRemote()));
((SCMSourceOwner) project).onSCMSourceUpdated(scmSource);
} else {
LOGGER.log(Level.FINE, "Ignore on push notification for scmSourceOwner {0} about changes for {1}",
toArray(project.getName(), gitSCMSource.getRemote()));
}
}
} catch (URISyntaxException e) {
// nothing to do
}
}
}
});
throw HttpResponses.ok();
}
}
}