package org.jenkinsci.plugins.p4.client; import com.perforce.p4java.client.IClient; import com.perforce.p4java.client.IClientSummary.IClientOptions; import com.perforce.p4java.core.IChangelist; import com.perforce.p4java.core.IChangelistSummary; import com.perforce.p4java.core.IRepo; import com.perforce.p4java.core.file.FileAction; 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.exception.RequestException; import com.perforce.p4java.impl.generic.client.ClientView; import com.perforce.p4java.impl.generic.core.Changelist; import com.perforce.p4java.impl.generic.core.file.FileSpec; import com.perforce.p4java.impl.mapbased.server.Parameters; import com.perforce.p4java.option.changelist.SubmitOptions; import com.perforce.p4java.option.client.AddFilesOptions; import com.perforce.p4java.option.client.ParallelSyncOptions; import com.perforce.p4java.option.client.ReconcileFilesOptions; import com.perforce.p4java.option.client.ReopenFilesOptions; import com.perforce.p4java.option.client.ResolveFilesAutoOptions; import com.perforce.p4java.option.client.RevertFilesOptions; import com.perforce.p4java.option.client.SyncOptions; import com.perforce.p4java.option.server.ChangelistOptions; import com.perforce.p4java.option.server.GetChangelistsOptions; import com.perforce.p4java.option.server.GetDepotFilesOptions; import com.perforce.p4java.option.server.GetFileContentsOptions; import com.perforce.p4java.option.server.OpenedFilesOptions; import com.perforce.p4java.server.CmdSpec; import hudson.AbortException; import hudson.model.Item; import hudson.model.ItemGroup; import hudson.model.TaskListener; import jenkins.model.Jenkins; import org.apache.commons.io.FileUtils; import org.apache.commons.lang.StringUtils; import org.jenkinsci.plugins.p4.changes.P4ChangeRef; import org.jenkinsci.plugins.p4.changes.P4GraphRef; import org.jenkinsci.plugins.p4.changes.P4LabelRef; import org.jenkinsci.plugins.p4.changes.P4Ref; import org.jenkinsci.plugins.p4.credentials.P4BaseCredentials; import org.jenkinsci.plugins.p4.populate.AutoCleanImpl; import org.jenkinsci.plugins.p4.populate.CheckOnlyImpl; import org.jenkinsci.plugins.p4.populate.FlushOnlyImpl; import org.jenkinsci.plugins.p4.populate.ForceCleanImpl; import org.jenkinsci.plugins.p4.populate.GraphHybridImpl; import org.jenkinsci.plugins.p4.populate.ParallelSync; import org.jenkinsci.plugins.p4.populate.Populate; import org.jenkinsci.plugins.p4.populate.SyncOnlyImpl; import org.jenkinsci.plugins.p4.publish.CommitImpl; import org.jenkinsci.plugins.p4.publish.Publish; import org.jenkinsci.plugins.p4.publish.ShelveImpl; import org.jenkinsci.plugins.p4.publish.SubmitImpl; import org.jenkinsci.plugins.p4.tasks.TimeTask; import org.jenkinsci.plugins.p4.workspace.StaticWorkspaceImpl; import org.jenkinsci.plugins.p4.workspace.TemplateWorkspaceImpl; import org.jenkinsci.plugins.p4.workspace.Workspace; import java.io.BufferedOutputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.logging.Logger; public class ClientHelper extends ConnectionHelper { private static Logger logger = Logger.getLogger(ClientHelper.class.getName()); private IClient iclient; @Deprecated public ClientHelper(String credential, TaskListener listener, String client) { super(Jenkins.getActiveInstance(), credential, listener); clientLogin(client, "none"); } @Deprecated public ClientHelper(String credential, TaskListener listener, String client, String charset) { super(Jenkins.getActiveInstance(), credential, listener); clientLogin(client, charset); } public ClientHelper(ItemGroup context, String credential, TaskListener listener, String client, String charset) { super(context, credential, listener); clientLogin(client, charset); } public ClientHelper(Item context, String credential, TaskListener listener, String client, String charset) { super(context, credential, listener); clientLogin(client, charset); } public ClientHelper(P4BaseCredentials credential, TaskListener listener, String client, String charset) { super(credential, listener); clientLogin(client, charset); } private void clientLogin(String client, String charset) { // Exit early if no connection if (connection == null) { return; } // Find workspace and set as current try { iclient = connection.getClient(client); connection.setCurrentClient(iclient); } catch (Exception e) { String err = "P4: Unable to use Workspace: " + e; logger.severe(err); log(err); e.printStackTrace(); } if (isUnicode()) { connection.setCharsetName(charset); } } public void setClient(Workspace workspace) throws Exception { // Setup/Create workspace based on type iclient = workspace.setClient(connection, authorisationConfig.getUsername()); // Exit early if client is not defined if (!isClientValid(workspace)) { String err = "P4: Undefined workspace: " + workspace.getFullName(); throw new AbortException(err); } // Exit early if client is Static if (workspace instanceof StaticWorkspaceImpl) { connection.setCurrentClient(iclient); return; } // Ensure root and host fields are not null if (workspace.getRootPath() != null) { iclient.setRoot(workspace.getRootPath()); } if (workspace.getHostName() != null) { iclient.setHostName(workspace.getHostName()); } // Set clobber on to ensure workspace is always good IClientOptions options = iclient.getOptions(); options.setClobber(true); iclient.setOptions(options); // Save client spec iclient.update(); // Set active client for this connection connection.setCurrentClient(iclient); return; } /** * Sync files to workspace at the specified change/label. * * @param buildChange Change to sync from * @param populate Populate strategy * @throws Exception push up stack */ public void syncFiles(P4Ref buildChange, Populate populate) throws Exception { TimeTask timer = new TimeTask(); // test label is valid if (buildChange.isLabel()) { String label = buildChange.toString(); try { int change = Integer.parseInt(label); log("P4 Task: label is a number! syncing files at change: " + change); } catch (NumberFormatException e) { if (!label.equals("now") && !isLabel(label) && !isClient(label) && !isCounter(label)) { String msg = "P4: Unable to find client/label/counter: " + label; log(msg); logger.warning(msg); throw new AbortException(msg); } else { log("P4 Task: syncing files at client/label: " + label); } } } else { log("P4 Task: syncing files at change: " + buildChange); } // Sync changes/labels if (buildChange instanceof P4ChangeRef || buildChange instanceof P4LabelRef) { // build file revision spec String path = iclient.getRoot() + "/..."; String revisions = path + "@" + buildChange; // Sync files if (populate instanceof CheckOnlyImpl || populate instanceof FlushOnlyImpl) { syncHaveList(revisions, populate); } else { syncFiles(revisions, populate); } } // Sync graph repos if (buildChange instanceof P4GraphRef && populate instanceof GraphHybridImpl) { String rev = ((P4GraphRef) buildChange).getRepo() + "/..."; syncFiles(rev, populate); } log("duration: " + timer.toString() + "\n"); } /** * Test to see if workspace is at the latest revision. * * @throws Exception */ private boolean syncHaveList(String revisions, Populate populate) throws Exception { // Preview (sync -k) SyncOptions syncOpts = new SyncOptions(); syncOpts.setClientBypass(populate.isHave()); syncOpts.setQuiet(populate.isQuiet()); List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(revisions); List<IFileSpec> syncMsg = iclient.sync(files, syncOpts); validate.check(syncMsg, "file(s) up-to-date.", "file does not exist", "no file(s) as of that date"); for (IFileSpec fileSpec : syncMsg) { if (fileSpec.getOpStatus() != FileSpecOpStatus.VALID) { String msg = fileSpec.getStatusMessage(); if (msg.contains("file(s) up-to-date.")) { return true; } } } return false; } private void syncFiles(String revisions, Populate populate) throws Exception { // set MODTIME if populate options is used only required before 15.1 if (populate.isModtime() && !checkVersion(20151)) { IClientOptions options = iclient.getOptions(); if (!options.isModtime()) { options.setModtime(true); iclient.setOptions(options); iclient.update(); // Save client spec } } // sync options SyncOptions syncOpts = new SyncOptions(); // setServerBypass (-p no have list) syncOpts.setServerBypass(!populate.isHave()); // setForceUpdate (-f only if no -p is set) syncOpts.setForceUpdate(populate.isForce() && populate.isHave()); syncOpts.setQuiet(populate.isQuiet()); // Sync files with asynchronous callback and parallel if enabled to SyncStreamingCallback callback = new SyncStreamingCallback(iclient.getServer(), listener); synchronized (callback) { List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(revisions); ParallelSync parallel = populate.getParallel(); if (parallel != null && parallel.isEnable()) { ParallelSyncOptions parallelOpts = parallel.getParallelOptions(); iclient.syncParallel(files, syncOpts, callback, 0, parallelOpts); } else { iclient.sync(files, syncOpts, callback, 0); } while (!callback.isDone()) { callback.wait(); } if (callback.isFail()) { throw new P4JavaException(callback.getException()); } } } private int CheckNativeUse(String revisions, SyncOptions syncOpts, ParallelSync parallel) { try { String p4 = parallel.getPath(); List<String> command = new ArrayList<String>(); String p4port = p4credential.getP4port(); p4port = (p4credential.isSsl()) ? "ssl:" + p4port : p4port; command.add(p4); command.add("-c" + iclient.getName()); command.add("-p" + p4port); command.add("-u" + p4credential.getUsername()); String p4host = p4credential.getP4host(); if (p4host != null && !p4host.isEmpty()) { command.add("-H" + p4host); } command.add("sync"); if (syncOpts.isForceUpdate()) { command.add("-f"); } if (syncOpts.isQuiet()) { command.add("-q"); } if (syncOpts.isClientBypass()) { command.add("-k"); } if (syncOpts.isSafetyCheck()) { command.add("-s"); } if (syncOpts.isServerBypass()) { command.add("-p"); } if (syncOpts.isNoUpdate()) { command.add("-n"); } String threads = parallel.getThreads(); String minfiles = parallel.getMinfiles(); String minbytes = parallel.getMinbytes(); command.add("--parallel"); command.add("threads=" + threads + ",min=" + minfiles + ",minsize=" + minbytes); command.add(revisions); ProcessBuilder builder = new ProcessBuilder(command); final Process process = builder.start(); InputStream inputStream = process.getInputStream(); InputStream errorStream = process.getErrorStream(); BufferedReader inputStreamReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); BufferedReader errorStreamReader = new BufferedReader(new InputStreamReader(errorStream, "UTF-8")); // Log commands log("(p4):cmd:... " + StringUtils.join(command, " ")); log(""); String line; while ((line = inputStreamReader.readLine()) != null) { log(line); } while ((line = errorStreamReader.readLine()) != null) { log(line); } int exitCode = process.waitFor(); inputStreamReader.close(); errorStreamReader.close(); log("exitCode=" + Integer.toString(exitCode)); log("(p4):stop:0"); return exitCode; } catch (UnsupportedEncodingException e) { log(e.getMessage()); } catch (IOException e) { log(e.getMessage()); } catch (InterruptedException e) { log(e.getMessage()); } return 1; } /** * Cleans up the Perforce workspace after a previous build. Removes all * pending and abandoned files (equivalent to 'p4 revert -w'). * * @param populate Jelly populate options * @throws Exception push up stack */ public void tidyWorkspace(Populate populate) throws Exception { // relies on workspace view for scope. log(""); String path = iclient.getRoot() + "/..."; if (populate instanceof AutoCleanImpl) { tidyAutoCleanImpl(path, populate); } if (populate instanceof ForceCleanImpl) { tidyForceSyncImpl(path, populate); } if (populate instanceof GraphHybridImpl) { tidyForceSyncImpl(path, populate); } if (populate instanceof SyncOnlyImpl) { tidySyncOnlyImpl(path, populate); } } private void tidySyncOnlyImpl(String path, Populate populate) throws Exception { SyncOnlyImpl syncOnly = (SyncOnlyImpl) populate; if (syncOnly.isRevert()) { tidyPending(path); } } private void tidyForceSyncImpl(String path, Populate populate) throws Exception { // remove all pending files within workspace tidyPending(path); // remove all versioned files (clean have list) String revisions = iclient.getRoot() + "/...#0"; // Only use quiet populate option to insure a clean sync boolean quiet = populate.isQuiet(); Populate clean = new AutoCleanImpl(false, false, false, quiet, null, null); syncFiles(revisions, clean); // remove all files from workspace String root = URLDecoder.decode(iclient.getRoot(), "UTF-8"); log("... rm -rf " + root); log(""); silentlyForceDelete(root); } private void silentlyForceDelete(String root) throws IOException { try { FileUtils.forceDelete(new File(root)); } catch (FileNotFoundException ignored) { } } private void tidyAutoCleanImpl(String path, Populate populate) throws Exception { // remove all pending files within workspace tidyPending(path); // clean files within workspace tidyClean(populate, path); } private void tidyPending(String path) throws Exception { TimeTask timer = new TimeTask(); log("P4 Task: reverting all pending and shelved revisions."); // revert all pending and shelved revisions RevertFilesOptions rOpts = new RevertFilesOptions(); List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(path); List<IFileSpec> list = iclient.revertFiles(files, rOpts); validate.check(list, "not opened on this client"); // check for added files and remove... log("... rm [abandoned files]"); for (IFileSpec file : list) { if (file.getAction() == FileAction.ABANDONED) { // first check if we have the local path String local = file.getLocalPathString(); if (local == null) { local = depotToLocal(file); } if (local != null) { File unlink = new File(local); boolean ok = unlink.delete(); if (!ok) { log("Not able to delete: " + local); } } } } log("duration: " + timer.toString() + "\n"); } private void tidyClean(Populate populate, String path) throws Exception { // Use old method if 'p4 clean' is not supported if (!checkVersion(20141)) { tidyRevisions(path, populate); return; } // Set options boolean delete = ((AutoCleanImpl) populate).isDelete(); boolean replace = ((AutoCleanImpl) populate).isReplace(); String[] base = {"-w", "-f"}; List<String> list = new ArrayList<String>(); list.addAll(Arrays.asList(base)); if (delete && !replace) { list.add("-a"); } if (replace && !delete) { list.add("-e"); list.add("-d"); } if (!replace && !delete) { log("P4 Task: skipping clean, no options set."); return; } // set MODTIME if populate options is used and server supports flag if (populate.isModtime()) { if (checkVersion(20141)) { list.add("-m"); } else { log("P4: Resolving files by MODTIME not supported (requires 2014.1 or above)"); } } TimeTask timer = new TimeTask(); log("P4 Task: cleaning workspace to match have list."); String[] args = list.toArray(new String[list.size()]); ReconcileFilesOptions cleanOpts = new ReconcileFilesOptions(args); // Reconcile with asynchronous callback ReconcileStreamingCallback callback = new ReconcileStreamingCallback(iclient.getServer(), listener); synchronized (callback) { List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(path); String[] params = Parameters.processParameters(cleanOpts, files, connection); connection.execStreamingMapCommand(CmdSpec.RECONCILE.toString(), params, null, callback, 0); while (!callback.isDone()) { callback.wait(); } } log("duration: " + timer.toString() + "\n"); } private void tidyRevisions(String path, Populate populate) throws Exception { TimeTask timer = new TimeTask(); log("P4 Task: tidying workspace to match have list."); boolean delete = ((AutoCleanImpl) populate).isDelete(); boolean replace = ((AutoCleanImpl) populate).isReplace(); // check status - find all missing, changed or added files String[] base = {"-n", "-a", "-e", "-d", "-l", "-f"}; List<String> list = new ArrayList<String>(); list.addAll(Arrays.asList(base)); String[] args = list.toArray(new String[list.size()]); ReconcileFilesOptions statusOpts = new ReconcileFilesOptions(args); List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(path); List<IFileSpec> status = iclient.reconcileFiles(files, statusOpts); validate.check(status, "also opened by", "no file(s) to reconcile", "must sync/resolve", "exclusive file already opened", "cannot submit from stream", "instead of", "empty, assuming text"); // Add missing, modified or locked files to a list, and delete the // unversioned files. List<IFileSpec> update = new ArrayList<IFileSpec>(); for (IFileSpec s : status) { if (s.getOpStatus() == FileSpecOpStatus.VALID) { String local = s.getLocalPathString(); if (local == null) { local = depotToLocal(s); } switch (s.getAction()) { case ADD: if (local != null && delete) { File unlink = new File(local); boolean ok = unlink.delete(); if (!ok) { log("Not able to delete: " + local); } } break; default: update.add(s); break; } } else { String msg = s.getStatusMessage(); if (msg.contains("exclusive file already opened")) { String rev = msg.substring(0, msg.indexOf(" - can't ")); IFileSpec spec = new FileSpec(rev); update.add(spec); } } } // Force sync missing and modified files if (!update.isEmpty() && replace) { SyncOptions syncOpts = new SyncOptions(); syncOpts.setForceUpdate(true); syncOpts.setQuiet(populate.isQuiet()); List<IFileSpec> syncMsg = iclient.sync(update, syncOpts); validate.check(syncMsg, "file(s) up-to-date.", "file does not exist"); } log("duration: " + timer.toString() + "\n"); } public void revertAllFiles(boolean virtual) throws Exception { String path = iclient.getRoot() + "/..."; List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(path); // revert all pending and shelved revisions RevertFilesOptions rOpts = new RevertFilesOptions(); rOpts.setNoClientRefresh(virtual); List<IFileSpec> list = iclient.revertFiles(files, rOpts); validate.check(list, "not opened on this client"); } public void versionFile(String file, Publish publish) throws Exception { // build file revision spec List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(file); findChangeFiles(files, publish.isDelete()); // Exit early if no change if (!isOpened(files)) { return; } // create changelist with files IChangelist change = createChangeList(files, publish); // submit changelist submitFiles(change, false); } public boolean buildChange(Publish publish) throws Exception { TimeTask timer = new TimeTask(); log("P4 Task: reconcile files to changelist."); List<IFileSpec> files; if (publish instanceof CommitImpl) { CommitImpl commit = (CommitImpl) publish; files = FileSpecBuilder.makeFileSpecList(commit.getFiles()); AddFilesOptions opts = new AddFilesOptions(); iclient.addFiles(files, opts); } else { // build file revision spec String ws = "//" + iclient.getName() + "/..."; files = FileSpecBuilder.makeFileSpecList(ws); findChangeFiles(files, publish.isDelete()); } // Check if file is open boolean open = isOpened(files); log("duration: " + timer.toString() + "\n"); return open; } private void findChangeFiles(List<IFileSpec> files, boolean delete) throws Exception { // cleanup pending changes (revert -k) RevertFilesOptions revertOpts = new RevertFilesOptions(); revertOpts.setNoClientRefresh(true); List<IFileSpec> revertStat = iclient.revertFiles(files, revertOpts); validate.check(revertStat, "file(s) not opened on this client."); // flush client to populate have (sync -k) SyncOptions syncOpts = new SyncOptions(); syncOpts.setClientBypass(true); List<IFileSpec> syncStat = iclient.sync(files, syncOpts); validate.check(syncStat, "file(s) up-to-date.", "no such file(s)."); // check status - find all changes to files ReconcileFilesOptions statusOpts = new ReconcileFilesOptions(); statusOpts.setUseWildcards(true); statusOpts.setOutsideAdd(true); statusOpts.setOutsideEdit(true); statusOpts.setRemoved(delete); List<IFileSpec> status = iclient.reconcileFiles(files, statusOpts); validate.check(status, "- no file(s) to reconcile", "instead of", "empty, assuming text", "also opened by"); } public void publishChange(Publish publish) throws Exception { TimeTask timer = new TimeTask(); log("P4 Task: publish files to Perforce."); // build file revision spec String ws = "//" + iclient.getName() + "/..."; List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(ws); // create changelist and open files IChangelist change = createChangeList(files, publish); // logging OpenedFilesOptions openOps = new OpenedFilesOptions(); List<IFileSpec> open = iclient.openedFiles(files, openOps); for (IFileSpec f : open) { FileAction action = f.getAction(); String path = f.getDepotPathString(); log("... ... " + action + " " + path); } // if SUBMIT if (publish instanceof SubmitImpl) { SubmitImpl submit = (SubmitImpl) publish; boolean reopen = submit.isReopen(); submitFiles(change, reopen); } // if SHELVE if (publish instanceof ShelveImpl) { ShelveImpl shelve = (ShelveImpl) publish; boolean revert = shelve.isRevert(); shelveFiles(change, files, revert); } // if COMMIT if (publish instanceof CommitImpl) { CommitImpl commit = (CommitImpl) publish; commitFiles(change); } log("duration: " + timer.toString() + "\n"); } private IChangelist createChangeList(List<IFileSpec> files, Publish publish) throws Exception { String desc = publish.getExpandedDesc(); // create new pending change and add description IChangelist change = new Changelist(); change.setDescription(desc); change = iclient.createChangelist(change); log("... pending change: " + change.getId()); // move files from default change ReopenFilesOptions reopenOpts = new ReopenFilesOptions(); reopenOpts.setChangelistId(change.getId()); // set purge if required if (publish instanceof SubmitImpl) { SubmitImpl submit = (SubmitImpl) publish; int purge = submit.getPurgeValue(); if (purge > 0) { reopenOpts.setFileType("+S" + purge); } } iclient.reopenFiles(files, reopenOpts); return change; } private void submitFiles(IChangelist change, boolean reopen) throws Exception { log("... submitting files"); SubmitOptions submitOpts = new SubmitOptions(); submitOpts.setReOpen(reopen); // submit with asynchronous callback SubmitStreamingCallback callback = new SubmitStreamingCallback(iclient.getServer(), listener); synchronized (callback) { change.submit(submitOpts, callback, 0); while (!callback.isDone()) { callback.wait(); } } long cngNumber = callback.getChange(); if (cngNumber > 0) { log("... submitted in change: " + cngNumber); } else { throw new P4JavaException("Unable to submit change."); } } private void commitFiles(IChangelist change) throws Exception { log("... committing files"); List<String> opts = new ArrayList<>(); opts.add("-c"); opts.add(String.valueOf(change.getId())); String[] args = opts.toArray(new String[opts.size()]); Map<String, Object>[] results = connection.execMapCmd(CmdSpec.SUBMIT.name(), args, null); for (Map<String, Object> map : results) { if (map.containsKey("submittedCommit")) { String sha = (String) map.get("submittedCommit"); log("... committing SHA: " + sha); return; } } throw new P4JavaException("Unable to commit change."); } private void shelveFiles(IChangelist change, List<IFileSpec> files, boolean revert) throws Exception { log("... shelving files"); List<IFileSpec> shelved = iclient.shelveChangelist(change); validate.check(shelved, ""); // post shelf cleanup RevertFilesOptions revertOpts = new RevertFilesOptions(); revertOpts.setChangelistId(change.getId()); revertOpts.setNoClientRefresh(!revert); String r = (revert) ? "(revert)" : "(revert -k)"; log("... reverting open files " + r); iclient.revertFiles(files, revertOpts); } private boolean isOpened(List<IFileSpec> files) throws Exception { OpenedFilesOptions openOps = new OpenedFilesOptions(); List<IFileSpec> open = iclient.openedFiles(files, openOps); for (IFileSpec file : open) { if (file != null && file.getAction() != null) { return true; } } return false; } /** * Workaround for p4java bug. The 'setLocalSyntax(true)' option does not * provide local syntax, so I have to use 'p4 where' to translate through * the client view. * * @param fileSpec Perforce file spec * @return Local syntax * @throws Exception push up stack */ private String depotToLocal(IFileSpec fileSpec) throws Exception { String depotPath = fileSpec.getDepotPathString(); if (depotPath == null) { depotPath = fileSpec.getOriginalPathString(); } if (depotPath == null) { return null; } List<IFileSpec> dSpec = FileSpecBuilder.makeFileSpecList(depotPath); List<IFileSpec> lSpec = iclient.where(dSpec); String path = lSpec.get(0).getLocalPathString(); return path; } private void printFile(String rev) throws Exception { byte[] buf = new byte[1024 * 64]; List<IFileSpec> file = FileSpecBuilder.makeFileSpecList(rev); GetFileContentsOptions printOpts = new GetFileContentsOptions(); printOpts.setNoHeaderLine(true); InputStream ins = connection.getFileContents(file, printOpts); String localPath = depotToLocal(file.get(0)); File target = new File(localPath); // Create directories as required JENKINS-37868 if(target.getParentFile().mkdirs()) { log("Directory created: " + target); } // Make writable if it exists if (target.exists()) { target.setWritable(true); } FileOutputStream outs = new FileOutputStream(target); BufferedOutputStream bouts = new BufferedOutputStream(outs); int len; while ((len = ins.read(buf)) > 0) { bouts.write(buf, 0, len); } ins.close(); bouts.close(); } /** * Unshelve review into workspace. Workspace is sync'ed to head first then * review unshelved. * * @param review Review number (perhaps long?) * @throws Exception push up stack */ public void unshelveFiles(int review) throws Exception { // skip if review is 0 or less if (review < 1) { log("P4 Task: skipping review: " + review); return; } TimeTask timer = new TimeTask(); log("P4 Task: unshelve review: " + review); // 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", "no file(s) to unshelve"); // force sync any files missed due to INFO messages e.g. exclusive files for (IFileSpec spec : shelveMsg) { if (spec.getOpStatus() != FileSpecOpStatus.VALID) { String msg = spec.getStatusMessage(); if (msg.contains("exclusive file already opened")) { String rev = msg.substring(0, msg.indexOf(" - can't ")); // JENKINS-37868 use '@= + review' for correct file printFile(rev + "@=" + review); } } else { log(spec.getDepotPathString()); } } log("... duration: " + timer.toString()); } /** * Resolve files in workspace with the specified option. * * @param mode Resolve mode * @throws Exception push up stack */ public void resolveFiles(String mode) throws Exception { if ("none".equals(mode)) { return; } TimeTask timer = new TimeTask(); log("P4 Task: resolve: -" + mode); // build file revision spec List<IFileSpec> files; String path = iclient.getRoot() + "/..."; files = FileSpecBuilder.makeFileSpecList(path); // Unshelve change for review ResolveFilesAutoOptions rsvOpts = new ResolveFilesAutoOptions(); rsvOpts.setAcceptTheirs("at".equals(mode)); rsvOpts.setAcceptYours("ay".equals(mode)); rsvOpts.setSafeMerge("as".equals(mode)); rsvOpts.setForceResolve("af".equals(mode)); List<IFileSpec> rsvMsg = iclient.resolveFilesAuto(files, rsvOpts); validate.check(rsvMsg, "no file(s) to resolve"); log("... duration: " + timer.toString()); } /** * Get the latest change on the given path * * @param path Perforce depot path //foo/... * @return change number * @throws Exception push up stack */ public long getHead(String path) throws Exception { List<IFileSpec> spec = FileSpecBuilder.makeFileSpecList(path); GetChangelistsOptions opts = new GetChangelistsOptions(); opts.setMaxMostRecent(1); List<IChangelistSummary> changes = connection.getChangelists(spec, opts); if (!changes.isEmpty()) { return changes.get(0).getId(); } return -1; } /** * Gets the Changelist (p4 describe -s); shouldn't need a client, but * p4-java throws an exception if one is not set. * * @param id Change number (long perhaps) * @return Perforce Changelist * @throws Exception push up stack */ public Changelist getChange(long id) throws Exception { try { return (Changelist) connection.getChangelist((int)id); } catch (RequestException e) { ChangelistOptions opts = new ChangelistOptions(); opts.setOriginalChangelist(true); return (Changelist) connection.getChangelist((int)id, opts); } } /** * Get the change number for the last change within the scope of the * workspace view. * * @return Perforce change * @throws Exception push up stack */ public int getClientHead() throws Exception { // get last change in server // This will returned the also shelved CLs String latestChange = connection.getCounter("change"); int change = Integer.parseInt(latestChange); // build file revision spec String ws = "//" + iclient.getName() + "/..."; List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(ws); GetChangelistsOptions opts = new GetChangelistsOptions(); // Question do we only want the last submmited change ? (not a shelved // one?) opts.setType(IChangelist.Type.SUBMITTED); opts.setMaxMostRecent(1); List<IChangelistSummary> list = connection.getChangelists(files, opts); if (!list.isEmpty() && list.get(0) != null) { change = list.get(0).getId(); } else { log("P4: no revisions under " + ws + " using latest change: " + change); } return change; } /** * List of Graph Repos within the client's view * * @return A list of Graph Repos, empty list on error. */ public List<IRepo> listRepos() { List<IRepo> repos = new ArrayList<>(); try { repos = iclient.getRepos(); } catch (Exception e) { logger.fine("No repos found: " + e.getMessage()); } return repos; } /** * Show all changes within the scope of the client, between the 'from' and * 'to' change limits. * * @param fromRefs list of from revisions (change or label) * @param to To revision (change or label) * @return List of changes * @throws Exception push up stack */ public List<P4Ref> listChanges(List<P4Ref> fromRefs, P4Ref to) throws Exception { P4Ref from = getSingleChange(fromRefs); // return empty array, if from and to are equal, or Perforce will report // a change if (from.equals(to)) { return new ArrayList<P4Ref>(); } String ws = "//" + iclient.getName() + "/...@" + from + "," + to; List<P4Ref> list = listChanges(ws); if (!from.isLabel()) { list.remove(from); } return list; } private P4Ref getSingleChange(List<P4Ref> refs) { // fetch single change and ignore commits for (P4Ref ref : refs) { if (!ref.isCommit()) { return ref; } } return null; } /** * Show all changes within the scope of the client, from the 'from' change * limits. * * @param from From revision (change or label) * @return List of changes * @throws Exception push up stack */ public List<P4Ref> listChanges(P4Ref from) throws Exception { String ws = "//" + iclient.getName() + "/...@" + from + ",now"; List<P4Ref> list = listChanges(ws); if (!from.isLabel()) { list.remove(from); } return list; } /** * Show all changes within the scope of the client. * * @return List of changes * @throws Exception push up stack */ public List<P4Ref> listChanges() throws Exception { String ws = "//" + iclient.getName() + "/..."; return listChanges(ws); } private List<P4Ref> listChanges(String ws) throws Exception { List<P4Ref> list = new ArrayList<P4Ref>(); GetChangelistsOptions opts = new GetChangelistsOptions(); opts.setMaxMostRecent(getMaxChangeLimit()); List<IFileSpec> spec = FileSpecBuilder.makeFileSpecList(ws); List<IChangelistSummary> cngs = connection.getChangelists(spec, opts); if (cngs != null) { for (IChangelistSummary c : cngs) { // don't try to add null or -1 changes if (c != null && c.getId() != -1) { P4Ref rev = new P4ChangeRef(c.getId()); // don't add change entries already in the list if (!(list.contains(rev))) { list.add(rev); } } } } Collections.sort(list); Collections.reverse(list); return list; } /** * Fetches a list of changes needed to update the workspace to head. * * @param fromRefs List from revisions * @return List of changes * @throws Exception push up stack */ public List<P4Ref> listHaveChanges(List<P4Ref> fromRefs) throws Exception { P4Ref from = getSingleChange(fromRefs); if (from.getChange() > 0) { log("P4: Polling with range: " + from + ",now"); return listChanges(from); } String path = "//" + iclient.getName() + "/..."; return listHaveChanges(path); } /** * Fetches a list of changes needed to update the workspace to the specified * limit. The limit could be a Perforce change number or label. * * @param fromRefs List of from revisions * @param changeLimit To Revision * @return List of changes * @throws Exception push up stack */ public List<P4Ref> listHaveChanges(List<P4Ref> fromRefs, P4Ref changeLimit) throws Exception { P4Ref from = getSingleChange(fromRefs); if (from.getChange() > 0) { log("P4: Polling with range: " + from + "," + changeLimit); return listChanges(fromRefs, changeLimit); } String path = "//" + iclient.getName() + "/..."; String fileSpec = path + "@" + changeLimit; return listHaveChanges(fileSpec); } private List<P4Ref> listHaveChanges(String fileSpec) throws Exception { log("P4: Polling with cstat: " + fileSpec); List<P4Ref> haveChanges = new ArrayList<P4Ref>(); Map<String, Object>[] map; map = connection.execMapCmd("cstat", new String[]{fileSpec}, null); for (Map<String, Object> entry : map) { String status = (String) entry.get("status"); if (status != null) { if (status.startsWith("have")) { String value = (String) entry.get("change"); int change = Integer.parseInt(value); haveChanges.add(new P4ChangeRef(change)); } } } Collections.sort(haveChanges); Collections.reverse(haveChanges); return haveChanges; } public ClientView getClientView() { return iclient.getClientView(); } public boolean isClientValid(Workspace workspace) { if (iclient == null) { String msg; if (workspace instanceof TemplateWorkspaceImpl) { TemplateWorkspaceImpl template = ((TemplateWorkspaceImpl) workspace); String name = template.getTemplateName(); msg = "P4: Template workspace not found: " + name; } else { String name = workspace.getFullName(); msg = "P4: Unable to use workspace: " + name; } logger.severe(msg); if (listener != null) { log(msg); } return false; } return true; } public IClient getClient() { return iclient; } public boolean hasFile(String depotPath) throws Exception { List<IFileSpec> files = FileSpecBuilder.makeFileSpecList(depotPath); GetDepotFilesOptions opts = new GetDepotFilesOptions(); List<IFileSpec> specs = connection.getDepotFiles(files, opts); return validate.checkCatch(specs, ""); } }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#148 | 29870 | Sandeep Kumar |
Merge pull request #194 from kjs104901/bugfix JENKINS-72098: Cant sync global libraries on Windows |
||
#147 | 29869 | Sandeep Kumar |
Move test from PerforceSCMSourceTest. Ignore NoSuchFileException As newer release of commons.io will use this exception when file is not found. |
||
#146 | 29848 | Sandeep Kumar |
Merge pull request #195 from jenkinsci/StreamAtChange Support StreamAtChange for ManualWorkspace. |
||
#145 | 29440 | Paul Allen |
Merge pull request #172 from skumar7322/ReconcileWithT Add '-t' flag to reconcile command. JENKINS-59922 This flag was suppoerted by p4d after 2019.1 release(#1726148 (Job #51996) ), so set it in p4jenkins for this or later versions. |
||
#144 | 28805 | Paul Allen |
Merge pull request #145 from joel-f-brown/master Allow option to not detect changelists for build, JENKINS-68516 |
||
#143 | 28508 | Paul Allen |
Cache P4TICKET in session. Session cache now keyed on Credential ID and stores: User, Expiry time and Ticket. |
||
#142 | 28464 | Paul Allen |
Formalise user session cache. Added Jelly options to Global Configuration. User cache is disabled by default. Ticket Minimum life is set to 30 minutes. JENKINS-60141 |
||
#141 | 28460 | Paul Allen |
Session cache to track ticket time. Initial design to reduce 'p4 login -s' calls with user session cache to track ticket time. |
||
#140 | 28021 | Paul Allen |
Alternative cleanup option. In some situations (Windows) we need to individually set the file to be writable and then delete. JENKINS-66089 |
||
#139 | 27924 | Paul Allen |
Merge pull request #128 from bsmoyers/poll-at-counter allow polling scm when populate options in job are set to pin to counter name in addition to changelist or label |
||
#138 | 27175 | Paul Allen |
Prevent dormant multi branch project continiously building. JENKINS-63494 and JENKINS-64193 When scanning a multi branch project if the history of changes is outside of the `Head change query limit` then no changes are found. The old behaviour used the latest change and triggered a build; continuously triggering dormant projects with each new submit. The new behaviour uses the last build change if no changes are found (preventing a triggered build) or if there was no previous builds (in the case of new projects) then the latest changes is used to triggering a build. |
||
#137 | 26603 | Paul Allen |
Merge pull request #119 from joel-f-brown/master Add virtual stream detection for MultiBranch pipeline. JENKINS-62699 |
||
#136 | 26601 | Paul Allen | Code cleanup for getHead and getLowestHead. | ||
#135 | 26122 | Paul Allen |
Added additional login to resolve parallel connection issue. Includes test case for parallel stages. JENKINS-52806 |
||
#134 | 26099 | Paul Allen |
Prevent infinite polling when last build and current are pinned to a label. Added test to verify support for static lables. JENKINS-58149 |
||
#133 | 26094 | Paul Allen |
Merge pull request #105 from jenkinsci/publishFilter Publish path filter for reconcile JENKINS-56501 |
||
#132 | 25826 | Paul Allen |
Global option to limit change query. By default 'Head change query limit' is set to 0 (no limit) and the plugin will query all changes when looking within a workspace view. For large servers set the value to a range based on commit rate and job queue duration. e.g. 200 global commits/hour and jobs are queued for no longer than 10 hours, so 2000 change query limit (200*10). JENKINS-58116 JENKINS-57870 |
||
#131 | 25760 | Paul Allen | FindBugs: Removed unused processParameters | ||
#130 | 25759 | matthew_smeeth |
Use iclient.reconcileFiles over old execMap method. Added extra logging to reconcile |
||
#129 | 25668 | Paul Allen |
Merge pull request #94 from lystor/bug-56414 JENKINS-56414: Support of Reconcile with Modtime in P4Publish |
||
#128 | 25651 | Paul Allen |
Merge pull request #100 from stuartrowe/JENKINS-57534 Limit buildChange to the next highest change number within the client's view |
||
#127 | 25437 | Paul Allen |
Skip build if polling finds no/null change. JENKINS-55075 |
||
#126 | 25115 | Paul Allen |
Merge pull request #91 from p4charu/jenkinsci-master Fixed JENKINS-39107 and JENKINS-55826 |
||
#125 | 25056 | Paul Allen |
Check for duplicate syncIDs and test polling after failed build. JENKINS-55075 |
||
#124 | 25018 | Paul Allen |
Merge pull request #88 from p4charu/jenkinsci-master. Fixed JENKINS-55430 and better credential error reporting |
||
#123 | 24987 | Paul Allen |
Diff client object before save to reduce unnecessary spec changes. JENKINS-52590 |
||
#122 | 24969 | Paul Allen |
Tidy up imports. TODO: throw AbortException from clientLogin(). |
||
#121 | 24968 | Paul Allen |
Merge pull request #85 from DrakkenWulf/master Fix ability to do Replay in Pipelines. JENKINS-55107 |
||
#120 | 24944 | Paul Allen |
Merge pull request #86 from p4charu/jenkinsci-master JENKINS-51632 and minor code cleanup |
||
#119 | 24940 | Paul Allen |
Skip sync when PreviewSync is used with quiet option `p4 sync -q -n`. JENKINS-54153 |
||
#118 | 24842 | Paul Allen | Add logging for Client View field on Workspace update. | ||
#117 | 24826 | Paul Allen | Update Perforce Server Unicode check. | ||
#116 | 24805 | Paul Allen |
Tidy up connection methods. Cache TagAction calls to getTicket() when adding P4_TICKET to the Environment. JENKINS-54222 |
||
#115 | 24492 | Paul Allen |
Initial work for MultiBranch Event trigger. JENKINS-52066 (Triggered Events and not Polling per change) |
||
#114 | 24487 | Paul Allen | Perforce Connection Refactor. | ||
#113 | 24359 | Paul Allen | Support ServerID on client for Edge type serves. | ||
#112 | 23821 | Paul Allen |
Raise Submit errors in Publish step. JENKINS-49825 |
||
#111 | 23620 | Paul Allen |
Merge pull request #69 from jfperusse-bhvr/master Fix issue unshelving move/delete of locked files |
||
#110 | 23589 | Paul Allen |
Remove forced clobber and set default clobber option to true. JENKINS-49041 |
||
#109 | 23327 | Paul Allen | Use client less connection when scanning MultiBranch. | ||
#108 | 23324 | Paul Allen | Use short lived temporary client when scanning MultiBranch. | ||
#107 | 23258 | Paul Allen | Clean up a few minor warnings. | ||
#106 | 23239 | Paul Allen |
Add configuration parameter for choosing whether to allow auto-submission of config changes to perforce. This can avoid creating floods of changelists at the cost of requiring a manual changelist submit. A scheduled task may be alleviate the need to manually submit the updated changes while still reducing the number of changelists created. @Tsaukpaetra https://swarm.workshop.perforce.com/reviews/23186 |
||
#105 | 22999 | Paul Allen |
Fix sync options on FlushOnly and CheckOnly. JENKINS-46352 |
||
#104 | 22914 | Paul Allen | (minor) Update Unshelve step from int to long. | ||
#103 | 22893 | Paul Allen |
Merge pull request #56 from jfperusse-bhvr/fix-unshelve-delete-exclusive. Manually delete exclusively locked files for reviews. JENKINS-47141 |
||
#102 | 22861 | Paul Allen | Use long for change number. | ||
#101 | 22808 | Paul Allen |
Create directories as required when printing locked file. JENKINS-37868 |
||
#100 | 22807 | Paul Allen |
Merge pull request #52 from jfperusse-bhvr/fix-unshelve-exclusive
Fix unshelving of exclusively checked out files. JENKINS-46599 |
||
#99 | 22683 | Paul Allen | Implement a FlushOnly sync for "Populate Options" (abbec) | ||
#98 | 22537 | Paul Allen |
Use Decoded URL path for local delete. Local client syntax uses URL @ encoding, but the local filesystem needs the decoded path. JENKINS-45339 |
||
#97 | 22532 | Paul Allen |
Return empty list on error for listRepos() JENKINS-45420 |
||
#96 | 22514 | Paul Allen | Raise Errors from StreamingCallbacks. | ||
#95 | 22396 | Paul Allen |
MultiBranch support for Perforce Swarm Reviews. A work in progress - adds Swarm API support to find branches and reviews from a Swarm project. #review-22354 |
||
#94 | 22349 | Paul Allen |
Parallel Sync using P4Java. Update to P4Java 2017.2; using RPC level parallel sync and threaded within the JVM. Native parallel sync no longer requires a 'p4d' binary. |
||
#93 | 22315 | Paul Allen |
Pending change cleanup for unshelve. Unshelve would leave files open for add/edit/delete, the 'tidy' option will 'revert -k' the files leaving the content in the workspace, but removing the 'have' list data. Unshelve can now specify a Credential and Workspace, important for situations where more than one sync occurs in a pipeline script. JENKINS-43430 |
||
#92 | 22257 | Paul Allen |
Streaming Asynchronous Callback for Submit. JENKINS-44427 |
||
#91 | 22231 | Paul Allen |
Fixed 'No such file(s)' on unshelve check. JENKINS-43430 |
||
#90 | 22128 | Paul Allen |
Helix Graph support for Jenkins. JENKINS-40354 |
||
#89 | 21923 | Paul Allen |
Added old ClientHelper constructor and mark Deprecated. ClientHelper constructor breaking change introduced in 1.3.6 #26 |
||
#88 | 21794 | Paul Allen |
Merge pull request #39 from Dohbedoh/JENKINS-34825 Jenkins 34825 |
||
#87 | 21758 | Paul Allen |
Merge pull request #38 from jenkinsci/dev scm-api 2.0.2 updates |
||
#86 | 21163 | Paul Allen | Add P4HOST to parallel sync. | ||
#85 | 21077 | Paul Allen |
Switch polling back to build.xml Change in Polling behaviour; use last build details in the build.xml file. It gathers 'last' change details on the master thread and not a slave thread. JENKINS-37462 - updated and improved test case |
||
#84 | 20986 | Paul Allen |
Ground work for MultiJob support. Pushed validate up to ConnectionHelper and added new Perforce functions. |
||
#83 | 20954 | Paul Allen |
Add purge -S<n> option for the Submit stage of Publish. If a value is provided it will set the purge option on all files found as part of the reconcile (ADD/EDIT). It will not remove the purge option once set. JENKINS-36112 |
||
#82 | 20897 | Paul Allen |
Report all P4Java errors on validate Ignore 'no such file' warning on populate, caused by empty file list on sync -k. |
||
#81 | 20873 | Paul Allen | Support Pin to a change in a counter. | ||
#80 | 20820 | Paul Allen | Find bug fix for null checking on Jenkins.getInstance() | ||
#79 | 20818 | Paul Allen |
Global limits for changes and files. Max number of change lists to show for a build (default 10) Max number of files to list in a change (default 50) |
||
#78 | 20788 | Paul Allen | Fix for findbugs | ||
#77 | 20787 | Paul Allen |
Polling Fix for use with quiet period. Switched all uses of change/label to P4Revision object and implemented Comparable. The changes to build are now calculated at build time (after the quiet period) not during the polling phase. JENKINS-36883 #review-20780 |
||
#76 | 20620 | Paul Allen |
Change 'now' to the latest change at the point of sync. JENKINS-36883 |
||
#75 | 20208 | Paul Allen |
Parallel sync for SSL connections. JENKINS-37476 |
||
#74 | 20179 | Paul Allen | Javadoc fixes for java 8 builds. | ||
#73 | 20015 | Paul Allen |
Force sort order for 'p4 cstat' and 'p4 changes'. Resolve issue for Polling. JENKINS-37124 |
||
#72 | 19918 | Paul Allen |
Use StreamingCallback for Reconcile. Reduce memory for servers 14.1 and above. |
||
#71 | 19790 | Paul Allen |
Add a propagate delete option to Publish. Originally only add/edits were permitted to prevent users deleting files with incorrect View maps, but this option allows deletes if enabled. JENKINS-27885 |
||
#70 | 19593 | Paul Allen | More minor fixes to satisfy FindBugs Analysis. | ||
#69 | 19581 | Paul Allen | Minor fixes to satisfy FindBugs Analysis. | ||
#68 | 19431 | Paul Allen |
Raise failed submit as Exception and mark build as failed. JENKINS-34770 |
||
#67 | 19374 | Paul Allen |
Poll on Master (without workspace) New Polling Filter ‘Poll on Master using Last Build’. Only polls on the master and fetches it change from the last Build. Note that 0 is assumed if no previous build is found. JENKINS-32814 |
||
#66 | 19365 | Paul Allen |
Track syncs with a 'Change' field in the Workspace Description. Polling used ‘p4 cstat’ to determine the last sync’ed change in a workspace. However, if the have list is empty (such as in the Force Clean Populate mode) it would appear as if no changes have been synced. JENKINS-32518 JENKINS-32523 |
||
#65 | 19324 | Paul Allen |
Added Parallel Sync. An Advanced Populate option. Must specify the PATH to the ‘p4’ executable as parallel sync is not supported by p4java. JENKINS-29228 |
||
#64 | 19090 | Paul Allen |
Revert files before deleting a Workspace. JENKINS-24070 Jenkins Log (successful delete): Apr 21, 2016 10:08:20 PM org.jenkinsci.plugins.p4.PerforceScm processWorkspaceBeforeDeletion INFO: processWorkspaceBeforeDeletion Apr 21, 2016 10:08:20 PM org.jenkinsci.plugins.p4.tasks.RemoveClientTask invoke INFO: Task: remove client. Apr 21, 2016 10:08:20 PM org.jenkinsci.plugins.p4.tasks.RemoveClientTask invoke INFO: P4: unsyncing client: jenkins-master-open Apr 21, 2016 10:08:20 PM org.jenkinsci.plugins.p4.tasks.RemoveClientTask invoke INFO: P4: remove client: jenkins-master-open Apr 21, 2016 10:08:20 PM org.jenkinsci.plugins.p4.PerforceScm processWorkspaceBeforeDeletion INFO: clean: true |
||
#63 | 18889 | Paul Allen |
Asynchronous sync operation. To avoid memory issues with very large sync operations. #review-18773 |
||
#62 | 18723 | Paul Allen |
Add a revert option to SyncOnly Populate mode. Remove the revert -k option from unshelve task and leave it to the Populate cleanup step. |
||
#61 | 18616 | mjoubert |
@pallen Unable to get current change: com.perforce.p4java.exception.RequestException: Can't use a pending changelist number for this command. FATAL: null java.lang.NullPointerException Fix for critial bug |
||
#60 | 18440 | Paul Allen |
Prevent Delete actions on the Publish step. Limit reconcile to -ae (add/edit) JENKINS-28448 |
||
#59 | 16836 | Paul Allen |
Versioning for Jenkins configuration pages. Add credential, depot path location and client workspace name, everything else should be automatic. JENKINS-25145 |
||
#58 | 16815 | Paul Allen |
Enable early binding for CHARSET Expose CHARSET to AbstractTask to allow the charset to be set at the point the client workspace is set as current in ClientHelper. |
||
#57 | 16722 | Paul Allen |
Enable Unicode Support for workspace. Originally hardcoded to UTF8 for *nix systems and web browsers. |
||
#56 | 16530 | Paul Allen | Skip unshelve task if change is 0 or less. | ||
#55 | 16529 | Paul Allen |
DSL for Unshelve task. DSL example: p4unshelve resolve: 'at', shelf: '10831' |
||
#54 | 16514 | Paul Allen |
Unshelve and resolve build step. Implements a classic Jenkins Build step (i.e. not Workflow). Must provide a shelf change number and resolve options. The shelf change number supports variable expansion ${VAR}. |
||
#53 | 16404 | Paul Allen |
Fix flags used during have list cleanup. Flags were inherited from Populate options and being used in the cleanup stage, when they should only be applied to the populate stage. |
||
#52 | 16397 | Paul Allen |
Fix for Force Clean with sync -p Fixed sync to use ...#0 (#none) to remove the have list as part of the cleanup. |
||
#51 | 16389 | Paul Allen |
Fix for Force Clean sync flags. You cannot use `-f` and `-p` together. |
||
#50 | 16325 | Paul Allen |
Enable 'Populate have list' to function in all modes. Previously if 'Populate have list' was unselected in Force Clean mode it would have no effect. @ksong |
||
#49 | 16272 | Paul Allen |
Filter reported changelists. Remove -1 and duplicate changes. |
||
#48 | 16086 | Paul Allen | Fix to enable -m on reconcile. | ||
#47 | 15866 | Paul Allen |
Abort if unshelve fails. JENKINS-30525 |
||
#46 | 15750 | Paul Allen |
Use a P4Revision object and not int/String as Object. In sure that there is no ambiguity with the revision specifier. Should fix change summary when using the Workflow plugin. JENKINS-30425 |
||
#45 | 15656 | Paul Allen |
Updated credentials to extend BaseStandardCredentials. Allows users to set the ID at creation. JENKINS-29702 |
||
#44 | 15489 | Paul Allen | (tidyup) formating. | ||
#43 | 15470 | mjoubert |
@pallen Fix typo |
||
#42 | 14173 | Paul Allen |
Support P4D 15.1 'reconcile -m'. Client workspace MODTIME option is no longer required with -m. |
||
#41 | 13700 | Paul Allen | minor tidy up. | ||
#40 | 13681 | Paul Allen |
Abstracted Expand class from Workspace. Added support for Label variable expansion in the name and description. |
||
#39 | 13676 | Paul Allen |
Added support for `p4 clean`. If the Perforce server is 14.1 or greater then the `-w` flag is used (p4 clean), otherwise the original auto clean up code. |
||
#38 | 13604 | Paul Allen | Improved error handling and fixed test case issue. | ||
#37 | 13603 | Paul Allen | Improved Error for Publish step when connection is down. | ||
#36 | 12977 | Paul Allen | Quiet option for Populate. | ||
#35 | 12976 | Paul Allen | Improved logging to include 'actual' Perforce command. | ||
#34 | 12953 | Paul Allen |
Update logging to support expand/collapse divs. - Additional Publish logging |
||
#33 | 12932 | Paul Allen |
New Populate CheckOnly Implementation. No tidy or sync steps only a have list update. Intended for use with polling and triggering other Jenkins jobs. #review-12933 @mjoubert |
||
#32 | 12888 | Paul Allen |
Merged Pull#11 @rpocase [JENKINS-28213] Force syncing a workspace that does not exist on the filesystem causes a FileNotFoundException. - Tidy up imports and white space |
||
#31 | 12310 | Paul Allen |
@mjoubert performance/logging Improved performance on validateFileSpecs() Reduce delete logging |
||
#30 | 12298 | Paul Allen | Implement the tidyWorkspace() method with a single reconcile. | ||
#29 | 12212 | Paul Allen |
Log reverted/shelved files. @mjoubert This changes add the files that got reverted and unshelved to the console log. This information is crucial for debugging purposes and allows steps later on to potentially change behaviour by using the console output e.g. do X when file Y is unshelved |
||
#28 | 12180 | Paul Allen |
Fix revert in unshelve step to use `-k` and not `-n`. https://github.com/jenkinsci/p4-plugin/pull/10 |
||
#27 | 11627 | Paul Allen |
Use localSyntax support in p4java 14.1 #review-11590 |
||
#26 | 11561 | Paul Allen |
Avoid writing `null` client root. Added protection to prevent a null client root value being written during polling. The client root is not set during polling as the workspace could be polled from another node. Change also prevents updates to Static workspaces. JENKINS-26589 |
||
#25 | 11452 | Paul Allen |
Push out mjoubert fixes for Force sync. @mjoubert |
||
#24 | 11368 | Paul Allen |
Publish - pre/post pending file cleanup. Pre-cleanup for publish and post-cleanup for shelving files. |
||
#23 | 11334 | Paul Allen |
Remote slave support for Publish Perforce Publish commands need to be executed from the remote slave. - Includes refactoring into task package |
||
#22 | 11331 | Paul Allen |
Publish logging and help pages Added logging details for shelved/submitted files. Added help dialogs for Publish configuration. Added -k option for shelve revert. |
||
#21 | 11258 | Paul Allen |
Minor fix for Publish Asset Set client root for new workspaces. Flush client to populate have list (sync -k) |
||
#20 | 11231 | Paul Allen |
Publish assets to Perforce. New feature to allow Jenkins to shelve or submit the build result to Perforce. Configured as a Post Build Step, the Perforce credential and workspace are required as the asset could be published to a different Perforce instance. |
||
#19 | 11166 | Paul Allen |
Behaviour: remove sync to head for unshelve step Current workflow for review is to: sync the change, or label provided then overrides this with a sync to head, before unshelving the content — forcing the resolve step. Removing the second sync allows users to determine the resolve point, although they might not be able to submit the shelf without a second resolve. Old behaviour can be restored by fixing the review ‘change’ parameter to ‘now’ a Perforce builtin revision. |
||
#18 | 11165 | Paul Allen |
Resolve unshelve issue from edge server. @mjoubert noticed the issue and that removing the path side steps the problem. Looks like a separate issue for P4JAVA. SCM Task: unshelve review: 12345 ... sync c:\jenkins\workspace\proj/... ... unshelve -f -s 12345 c:\jenkins\workspace\proj/... P4JAVA: Path 'c:\jenkins\cryptos\workspace\proj/...' is not under client's root 'c:\jenkins\workspace\proj'. ERROR: Unable to update workspace: hudson.AbortException: P4JAVA: Error(s) |
||
#17 | 10939 | Paul Allen |
Tidy up changes. - Moved duration timings to ClientHelper and measure each task individually. - White space/formatting on P4ChangeEntry. - Temporarily backed-out p4-plugin.properties file (could damage builds - need to understand requirements) |
||
#16 | 10914 | Paul Allen |
SCM Polling Use client path syntax for listing have changes as using a windows path on a linux master will fail. Exit early from polling if a change is found. |
||
#15 | 10853 | Paul Allen |
Latest change for parent builds. Uses the latest change for parent builds that have no real changes within their workspace build. |
||
#14 | 10841 | Paul Allen |
Support for 2014.1 reconcile by MODTIME Feature allows a user check the option ‘Sync with MODTIME for consistency check’ under the ‘Populate’ option to enable reconcile to check files based on MODTIME. Includes test case for MODTIME and minor fix for fetching a Label when there is no previous change. JENKINS-25341 |
||
#13 | 10801 | Paul Allen |
Improvements to SCM Polling Moved change list calculation into a new class CheckoutChanges. Should enable normal build to build every thing and poll to follow polling behaviour: limit based on pin and per-change if set. JENKINS-25300 |
||
#12 | 10525 | Paul Allen |
Update Changelist calculation. Changed the SCM Polling and change listing reporting to calculate the last change based on the highest reported 'have' entry from 'p4 cstat' and then list using 'p4 changes'. JENKINS-24978 JENKINS-24607 |
||
#11 | 10013 | Paul Allen |
Added Client support to sync check https://issues.jenkins-ci.org/browse/JENKINS-24055 |
||
#10 | 9987 | Paul Allen | Better logging for slave connections. | ||
#9 | 9984 | Paul Allen |
Set P4_CHANGELIST to change for Automatic Labels. P4_CHANGELIST is set to a change number or label if defined in Populate as pinned or a build parameter. |
||
#8 | 9980 | Paul Allen |
Changed error reporting to use AbortException. - Use direct access of scm attributes, not via build.getProject().getScm() - Improved logging around labels/changes. |
||
#7 | 9851 | Paul Allen | Merging using p4-jenkins | ||
#6 | 9803 | Paul Allen | Merging using p4-jenkins | ||
#5 | 9787 | Paul Allen | Merging using p4-jenkins | ||
#4 | 9769 | Paul Allen | Copying using p4-jenkins | ||
#3 | 9738 | Paul Allen | Merging using p4-jenkins | ||
#2 | 9722 | Paul Allen | Copying using p4-jenkins | ||
#1 | 9690 | Paul Allen |
[Branching using p4-jenkins] Release 1.0.1 |
||
//guest/paul_allen/dev/p4-jenkins/p4-client/src/main/java/org/jenkinsci/plugins/p4/client/ClientHelper.java | |||||
#1 | 9672 | Paul Allen | Refactor name from 'p4_client' to 'p4'. | ||
//guest/paul_allen/dev/p4-jenkins/p4-client/src/main/java/org/jenkinsci/plugins/p4_client/client/ClientHelper.java | |||||
#8 | 9429 | Paul Allen |
Fix SCM Polling bug. Client workspace was not set (unless by an earlier build). - Move listChanges method to ClientHelper. |
||
#7 | 9133 | Paul Allen |
Added Workspace mode for versioned Client Specs. Client Spec can be specified by depot path and used to configure workspace for build. - Includes fix for removing header when using p4 print. |
||
#6 | 9125 | Paul Allen | Added cleanup for Force Clean/Sync populate mode. | ||
#5 | 9115 | Paul Allen |
Initial implementation of workspace Cleanup and Sync options. - Includes 3 modes: Automatic Clean/Sync, Force Clean/Sync, Sync Only Automatic Clean/Sync Uses reconcile to clean up workspace and sync changes. Force Clean/Sync Force sync of all files (does not remove files yet...) Sync Only Normal sync with no cleanup TODO: - remove of files in Force Clean/Sync mode - Inline help - Update docs - Add unit/functional tests |
||
#4 | 9069 | Paul Allen |
Adding initial support for tagging Jenkins builds as Perforce Automatic Labels. Only implements TagAction (manual labels); TagNotifier and test cases TODO. |
||
#3 | 9055 | Paul Allen |
Label support. Build at a label using the pram 'label'. This includes adding the label to the ChangeEntry, building the change reports and Browser links to Swarm. (TPI-102) |
||
#2 | 9039 | Paul Allen |
Bug Fix: Array index error. The method getClientHead() will return a change of 0 when trying to sync a stream with no changes. |
||
#1 | 8940 | Paul Allen |
Major refactor for the ConnectionHelper class to simplify serialisation. Fixed remote Jenkins JNLP slave connection issue. ClientHelper now extends ConnectionHelper and takes on all methods that require a client workspace. P4StandardCredentials is sent to the remote node instead of Credentials ID due to an issue accessing the Credentials store over a remote connection. For simplicity Client ID (workspace name) is serialised instead of the Workspace object. |