Move generation of status json and png to separate classes
This commit is contained in:
parent
d11942efe3
commit
a28a4b4af6
|
@ -3,6 +3,8 @@ package com.dabsquared.gitlabjenkins;
|
|||
import com.dabsquared.gitlabjenkins.connection.GitLabConnectionProperty;
|
||||
import com.dabsquared.gitlabjenkins.data.LastCommit;
|
||||
import com.dabsquared.gitlabjenkins.data.ObjectAttributes;
|
||||
import com.dabsquared.gitlabjenkins.webhook.StatusJsonAction;
|
||||
import com.dabsquared.gitlabjenkins.webhook.StatusPngAction;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import hudson.Extension;
|
||||
|
@ -33,6 +35,7 @@ import org.gitlab.api.models.GitlabBranch;
|
|||
import org.gitlab.api.models.GitlabCommit;
|
||||
import org.gitlab.api.models.GitlabMergeRequest;
|
||||
import org.gitlab.api.models.GitlabProject;
|
||||
import org.kohsuke.stapler.HttpResponse;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
|
@ -78,25 +81,25 @@ public class GitLabWebHook implements UnprotectedRootAction {
|
|||
public void getDynamic(final String projectName, final StaplerRequest req, StaplerResponse res) {
|
||||
LOGGER.log(Level.INFO, "WebHook called with url: {0}", req.getRestOfPath());
|
||||
final Iterator<String> restOfPathParts = Splitter.on('/').omitEmptyStrings().split(req.getRestOfPath()).iterator();
|
||||
final Job<?, ?>[] projectHolder = new Job<?, ?>[] { null };
|
||||
final AbstractProject<?, ?>[] projectHolder = new AbstractProject<?, ?>[] { null };
|
||||
ACL.impersonate(ACL.SYSTEM, new Runnable() {
|
||||
|
||||
public void run() {
|
||||
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 AbstractProject<?, ?>) && restOfPathParts.hasNext()) {
|
||||
item = jenkins.getItem(restOfPathParts.next(), (ItemGroup<?>) item);
|
||||
}
|
||||
if (item instanceof Job<?, ?>) {
|
||||
projectHolder[0] = (Job<?, ?>) item;
|
||||
if (item instanceof AbstractProject<?, ?>) {
|
||||
projectHolder[0] = (AbstractProject<?, ?>) item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
final Job<?, ?> project = projectHolder[0];
|
||||
final AbstractProject<?, ?> project = projectHolder[0];
|
||||
if (project == null) {
|
||||
throw HttpResponses.notFound();
|
||||
}
|
||||
|
@ -153,22 +156,11 @@ public class GitLabWebHook implements UnprotectedRootAction {
|
|||
String lastPath = paths.get(paths.size()-1);
|
||||
String firstPath = paths.get(0);
|
||||
if(lastPath.equals("status.json") && !firstPath.equals("!builds")) {
|
||||
String commitSHA1 = paths.get(1);
|
||||
this.generateStatusJSON(commitSHA1, project, req, res);
|
||||
new StatusJsonAction(project, paths.get(1)).execute(res);
|
||||
} else if(lastPath.equals("build") || (lastPath.equals("status.json") && firstPath.equals("!builds"))) {
|
||||
this.generateBuild(theString, project, req, res);
|
||||
} else if(lastPath.equals("status.png")) {
|
||||
String branch = req.getParameter("ref");
|
||||
String commitSHA1 = req.getParameter("sha1");
|
||||
try {
|
||||
this.generateStatusPNG(branch, commitSHA1, project, req, res);
|
||||
} catch (ServletException e) {
|
||||
e.printStackTrace();
|
||||
throw HttpResponses.error(500,"Could not generate an image.");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
throw HttpResponses.error(500,"Could not generate an image.");
|
||||
}
|
||||
new StatusPngAction(project, req.getParameter("sha1"), req.getParameter("ref")).execute(res);
|
||||
} else if((firstPath.equals("commits") || firstPath.equals("builds")) && !lastPath.equals("status.json")) {
|
||||
Run build = this.getBuildBySHA1(project, lastPath, true);
|
||||
redirectToBuildPage(res, build);
|
||||
|
@ -194,119 +186,6 @@ public class GitLabWebHook implements UnprotectedRootAction {
|
|||
}
|
||||
}
|
||||
|
||||
private GitSCM getGitSCM(SCMTriggerItem item) {
|
||||
if(item != null) {
|
||||
for(SCM scm : item.getSCMs()) {
|
||||
if(scm instanceof GitSCM) {
|
||||
return (GitSCM) scm;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void generateStatusJSON(String commitSHA1, Job project, StaplerRequest req, StaplerResponse rsp) {
|
||||
SCMTriggerItem item = SCMTriggerItems.asSCMTriggerItem(project);
|
||||
GitSCM gitSCM = getGitSCM(item);
|
||||
|
||||
if(gitSCM == null) {
|
||||
throw new IllegalArgumentException("This repo does not use git.");
|
||||
}
|
||||
|
||||
Run mainBuild = this.getBuildBySHA1(project, commitSHA1, true);
|
||||
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("sha", commitSHA1);
|
||||
|
||||
if(mainBuild == null) {
|
||||
try {
|
||||
object.put("status", "pending");
|
||||
this.writeJSON(rsp, object);
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
throw HttpResponses.error(500,"Could not generate response.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
object.put("id", mainBuild.getNumber());
|
||||
|
||||
Result res = mainBuild.getResult();
|
||||
|
||||
//TODO: add status of pending when we figure it out.
|
||||
if(mainBuild.isBuilding()) {
|
||||
object.put("status", "running");
|
||||
}else if(res == Result.ABORTED) {
|
||||
object.put("status", "canceled");
|
||||
}else if(res == Result.SUCCESS) {
|
||||
object.put("status", "success");
|
||||
}else {
|
||||
object.put("status", "failed");
|
||||
}
|
||||
|
||||
try {
|
||||
this.writeJSON(rsp, object);
|
||||
} catch (IOException e) {
|
||||
throw HttpResponses.error(500,"Could not generate response.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void generateStatusPNG(String branch, String commitSHA1, Job project, final StaplerRequest req, final StaplerResponse rsp) throws ServletException, IOException {
|
||||
SCMTriggerItem item = SCMTriggerItems.asSCMTriggerItem(project);
|
||||
GitSCM gitSCM = getGitSCM(item);
|
||||
|
||||
if(gitSCM == null) {
|
||||
throw new IllegalArgumentException("This repo does not use git.");
|
||||
}
|
||||
|
||||
Run mainBuild = null;
|
||||
|
||||
if(branch != null) {
|
||||
mainBuild = this.getBuildByBranch(project, branch);
|
||||
} else if(commitSHA1 != null) {
|
||||
mainBuild = this.getBuildBySHA1(project, commitSHA1, false);
|
||||
}
|
||||
|
||||
String baseUrl = Jenkins.getInstance().getRootUrl();
|
||||
// Remove trailing slash
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.substring(0, baseUrl.length() - 1);
|
||||
}
|
||||
String imageUrl = "images/unknown.png";
|
||||
if(null != mainBuild) {
|
||||
Result res = mainBuild.getResult();
|
||||
if(mainBuild.isBuilding()) {
|
||||
imageUrl = "images/running.png";
|
||||
}else if(res == Result.SUCCESS) {
|
||||
imageUrl = "images/success.png";
|
||||
}else if(res == Result.FAILURE) {
|
||||
imageUrl = "images/failed.png";
|
||||
}else if(res == Result.UNSTABLE) {
|
||||
imageUrl = "images/unstable.png";
|
||||
}else {
|
||||
imageUrl = "images/unknown.png";
|
||||
}
|
||||
}
|
||||
Authentication old = SecurityContextHolder.getContext().getAuthentication();
|
||||
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
|
||||
try {
|
||||
URL resourceUrl = new URL(Jenkins.getInstance().getPlugin("gitlab-plugin").getWrapper().baseResourceURL + imageUrl);
|
||||
LOGGER.info("serving image "+resourceUrl.toExternalForm());
|
||||
rsp.setHeader("Expires","Fri, 01 Jan 1984 00:00:00 GMT");
|
||||
rsp.setHeader("Cache-Control", "no-cache, private");
|
||||
rsp.setHeader("Content-Type", "image/png");
|
||||
hudson.util.IOUtils.copy(new File(resourceUrl.toURI()), rsp.getOutputStream());
|
||||
rsp.flushBuffer();
|
||||
} catch (Exception e) {
|
||||
throw HttpResponses.error(500,"Could not generate response.");
|
||||
} finally {
|
||||
SecurityContextHolder.getContext().setAuthentication(old);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Take the GitLab Data and parse through it.
|
||||
* {
|
||||
|
@ -610,27 +489,6 @@ public class GitLabWebHook implements UnprotectedRootAction {
|
|||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @param rsp The stapler response to write the output to.
|
||||
* @throws IOException
|
||||
*/
|
||||
private void writeJSON(StaplerResponse rsp, JSONObject jsonObject) throws IOException {
|
||||
rsp.setContentType("application/json");
|
||||
PrintWriter w = rsp.getWriter();
|
||||
|
||||
if(jsonObject == null) {
|
||||
w.write("null");
|
||||
} else {
|
||||
w.write(jsonObject.toString());
|
||||
}
|
||||
|
||||
w.flush();
|
||||
w.close();
|
||||
|
||||
}
|
||||
|
||||
@Extension
|
||||
public static class GitlabWebHookCrumbExclusion extends CrumbExclusion {
|
||||
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package com.dabsquared.gitlabjenkins.webhook;
|
||||
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Result;
|
||||
import hudson.plugins.git.GitSCM;
|
||||
import hudson.plugins.git.util.Build;
|
||||
import hudson.plugins.git.util.BuildData;
|
||||
import hudson.scm.SCM;
|
||||
import hudson.util.HttpResponses;
|
||||
import jenkins.triggers.SCMTriggerItem;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
public abstract class BuildStatusAction implements WebHookAction {
|
||||
|
||||
private final AbstractProject<?, ?> project;
|
||||
|
||||
public BuildStatusAction(AbstractProject<?, ?> project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public void execute(StaplerResponse response) {
|
||||
SCMTriggerItem item = SCMTriggerItem.SCMTriggerItems.asSCMTriggerItem(project);
|
||||
if (!hasGitSCM(item)) {
|
||||
throw HttpResponses.error(409, "The project has no GitSCM configured");
|
||||
}
|
||||
AbstractBuild<?, ?> build = retrieveBuild(project);
|
||||
writeStatusBody(response, build, getStatus(build));
|
||||
}
|
||||
|
||||
protected abstract AbstractBuild<?, ?> retrieveBuild(AbstractProject<?, ?> project);
|
||||
|
||||
protected abstract void writeStatusBody(StaplerResponse response, AbstractBuild<?, ?> build, BuildStatus status);
|
||||
|
||||
private boolean hasGitSCM(SCMTriggerItem item) {
|
||||
if(item != null) {
|
||||
for(SCM scm : item.getSCMs()) {
|
||||
if(scm instanceof GitSCM) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private BuildStatus getStatus(AbstractBuild<?, ?> build) {
|
||||
if (build == null) {
|
||||
return BuildStatus.PENDING;
|
||||
} else if (build.isBuilding()) {
|
||||
return BuildStatus.RUNNING;
|
||||
} else if (build.getResult() == Result.ABORTED) {
|
||||
return BuildStatus.CANCELED;
|
||||
} else if (build.getResult() == Result.SUCCESS) {
|
||||
return BuildStatus.SUCCESS;
|
||||
} else if (build.getResult() == Result.UNSTABLE) {
|
||||
return BuildStatus.UNSTABLE;
|
||||
} else {
|
||||
return BuildStatus.FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
protected enum BuildStatus {
|
||||
PENDING("pending"), RUNNING("running"), CANCELED("canceled"), SUCCESS("success"), FAILED("failed"), UNSTABLE("failed");
|
||||
|
||||
private String value;
|
||||
|
||||
BuildStatus(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package com.dabsquared.gitlabjenkins.webhook;
|
||||
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.plugins.git.util.Build;
|
||||
import hudson.plugins.git.util.BuildData;
|
||||
import hudson.util.HttpResponses;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
public class StatusJsonAction extends BuildStatusAction {
|
||||
|
||||
private String commitSHA1;
|
||||
|
||||
public StatusJsonAction(AbstractProject<?, ?> project, String commitSHA1) {
|
||||
super(project);
|
||||
this.commitSHA1 = commitSHA1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeStatusBody(StaplerResponse response, AbstractBuild<?, ?> build, BuildStatus status) {
|
||||
try {
|
||||
JSONObject object = new JSONObject();
|
||||
object.put("sha", commitSHA1);
|
||||
if (build != null) {
|
||||
object.put("id", build.getNumber());
|
||||
}
|
||||
object.put("status", status.getValue());
|
||||
writeBody(response, object);
|
||||
} catch (IOException e) {
|
||||
throw HttpResponses.error(500, "Failed to generate response");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractBuild<?, ?> retrieveBuild(AbstractProject<?, ?> project) {
|
||||
for (AbstractBuild build : project.getBuilds()) {
|
||||
BuildData data = build.getAction(BuildData.class);
|
||||
if (data != null && data.lastBuild != null) {
|
||||
if (data.lastBuild.isFor(commitSHA1)) {
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void writeBody(StaplerResponse response, JSONObject body) throws IOException {
|
||||
response.setContentType("application/json");
|
||||
PrintWriter writer = response.getWriter();
|
||||
writer.write(body.toString());
|
||||
writer.flush();
|
||||
writer.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
package com.dabsquared.gitlabjenkins.webhook;
|
||||
|
||||
import hudson.model.AbstractBuild;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.plugins.git.Branch;
|
||||
import hudson.plugins.git.util.Build;
|
||||
import hudson.plugins.git.util.BuildData;
|
||||
import hudson.plugins.git.util.MergeRecord;
|
||||
import hudson.security.ACL;
|
||||
import hudson.util.HttpResponses;
|
||||
import jenkins.model.Jenkins;
|
||||
import org.acegisecurity.Authentication;
|
||||
import org.acegisecurity.context.SecurityContextHolder;
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
public class StatusPngAction extends BuildStatusAction {
|
||||
|
||||
|
||||
private final String commitSHA1;
|
||||
private final String branchName;
|
||||
|
||||
public StatusPngAction(AbstractProject<?, ?> project, String commitSHA1, String branchName) {
|
||||
super(project);
|
||||
this.commitSHA1 = commitSHA1;
|
||||
this.branchName = branchName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void writeStatusBody(StaplerResponse response, AbstractBuild<?, ?> build, BuildStatus status) {
|
||||
Authentication old = SecurityContextHolder.getContext().getAuthentication();
|
||||
SecurityContextHolder.getContext().setAuthentication(ACL.SYSTEM);
|
||||
try {
|
||||
URL resourceUrl = new URL(Jenkins.getInstance().getPlugin("gitlab-plugin").getWrapper().baseResourceURL + getStatusImageUrl(status));
|
||||
response.setHeader("Expires","Fri, 01 Jan 1984 00:00:00 GMT");
|
||||
response.setHeader("Cache-Control", "no-cache, private");
|
||||
response.setHeader("Content-Type", "image/png");
|
||||
hudson.util.IOUtils.copy(new File(resourceUrl.toURI()), response.getOutputStream());
|
||||
response.flushBuffer();
|
||||
} catch (Exception e) {
|
||||
throw HttpResponses.error(500, "Could not generate response.");
|
||||
} finally {
|
||||
SecurityContextHolder.getContext().setAuthentication(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected AbstractBuild<?, ?> retrieveBuild(AbstractProject<?, ?> project) {
|
||||
if (branchName != null) {
|
||||
return getBuildByBranch(project, branchName);
|
||||
} else {
|
||||
return getBuildBySHA1(project, commitSHA1);
|
||||
}
|
||||
}
|
||||
|
||||
private AbstractBuild<?, ?> getBuildByBranch(AbstractProject<?, ?> project, String branchName) {
|
||||
for (AbstractBuild<?, ?> build : project.getBuilds()) {
|
||||
BuildData data = build.getAction(BuildData.class);
|
||||
MergeRecord merge = build.getAction(MergeRecord.class);
|
||||
if (hasLastBuild(data) && isNoMergeBuild(data, merge)) {
|
||||
for (Branch branch : data.lastBuild.getRevision().getBranches()) {
|
||||
if (branch.getName().endsWith("/" + branchName)) {
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private AbstractBuild<?, ?> getBuildBySHA1(AbstractProject<?, ?> project, String sha1) {
|
||||
for (AbstractBuild build : project.getBuilds()) {
|
||||
BuildData data = build.getAction(BuildData.class);
|
||||
MergeRecord merge = build.getAction(MergeRecord.class);
|
||||
if (hasLastBuild(data) && isNoMergeBuild(data, merge)) {
|
||||
if (data.lastBuild.isFor(sha1)) {
|
||||
return build;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isNoMergeBuild(BuildData data, MergeRecord merge) {
|
||||
return merge == null || merge.getSha1().equals(data.lastBuild.getMarked().getSha1String());
|
||||
}
|
||||
|
||||
private boolean hasLastBuild(BuildData data) {
|
||||
return data != null && data.lastBuild != null && data.lastBuild.getRevision() != null;
|
||||
}
|
||||
|
||||
private String getStatusImageUrl(BuildStatus status) {
|
||||
if(status == BuildStatus.RUNNING) {
|
||||
return "images/running.png";
|
||||
} else if (status == BuildStatus.SUCCESS) {
|
||||
return "images/success.png";
|
||||
} else if (status == BuildStatus.FAILED) {
|
||||
return "images/failed.png";
|
||||
} else if (status == BuildStatus.UNSTABLE) {
|
||||
return "images/unstable.png";
|
||||
} else {
|
||||
return "images/unknown.png";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package com.dabsquared.gitlabjenkins.webhook;
|
||||
|
||||
import org.kohsuke.stapler.StaplerResponse;
|
||||
|
||||
/**
|
||||
* @author Robin Müller
|
||||
*/
|
||||
public interface WebHookAction {
|
||||
void execute(StaplerResponse response);
|
||||
}
|
Loading…
Reference in New Issue