Skip to content

Commit

Permalink
Merge pull request #149 from yacaovsnc/master
Browse files Browse the repository at this point in the history
Support WorkflowJob (Pipeline) jobs
  • Loading branch information
yacaovsnc authored Feb 17, 2017
2 parents c619432 + 9fe3c7c commit 1598bf5
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 24 deletions.
105 changes: 92 additions & 13 deletions tfs/src/main/java/hudson/plugins/tfs/TeamBuildEndpoint.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.Extension;
import hudson.model.AbstractProject;
import hudson.model.BuildAuthorizationToken;
import hudson.model.BuildableItem;
import hudson.model.Item;
import hudson.model.Job;
import hudson.model.UnprotectedRootAction;
import hudson.plugins.tfs.model.AbstractCommand;
Expand All @@ -14,6 +15,7 @@
import hudson.plugins.tfs.util.EndpointHelper;
import hudson.plugins.tfs.util.MediaType;
import jenkins.model.Jenkins;
import jenkins.model.ParameterizedJobMixIn;
import jenkins.util.TimeDuration;
import net.sf.json.JSONObject;
import org.apache.commons.io.IOUtils;
Expand All @@ -32,6 +34,8 @@
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
Expand All @@ -53,6 +57,7 @@ public class TeamBuildEndpoint implements UnprotectedRootAction {
private static final Map<String, AbstractCommand.Factory> COMMAND_FACTORIES_BY_NAME;
public static final String URL_NAME = "team-build";
public static final String PARAMETER = "parameter";
public static final String BUILD_SOURCE_BRANCH = "Build.SourceBranch";
static final String URL_PREFIX = "/" + URL_NAME + "/";

static {
Expand Down Expand Up @@ -129,6 +134,7 @@ public HttpResponse doIndex(final HttpServletRequest request) throws IOException
IOUtils.closeQuietly(stream);
}
}

