changeset 1123:7f82951e9391 development/2023.11.x

merge default to development/2023.11.x branch
author Nadia Burnasheva <nadezhda.burnasheva@jetbrains.com>
date Wed, 18 Oct 2023 13:30:24 +0200
parents 53998d708051 (current diff) e6e3f80c9313 (diff)
children
files
diffstat 31 files changed, 305 insertions(+), 46 deletions(-) [+]
line wrap: on
line diff
--- a/.idea/artifacts/mercurial_vcs_worker.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/artifacts/mercurial_vcs_worker.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -12,8 +12,6 @@
         <element id="artifact" artifact-name="mercurial-common.jar" />
         <element id="artifact" artifact-name="mercurial-server.jar" />
         <element id="library" level="project" name="quartz-2.3.2" />
-        <element id="library" level="project" name="commons-compress-1.5" />
-        <element id="library" level="project" name="commons-codec-1.4" />
       </element>
       <element id="file-copy" path="$PROJECT_DIR$/teamcity-plugin.xml" />
     </root>
--- a/.idea/artifacts/plugin.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/artifacts/plugin.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -13,8 +13,6 @@
         <element id="artifact" artifact-name="mercurial-common.jar" />
         <element id="artifact" artifact-name="mercurial-server-tc.jar" />
         <element id="library" level="project" name="quartz-2.3.2" />
-        <element id="library" level="project" name="commons-compress-1.5" />
-        <element id="library" level="project" name="commons-codec-1.4" />
       </element>
       <element id="directory" name="agent">
         <element id="archive" name="mercurial.zip">
@@ -22,8 +20,6 @@
             <element id="directory" name="lib">
               <element id="artifact" artifact-name="mercurial-common.jar" />
               <element id="artifact" artifact-name="mercurial-agent.jar" />
-              <element id="library" level="project" name="commons-codec-1.4" />
-              <element id="library" level="project" name="commons-compress-1.5" />
             </element>
           </element>
         </element>
--- a/.idea/libraries/IDEA_openapi.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/IDEA_openapi.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -3,6 +3,8 @@
     <CLASSES>
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/resources_en.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/util.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/idea-obsolete-openapi.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/logging.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/openapi.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/annotations.jar!/" />
     </CLASSES>
--- a/.idea/libraries/Log4j.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/Log4j.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -4,6 +4,9 @@
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-1.2-api-2.17.2.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-api-2.17.2.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-core-2.17.2.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-1.2-api.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-api.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/log4j-core.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES />
--- a/.idea/libraries/TeamCityAPI_agent.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/TeamCityAPI_agent.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -3,6 +3,7 @@
     <CLASSES>
       <root url="jar://$TeamCityDistribution$/devPackage/agent-api.jar!/" />
       <root url="jar://$TeamCityDistribution$/buildAgent/lib/agent.jar!/" />
+      <root url="jar://$TeamCityDistribution$/buildAgent/lib/agent-core.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
--- a/.idea/libraries/TeamCityAPI_common.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/TeamCityAPI_common.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -1,10 +1,22 @@
 <component name="libraryTable">
   <library name="TeamCityAPI-common">
     <CLASSES>
+      <root url="jar://$TeamCityDistribution$/devPackage/common!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/build-version!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/common-runtime!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/messages!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/patches!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/runtime-util!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/xml-rpc-wrapper!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/common-vcs!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/utils!/" />
+      <root url="jar://$TeamCityDistribution$/devPackage/common-tools!/" />
       <root url="jar://$TeamCityDistribution$/devPackage/common-api.jar!/" />
       <root url="jar://$TeamCityDistribution$/devPackage/serviceMessages.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/commons-httpclient.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/commons-httpclient-3.1.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/commons-logging.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/commons-logging-1.2.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES>
--- a/.idea/libraries/TeamCityAPI_server.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/TeamCityAPI_server.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -2,6 +2,24 @@
   <library name="TeamCityAPI-server">
     <CLASSES>
       <root url="jar://$TeamCityDistribution$/devPackage/server-api.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-core.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/buildLog-common.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/common-step-conditions.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/configs-impl.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/ide-notificator.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/idea-settings.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/messages-processing.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/plugin-model-server.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/ro-server-util.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-common-api-impl.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-diagnostic.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-startup.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-vcs-api.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-vcs-api-ex.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server-vcs-impl.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/thread-dump-analyzer.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/vcs-api-impl.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/pipelines-server.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/server.jar!/" />
     </CLASSES>
     <JAVADOC />
