Skip to content

Commit

Permalink
MultiBranch support for Perforce Swarm Reviews.
Browse files Browse the repository at this point in the history
A work in progress - adds Swarm API support to find branches and
reviews from a Swarm project.
  • Loading branch information
p4paul committed Jun 19, 2017
1 parent d967213 commit 457e5ea
Show file tree
Hide file tree
Showing 20 changed files with 720 additions and 99 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.3.1</version>
</dependency>

</dependencies>

<build>
Expand Down
17 changes: 17 additions & 0 deletions src/main/java/org/jenkinsci/plugins/p4/PerforceScm.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@
import org.jenkinsci.plugins.p4.filters.FilterPerChangeImpl;
import org.jenkinsci.plugins.p4.matrix.MatrixOptions;
import org.jenkinsci.plugins.p4.populate.Populate;
import org.jenkinsci.plugins.p4.review.P4Review;
import org.jenkinsci.plugins.p4.review.ReviewProp;
import org.jenkinsci.plugins.p4.tagging.TagAction;
import org.jenkinsci.plugins.p4.tasks.CheckoutStatus;
import org.jenkinsci.plugins.p4.tasks.CheckoutTask;
import org.jenkinsci.plugins.p4.tasks.PollTask;
import org.jenkinsci.plugins.p4.tasks.RemoveClientTask;
Expand Down Expand Up @@ -89,6 +91,7 @@ public class PerforceScm extends SCM {

private transient List<P4Ref> incrementalChanges;
private transient P4Ref parentChange;
private transient P4Review review;

public static final int DEFAULT_FILE_LIMIT = 50;
public static final int DEFAULT_CHANGE_LIMIT = 20;
Expand Down Expand Up @@ -124,6 +127,14 @@ public List<P4Ref> getIncrementalChanges() {
return incrementalChanges;
}

public P4Review getReview() {
return review;
}

public void setReview(P4Review review) {
this.review = review;
}

/**
* Helper function for converting an SCM object into a
* PerforceScm object when appropriate.
Expand Down Expand Up @@ -404,6 +415,12 @@ public void checkout(Run<?, ?> run, Launcher launcher, FilePath buildWorkspace,
// Get workspace used for the Task
Workspace ws = task.setEnvironment(run, workspace, buildWorkspace);

// Add review to environment, if defined
if(review != null) {
ws.addEnv(ReviewProp.REVIEW.toString(), review.getId());
ws.addEnv(ReviewProp.STATUS.toString(), CheckoutStatus.SHELVED.toString());
}

// Set the Workspace and initialise
task.setWorkspace(ws);
task.initialise();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,8 @@ public void unshelveFiles(int review) throws Exception {
// Unshelve change for review
List<IFileSpec> shelveMsg;
shelveMsg = iclient.unshelveChangelist(review, null, 0, true, false);
validate.check(shelveMsg, false, "also opened by", "No such file(s)", "exclusive file already opened");
validate.check(shelveMsg, false, "also opened by", "No such file(s)",
"exclusive file already opened", "no file(s) to unshelve");

// force sync any files missed due to INFO messages e.g. exclusive files
for (IFileSpec spec : shelveMsg) {
Expand Down
22 changes: 22 additions & 0 deletions src/main/java/org/jenkinsci/plugins/p4/review/P4Review.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.jenkinsci.plugins.p4.review;

import org.jenkinsci.plugins.p4.tasks.CheckoutStatus;

public class P4Review {

private String id;
private CheckoutStatus status;

public P4Review(String id, CheckoutStatus status) {
this.id = id;
this.status = status;
}

public String getId() {
return id;
}

public CheckoutStatus getStatus() {
return status;
}
}
86 changes: 56 additions & 30 deletions src/main/java/org/jenkinsci/plugins/p4/scm/AbstractP4ScmSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import hudson.model.TaskListener;
import hudson.scm.SCM;
import jenkins.scm.api.SCMHead;
import jenkins.scm.api.SCMHeadEvent;
import jenkins.scm.api.SCMHeadObserver;
Expand All @@ -12,11 +11,13 @@
import jenkins.scm.api.SCMSourceCriteria;
import org.jenkinsci.plugins.p4.PerforceScm;
import org.jenkinsci.plugins.p4.browsers.P4Browser;
import org.jenkinsci.plugins.p4.changes.P4GraphRef;
import org.jenkinsci.plugins.p4.changes.P4Ref;
import org.jenkinsci.plugins.p4.client.ClientHelper;
import org.jenkinsci.plugins.p4.populate.Populate;
import org.jenkinsci.plugins.p4.review.P4Review;
import org.jenkinsci.plugins.p4.tasks.CheckoutStatus;
import org.jenkinsci.plugins.p4.workspace.ManualWorkspaceImpl;
import org.jenkinsci.plugins.p4.workspace.Workspace;
import org.jenkinsci.plugins.p4.workspace.WorkspaceSpec;
import org.kohsuke.stapler.DataBoundSetter;

import java.io.IOException;
Expand All @@ -31,27 +32,29 @@ public abstract class AbstractP4ScmSource extends SCMSource {

protected final String credential;

private final String includes;
private String includes;
private final String charset;
private final String format;
private Populate populate;
private final P4Browser browser;

public AbstractP4ScmSource(String id, String credential, String includes, String charset, String format, P4Browser browser) {
public AbstractP4ScmSource(String id, String credential, String charset, String format) {
super(id);

this.credential = credential;
this.includes = includes;
this.charset = charset;
this.format = format;
this.browser = browser;
}

@DataBoundSetter
public void setPopulate(Populate populate) {
this.populate = populate;
}

@DataBoundSetter
public void setIncludes(String includes) {
this.includes = includes;
}

public String getCredential() {
return credential;
}
Expand All @@ -68,45 +71,72 @@ public String getFormat() {
return format;
}

public P4Browser getBrowser() {
return browser;
}

public Populate getPopulate() {
return populate;
}

public abstract P4Browser getBrowser();

public abstract List<P4Head> getHeads(@NonNull TaskListener listener) throws Exception;

public abstract Workspace getWorkspace(String path);
public abstract List<P4ChangeRequestSCMHead> getTags(@NonNull TaskListener listener) throws Exception;

@Override
public SCM build(SCMHead head, SCMRevision revision) {
public Workspace getWorkspace(List<String> paths) {
String client = getFormat();

StringBuffer sb = new StringBuffer();
for (String path : paths) {
String view = "+" + path + "/Jenkinsfile" + " //" + client + "/Jenkinsfile";
sb.append(view).append("\n");
}

WorkspaceSpec spec = new WorkspaceSpec(false, false, false, false,
false, false, null, "LOCAL", sb.toString(), null,
null, null, true);

return new ManualWorkspaceImpl(getCharset(), false, client, spec);
}

@Override
public PerforceScm build(SCMHead head, SCMRevision revision) {
if (head instanceof P4Head) {
P4Head perforceHead = (P4Head) head;
String path = perforceHead.getPath();
List<String> paths = perforceHead.getPaths();
Workspace workspace = getWorkspace(paths);
PerforceScm scm = new PerforceScm(credential, workspace, null, populate, getBrowser());
return scm;
}

Workspace workspace = getWorkspace(path);
PerforceScm scm = new PerforceScm(credential, workspace, null, populate, browser);
if (head instanceof P4ChangeRequestSCMHead) {
P4ChangeRequestSCMHead perforceTag = (P4ChangeRequestSCMHead) head;
List<String> paths = perforceTag.getPaths();
Workspace workspace = getWorkspace(paths);
PerforceScm scm = new PerforceScm(credential, workspace, null, populate, getBrowser());

P4Review review = new P4Review(head.getName(), CheckoutStatus.SHELVED);
scm.setReview(review);
return scm;
}
throw new IllegalArgumentException("SCMHead not an instance of PerforceHead!");
throw new IllegalArgumentException("SCMHead not a Perforce instance!");
}

@Override
protected void retrieve(@CheckForNull SCMSourceCriteria criteria, @NonNull SCMHeadObserver observer, @CheckForNull SCMHeadEvent<?> event, @NonNull TaskListener listener) throws IOException, InterruptedException {
try {
List<P4Head> heads = getHeads(listener);

List<P4ChangeRequestSCMHead> tags = getTags(listener);
heads.addAll(tags);

for (P4Head head : heads) {
// null criteria means that all branches match.
if (criteria == null) {
// get revision and add observe
SCMRevision revision = getRevision(head, listener);
observer.observe(head, revision);
} else {
String base = head.getPath();
SCMSourceCriteria.Probe probe = new P4Probe(getOwner(), credential, listener, charset, base);
ClientHelper p4 = new ClientHelper(getOwner(), credential, listener, scmSourceClient, charset);
SCMSourceCriteria.Probe probe = new P4Probe(p4, head);
if (criteria.isHead(probe, listener)) {
// get revision and add observe
SCMRevision revision = getRevision(head, listener);
Expand All @@ -126,16 +156,12 @@ protected List<String> getIncludePaths() {
return Arrays.asList(array);
}

protected P4Revision getRevision(P4Head head, TaskListener listener) throws Exception {

public P4Revision getRevision(P4Head head, TaskListener listener) throws Exception {
try (ClientHelper p4 = new ClientHelper(getOwner(), credential, listener, scmSourceClient, charset)) {
long change = p4.getHead(head.getPath() + "/...");
if(change < 0) {
P4Ref ref = p4.getGraphHead(head.getPath());
if (ref instanceof P4GraphRef) {
P4GraphRef graphHead = (P4GraphRef) ref;
change = graphHead.getDate();
}
long change = -1;
for (String path : head.getPaths()) {
long c = p4.getHead(path + "/...");
change = (c > change) ? c : change;
}
P4Revision revision = new P4Revision(head, change);
return revision;
Expand Down
38 changes: 25 additions & 13 deletions src/main/java/org/jenkinsci/plugins/p4/scm/BranchesScmSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,42 @@
import hudson.model.TaskListener;
import org.jenkinsci.plugins.p4.browsers.P4Browser;
import org.jenkinsci.plugins.p4.client.ConnectionHelper;
import org.jenkinsci.plugins.p4.workspace.ManualWorkspaceImpl;
import org.jenkinsci.plugins.p4.workspace.Workspace;
import org.jenkinsci.plugins.p4.workspace.WorkspaceSpec;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;

public class BranchesScmSource extends AbstractP4ScmSource {

private P4Browser browser;

@DataBoundConstructor
public BranchesScmSource(String id, String credential, String includes, String charset, String format, P4Browser browser) {
super(id, credential, includes, charset, format, browser);
public BranchesScmSource(String id, String credential, String includes, String charset, String format) {
super(id, credential, charset, format);
setIncludes(includes);
}

@DataBoundSetter
public void setBrowser(P4Browser browser) {
this.browser = browser;
}

@Override
public P4Browser getBrowser() {
return browser;
}

@Override
public List<P4ChangeRequestSCMHead> getTags(@NonNull TaskListener listener) throws Exception {
return new ArrayList<>();
}

@Override
public List<P4Head> getHeads(@NonNull TaskListener listener) throws Exception {

List<String> paths = getIncludePaths();
Expand All @@ -47,21 +65,15 @@ public List<P4Head> getHeads(@NonNull TaskListener listener) throws Exception {
continue;
}

P4Head head = new P4Head(file.toString(), branch, false);
P4Head head = new P4Head(file.toString(), Arrays.asList(branch), false);
list.add(head);
}
p4.disconnect();

return new ArrayList<>(list);
}

@Override
public Workspace getWorkspace(String path) {
String client = getFormat();
String view = path + "/..." + " //" + client + "/...";
WorkspaceSpec spec = new WorkspaceSpec(false, false, false, false, false, false, null, "LOCAL", view);
return new ManualWorkspaceImpl(getCharset(), false, client, spec);
}


@Extension
public static final class DescriptorImpl extends P4ScmSourceDescriptor {
Expand Down

0 comments on commit 457e5ea

Please sign in to comment.