From 814a5e8933e5cd03df0203b235f5a91e660438aa Mon Sep 17 00:00:00 2001 From: Philipp Homann Date: Wed, 4 Nov 2020 14:12:06 +0100 Subject: [PATCH] Remove working copy if external is broken --- pom.xml | 6 ++ .../scm/subversion/CheckoutUpdater.java | 10 ++- .../SubversionUpdateEventHandler.java | 9 ++- .../hudson/scm/subversion/UpdateUpdater.java | 5 ++ .../java/hudson/scm/SubversionSCMTest.java | 79 +++++++++++++++++++ 5 files changed, 103 insertions(+), 6 deletions(-) diff --git a/pom.xml b/pom.xml index e677ef249..dd8178ac9 100644 --- a/pom.xml +++ b/pom.xml @@ -293,6 +293,12 @@ THE SOFTWARE. 2.0.3 test + + org.xerial + sqlite-jdbc + 3.32.3.2 + test + diff --git a/src/main/java/hudson/scm/subversion/CheckoutUpdater.java b/src/main/java/hudson/scm/subversion/CheckoutUpdater.java index f480a8a21..727ef4c18 100755 --- a/src/main/java/hudson/scm/subversion/CheckoutUpdater.java +++ b/src/main/java/hudson/scm/subversion/CheckoutUpdater.java @@ -37,6 +37,7 @@ import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec; import org.tmatesoft.svn.core.wc.SVNRevision; @@ -137,8 +138,13 @@ public List perform() throws IOException, InterruptedException { throw (InterruptedException)new InterruptedException().initCause(e); } } catch (SVNException e) { - e.printStackTrace(listener.error("Failed to check out " + location.remote)); - throw new IOException("Failed to check out " + location.remote, e) ; + if (e.getErrorMessage().getErrorCode() == SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS && + !location.isCancelProcessOnExternalsFail()) { + // we should not fail if external failed + } else { + e.printStackTrace(listener.error("Failed to check out " + location.remote)); + throw new IOException("Failed to check out " + location.remote, e); + } } finally { try { pos.close(); diff --git a/src/main/java/hudson/scm/subversion/SubversionUpdateEventHandler.java b/src/main/java/hudson/scm/subversion/SubversionUpdateEventHandler.java index c48e86554..3cea647da 100755 --- a/src/main/java/hudson/scm/subversion/SubversionUpdateEventHandler.java +++ b/src/main/java/hudson/scm/subversion/SubversionUpdateEventHandler.java @@ -32,6 +32,7 @@ import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.wc.ISVNExternalsHandler; import org.tmatesoft.svn.core.wc.SVNEvent; @@ -139,10 +140,10 @@ public void handleEvent(SVNEvent event, double progress) throws SVNException { + " failed!"); } - if (cancelProcessOnExternalsFailed) { - throw new SVNException(new RemotableSVNErrorMessage(SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS, - SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS.getDescription() + ": <" + file.getName() + ">")); - } + SVNErrorMessage error = new RemotableSVNErrorMessage(SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS, + SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS.getDescription() + ": <" + file.getName() + ">"); + + throw new SVNException(error, new SVNException(event.getErrorMessage())); } super.handleEvent(event, progress); diff --git a/src/main/java/hudson/scm/subversion/UpdateUpdater.java b/src/main/java/hudson/scm/subversion/UpdateUpdater.java index 5b30261d8..5fbaece22 100755 --- a/src/main/java/hudson/scm/subversion/UpdateUpdater.java +++ b/src/main/java/hudson/scm/subversion/UpdateUpdater.java @@ -178,6 +178,11 @@ public List perform() throws IOException, InterruptedException { } catch (final SVNException e) { SVNException cause = e; do { + if (location.isCancelProcessOnExternalsFail() && + cause.getErrorMessage().getErrorCode() == SVNErrorCode.CL_ERROR_PROCESSING_EXTERNALS) { + break; + } + SVNErrorCode errorCode = cause.getErrorMessage().getErrorCode(); if (errorCode == SVNErrorCode.WC_LOCKED) { // work space locked. try fresh check out diff --git a/src/test/java/hudson/scm/SubversionSCMTest.java b/src/test/java/hudson/scm/SubversionSCMTest.java index 8f62e0f70..c53975056 100644 --- a/src/test/java/hudson/scm/SubversionSCMTest.java +++ b/src/test/java/hudson/scm/SubversionSCMTest.java @@ -66,6 +66,9 @@ import java.net.MalformedURLException; import java.net.URL; import java.nio.charset.Charset; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; import java.util.*; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -1807,4 +1810,80 @@ private void invokeTestPollingExternalsForFile() throws Exception { // should detect change assertTrue(p.poll(StreamTaskListener.fromStdout()).hasChanges()); } + + void lockWorkspace(String dbFile, String pathToLock) throws Exception { + Class.forName("org.sqlite.SQLiteJDBCLoader"); + Connection conn = DriverManager.getConnection("jdbc:sqlite:" + dbFile); + PreparedStatement stmt = conn.prepareStatement("INSERT INTO WC_LOCK VALUES(1, ?, 1)"); + stmt.setString(1, pathToLock); + stmt.executeUpdate(); + conn.close(); + } + + @Issue("JENKINS-47803") + @Test + public void lockedExternal() throws Exception { + Proc p = runSvnServe(getClass().getResource("JENKINS-777.zip")); + + try { + configureSvnWorkspaceFormat(SubversionWorkspaceSelector.WC_FORMAT_17); + + FreeStyleProject b = r.createFreeStyleProject(); + + ModuleLocation[] locations = { + new ModuleLocation("svn://localhost/jenkins-777/proja", "proja", "infinity", false) + }; + + // do initial checkout + b.setScm(new SubversionSCM(Arrays.asList(locations), new UpdateUpdater(), null, null, null, null, null, null)); + FreeStyleBuild build = r.assertBuildStatusSuccess(b.scheduleBuild2(0)); + + // Check that the external exists + FilePath ws = build.getWorkspace(); + assertTrue(ws.child("proja").child("externals").child("projb").exists()); + + // mark external as locked + String db = ws.child("proja").child("externals").child("projb").child(".svn").child("wc.db").getRemote(); + lockWorkspace(db, "externals"); + + // start second build and check if workspace is being reset (just check for the log) + build = r.assertBuildStatusSuccess(b.scheduleBuild2(0)); + r.assertLogContains("Workspace appear to be locked, so getting a fresh workspace", build); + } finally { + p.kill(); + } + } + + @Test + public void ensureCancelOnExternalsFail() throws Exception { + Proc p = runSvnServe(getClass().getResource("JENKINS-777.zip")); + + try { + configureSvnWorkspaceFormat(SubversionWorkspaceSelector.WC_FORMAT_17); + + FreeStyleProject b = r.createFreeStyleProject(); + + ModuleLocation[] locations = { + new ModuleLocation("svn://localhost/jenkins-777/proja", null, "proja", "infinity", false, true) + }; + + // do initial checkout + b.setScm(new SubversionSCM(Arrays.asList(locations), new UpdateUpdater(), null, null, null, null, null, null)); + FreeStyleBuild build = r.assertBuildStatusSuccess(b.scheduleBuild2(0)); + + // Check that the external exists + FilePath ws = build.getWorkspace(); + assertTrue(ws.child("proja").child("externals").child("projb").exists()); + + // mark external as locked + String db = ws.child("proja").child("externals").child("projb").child(".svn").child("wc.db").getRemote(); + lockWorkspace(db, "externals"); + + // start second build and check if build is canceled, caused by external failure + build = r.assertBuildStatus(Result.FAILURE, b.scheduleBuild2(0)); + r.assertLogContains("Failed processing one or more externals definitions", build); + } finally { + p.kill(); + } + } }