--- a/.idea/libraries/TeamCity_impl.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/TeamCity_impl.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -2,7 +2,11 @@
   <library name="TeamCity-impl">
     <CLASSES>
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/patches-impl.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/com.github.adedayo.intellij.sdk-trove4j.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/trove4j-142.1.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/net.sf.trove4j-trove4j!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/trove4j.jar!/" />
+      <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/trove.jar!/" />
       <root url="jar://$TeamCityDistribution$/webapps/ROOT/WEB-INF/lib/trove-3.0.3.jar!/" />
     </CLASSES>
     <JAVADOC />
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/.idea/libraries/commons_codec_1_16_0.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -0,0 +1,10 @@
+<component name="libraryTable">
+  <library name="commons-codec-1.16.0">
+    <CLASSES>
+      <root url="jar://$PROJECT_DIR$/lib/commons-codec-1.16.0.jar!/" />
+      <root url="jar://$PROJECT_DIR$/lib/commons-codec.jar!/" />
+    </CLASSES>
+    <JAVADOC />
+    <SOURCES />
+  </library>
+</component>
\ No newline at end of file
--- a/.idea/libraries/commons_codec_1_4.xml	Tue Oct 17 12:52:06 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-<component name="libraryTable">
-  <library name="commons-codec-1.4">
-    <CLASSES>
-      <root url="jar://$PROJECT_DIR$/lib/commons-codec-1.4.jar!/" />
-    </CLASSES>
-    <JAVADOC />
-    <SOURCES />
-  </library>
-</component>
\ No newline at end of file
--- a/.idea/libraries/commons_compress_1_5.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/libraries/commons_compress_1_5.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -2,6 +2,7 @@
   <library name="commons-compress-1.5">
     <CLASSES>
       <root url="jar://$PROJECT_DIR$/lib/commons-compress-1.5.jar!/" />
+      <root url="jar://$PROJECT_DIR$/lib/commons-compress.jar!/" />
     </CLASSES>
     <JAVADOC />
     <SOURCES />
--- a/.idea/misc.xml	Tue Oct 17 12:52:06 2023 +0200
+++ b/.idea/misc.xml	Wed Oct 18 13:30:24 2023 +0200
@@ -1,8 +1,7 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<project version="4">
-  <component name="EntryPointsManager">
-    <entry_points version="2.0" />
-  </component>
-  <component name="IdProvider" IDEtalkID="C52C76224CD45BEC1DC62428B699D800" />
-  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
+<project version="4">
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="IdProvider" IDEtalkID="C52C76224CD45BEC1DC62428B699D800" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK" />
 </project>
\ No newline at end of file
Binary file lib/commons-codec-1.16.0.jar has changed
Binary file lib/commons-codec-1.4.jar has changed
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfig.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfig.java	Wed Oct 18 13:30:24 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	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentPluginConfigImpl.java	Wed Oct 18 13:30:24 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	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialIncludeRuleUpdater.java	Wed Oct 18 13:30:24 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,36 @@
       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, @NotNull String revision) throws VcsException {
+    doPull(repo, repositoryUrl, revision, false);
+  }
+
+  private void doPull(@NotNull HgRepo repo, @NotNull String repositoryUrl, @NotNull String revision, boolean explicitRevision) throws VcsException {
+    final PullCommand pullCommand = repo.pull().fromRepository(repositoryUrl)
+            .withTraceback(myUseTraceback)
+            .withProfile(myProfile)
+            .withTimeout(myPullTimeout);
+    if (explicitRevision) {
+      pullCommand.withNamedRevision(revision);
+    }
+
+    try {
+      pullCommand.call();
+    } catch (PathIllegalComponentException e) {
+      if (!myRetryPullForIllegalPathComponent || explicitRevision) {
+        throw e;
+      }
+      myLogger.message("Pull encountered illegal path component " + e.getComponent() + " retrying with explicit revision " + revision);
+      doPull(repo, repositoryUrl, revision, true);
+    }
+  }
 
   private void disableSharing(@NotNull File workingDir) {
     File dotHg = new File(workingDir, ".hg");
--- a/mercurial-common/mercurial-common.iml	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-common/mercurial-common.iml	Wed Oct 18 13:30:24 2023 +0200
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <module relativePaths="true" type="JAVA_MODULE" version="4">
-  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8" inherit-compiler-output="false">
+  <component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
     <output url="file://$MODULE_DIR$/classes" />
     <exclude-output />
     <content url="file://$MODULE_DIR$">
@@ -11,6 +11,6 @@
     <orderEntry type="library" exported="" name="TeamCityAPI-common" level="project" />
     <orderEntry type="library" exported="" name="IDEA-openapi" level="project" />
     <orderEntry type="library" name="jdom" level="project" />
-    <orderEntry type="library" name="commons-codec-1.4" level="project" />
+    <orderEntry type="library" name="commons-codec-1.16.0" level="project" />
   </component>
 </module>
\ No newline at end of file
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java	Wed Oct 18 13:30:24 2023 +0200
@@ -242,6 +242,7 @@
     checkMergeWithWorkDirAncestor(stderr);
     checkNothingToMerge(stderr);
     checkUnknownException(stderr);
+    checkPathIllegalComponent(stderr);
   }
 
   private void checkUnrelatedRepository(@NotNull final String stderr) throws UnrelatedRepositoryException {
@@ -298,6 +299,16 @@
       throw new UnknownMercurialException(stderr);
   }
 
