changeset 1118:54472aa4b7b2 TW-83599

TW-83599: recognize "path contains illegal component" error and retry pull command with explicit revision, agent side logic
author Andreas Eisele <andreas.eisele@jetbrains.com>
date Tue, 10 Oct 2023 12:11:40 +0200
parents c1a251997254
children 1a43a8c758a4
files mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfig.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfigImpl.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java
diffstat 3 files changed, 38 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfig.java	Mon Oct 09 18:18:26 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfig.java	Tue Oct 10 12:11:40 2023 +0200
@@ -37,6 +37,8 @@
 
   boolean runWithProfile(@NotNull  AgentRunningBuild build);
 
+  boolean retryPullForIllegalPathComponent(@NotNull AgentRunningBuild build);
+
   @NotNull
   File getTempDir();
 }
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfigImpl.java	Mon Oct 09 18:18:26 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfigImpl.java	Tue Oct 10 12:11:40 2023 +0200
@@ -96,6 +96,11 @@
     return Boolean.parseBoolean(build.getSharedConfigParameters().get("teamcity.hg.runCommandsWithProfile"));
   }
 
+  @Override
+  public boolean retryPullForIllegalPathComponent(@NotNull AgentRunningBuild build) {
+    return Boolean.parseBoolean(build.getSharedConfigParameters().getOrDefault("teamcity.hg.retryPullForIllegalPathComponent", "true"));
+  }
+
   @Nullable
   public Integer parseTimeout(@Nullable String timeoutStr) {
     if (timeoutStr == null)
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Mon Oct 09 18:18:26 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Tue Oct 10 12:11:40 2023 +0200
@@ -23,6 +23,7 @@
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.PullCommand;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.AbandonedTransactionFound;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.PathIllegalComponentException;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.WrongSubrepoUrlException;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.ext.BeforeWorkingDirUpdateExtension;
@@ -33,6 +34,7 @@
 import jetbrains.buildServer.vcs.VcsException;
 import jetbrains.buildServer.vcs.VcsRoot;
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 import java.io.File;
 import java.io.IOException;
@@ -57,6 +59,7 @@
   private int myPullTimeout;
   private final boolean myUseTraceback;
   private final boolean myProfile;
+  private final boolean myRetryPullForIllegalPathComponent;
   private final List<MercurialExtension> myExtensions = new ArrayList<>();
   protected final MercurialProgress myProgress;
 
@@ -76,6 +79,7 @@
     myPullTimeout = pluginConfig.getPullTimeout(build);
     myUseTraceback = pluginConfig.runWithTraceback(build);
     myProfile = pluginConfig.runWithProfile(build);
+    myRetryPullForIllegalPathComponent = pluginConfig.retryPullForIllegalPathComponent(build);
     myProgress = new MercurialBuildLogProgress(build.getBuildLogger().getFlowLogger("-1"));
   }
 
@@ -126,16 +130,12 @@
     if (mirrorRepo.containsRevision(revision)) {
       myLogger.message("Local mirror is already up-to-date");
     } else {
-      PullCommand pull = mirrorRepo.pull().fromRepository(repositoryUrl)
-              .withTraceback(myUseTraceback)
-              .withProfile(myProfile)
-              .withTimeout(myPullTimeout);
       try {
-        pull.call();
+        doPull(mirrorRepo, repositoryUrl, myToVersion);
       } catch (AbandonedTransactionFound e) {
         myLogger.message("Abandoned transaction found, trying to recover");
         mirrorRepo.recover().call();
-        pull.call();
+        doPull(mirrorRepo, repositoryUrl, myToVersion);
       }
     }
   }
@@ -154,17 +154,37 @@
       myLogger.message("Repository already contains revision " + myToVersion);
     } else {
       try {
-        repo.pull().fromRepository(repositoryUrl)
-                .withTraceback(myUseTraceback)
-                .withProfile(myProfile)
-                .withTimeout(myPullTimeout)
-                .call();
+        doPull(repo, repositoryUrl, myToVersion);
       } catch (UnrelatedRepositoryException e) {
         throw new UnrelatedRepositoryException(myAuthSettings.getRepositoryUrlWithHiddenPassword(repositoryUrl), workingDir);
       }
     }
   }
 
+  private void doPull(@NotNull HgRepo repo, @NotNull String repositoryUrl, @Nullable String revision) throws VcsException {
+    doPull(repo, repositoryUrl, revision, false);
+  }
+
+  private void doPull(@NotNull HgRepo repo, @NotNull String repositoryUrl, @Nullable String revision, boolean explicitRevision) throws VcsException {
+    final String effectiveRevision = revision != null ? revision : "tip";
+    final PullCommand pullCommand = repo.pull().fromRepository(repositoryUrl)
+            .withTraceback(myUseTraceback)
+            .withProfile(myProfile)
+            .withTimeout(myPullTimeout);
+    if (explicitRevision) {
+      pullCommand.withNamedRevision(effectiveRevision);
+    }
+
+    try {
+      pullCommand.call();
+    } catch (PathIllegalComponentException e) {
+      if (!myRetryPullForIllegalPathComponent || explicitRevision) {
+        throw e;
+      }
+      myLogger.message("Pull encountered illegal path component " + e.getComponent() + " retrying with explicit revision " + effectiveRevision);
+      doPull(repo, repositoryUrl, revision, true);
+    }
+  }
 
   private void disableSharing(@NotNull File workingDir) {
     File dotHg = new File(workingDir, ".hg");