Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Lightweight checkout support.
Implementation for scm-api classes SCMFile and SCMFileSystem, allowing
Jenkins to navigate Perforce within the scope of a workspace view.

Lightweight checkout uses a tempoary Perforce workspace to naviagete
and fetch the files.  The client name and client view mapping will be
modified from a template name e.g. jenkins-${NODE_NAME}-${JOB_NAME} to
the tempoary name jenkinsTemp-UUID.  Alternativly if a user as used
${P4_CLIENT} in the client mapping this will remain unchanged and will
be get expanded during the job run.

JENKINS-45999
JENKINS-46269
  • Loading branch information
p4paul committed Dec 13, 2017
1 parent 9116022 commit 3ef0aa5
Show file tree
Hide file tree
Showing 7 changed files with 594 additions and 25 deletions.
116 changes: 95 additions & 21 deletions src/main/java/org/jenkinsci/plugins/p4/client/NavigateHelper.java
Expand Up @@ -2,6 +2,7 @@

import com.perforce.p4java.core.IDepot;
import com.perforce.p4java.core.file.FileSpecBuilder;
import com.perforce.p4java.core.file.FileSpecOpStatus;
import com.perforce.p4java.core.file.IFileSpec;
import com.perforce.p4java.exception.P4JavaException;
import com.perforce.p4java.option.server.GetDepotFilesOptions;
Expand All @@ -18,27 +19,52 @@ public class NavigateHelper implements Closeable {

private final int max;
private final IOptionsServer p4;
private final String root;

private List<String> paths;
private List<Node> nodes;

public NavigateHelper() {
this(0);
public NavigateHelper(IOptionsServer p4) {
this.max = 0;
this.p4 = p4;

String client = p4.getCurrentClient().getName();
this.root = "//" + client + "/";
}

public NavigateHelper(int max) {
this.max = max;
paths = new ArrayList<>();
p4 = ConnectionFactory.getConnection();
root = "";
}

public AutoCompletionCandidates getCandidates(String value) {
buildPaths(value);
/**
* Matches for a partial depot path
*
* @param depotPath a Perforce Depot path e.g. //depot/pro
* @return matches for the depot path e.g. //depot/projA
*/
public AutoCompletionCandidates getCandidates(String depotPath) {
nodes = new ArrayList<>();
buildPaths(depotPath);
return getCandidates();
}

public List<String> getPaths(String value) {
buildPaths(value);
return paths;
/**
* Get a list of path nodes.
*
* @param localPath a relative local path e.g. "" for root or "projA/comX"
* @return list of nodes
*/
public List<Node> getNodes(String localPath) {
nodes = new ArrayList<>();

String path = root + localPath;
if (!path.isEmpty() && !path.endsWith("/")) {
path = path + "/";
}
buildPaths(path);

return nodes;
}

private void buildPaths(String value) {
Expand All @@ -50,8 +76,9 @@ private void buildPaths(String value) {
// remove leading '//' markers for depot matching
String depot = value.substring(2);
if (!depot.contains("/")) {
listDepots(depot);
return;
if (!listDepots(depot)) {
return;
}
}

listDirs(value);
Expand All @@ -60,22 +87,34 @@ private void buildPaths(String value) {
}
}

private void listDepots(String value) throws P4JavaException {
/**
* @param value path to match
* @return true if value is a depot, false if partial match. e.g. false is returned
* for 'dep' as it is only partial match to 'depot', even thought there may be only one match.
* @throws P4JavaException
*/
private boolean listDepots(String value) throws P4JavaException {
if (p4 != null) {
List<IDepot> list = p4.getDepots();
for (IDepot l : list) {
if (l.getName().equals(value)) {
// complete match, return early
nodes = new ArrayList<>();
return true;
}
if (l.getName().startsWith(value)) {
paths.add("//" + l.getName());
nodes.add(new Node("//" + l.getName(), true));
}
}
}
return false;
}

private void listDirs(String value) throws P4JavaException {
if (p4 != null && value.length() > 4) {

List<IFileSpec> dirs;
dirs = FileSpecBuilder.makeFileSpecList(value + "*");
List<IFileSpec> dirs = specBuilder(value);

GetDirectoriesOptions opts = new GetDirectoriesOptions();
List<IFileSpec> list = p4.getDirectories(dirs, opts);

Expand All @@ -89,7 +128,7 @@ private void listDirs(String value) throws P4JavaException {
for (IFileSpec l : list) {
String dir = l.getOriginalPathString();
if (dir != null) {
paths.add(dir);
nodes.add(new Node(dir, true));
}
}
}
Expand All @@ -98,8 +137,7 @@ private void listDirs(String value) throws P4JavaException {
private void listFiles(String value) throws P4JavaException {
if (p4 != null && value.length() > 4) {

List<IFileSpec> files;
files = FileSpecBuilder.makeFileSpecList(value + "...");
List<IFileSpec> files = specBuilder(value);

GetDepotFilesOptions opts = new GetDepotFilesOptions();
if (max > 0) {
Expand All @@ -109,15 +147,22 @@ private void listFiles(String value) throws P4JavaException {
List<IFileSpec> list = p4.getDepotFiles(files, opts);

for (IFileSpec l : list) {
paths.add(l.getDepotPathString());
if (l.getOpStatus().equals(FileSpecOpStatus.VALID)) {
nodes.add(new Node(l.getDepotPathString(), false));
}
}
}
}

private List<IFileSpec> specBuilder(String value) {
List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(value + "*");
return files;
}

private AutoCompletionCandidates getCandidates() {
AutoCompletionCandidates c = new AutoCompletionCandidates();
for (String path : paths) {
c.add(path);
for (Node node : nodes) {
c.add(node.getDepotPath());
}
return c;
}
Expand All @@ -130,4 +175,33 @@ public void close() throws IOException {
throw new IOException(e);
}
}

public static final class Node {

private String name;
private String depotPath;
private boolean isDir;

protected Node(String depotPath, boolean isDir) {
this.isDir = isDir;
this.depotPath = depotPath;
this.name = depotPath.substring(depotPath.lastIndexOf("/") + 1);

if (isDir && !depotPath.endsWith("/")) {
this.depotPath = depotPath + "/";
}
}

public String getName() {
return name;
}

public String getDepotPath() {
return depotPath;
}

public boolean isDir() {
return isDir;
}
}
}
Expand Up @@ -2,9 +2,13 @@

import hudson.model.Item;
import hudson.model.TaskListener;
import org.jenkinsci.plugins.p4.workspace.ManualWorkspaceImpl;
import org.jenkinsci.plugins.p4.workspace.Workspace;
import org.jenkinsci.plugins.p4.workspace.WorkspaceSpec;

import java.io.Closeable;
import java.io.IOException;
import java.util.HashMap;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
Expand All @@ -15,10 +19,26 @@ public class TempClientHelper extends ClientHelper implements Closeable {

private final String clientUUID;

public TempClientHelper(Item context, String credential, TaskListener listener, String charset) {
public TempClientHelper(Item context, String credential, TaskListener listener, Workspace workspace) throws Exception {
super(context, credential, listener);
String oldName = workspace.getName();

this.clientUUID = "jenkinsTemp-" + UUID.randomUUID().toString();
clientLogin(clientUUID, charset);
workspace.setName(clientUUID);
workspace.setExpand(new HashMap<String, String>());

// Update view with new name
if(workspace instanceof ManualWorkspaceImpl) {
ManualWorkspaceImpl manual = (ManualWorkspaceImpl) workspace;
WorkspaceSpec spec = manual.getSpec();
String view = spec.getView();
view = view.replace(oldName, clientUUID);
spec.setView(view);
manual.setSpec(spec);
}

//clientLogin(clientUUID, workspace.getCharset());
setClient(workspace);
}

@Override
Expand All @@ -30,4 +50,8 @@ public void close() throws IOException {
}
disconnect();
}

public String getClientUUID() {
return clientUUID;
}
}
Expand Up @@ -94,7 +94,7 @@ public Workspace getWorkspace(List<P4Path> paths) {
String scriptPath = getScriptPathOrDefault("Jenkinsfile");
StringBuffer sb = new StringBuffer();
for (P4Path path : paths) {
String view = String.format("%s/%s //%s/%s", path.getPath(), scriptPath, client, scriptPath);
String view = String.format("%s/%s //%s/%s", path.getPath(), scriptPath, "${P4_CLIENT}", scriptPath);
sb.append(view).append("\n");
}

Expand Down
Expand Up @@ -125,7 +125,7 @@ public Workspace getWorkspace(List<P4Path> paths) {
}

String client = getFormat();
String mappingFormat = String.format("%1s/%%1$s //%2$s/%%1$s", branchPath.getPath(), client);
String mappingFormat = String.format("%1s/%%1$s //%2$s/%%1$s", branchPath.getPath(), "${P4_CLIENT}");

StringBuffer workspaceView = new StringBuffer(1024);
workspaceView.append(String.format(mappingFormat, getScriptPathOrDefault("Jenkinsfile")));
Expand Down

0 comments on commit 3ef0aa5

Please sign in to comment.