+  private void checkPathIllegalComponent(@NotNull final String stderr) throws PathIllegalComponentException {
+    final String prefix = "abort: path contains illegal component: ";
+    int idx = stderr.indexOf(prefix);
+    if (idx != -1) {
+      int startIdx = idx + prefix.length();
+      final String component = stderr.substring(startIdx);
+      throw new PathIllegalComponentException(component);
+    }
+  }
+
   private static Set<Integer> setOf(Integer... ints) {
     return new HashSet<>(asList(ints));
   }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Wed Oct 18 13:30:24 2023 +0200
@@ -31,6 +31,8 @@
   private int myTimeout;
   private boolean myTraceback;
   private boolean myProfile;
+  private ChangeSet myChangeSet;
+  private String myNamedRevision;
 
   public PullCommand(@NotNull CommandSettings commandSettings,
                      @NotNull String hgPath,
@@ -64,6 +66,16 @@
     return this;
   }
 
+  public PullCommand withChangeSet(@NotNull ChangeSet changeSet) {
+    myChangeSet = changeSet;
+    return this;
+  }
+
+  public PullCommand withNamedRevision(@NotNull String namedRevision) {
+    myNamedRevision = namedRevision;
+    return this;
+  }
+
   public void call() throws VcsException {
     removeLocks();
 
@@ -84,6 +96,12 @@
       cli.setHasProgress(true);
     }
 
+    if (myChangeSet != null) {
+      cli.addParameters("--rev", myChangeSet.getId());
+    } else if (myNamedRevision != null) {
+      cli.addParameters("--rev", myNamedRevision);
+    }
+
     String pullUrl = myAuthSettings.getRepositoryUrlWithCredentials(myPullUrl);
     cli.addParameter(pullUrl);
     runCommand(cli, settings);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/PathIllegalComponentException.java	Wed Oct 18 13:30:24 2023 +0200
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2000-2023 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.command.exception;
+
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+public class PathIllegalComponentException extends VcsException {
+
+  @NotNull private final String myComponent;
+
+  public PathIllegalComponentException(@NotNull String component) {
+    super("Path contains illegal component: " + component);
+    myComponent = component;
+  }
+
+  @NotNull
+  public String getComponent() {
+    return myComponent;
+  }
+}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Wed Oct 18 13:30:24 2023 +0200
@@ -19,6 +19,7 @@
 import jetbrains.buildServer.buildTriggers.vcs.AbstractVcsPropertiesProcessor;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
 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.log.Loggers;
@@ -539,8 +540,12 @@
     }
   }
 
+  private void syncRepository(@NotNull final HgVcsRoot root, @NotNull final ChangeSet cset) throws VcsException {
+    syncRepository(root, cset, false);
+  }
+
   /* clone the repo if it doesn't exist, pull the repo if it doesn't contain specified changeSet */