static String describeCommands(final Map<String, AbstractCommand.Factory> commandMap, final String urlName) {
final String newLine = System.getProperty("line.separator");
final StringBuilder sb = new StringBuilder();
Expand All @@ -146,11 +152,11 @@ static String describeCommands(final Map<String, AbstractCommand.Factory> comman
return sb.toString();
}


@SuppressWarnings("deprecation" /* We want to do exactly what Jenkins does */)
void checkPermission(final AbstractProject project, final StaplerRequest req, final StaplerResponse rsp) throws IOException {
Job<?, ?> job = project;
final BuildAuthorizationToken authToken = project.getAuthToken();
void checkPermission(final Job job, final ParameterizedJobMixIn.ParameterizedJob jobMixin,
final StaplerRequest req, final StaplerResponse rsp) throws IOException {

final BuildAuthorizationToken authToken = jobMixin.getAuthToken();
hudson.model.BuildAuthorizationToken.checkPermission(job, authToken, req, rsp);
}

Expand Down Expand Up @@ -185,6 +191,78 @@ void dispatch(final StaplerRequest req, final StaplerResponse rsp, final TimeDur
}
}

/**
* If we are calling this method, it means we didn't find any job or project with jobName. Assuming we are building
* multibranch pipeline projects in this case.
*
* We will try to determine the branch name in the following sequence:
* 1. Check if the jobName is composed from ${multibranch_pipeline}/${branch_name}
* 2. Check if the payload has BuildSource variable defined (for PR builds)
*
* If we can't determine the branch name, throw.
*/
private String getBranch(final String jobName, final StaplerRequest req) {
String sourceBranch = null;

if (jobName.indexOf('/') > 0) {
sourceBranch = jobName.substring(jobName.indexOf('/') + 1);
} else {
final String json = req.getParameter("json");
final JSONObject formData = JSONObject.fromObject(json);
final TeamBuildPayload payload = EndpointHelper.MAPPER.convertValue(formData, TeamBuildPayload.class);

sourceBranch = payload.BuildVariables.get(BUILD_SOURCE_BRANCH);
}

if (sourceBranch == null || sourceBranch.trim().isEmpty()) {
throw new IllegalArgumentException("Could not find branch from job name. If building a multibranch"
+ "pipeline job, the job name should be in the format of '${multibranch pipeline name}/${branch}.'");
}

try {
return URLEncoder.encode(sourceBranch.replace("refs/heads/", ""), "UTF-8");
} catch (final UnsupportedEncodingException e) {
throw new RuntimeException("Failed to encode branch: " + sourceBranch, e);
}
}

private String getJobNameFromNestedFolder(final String jobName) {
final int idx = jobName.indexOf('/');
if (idx > 0) {
return jobName.substring(0, idx);
}

return jobName;
}

private Job getJob(final String jobName, final StaplerRequest req) {
final Jenkins jenkins = Jenkins.getInstance();

Job job = jenkins.getItemByFullName(jobName, Job.class);

if (job == null) {
final Item item = jenkins.getItemByFullName(getJobNameFromNestedFolder(jobName));

if (item != null) {
final Collection<? extends Job> allJobs = item.getAllJobs();
final String sourceBranch = getBranch(jobName, req);

for (final Job j : allJobs) {
if (j.getName().equals(sourceBranch)) {
job = j;
break;
}
}
}
}

if (job == null) {
throw new IllegalArgumentException("Job: " + jobName + " not found");
}

return job;
}

private JSONObject innerDispatch(final StaplerRequest req, final StaplerResponse rsp, final TimeDuration delay) throws IOException, ServletException {
commandName = null;
jobName = null;
Expand All @@ -202,22 +280,23 @@ private JSONObject innerDispatch(final StaplerRequest req, final StaplerResponse
throw new IllegalArgumentException("Command not implemented");
}

final Jenkins jenkins = Jenkins.getInstance();
final AbstractProject project = jenkins.getItemByFullName(jobName, AbstractProject.class);
if (project == null) {
throw new IllegalArgumentException("Project not found");
}
checkPermission(project, req, rsp);
final Job job = getJob(jobName, req);

final ParameterizedJobMixIn.ParameterizedJob jobMixin = (ParameterizedJobMixIn.ParameterizedJob) job;

checkPermission(job, jobMixin, req, rsp);
final TimeDuration actualDelay =
delay == null ? new TimeDuration(project.getQuietPeriod()) : delay;
delay == null ? new TimeDuration(jobMixin.getQuietPeriod()) : delay;

final AbstractCommand.Factory factory = COMMAND_FACTORIES_BY_NAME.get(commandName);
final AbstractCommand command = factory.create();
final JSONObject response;
final JSONObject formData = req.getSubmittedForm();
final ObjectMapper mapper = EndpointHelper.MAPPER;
final TeamBuildPayload teamBuildPayload = mapper.convertValue(formData, TeamBuildPayload.class);
response = command.perform(project, req, formData, mapper, teamBuildPayload, actualDelay);

final BuildableItem buildable = (BuildableItem) job;
response = command.perform(job, buildable, req, formData, mapper, teamBuildPayload, actualDelay);
return response;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package hudson.plugins.tfs.model;

import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.model.AbstractProject;
import hudson.model.BuildableItem;
import hudson.model.Job;
import hudson.plugins.tfs.model.servicehooks.Event;
import jenkins.util.TimeDuration;
import net.sf.json.JSONObject;
Expand All @@ -21,7 +22,8 @@ public interface Factory {
* Actually do the work of the command, using the supplied {@code requestPayload} and
* {@code teamBuildPayload}, then returning the output as a {@link JSONObject}.
*
* @param project an {@link AbstractProject to operate on}
* @param job an {@link Job to operate on}
* @param buildableItem an {@link BuildableItem to operate on}
* @param request a {@link StaplerRequest} to help build parameter values
* @param requestPayload a {@link JSONObject} representing the command's input
* @param mapper an {@link ObjectMapper} instance to use to convert the {@link Event#resource}
Expand All @@ -30,6 +32,6 @@ public interface Factory {
*
* @return a {@link JSONObject} representing the hook event's output
*/
public abstract JSONObject perform(final AbstractProject project, final StaplerRequest request, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay);
public abstract JSONObject perform(final Job<?, ?> job, final BuildableItem buildableItem, final StaplerRequest request, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay);

}
13 changes: 7 additions & 6 deletions tfs/src/main/java/hudson/plugins/tfs/model/BuildCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import com.fasterxml.jackson.databind.ObjectMapper;
import com.microsoft.teamfoundation.sourcecontrol.webapi.model.GitPush;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.BuildableItem;
import hudson.model.Cause;
import hudson.model.CauseAction;
import hudson.model.Job;
Expand Down Expand Up @@ -82,15 +82,15 @@ public String getSampleRequestPayload() {
}
}

protected JSONObject innerPerform(final AbstractProject project, final TimeDuration delay, final List<Action> extraActions) {
protected JSONObject innerPerform(final BuildableItem buildableItem, final TimeDuration delay, final List<Action> extraActions) {
final JSONObject result = new JSONObject();

final Jenkins jenkins = Jenkins.getInstance();
final Queue queue = jenkins.getQueue();
final Cause cause = new Cause.UserIdCause();
final CauseAction causeAction = new CauseAction(cause);
final Action[] actionArray = ActionHelper.create(extraActions, causeAction);
final ScheduleResult scheduleResult = queue.schedule2(project, delay.getTime(), actionArray);
final ScheduleResult scheduleResult = queue.schedule2(buildableItem, delay.getTime(), actionArray);
final Queue.Item item = scheduleResult.getItem();
if (item != null) {
result.put("created", jenkins.getRootUrl() + item.getUrl());
Expand All @@ -99,7 +99,9 @@ protected JSONObject innerPerform(final AbstractProject project, final TimeDurat
}

@Override
public JSONObject perform(final AbstractProject project, final StaplerRequest req, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay) {
public JSONObject perform(final Job<?, ?> job, final BuildableItem buildableItem, final StaplerRequest req,
final JSONObject requestPayload, final ObjectMapper mapper,
final TeamBuildPayload teamBuildPayload, final TimeDuration delay) {

// These values are for optional parameters of the same name, for the git.pullrequest.merged event
String commitId = null;
Expand Down Expand Up @@ -138,7 +140,6 @@ else if ("git.pullrequest.merged".equals(eventType)) {
}

//noinspection UnnecessaryLocalVariable
final Job<?, ?> job = project;
final ParametersDefinitionProperty pp = job.getProperty(ParametersDefinitionProperty.class);
if (pp != null && requestPayload.containsKey(TeamBuildEndpoint.PARAMETER)) {
final List<ParameterValue> values = new ArrayList<ParameterValue>();
Expand Down Expand Up @@ -199,7 +200,7 @@ else if (name.equals(PULL_REQUEST_ID) && pullRequestId != null & d instanceof Si
actions.add(action);
}

return innerPerform(project, delay, actions);
return innerPerform(buildableItem, delay, actions);
}

static void contributeTeamBuildParameterActions(final Map<String, String> teamBuildParameters, final List<Action> actions) {
Expand Down
7 changes: 5 additions & 2 deletions tfs/src/main/java/hudson/plugins/tfs/model/PingCommand.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package hudson.plugins.tfs.model;

import com.fasterxml.jackson.databind.ObjectMapper;
import hudson.model.AbstractProject;
import hudson.model.BuildableItem;
import hudson.model.Job;
import jenkins.util.TimeDuration;
import net.sf.json.JSONObject;
import org.kohsuke.stapler.StaplerRequest;
Expand All @@ -27,7 +28,9 @@ public String getSampleRequestPayload() {
}

@Override
public JSONObject perform(final AbstractProject project, final StaplerRequest request, final JSONObject requestPayload, final ObjectMapper mapper, final TeamBuildPayload teamBuildPayload, final TimeDuration delay) {
public JSONObject perform(final Job project, final BuildableItem buildableItem, final StaplerRequest request,
final JSONObject requestPayload, final ObjectMapper mapper,
final TeamBuildPayload teamBuildPayload, final TimeDuration delay) {
return requestPayload;
}

Expand Down

0 comments on commit 1598bf5

Please sign in to comment.