Skip to content

Commit

Permalink
[FIXED JENKINS-7461] When adding a new module location to an existing…
Browse files Browse the repository at this point in the history
… one,

Jenkins re-checks out all module locations.
  • Loading branch information
sogabe committed Oct 30, 2011
1 parent d13fd0c commit f406058
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 77 deletions.
23 changes: 14 additions & 9 deletions src/main/java/hudson/scm/SubversionSCM.java
Expand Up @@ -729,8 +729,13 @@ private List<External> checkout(AbstractBuild build, FilePath workspace, TaskLis
return null;
}
}

List<External> externals = new ArrayList<External>();
for (ModuleLocation location : getLocations(env, build)) {
externals.addAll(workspace.act(new CheckOutTask(build, this, location, build.getTimestamp().getTime(), listener, env)));
}

return workspace.act(new CheckOutTask(build, this, build.getTimestamp().getTime(), listener, env));
return externals;
}


Expand All @@ -740,15 +745,15 @@ private List<External> checkout(AbstractBuild build, FilePath workspace, TaskLis
private static class CheckOutTask extends UpdateTask implements FileCallable<List<External>> {
private final UpdateTask task;

public CheckOutTask(AbstractBuild<?, ?> build, SubversionSCM parent, Date timestamp, TaskListener listener, EnvVars env) {
public CheckOutTask(AbstractBuild<?, ?> build, SubversionSCM parent, ModuleLocation location, Date timestamp, TaskListener listener, EnvVars env) {
this.authProvider = parent.getDescriptor().createAuthenticationProvider(build.getParent());
this.timestamp = timestamp;
this.listener = listener;
this.locations = parent.getLocations(env, build);
this.location = location;
this.revisions = build.getAction(RevisionParameterAction.class);
this.task = parent.getWorkspaceUpdater().createTask();
}

public List<External> invoke(File ws, VirtualChannel channel) throws IOException {
manager = createSvnClientManager(authProvider);
this.ws = ws;
Expand Down Expand Up @@ -777,11 +782,11 @@ public List<External> perform() throws IOException, InterruptedException {

private void checkClockOutOfSync() {
try {
for (ModuleLocation l : locations) {
SVNDirEntry dir = manager.createRepository(l.getSVNURL(),true).info("/",-1);
if(dir!=null) {// I don't think this can ever be null, but be defensive
if(dir.getDate()!=null && dir.getDate().after(new Date())) // see http://www.nabble.com/NullPointerException-in-SVN-Checkout-Update-td21609781.html that reported this being null.
listener.getLogger().println(Messages.SubversionSCM_ClockOutOfSync());
SVNDirEntry dir = manager.createRepository(location.getSVNURL(), true).info("/", -1);
if (dir != null) {// I don't think this can ever be null, but be defensive
if (dir.getDate() != null && dir.getDate().after(new Date())) // see http://www.nabble.com/NullPointerException-in-SVN-Checkout-Update-td21609781.html that reported this being null.
{
listener.getLogger().println(Messages.SubversionSCM_ClockOutOfSync());
}
}
} catch (SVNAuthenticationException e) {
Expand Down
18 changes: 7 additions & 11 deletions src/main/java/hudson/scm/subversion/CheckoutUpdater.java
Expand Up @@ -29,7 +29,6 @@
import hudson.Extension;
import hudson.Util;
import hudson.scm.SubversionSCM.External;
import hudson.scm.SubversionSCM.ModuleLocation;
import hudson.util.IOException2;
import hudson.util.StreamCopyThread;
import org.kohsuke.stapler.DataBoundConstructor;
Expand Down Expand Up @@ -70,25 +69,22 @@ public List<External> perform() throws IOException, InterruptedException {
final SVNUpdateClient svnuc = manager.getUpdateClient();
final List<External> externals = new ArrayList<External>(); // store discovered externals to here

listener.getLogger().println("Cleaning workspace " + ws.getCanonicalPath());
Util.deleteContentsRecursive(ws);
listener.getLogger().println("Cleaning local Directory " + location.getLocalDir());
Util.deleteContentsRecursive(new File(ws, location.getLocalDir()));

// buffer the output by a separate thread so that the update operation
// won't be blocked by the remoting of the data
PipedOutputStream pos = new PipedOutputStream();
StreamCopyThread sct = new StreamCopyThread("svn log copier", new PipedInputStream(pos), listener.getLogger());
sct.start();

ModuleLocation location = null;
try {
for (final ModuleLocation l : locations) {
location = l;
listener.getLogger().println("Checking out " + l.remote);
listener.getLogger().println("Checking out " + location.remote);

File local = new File(ws, location.getLocalDir());
svnuc.setEventHandler(new SubversionUpdateEventHandler(new PrintStream(pos), externals, local, location.getLocalDir()));
svnuc.doCheckout(location.getSVNURL(), local.getCanonicalFile(), SVNRevision.HEAD, getRevision(location), SVNDepth.INFINITY, true);

File local = new File(ws, l.getLocalDir());
svnuc.setEventHandler(new SubversionUpdateEventHandler(new PrintStream(pos), externals, local, l.getLocalDir()));
svnuc.doCheckout(l.getSVNURL(), local.getCanonicalFile(), SVNRevision.HEAD, getRevision(l), SVNDepth.INFINITY, true);
}
} catch (SVNException e) {
e.printStackTrace(listener.error("Failed to check out " + location.remote));
return null;
Expand Down
107 changes: 52 additions & 55 deletions src/main/java/hudson/scm/subversion/UpdateUpdater.java
Expand Up @@ -73,33 +73,31 @@ public static class TaskImpl extends UpdateTask {
* Returns true if we can use "svn update" instead of "svn checkout"
*/
protected boolean isUpdatable() throws IOException {
for (ModuleLocation l : locations) {
String moduleName = l.getLocalDir();
File module = new File(ws,moduleName).getCanonicalFile(); // canonicalize to remove ".." and ".". See #474
String moduleName = location.getLocalDir();
File module = new File(ws, moduleName).getCanonicalFile(); // canonicalize to remove ".." and ".". See #474

if(!module.exists()) {
listener.getLogger().println("Checking out a fresh workspace because "+module+" doesn't exist");
return false;
}
if (!module.exists()) {
listener.getLogger().println("Checking out a fresh workspace because " + module + " doesn't exist");
return false;
}

try {
SVNInfo svnkitInfo = parseSvnInfo(module);
SvnInfo svnInfo = new SvnInfo(svnkitInfo);

String url = l.getURL();
if(!svnInfo.url.equals(url)) {
listener.getLogger().println("Checking out a fresh workspace because the workspace is not "+url);
return false;
}
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode()==SVNErrorCode.WC_NOT_DIRECTORY) {
listener.getLogger().println("Checking out a fresh workspace because there's no workspace at "+module);
} else {
listener.getLogger().println("Checking out a fresh workspace because Jenkins failed to detect the current workspace "+module);
e.printStackTrace(listener.error(e.getMessage()));
}
try {
SVNInfo svnkitInfo = parseSvnInfo(module);
SvnInfo svnInfo = new SvnInfo(svnkitInfo);

String url = location.getURL();
if (!svnInfo.url.equals(url)) {
listener.getLogger().println("Checking out a fresh workspace because the workspace is not " + url);
return false;
}
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) {
listener.getLogger().println("Checking out a fresh workspace because there's no workspace at " + module);
} else {
listener.getLogger().println("Checking out a fresh workspace because Jenkins failed to detect the current workspace " + module);
e.printStackTrace(listener.error(e.getMessage()));
}
return false;
}
return true;
}
Expand All @@ -117,45 +115,44 @@ private SVNInfo parseSvnInfo(File workspace) throws SVNException {

@Override
public List<External> perform() throws IOException, InterruptedException {
if (!isUpdatable())
if (!isUpdatable()) {
return delegateTo(new CheckoutUpdater());
}


final SVNUpdateClient svnuc = manager.getUpdateClient();
final List<External> externals = new ArrayList<External>(); // store discovered externals to here

for (final ModuleLocation l : locations) {
try {
File local = new File(ws, l.getLocalDir());
svnuc.setEventHandler(new SubversionUpdateEventHandler(listener.getLogger(), externals, local, l.getLocalDir()));

SVNRevision r = getRevision(l);

preUpdate(l, local);
listener.getLogger().println("Updating " + l.remote);
svnuc.doUpdate(local.getCanonicalFile(), r, SVNDepth.INFINITY, true, false);

} catch (final SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
// work space locked. try fresh check out
listener.getLogger().println("Workspace appear to be locked, so getting a fresh workspace");
return delegateTo(new CheckoutUpdater());
}
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_OBSTRUCTED_UPDATE) {
// HUDSON-1882. If existence of local files cause an update to fail,
// revert to fresh check out
listener.getLogger().println(e.getMessage()); // show why this happened. Sometimes this is caused by having a build artifact in the repository.
listener.getLogger().println("Updated failed due to local files. Getting a fresh workspace");
return delegateTo(new CheckoutUpdater());
}

e.printStackTrace(listener.error("Failed to update " + l.remote));
// trouble-shooting probe for #591
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
listener.getLogger().println("Polled jobs are " + Hudson.getInstance().getDescriptorByType(SCMTrigger.DescriptorImpl.class).getItemsBeingPolled());
}
return null;
try {
File local = new File(ws, location.getLocalDir());
svnuc.setEventHandler(new SubversionUpdateEventHandler(listener.getLogger(), externals, local, location.getLocalDir()));

SVNRevision r = getRevision(location);

preUpdate(location, local);
listener.getLogger().println("Updating " + location.remote);
svnuc.doUpdate(local.getCanonicalFile(), r, SVNDepth.INFINITY, true, false);

} catch (final SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
// work space locked. try fresh check out
listener.getLogger().println("Workspace appear to be locked, so getting a fresh workspace");
return delegateTo(new CheckoutUpdater());
}
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_OBSTRUCTED_UPDATE) {
// HUDSON-1882. If existence of local files cause an update to fail,
// revert to fresh check out
listener.getLogger().println(e.getMessage()); // show why this happened. Sometimes this is caused by having a build artifact in the repository.
listener.getLogger().println("Updated failed due to local files. Getting a fresh workspace");
return delegateTo(new CheckoutUpdater());
}

e.printStackTrace(listener.error("Failed to update " + location.remote));
// trouble-shooting probe for #591
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
listener.getLogger().println("Polled jobs are " + Hudson.getInstance().getDescriptorByType(SCMTrigger.DescriptorImpl.class).getItemsBeingPolled());
}
return null;
}

return externals;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/hudson/scm/subversion/WorkspaceUpdater.java
Expand Up @@ -101,7 +101,7 @@ public static abstract class UpdateTask implements Serializable {
/**
* Modules to check out. Never null.
*/
public ModuleLocation[] locations;
public ModuleLocation location;

/**
* Build workspace. Never null.
Expand Down Expand Up @@ -129,7 +129,7 @@ protected List<External> delegateTo(UpdateTask t) throws IOException, Interrupte
t.authProvider = this.authProvider;
t.timestamp = this.timestamp;
t.listener = this.listener;
t.locations = this.locations;
t.location = this.location;
t.revisions = this.revisions;
t.ws = this.ws;

Expand Down
25 changes: 25 additions & 0 deletions src/test/java/hudson/scm/SubversionSCMTest.java
Expand Up @@ -105,6 +105,7 @@
import com.gargoylesoftware.htmlunit.html.HtmlAnchor;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import hudson.model.Run;

/**
* @author Kohsuke Kawaguchi
Expand Down Expand Up @@ -499,7 +500,31 @@ public void testPollMultipleRepositories() throws Exception {
createCommit(scm,"trunk/foo");
assertTrue("another change in the repo should be detected separately", p.pollSCMChanges(listener));
}

/**
* Test that multiple repository URLs are all polled.
*/
@Bug(7461)
public void testMultipleRepositories() throws Exception {
// fetch the current workspaces
FreeStyleProject p = createFreeStyleProject();
String svnBase = "file://" + new CopyExisting(getClass().getResource("/svn-repo.zip")).allocate().toURI().toURL().getPath();
SubversionSCM scm = new SubversionSCM(
Arrays.asList(new ModuleLocation(svnBase + "trunk", "trunk")),
true, false, null, null, null, null, null);
p.setScm(scm);
Run r1 = p.scheduleBuild2(0, new Cause.UserCause()).get();
assertLogContains("Cleaning local Directory trunk", r1);

scm = new SubversionSCM(
Arrays.asList(new ModuleLocation(svnBase + "trunk", "trunk"), new ModuleLocation(svnBase + "branches", "branches")),
true, false, null, null, null, null, null);
p.setScm(scm);
Run r2 = p.scheduleBuild2(0, new Cause.UserCause()).get();
assertLogContains("Updating " + svnBase + "trunk", r2);
assertLogContains("Cleaning local Directory branches", r2);
}

public void testConfigRoundtrip() throws Exception {
FreeStyleProject p = createFreeStyleProject();

Expand Down

0 comments on commit f406058

Please sign in to comment.