-  private void syncRepository(@NotNull final HgVcsRoot root, @NotNull final ChangeSet cset) throws VcsException {
+  private void syncRepository(@NotNull final HgVcsRoot root, @NotNull final ChangeSet cset, boolean explicitRevision) throws VcsException {
     File workingDir = getWorkingDir(root);
     lockWorkDir(workingDir);
     HgRepo repo = createRepo(root);
@@ -552,14 +557,27 @@
       repo.setDefaultPath(root.getRepository());
       repo.setTeamCityConfig(root.getCustomHgConfigServer());
       try {
-        repo.pull().fromRepository(root.getRepository())
+        final PullCommand pullCommand = repo.pull().fromRepository(root.getRepository())
                 .withTimeout(myConfig.getPullTimeout())
-                .withProfile(myConfig.runWithProfile(root))
-                .call();
+                .withProfile(myConfig.runWithProfile(root));
+        if (explicitRevision) {
+          pullCommand.withChangeSet(cset);
+        }
+        pullCommand.call();
       } catch (UnrelatedRepositoryException e) {
         Loggers.VCS.warn("Repository at " + root.getRepository() + " is unrelated, clone it again");
         myMirrorManager.forgetDir(workingDir);
         syncRepository(root, cset);
+      } catch (PathIllegalComponentException e) {
+        final StringBuilder logMessage = new StringBuilder("Pulling from repository at ").append(root.getRepository())
+                .append(" failed because of illegal path component ").append(e.getComponent()).append(".");
+        if (myConfig.retryPullForIllegalPathComponent() && !explicitRevision) {
+          logMessage.append(" Retrying with explicit pull for revision ").append(cset).append(".");
+          Loggers.VCS.warn(logMessage.toString());
+          syncRepository(root, cset, true);
+          return;
+        }
+        Loggers.VCS.warn(logMessage.toString());
       }
     } finally {
       unlockWorkDir(workingDir);
@@ -581,11 +599,12 @@
   public <T> T syncRepository(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings, @Nullable OperationContext context) throws VcsException {
     boolean customWorkingDir = root.getCustomWorkingDir() != null;
     File workingDir = getWorkingDir(root);
+    boolean pullFromTip = false;
     int attemptsLeft = 3;
     VcsException lastError = null;
     while (attemptsLeft-- > 0) {
       try {
-        return syncRepositoryOnce(root, settings, workingDir, context);
+        return syncRepositoryOnce(root, settings, workingDir, context, pullFromTip);
       } catch (UnrelatedRepositoryException e) {
         if (customWorkingDir)
           throw new VcsException(e.getMessage() + ". VCS root uses a custom clone dir, manual recovery is required.", e);
@@ -600,13 +619,26 @@
                 + workingDir.getAbsolutePath() + ". Clone it again, attempts left " + attemptsLeft);
         myMirrorManager.forgetDir(workingDir);
         lastError = e;
+      } catch (PathIllegalComponentException e) {
+        final StringBuilder logMessage = new StringBuilder("Pull failed because of illegal path component ").append(e.getComponent())
+                .append(" for repository ").append(root.getRepository()).append(" at ").append(workingDir.getAbsolutePath()).append(".");
+        if (myConfig.retryPullForIllegalPathComponent()) {
+          logMessage.append(" Retrying with pull from tip revision, attempts left ").append(attemptsLeft);
+          pullFromTip = true;
+        }
+        Loggers.VCS.warn(logMessage.toString());
+        lastError = e;
       }
     }
     throw lastError;
   }
 
 
-  private <T> T syncRepositoryOnce(@NotNull HgVcsRoot root, @NotNull SyncSettings<T> settings, @NotNull File workingDir, @Nullable OperationContext context) throws VcsException {
+  private <T> T syncRepositoryOnce(@NotNull HgVcsRoot root,
+                                   @NotNull SyncSettings<T> settings,
+                                   @NotNull File workingDir,
+                                   @Nullable OperationContext context,
+                                   boolean fromTip) throws VcsException {
     lockWorkDir(workingDir, settings.getProgressConsumer());
     HgRepo repo = context != null ? context.createRepo(root) : createRepo(root);
     try {
@@ -615,10 +647,13 @@
       repo.setDefaultPath(root.getRepository());
       repo.setTeamCityConfig(root.getCustomHgConfigServer());
       resetBookmarks(repo);
-      repo.pull().fromRepository(root.getRepository())
+      final PullCommand pullCommand = repo.pull().fromRepository(root.getRepository())
               .withTimeout(myConfig.getPullTimeout())
-              .withProfile(myConfig.runWithProfile(root))
-              .call();
+              .withProfile(myConfig.runWithProfile(root));
+      if (fromTip) {
+        pullCommand.withNamedRevision("tip");
+      }
+      pullCommand.call();
       return settings.getCmd().call();
     } finally {
       unlockWorkDir(workingDir);
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgRepo.java	Wed Oct 18 13:30:24 2023 +0200
@@ -37,6 +37,7 @@
 public class ServerHgRepo extends HgRepo {
 
   public final static HgVersion REVSET_HG_VERSION = new HgVersion(1, 7, 0);
+  public final static HgVersion MERGE_WITHOUT_CHANGED_FILES_VERSION = new HgVersion(5, 2, 0);
   private final CommandSettingsFactory myCommandSettingsFactory;
   private final ServerPluginConfig myConfig;
   protected final MercurialClasspathTemplate myLogTemplate = new MercurialClasspathTemplate("/buildServerResources/log.template", "hg.log.template");
@@ -99,6 +100,12 @@
     return hgVersion.isEqualsOrGreaterThan(REVSET_HG_VERSION);
   }
 
+  // mercurial > 5.1 no longer reports changed files for the merge commits, see https://youtrack.jetbrains.com/issue/TW-79815
+  public boolean supportsChangedFilesForMerges() throws VcsException {
+    HgVersion hgVersion = getHgVersion();
+    return hgVersion.isLessThan(MERGE_WITHOUT_CHANGED_FILES_VERSION);
+  }
+
   private HgVersion getHgVersion() throws VcsException {
     if (myContext != null)
       return myContext.getHgVersion(this);
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java	Wed Oct 18 13:30:24 2023 +0200
@@ -61,4 +61,6 @@
   boolean runWithProfile(@NotNull HgVcsRoot root);
 
   boolean computeFromRevisions();
+
+  boolean retryPullForIllegalPathComponent();
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java	Wed Oct 18 13:30:24 2023 +0200
@@ -133,4 +133,8 @@
   public boolean computeFromRevisions() {
     return TeamCityProperties.getBooleanOrTrue("teamcity.hg.computeFromRevisions");
   }
+
+  public boolean retryPullForIllegalPathComponent() {
+    return TeamCityProperties.getBooleanOrTrue("teamcity.hg.retryPullForIllegalPathComponent");
+  }
 }
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CollectChangesWithRevsets.java	Wed Oct 18 13:30:24 2023 +0200
@@ -52,9 +52,22 @@
         revsets.append(" + ").append(from);
       }
     }
-    return myRepo.log(myRoot)
+    List<ChangeSet> result = myRepo.log(myRoot)
             .showCommitsFromAllBranches()
             .withRevsets(revsets.toString())
             .call();
+
+    if (!myRepo.supportsChangedFilesForMerges()) {
+      for (ChangeSet cs: result) {
+        if (cs.getParents().size() > 1) {
+          // new version of Mercurial does not include merge commit changed files into the log
+          // let's fetch the files with a separate command (https://youtrack.jetbrains.com/issue/TW-79815)
+          final List<FileStatus> files = myRepo.status().fromRevision(cs.getParents().get(0).getId()).toRevision(cs.getId()).call();
+          cs.setModifiedFiles(files);
+        }
+      }
+    }
+
+    return result;
   }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSharedMirrorsTest.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutWithSharedMirrorsTest.java	Wed Oct 18 13:30:24 2023 +0200
@@ -16,6 +16,7 @@
 
 package jetbrains.buildServer.buildTriggers.vcs.mercurial;
 
+import com.intellij.openapi.util.SystemInfo;
 import jetbrains.buildServer.agent.AgentRunningBuild;
 import jetbrains.buildServer.agent.BuildAgentConfiguration;
 import jetbrains.buildServer.agent.BuildProgressLogger;
@@ -32,6 +33,7 @@
 import org.jetbrains.annotations.NotNull;
 import org.jmock.Expectations;
 import org.jmock.Mockery;
+import org.testng.SkipException;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
@@ -114,6 +116,11 @@
 
 
   public void turn_off_sharing() throws Exception {
+    // TODO: TW-84326: share extension doesn't work as expected on ARM agents
+    if (SystemInfo.isARM64) {
+      throw new SkipException("Ignore test on ARM64 machine");
+    }
+
     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"));
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialSupportBuilder.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialSupportBuilder.java	Wed Oct 18 13:30:24 2023 +0200
@@ -68,7 +68,6 @@
     return vcs;
   }
 
-
   public MercurialSupportBuilder withConfig(@NotNull ServerPluginConfig config) {
     myConfig = config;
     return this;
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Wed Oct 18 13:30:24 2023 +0200
@@ -17,6 +17,7 @@
 
 import com.intellij.openapi.util.SystemInfo;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.*;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.PathIllegalComponentException;
 import jetbrains.buildServer.util.FileUtil;
 import jetbrains.buildServer.util.TestFor;
 import jetbrains.buildServer.vcs.*;
@@ -809,6 +810,26 @@
             CheckoutRules.DEFAULT);
   }
 
