From 5a370254285d0c997a7664c10c0c4a5917f32585 Mon Sep 17 00:00:00 2001 From: Erik Ramfelt Date: Sat, 1 Feb 2014 14:56:13 +0100 Subject: [PATCH 1/3] JENKINS-5347 Fixed - Added use commit times on files This fixes JENKINS-5347, we have a third party tool that uses the timestamp on files to notify a cache in the tool to be updated. This fix will add an option to the Subversion configuration to use commit times. As the "use-commit-times" is a global configuration but we are only interested in it as a per-project configuration to not affect our other builds. --- pom.xml | 8 +++- src/main/java/hudson/scm/SubversionSCM.java | 20 +++++++-- .../hudson/scm/SubversionSCM/config.jelly | 3 ++ .../SubversionSCM/help-usingCommitTimes.html | 7 ++++ .../java/hudson/scm/SubversionSCMTest.java | 41 ++++++++++++++++++- 5 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/main/resources/hudson/scm/SubversionSCM/help-usingCommitTimes.html diff --git a/pom.xml b/pom.xml index 5a8f9da60..4f59f2897 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,13 @@ THE SOFTWARE. 1.5 test - + + uk.co.modular-it + hamcrest-date + 0.9.5 + test + + org.jenkins-ci.main jenkins-test-harness diff --git a/src/main/java/hudson/scm/SubversionSCM.java b/src/main/java/hudson/scm/SubversionSCM.java index 4140139e5..1e7102f14 100755 --- a/src/main/java/hudson/scm/SubversionSCM.java +++ b/src/main/java/hudson/scm/SubversionSCM.java @@ -267,6 +267,7 @@ public class SubversionSCM extends SCM implements Serializable { private boolean ignoreDirPropChanges; private boolean filterChangelog; + private boolean usingCommitTimes; /** * A cache of the svn:externals (keyed by project). @@ -357,7 +358,7 @@ public SubversionSCM(List locations, WorkspaceUpdater workspaceU public SubversionSCM(List locations, WorkspaceUpdater workspaceUpdater, SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages, String includedRegions, boolean ignoreDirPropChanges) { - this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages, includedRegions, ignoreDirPropChanges, false, null); + this(locations, workspaceUpdater, browser, excludedRegions, excludedUsers, excludedRevprop, excludedCommitMessages, includedRegions, ignoreDirPropChanges, false, null, false); } @DataBoundConstructor @@ -365,7 +366,8 @@ public SubversionSCM(List locations, WorkspaceUpdater workspaceU SubversionRepositoryBrowser browser, String excludedRegions, String excludedUsers, String excludedRevprop, String excludedCommitMessages, String includedRegions, boolean ignoreDirPropChanges, boolean filterChangelog, - List additionalCredentials) { + List additionalCredentials, boolean usingCommitTimes) { + this.usingCommitTimes = usingCommitTimes; for (Iterator itr = locations.iterator(); itr.hasNext(); ) { ModuleLocation ml = itr.next(); String remote = Util.fixEmptyAndTrim(ml.remote); @@ -674,6 +676,11 @@ public boolean isFilterChangelog() { return filterChangelog; } + @Exported + public boolean isUsingCommitTimes() { + return usingCommitTimes; + } + /** * Sets the SVN_REVISION_n and SVN_URL_n environment variables during the build. */ @@ -980,6 +987,7 @@ private synchronized Map> getProjectExternalsCac */ private static class CheckOutTask extends UpdateTask implements FileCallable> { private final UpdateTask task; + private final boolean isUsingCommitTimes; public CheckOutTask(AbstractBuild build, SubversionSCM parent, ModuleLocation location, Date timestamp, TaskListener listener, EnvVars env) { this.authProvider = parent.createAuthenticationProvider(build.getParent(), location); @@ -988,6 +996,7 @@ public CheckOutTask(AbstractBuild build, SubversionSCM parent, ModuleLocat this.location = location; this.revisions = build.getAction(RevisionParameterAction.class); this.task = parent.getWorkspaceUpdater().createTask(); + this.isUsingCommitTimes = parent.isUsingCommitTimes(); } public Set getUnauthenticatedRealms() { @@ -998,7 +1007,12 @@ public Set getUnauthenticatedRealms() { } public List invoke(File ws, VirtualChannel channel) throws IOException { - clientManager = createClientManager(authProvider); + DefaultSVNOptions defaultSVNOptions = createDefaultSVNOptions(); + if (isUsingCommitTimes) { + defaultSVNOptions.setUseCommitTimes(isUsingCommitTimes); + } + + clientManager = new SvnClientManager(SVNClientManager.newInstance(defaultSVNOptions, createSvnAuthenticationManager(authProvider))); manager = clientManager.getCore(); this.ws = ws; try { diff --git a/src/main/resources/hudson/scm/SubversionSCM/config.jelly b/src/main/resources/hudson/scm/SubversionSCM/config.jelly index 96095feea..4b637e9ec 100644 --- a/src/main/resources/hudson/scm/SubversionSCM/config.jelly +++ b/src/main/resources/hudson/scm/SubversionSCM/config.jelly @@ -111,5 +111,8 @@ THE SOFTWARE. + + + diff --git a/src/main/resources/hudson/scm/SubversionSCM/help-usingCommitTimes.html b/src/main/resources/hudson/scm/SubversionSCM/help-usingCommitTimes.html new file mode 100644 index 000000000..77a86c8c0 --- /dev/null +++ b/src/main/resources/hudson/scm/SubversionSCM/help-usingCommitTimes.html @@ -0,0 +1,7 @@ +
+ If set Jenkins will set the file timestamps to the last commit time (of each file) when doing a checkout or an update. Otherwise Jenkins will set the current date as the timestamp of each file. +

+ Normally the working copy files have timestamps that reflect the last time they were touched by any process. This is generally convenient for most build systems as they look at timestamps as a way of deciding which files need to be recompiled. + In other situations, however, it's sometimes nice for the working copy files to have timestamps that reflect the last time they were changed in the repository. The svn export command always places these 'last-commit timestamps' on trees that it produces.. +

+
\ No newline at end of file diff --git a/src/test/java/hudson/scm/SubversionSCMTest.java b/src/test/java/hudson/scm/SubversionSCMTest.java index d4d04ee9c..1d4a95b73 100644 --- a/src/test/java/hudson/scm/SubversionSCMTest.java +++ b/src/test/java/hudson/scm/SubversionSCMTest.java @@ -26,6 +26,8 @@ package hudson.scm; import static hudson.scm.SubversionSCM.compareSVNAuthentications; +import static org.hamcrest.Matchers.not; +import static org.junit.Assert.assertThat; import static org.jvnet.hudson.test.recipes.PresetData.DataSet.ANONYMOUS_READONLY; import com.cloudbees.plugins.credentials.Credentials; @@ -33,6 +35,7 @@ import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.domains.Domain; import com.cloudbees.plugins.credentials.impl.UsernamePasswordCredentialsImpl; + import hudson.FilePath; import hudson.Launcher; import hudson.Proc; @@ -110,6 +113,9 @@ import org.tmatesoft.svn.core.wc.SVNStatus; import org.tmatesoft.svn.core.wc.SVNWCUtil; +import uk.co.it.modular.hamcrest.date.DateMatchers; +import uk.co.it.modular.hamcrest.date.Months; + import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; @@ -119,6 +125,7 @@ import com.gargoylesoftware.htmlunit.html.HtmlAnchor; import com.gargoylesoftware.htmlunit.html.HtmlForm; import com.gargoylesoftware.htmlunit.html.HtmlPage; + import hudson.EnvVars; import hudson.model.EnvironmentContributor; @@ -866,7 +873,7 @@ private void verifyChangelogFilter(boolean shouldFilterLog) throws Exception, File repo = new CopyExisting(getClass().getResource("JENKINS-10449.zip")).allocate(); SubversionSCM scm = new SubversionSCM(ModuleLocation.parse(new String[]{"file://" + repo.toURI().toURL().getPath()}, new String[]{"."},null,null), - new UpdateUpdater(), null, "/z.*", "", "", "", "", false, shouldFilterLog, null); + new UpdateUpdater(), null, "/z.*", "", "", "", "", false, shouldFilterLog, null, false); FreeStyleProject p = createFreeStyleProject(String.format("testFilterChangelog-%s", shouldFilterLog)); p.setScm(scm); @@ -1738,4 +1745,36 @@ private void invokeTestPollingExternalsForFile() throws Exception { // should detect change assertTrue(p.poll(StreamTaskListener.fromStdout()).hasChanges()); } + + public void testUseCommitTimes() throws Throwable { + // Given a subversion workspace where the commit times should be used + // When the workspace is checked out + // Then verify that the last modified time stamp of the format file is 2010-12-31 + + FreeStyleProject p = createFreeStyleProject(); + File repo = new CopyExisting(getClass().getResource("small.zip")).allocate(); + SubversionSCM scm = new SubversionSCM(ModuleLocation.parse(new String[]{"file://" + repo.toURI().toURL().getPath()}, + new String[]{"."},null,null), new UpdateUpdater(), null, "/z.*", "", "", "", "", false, false, null, true); + p.setScm(scm); + + FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); + Date fileInWorkspaceLastModified = new Date(b.getWorkspace().child("b").lastModified()); + assertThat(fileInWorkspaceLastModified, DateMatchers.sameDay(2011, Months.JANUARY, 1)); + } + + public void testNotUseCommitTimes() throws Throwable { + // Given a subversion workspace where the commit times should NOT be used + // When the workspace is checked out + // Then verify that the last modified time stamp of the format file NOT is 2011-01-01 + + FreeStyleProject p = createFreeStyleProject(); + File repo = new CopyExisting(getClass().getResource("small.zip")).allocate(); + SubversionSCM scm = new SubversionSCM(ModuleLocation.parse(new String[]{"file://" + repo.toURI().toURL().getPath()}, + new String[]{"."},null,null), new UpdateUpdater(), null, "/z.*", "", "", "", "", false, false, null, false); + p.setScm(scm); + + FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); + Date fileInWorkspaceLastModified = new Date(b.getWorkspace().child("b").lastModified()); + assertThat(fileInWorkspaceLastModified, not(DateMatchers.sameDay(2011, Months.JANUARY, 1))); + } } From 628a0c28ad0837ce0e5357d6024019264675e8c2 Mon Sep 17 00:00:00 2001 From: Erik Ramfelt Date: Sat, 1 Feb 2014 19:29:39 +0100 Subject: [PATCH 2/3] Change last modified assert Apparently there is some Timezone issues when running the tests on my and the cloudbees machine. Have reverted the last modified assertion to a simple long assertion. --- pom.xml | 8 +------- src/test/java/hudson/scm/SubversionSCMTest.java | 13 ++++++------- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/pom.xml b/pom.xml index 4f59f2897..5a8f9da60 100644 --- a/pom.xml +++ b/pom.xml @@ -143,13 +143,7 @@ THE SOFTWARE. 1.5 test
- - uk.co.modular-it - hamcrest-date - 0.9.5 - test - - + org.jenkins-ci.main jenkins-test-harness diff --git a/src/test/java/hudson/scm/SubversionSCMTest.java b/src/test/java/hudson/scm/SubversionSCMTest.java index 1d4a95b73..287a9898c 100644 --- a/src/test/java/hudson/scm/SubversionSCMTest.java +++ b/src/test/java/hudson/scm/SubversionSCMTest.java @@ -26,6 +26,7 @@ package hudson.scm; import static hudson.scm.SubversionSCM.compareSVNAuthentications; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import static org.jvnet.hudson.test.recipes.PresetData.DataSet.ANONYMOUS_READONLY; @@ -74,6 +75,7 @@ import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; +import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -113,9 +115,6 @@ import org.tmatesoft.svn.core.wc.SVNStatus; import org.tmatesoft.svn.core.wc.SVNWCUtil; -import uk.co.it.modular.hamcrest.date.DateMatchers; -import uk.co.it.modular.hamcrest.date.Months; - import com.gargoylesoftware.htmlunit.ElementNotFoundException; import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException; import com.gargoylesoftware.htmlunit.HttpMethod; @@ -1758,8 +1757,8 @@ public void testUseCommitTimes() throws Throwable { p.setScm(scm); FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); - Date fileInWorkspaceLastModified = new Date(b.getWorkspace().child("b").lastModified()); - assertThat(fileInWorkspaceLastModified, DateMatchers.sameDay(2011, Months.JANUARY, 1)); + // Using long matching to prevent Timezone issues + assertThat(b.getWorkspace().child("b").lastModified(), is(1293845528558l)); } public void testNotUseCommitTimes() throws Throwable { @@ -1774,7 +1773,7 @@ public void testNotUseCommitTimes() throws Throwable { p.setScm(scm); FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); - Date fileInWorkspaceLastModified = new Date(b.getWorkspace().child("b").lastModified()); - assertThat(fileInWorkspaceLastModified, not(DateMatchers.sameDay(2011, Months.JANUARY, 1))); + // Using long matching to prevent Timezone issues + assertThat(b.getWorkspace().child("b").lastModified(), not(is(1293845528558l))); } } From 5bce4f0bb2e4bd04f1ab1a174e1b79ab8db0d0ac Mon Sep 17 00:00:00 2001 From: Erik Ramfelt Date: Sat, 1 Feb 2014 20:02:01 +0100 Subject: [PATCH 3/3] file.lastModified is different on OS It seems that the file.lastmodified() is different depending on the OS. Some OS'es return the exact milliseconds while some doesnt. This fixes the test so they work both on both type of OSes --- src/test/java/hudson/scm/SubversionSCMTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/java/hudson/scm/SubversionSCMTest.java b/src/test/java/hudson/scm/SubversionSCMTest.java index 287a9898c..c4eba3b76 100644 --- a/src/test/java/hudson/scm/SubversionSCMTest.java +++ b/src/test/java/hudson/scm/SubversionSCMTest.java @@ -1757,8 +1757,8 @@ public void testUseCommitTimes() throws Throwable { p.setScm(scm); FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); - // Using long matching to prevent Timezone issues - assertThat(b.getWorkspace().child("b").lastModified(), is(1293845528558l)); + // Using long matching to prevent Timezone issues, divided by 1000 as some OS does not return the exact milliseconds + assertThat(b.getWorkspace().child("b").lastModified() / 1000, is(1293845528l)); } public void testNotUseCommitTimes() throws Throwable { @@ -1773,7 +1773,7 @@ public void testNotUseCommitTimes() throws Throwable { p.setScm(scm); FreeStyleBuild b = assertBuildStatusSuccess(p.scheduleBuild2(0, new Cause.UserIdCause()).get()); - // Using long matching to prevent Timezone issues - assertThat(b.getWorkspace().child("b").lastModified(), not(is(1293845528558l))); + // Using long matching to prevent Timezone issues, divided by 1000 as some OS does not return the exact milliseconds + assertThat(b.getWorkspace().child("b").lastModified() / 1000, not(is(1293845528l))); } }