Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 28 additions & 5 deletions src/main/java/hudson/scm/SubversionSCM.java
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,7 @@ public class SubversionSCM extends SCM {
private boolean ignoreDirPropChanges;
private boolean filterChangelog;
private boolean quietOperation;
private boolean cleanupOnLockedWorkspace;

/**
* A cache of the svn:externals (keyed by project).
Expand Down Expand Up @@ -369,16 +370,29 @@ public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceU
String excludedRevprop, String excludedCommitMessages,
String includedRegions, boolean ignoreDirPropChanges, boolean filterChangelog,
List<AdditionalCredentials> additionalCredentials) {
this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages,
this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages,
includedRegions, ignoreDirPropChanges, filterChangelog, additionalCredentials, false);
}

@DataBoundConstructor
/**
* @deprecated by cleanupOnLockedWorkspace
*/
public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceUpdater,
SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers,
String excludedRevprop, String excludedCommitMessages,
String includedRegions, boolean ignoreDirPropChanges, boolean filterChangelog,
List<AdditionalCredentials> additionalCredentials, boolean quietOperation) {
this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages,
includedRegions, ignoreDirPropChanges, filterChangelog, additionalCredentials, quietOperation, false);
}

@DataBoundConstructor
public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceUpdater,
SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers,
String excludedRevprop, String excludedCommitMessages,
String includedRegions, boolean ignoreDirPropChanges, boolean filterChangelog,
List<AdditionalCredentials> additionalCredentials, boolean quietOperation,
boolean cleanupOnLockedWorkspace) {
for (Iterator<ModuleLocation> itr = locations.iterator(); itr.hasNext(); ) {
ModuleLocation ml = itr.next();
String remote = Util.fixEmptyAndTrim(ml.remote);
Expand All @@ -403,6 +417,7 @@ public SubversionSCM(List<ModuleLocation> locations, WorkspaceUpdater workspaceU
this.ignoreDirPropChanges = ignoreDirPropChanges;
this.filterChangelog = filterChangelog;
this.quietOperation = quietOperation;
this.cleanupOnLockedWorkspace = cleanupOnLockedWorkspace;
}

/**
Expand Down Expand Up @@ -714,6 +729,11 @@ public boolean isQuietOperation() {
return quietOperation;
}

@Exported
public boolean isCleanupOnLockedWorkspace() {
return cleanupOnLockedWorkspace;
}

/**
* Convenience method solely for testing.
*/
Expand Down Expand Up @@ -965,7 +985,8 @@ private Map<String, List<External>> checkout(Run build, FilePath workspace, Task
Set<String> unauthenticatedRealms = new LinkedHashSet<>();
for (ModuleLocation location : getLocations(env, build)) {
CheckOutTask checkOutTask =
new CheckOutTask(new CheckOutUpdateTask(build, this, location, build.getTimestamp().getTime(), listener, env, quietOperation));
new CheckOutTask(new CheckOutUpdateTask(build, this, location, build.getTimestamp().getTime(),
listener, env, quietOperation, cleanupOnLockedWorkspace));
List<External> externals = new ArrayList<>(workspace.act(checkOutTask));
// save location <---> externals maps
externalsMap.put(location.remote, externals);
Expand Down Expand Up @@ -1046,15 +1067,17 @@ private static class CheckOutUpdateTask extends UpdateTask {
private final boolean storeAuthToDisk = descriptor().isStoreAuthToDisk();
private final int workspaceFormat = descriptor().getWorkspaceFormat();

CheckOutUpdateTask(Run<?, ?> build, SubversionSCM parent, ModuleLocation location, Date timestamp,
TaskListener listener, EnvVars env, boolean quietOperation) {
CheckOutUpdateTask(Run<?, ?> build, SubversionSCM parent, ModuleLocation location, Date timestamp,
TaskListener listener, EnvVars env, boolean quietOperation,
boolean cleanupOnLockedWorkspace) {
this.authProvider = parent.createAuthenticationProvider(build.getParent(), location, listener);
this.timestamp = timestamp;
this.listener = listener;
this.location = location;
this.revisions = build.getAction(RevisionParameterAction.class);
this.task = parent.getWorkspaceUpdater().createTask(workspaceFormat);
this.quietOperation = quietOperation;
this.cleanupOnLockedWorkspace = cleanupOnLockedWorkspace;
}

List<External> run(File ws) throws IOException {
Expand Down
42 changes: 42 additions & 0 deletions src/main/java/hudson/scm/subversion/UpdateUpdater.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNUpdateClient;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import org.tmatesoft.svn.core.wc2.SvnCleanup;
import org.tmatesoft.svn.core.wc2.SvnGetStatus;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import org.tmatesoft.svn.core.wc2.SvnStatus;
import org.tmatesoft.svn.core.wc2.SvnTarget;

import java.io.File;
import java.io.IOException;
Expand Down Expand Up @@ -155,6 +160,9 @@ public List<External> perform() throws IOException, InterruptedException {
fmt.format(r.getDate()) : r.toString();

svnuc.setIgnoreExternals(location.isIgnoreExternalsOption());
if (cleanupOnLockedWorkspace) {
cleanupWorkspaceIfLocked(local);
}
preUpdate(location, local);
SVNDepth svnDepth = location.getSvnDepthForUpdate();

Expand Down Expand Up @@ -186,6 +194,9 @@ public List<External> perform() throws IOException, InterruptedException {
do {
SVNErrorCode errorCode = cause.getErrorMessage().getErrorCode();
if (errorCode == SVNErrorCode.WC_LOCKED) {
if (cleanupOnLockedWorkspace) {
throw new IOException(new UpdaterException("failed to perform svn cleanup, workspace is still locked", e));
}
// work space locked. try fresh check out
listener.getLogger().println("Workspace appear to be locked, so getting a fresh workspace");
return delegateTo(new CheckoutUpdater(), workspaceFormat);
Expand Down Expand Up @@ -246,6 +257,37 @@ private SVNException getNestedSVNException(Throwable e) {
protected void preUpdate(ModuleLocation module, File local) throws SVNException, IOException {
// noop by default
}

private void cleanupWorkspaceIfLocked(File local) throws IOException, SVNException {
if (!isWorkspaceLocked(local)) {
return;
}
listener.getLogger().println("Workspace appears to be locked, attempting to run 'svn cleanup'...");
SvnOperationFactory operationFactory = new SvnOperationFactory();
try {
SvnCleanup svnCleanup = operationFactory.createCleanup();
svnCleanup.setSingleTarget(SvnTarget.fromFile(local.getCanonicalFile()));
svnCleanup.setBreakLocks(true);
svnCleanup.run();
} finally {
operationFactory.dispose();
}
listener.getLogger().println("Cleanup completed.");
}

private boolean isWorkspaceLocked(File local) throws IOException, SVNException {
SvnOperationFactory operationFactory = new SvnOperationFactory();
try {
SvnGetStatus statusOp = operationFactory.createGetStatus();
statusOp.setSingleTarget(SvnTarget.fromFile(local.getCanonicalFile()));
statusOp.setDepth(SVNDepth.EMPTY); // avoid recursion
statusOp.setRemote(false);
SvnStatus status = statusOp.run();
return status != null && status.isWcLocked();
} finally {
operationFactory.dispose();
}
}
}

@Extension(ordinal=100) // this is the default, so given a higher ordinal
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/hudson/scm/subversion/WorkspaceUpdater.java
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ public static abstract class UpdateTask implements SerializableOnlyOverRemoting
*/
public boolean quietOperation;

public boolean cleanupOnLockedWorkspace;

/**
* If the build parameter is specified with specific version numbers, this field captures that. Can be null.
*/
Expand All @@ -164,6 +166,7 @@ protected List<External> delegateTo(UpdateTask t) throws IOException, Interrupte
t.revisions = this.revisions;
t.ws = this.ws;
t.quietOperation = this.quietOperation;
t.cleanupOnLockedWorkspace = this.cleanupOnLockedWorkspace;

return t.perform();
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/hudson/scm/SubversionSCM/config.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ THE SOFTWARE.
<f:checkbox default="true"/>
</f:entry>

<f:entry title="${%Cleanup workspace when locked}" field="cleanupOnLockedWorkspace">
<f:checkbox default="false"/>
</f:entry>

<j:set var="scm" value="${instance}"/>
<t:listScmBrowsers name="svn.browser" />
<f:advanced>
Expand Down