changeset 847:fbd8e6725fe5

Support sharing subrepo mirrors + tests
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Mon, 30 Jun 2014 22:27:43 +0200
parents cea91fa66d3b
children e3fd75845c5d
files mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SharingMercurialUpdater.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSharedMirrorsTest.java mercurial-tests/src/testng.xml
diffstat 4 files changed, 225 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Mon Jun 30 21:02:29 2014 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Mon Jun 30 22:27:43 2014 +0200
@@ -24,6 +24,7 @@
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.WrongSubrepoUrlException;
 import jetbrains.buildServer.log.Loggers;
+import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.vcs.IncludeRule;
 import jetbrains.buildServer.vcs.VcsException;
 import jetbrains.buildServer.vcs.VcsRoot;
@@ -87,7 +88,7 @@
   }
 
 
-  private void updateLocalMirror(@NotNull String repositoryUrl, @NotNull String revision) throws VcsException, IOException {
+  protected void updateLocalMirror(@NotNull String repositoryUrl, @NotNull String revision) throws VcsException, IOException {
     File mirrorDir = myMirrorManager.getMirrorDir(repositoryUrl);
     HgRepo mirrorRepo = myRepoFactory.createRepo(myRoot, mirrorDir);
     if (!mirrorRepo.isValidRepository()) {
@@ -120,6 +121,7 @@
     String repositoryUrl = getDefaultPullUrl(myRoot, myUseLocalMirrors);
     HgRepo repo = myRepoFactory.createRepo(myRoot, workingDir);
     myLogger.message("Update repository " + workingDir.getAbsolutePath());
+    disableSharing(workingDir);
     if (repo.isEmpty()) {//can do clone only in empty dir
       myLogger.message("Start cloning from " + (myUseLocalMirrors ? "local mirror " : "") + myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl));
       repo.doClone().fromRepository(repositoryUrl)
@@ -152,6 +154,14 @@
   }
 
 
+  private void disableSharing(@NotNull File workingDir) {
+    File dotHg = new File(workingDir, ".hg");
+    File sharedpath = new File(dotHg, "sharedpath");
+    if (sharedpath.exists())
+      FileUtil.delete(sharedpath);
+  }
+
+
   private void updateWorkingDir(@NotNull File workingDir, @NotNull String toVersion, @NotNull String repositoryUrl) throws VcsException, IOException {
     HgRepo repo = myRepoFactory.createRepo(myRoot, workingDir);
     List<File> repos = new ArrayList<File>();
@@ -211,10 +221,11 @@
     return url.startsWith(".");
   }
 
-  private void syncSubrepo(@NotNull HgRepo subrepository, @NotNull String url, @NotNull String revision) throws VcsException, IOException {
-    if (!subrepository.isValidRepository() || !subrepository.containsRevision(revision)) {
-      updateLocalMirror(url, revision);
-      File mirrorDir = myMirrorManager.getMirrorDir(url);
+  protected void syncSubrepo(@NotNull HgRepo subrepository, @NotNull String subrepoUrl, @NotNull String subrepoRevision) throws VcsException, IOException {
+    disableSharing(subrepository.getWorkingDir());
+    if (!subrepository.isValidRepository() || !subrepository.containsRevision(subrepoRevision)) {
+      updateLocalMirror(subrepoUrl, subrepoRevision);
+      File mirrorDir = myMirrorManager.getMirrorDir(subrepoUrl);
       if (subrepository.isValidRepository()) {
         myLogger.message("Pull from local mirror");
         subrepository.pull().fromRepository(mirrorDir)
@@ -229,7 +240,7 @@
                 .setUpdateWorkingDir(false)
                 .setUsePullProtocol(false)
                 .call();
-        subrepository.setDefaultPath(url);
+        subrepository.setDefaultPath(subrepoUrl);
         myLogger.message("done");
       }
     }
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SharingMercurialUpdater.java	Mon Jun 30 21:02:29 2014 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SharingMercurialUpdater.java	Mon Jun 30 22:27:43 2014 +0200
@@ -43,17 +43,34 @@
       repo.init().call();
     repo.setDefaultPath(myRoot.getRepository());
 
-    File mirrorDir = myMirrorManager.getMirrorDir(myRoot.getRepository());
-    File mirrorHg = new File(mirrorDir, ".hg");
+    File mirrorHg = getMirrorHg(myRoot.getRepository());
     String sharedPath = readSharedPath(workingDir);
     if (mirrorHg.getCanonicalPath().equals(sharedPath)) {
       writeRequires(mirrorHg, workingDir);
     } else {
       writeRequires(mirrorHg, workingDir);
-      writeSharedPath(workingDir, mirrorDir);
+      writeSharedPath(mirrorHg, workingDir);
     }
   }
 
+  @Override
+  protected void syncSubrepo(@NotNull HgRepo subrepository, @NotNull String subrepoUrl, @NotNull String subrepoRevision) throws VcsException, IOException {
+    if (subrepository.isEmpty() || !subrepository.isValidRepository())
+      subrepository.init().call();
+    subrepository.setDefaultPath(subrepoUrl);
+
+    if (!subrepository.containsRevision(subrepoRevision)) {
+      updateLocalMirror(subrepoUrl, subrepoRevision);
+      File mirrorHg = getMirrorHg(subrepoUrl);
+      String sharedPath = readSharedPath(subrepository.getWorkingDir());
+      if (mirrorHg.getCanonicalPath().equals(sharedPath)) {
+        writeRequires(mirrorHg, subrepository.getWorkingDir());
+      } else {
+        writeRequires(mirrorHg, subrepository.getWorkingDir());
+        writeSharedPath(mirrorHg, subrepository.getWorkingDir());
+      }
+    }
+  }
 
   @Nullable
   private String readSharedPath(@NotNull File workingDir) throws IOException {
@@ -63,8 +80,8 @@
     return FileUtil.readText(sharedPath);
   }
 
-  private void writeSharedPath(@NotNull File workingDir, @NotNull File mirrorDir) throws IOException {
-    FileUtil.writeToFile(getSharedPath(workingDir), (new File(mirrorDir, ".hg").getCanonicalPath() + "\n").getBytes("UTF-8"));
+  private void writeSharedPath(@NotNull File mirrorHg, @NotNull File workingDir) throws IOException {
+    FileUtil.writeToFile(getSharedPath(workingDir), (mirrorHg.getCanonicalPath() + "\n").getBytes("UTF-8"));
   }
 
   private void writeRequires(@NotNull File mirrorHg, @NotNull File workingDir) throws IOException {
@@ -82,4 +99,10 @@
   private File getSharedPath(File workingDir) {
     return new File(new File(workingDir, ".hg"), "sharedpath");
   }
+
+  @NotNull
+  private File getMirrorHg(@NotNull String repositoryUrl) {
+    File mirrorDir = myMirrorManager.getMirrorDir(repositoryUrl);
+    return new File(mirrorDir, ".hg");
+  }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSharedMirrorsTest.java	Mon Jun 30 22:27:43 2014 +0200
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.agent.AgentRunningBuild;
+import jetbrains.buildServer.agent.BuildAgentConfiguration;
+import jetbrains.buildServer.agent.BuildProgressLogger;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandSettingsForRootImpl;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandlineViaFileWrapperWeaver;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.ExtensionsWeaver;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.TestCommandSettingsFactory;
+import jetbrains.buildServer.util.FileUtil;
+import jetbrains.buildServer.vcs.CheckoutRules;
+import jetbrains.buildServer.vcs.IncludeRule;
+import jetbrains.buildServer.vcs.VcsException;
+import jetbrains.buildServer.vcs.VcsRoot;
+import jetbrains.buildServer.vcs.impl.VcsRootImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+
+import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot;
+import static jetbrains.buildServer.util.Util.map;
+
+@Test
+public class AgentSideCheckoutWithSharedMirrorsTest extends BaseMercurialPatchTestCase {
+
+  private File myOriginalRepositoriesParentDir;
+  private MercurialAgentSideVcsSupport myVcsSupport;
+  private File myWorkDir;
+  private File myMirrorsRootDir;
+  private Mockery myContext;
+  private BuildProgressLogger myLogger;
+  private int myBuildCounter = 0;
+
+  @Override
+  @BeforeMethod
+  protected void setUp() throws Exception {
+    super.setUp();
+
+    myOriginalRepositoriesParentDir = myTempFiles.createTempDir();
+    myContext = new Mockery();
+
+    myMirrorsRootDir = myTempFiles.createTempDir();
+
+    final BuildAgentConfiguration agentConfig = myContext.mock(BuildAgentConfiguration.class);
+    myContext.checking(new Expectations() {{
+      allowing(agentConfig).getCacheDirectory("mercurial"); will(returnValue(myMirrorsRootDir));
+      allowing(agentConfig).getTempDirectory(); will(returnValue(myTempFiles.createTempDir()));
+      allowing(agentConfig).getParametersResolver(); will(returnValue(new HgPathResolver()));
+    }});
+
+    final AgentPluginConfigImpl pluginConfig = new AgentPluginConfigImpl(agentConfig);
+    myVcsSupport = new MercurialAgentSideVcsSupport(pluginConfig,
+            new MirrorManagerImpl(pluginConfig),
+            new AgentRepoFactory(new CommandSettingsForRootImpl(new TestCommandSettingsFactory(), new ExtensionsWeaver(),
+                    new CommandlineViaFileWrapperWeaver()), new AgentHgPathProvider(agentConfig)));
+
+    myLogger = myContext.mock(BuildProgressLogger.class);
+    myContext.checking(new Expectations() {{
+      allowing(myLogger).message(with(any(String.class)));
+    }});
+
+    myWorkDir = myTempFiles.createTempDir();
+  }
+
+
+  public void working_dir_store_should_be_empty() throws Exception {
+    VcsRoot root = createVcsRoot(simpleRepo());
+    String version = "4:b06a290a363b";
+    update(root, version);
+    File workingDirStore = new File(new File(myWorkDir, ".hg"), "store");
+    assertTrue(!workingDirStore.exists() || listFiles(workingDirStore).length == 0);
+    checkWorkingDir("patch1/after", myWorkDir);
+  }
+
+
+  public void subrepos_store_should_be_empty() throws Exception {
+    File myR1Dir = copy(new File("mercurial-tests/testData/subrepos/r1"));
+    copy(new File("mercurial-tests/testData/subrepos/r2"));
+    copy(new File("mercurial-tests/testData/subrepos/r3"));
+    VcsRootImpl root = vcsRoot().withUrl(myR1Dir.getAbsolutePath()).build();
+    update(root, "34017377d9c3");
+
+    File subrepoStore = new File(new File(new File(myWorkDir, "r2"), ".hg"), "store");
+    assertTrue(!subrepoStore.exists() || listFiles(subrepoStore).length == 0);
+  }
+
+
+  public void turn_off_sharing() throws Exception {
+    File myR1Dir = copy(new File("mercurial-tests/testData/subrepos/r1"));
+    copy(new File("mercurial-tests/testData/subrepos/r2"));
+    copy(new File("mercurial-tests/testData/subrepos/r3"));
+    VcsRootImpl root = vcsRoot().withUrl(myR1Dir.getAbsolutePath()).build();
+    update(root, "34017377d9c3");
+
+    update(root, "34017377d9c3", false);
+    File workingDirSharedPath = new File(new File(myWorkDir, ".hg"), "sharedpath");
+    File subrepoSharedPath = new File(new File(new File(myWorkDir, "r2"), ".hg"), "sharedpath");
+    File workingDirStore = new File(new File(myWorkDir, ".hg"), "store");
+    File subrepoStore = new File(new File(new File(myWorkDir, "r2"), ".hg"), "store");
+
+    assertFalse(workingDirSharedPath.exists());
+    assertFalse(subrepoSharedPath.exists());
+    assertTrue(listFiles(workingDirStore).length > 0);
+    assertTrue(listFiles(subrepoStore).length > 0);
+  }
+
+
+  @NotNull
+  private File[] listFiles(@NotNull File dir) {
+    File[] files = dir.listFiles();
+    if (files == null)
+      return new File[0];
+    return files;
+  }
+
+
+  private void update(@NotNull VcsRoot root, @NotNull String version) throws VcsException {
+    update(root, version, true);
+  }
+
+  private void update(@NotNull VcsRoot root, @NotNull String version, boolean share) throws VcsException {
+    AgentRunningBuild build = createBuild(share);
+    myVcsSupport.getUpdater(root, CheckoutRules.DEFAULT, version, myWorkDir, build, false)
+            .process(IncludeRule.createDefaultInstance(), myWorkDir);
+  }
+
+
+  private void checkWorkingDir(final String expected, final File workDir) throws IOException {
+    FileUtil.delete(new File(workDir, ".hg"));
+    checkDirectoriesAreEqual(new File(getTestDataPath(), expected), workDir);
+  }
+
+
+  private AgentRunningBuild createBuild(boolean share) {
+    final Map<String, String> sharedConfigParameters = map("teamcity.hg.use.local.mirrors", "true", "teamcity.hg.shareLocalMirrors", String.valueOf(share));
+    final AgentRunningBuild build = myContext.mock(AgentRunningBuild.class, "build" + myBuildCounter++);
+    myContext.checking(new Expectations() {{
+      allowing(build).getBuildLogger(); will(returnValue(myLogger));
+      allowing(build).getSharedConfigParameters(); will(returnValue(sharedConfigParameters));
+    }});
+    return build;
+  }
+
+  protected String getTestDataPath() {
+    return "mercurial-tests/testData";
+  }
+
+  private File copy(@NotNull File originalRepositoryDir) throws IOException {
+    String dirName = originalRepositoryDir.getName();
+    File copyDir = new File(myOriginalRepositoriesParentDir, dirName);
+    FileUtil.copyDir(originalRepositoryDir, copyDir);
+    if (new File(copyDir, "hg").isDirectory()) {
+      FileUtil.rename(new File(copyDir, "hg"), new File(copyDir, ".hg"));
+    }
+    return copyDir;
+  }
+}
--- a/mercurial-tests/src/testng.xml	Mon Jun 30 21:02:29 2014 +0200
+++ b/mercurial-tests/src/testng.xml	Mon Jun 30 22:27:43 2014 +0200
@@ -29,6 +29,7 @@
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialVcsSupportTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.AgentSideCheckoutTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.AgentSideCheckoutWithSubreposTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.AgentSideCheckoutWithSharedMirrorsTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVcsRootTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MirrorManagerTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVersionTest"/>