+  @TestFor(issues = "TW-83599")
+  public void should_retry_pull_on_illegal_path_component() throws Exception {
+    final VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
+    // we can't realiably force illegal path component errors with real hg repos
+    final RepoFactory failOnceRepoFactory = failOnceRepoFactory(new PathIllegalComponentException("some-component"));
+    myVcs = mercurialSupport().withConfig(myPluginConfig).withRepoFactory(failOnceRepoFactory).build();
+
+    myVcs.syncRepository(vcsRoot);
+  }
+
+  @TestFor(issues = "TW-83599")
+  public void get_content_should_retry_pull_on_illegal_path_component() throws IOException, VcsException {
+    VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
+    // we can't realiably force illegal path component errors with real hg repos
+    final RepoFactory failOnceRepoFactory = failOnceRepoFactory(new PathIllegalComponentException("some-component"));
+    myVcs = mercurialSupport().withConfig(myPluginConfig).withRepoFactory(failOnceRepoFactory).build();
+
+    byte[] content = myVcs.getContent("dir1/subdir/file2.txt", vcsRoot, "4:b06a290a363b");
+    assertEquals(new String(content), "bbb");
+  }
 
   private void assertFiles(final List<String> expectedFiles, final ModificationData modificationData) {
     Set<String> actualFiles = new HashSet<>();
@@ -838,5 +859,36 @@
   public void test_collect_changes_using_checkout_rules() {
     assertTrue(myVcs.getCollectChangesPolicy() instanceof CollectChangesByCheckoutRules);
   }
+
+  @NotNull
+  private RepoFactory failOnceRepoFactory(@NotNull VcsException failure) throws IOException {
+    final CommandSettingsForRootImpl commandSettingsFactory = new CommandSettingsForRootImpl(new TestCommandSettingsFactory(), new ExtensionsWeaver(), new CommandlineViaFileWrapperWeaver());
+
+    return new RepoFactory(myPluginConfig, commandSettingsFactory, myHgPathProvider) {
+      private boolean myDidFail = false;
+
+      @NotNull
+      @Override
+      public ServerHgRepo createRepo(@NotNull HgVcsRoot root, @NotNull File workingDir) throws VcsException {
+        if (myDidFail) {
+          return super.createRepo(root, workingDir);
+        } else {
+          myDidFail = true;
+          final CommandSettingsFactory rootSettingsFactory = commandSettingsFactory.forRoot(root);
+          return new ServerHgRepo(rootSettingsFactory, myConfig, workingDir, myHgPathProvider.getHgPath(root), root.getAuthSettings()) {
+            @Override
+            public PullCommand pull() {
+              return new PullCommand(rootSettingsFactory.create(), myHgPath, myWorkingDir, myAuthSettings) {
+                @Override
+                public void call() throws VcsException {
+                  throw failure;
+                }
+              };
+            }
+          };
+        }
+      }
+    };
+  }
 }
 
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java	Wed Oct 18 13:30:24 2023 +0200
@@ -115,6 +115,10 @@
       public boolean computeFromRevisions() {
         return true;
       }
+
+      public boolean retryPullForIllegalPathComponent() {
+        return true;
+      }
     };
   }
 
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java	Tue Oct 17 12:52:06 2023 +0200
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java	Wed Oct 18 13:30:24 2023 +0200
@@ -21,6 +21,7 @@
 import jetbrains.buildServer.ExecResult;
 import jetbrains.buildServer.StreamGobbler;
 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.UnknownRevisionException;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException;
 import jetbrains.buildServer.util.TestFor;
@@ -136,6 +137,16 @@
     }
   }
 
+  public void should_detect_illegal_path_component() throws VcsException {
+    final CommandResult commandResult = commandResultFor(execResult().withStderr("abort: path contains illegal component: /_mercurial_classpath_template.java.i"));
+    try {
+      commandResult.checkCommandFailed();
+      fail("expected PathIllegalComponentException to be thrown");
+    } catch (PathIllegalComponentException e) {
+      assertEquals("/_mercurial_classpath_template.java.i", e.getComponent());
+    }
+  }
+
   public void exception_should_not_contain_command_stdout_or_stderr() {
     final String stdout = "300Mb of output";
     final String stderr = "300Mb from stderr";