changeset 280:8c1fd2e565ae

Implement mercurial detection on the agents When agent starts, hg-plugin detects installed hg (searches it in the $PATH). If plugin is able to run hg and hg has an approrpiate version (1.5.2+), then plugin reports path to hg in the 'teamcity.hg.agent.path' parameter. This parameter can be used in the "HG command path" field in a VCS root settings, configurations with such root will be run only on agents which report path to hg. Also user can set this parameter manually in the buildAgent.properties. A server side of plugin first checks value of internal property 'teamcity.hg.server.path' and if property is set, its value is used. Second, plugin tries to use path from the settings of VCS root: if path is equal to '%teamcity.hg.agent.path%' - use 'hg' as path, otherwise use a value from the root. With such order old setups, where path in the VCS root was used on both server and agent, will continue to work. New VCS roots with references in the path will also work if hg is in the $PATH on the server or internal property is set.
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Fri, 19 Aug 2011 15:21:38 +0400
parents f80e17ac2da6
children 8875a57ca5e2
files mercurial-agent/src/META-INF/build-agent-plugin-mercurial.xml mercurial-agent/src/build-agent-plugin.xml mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentHgPathProvider.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgDetector.java mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgPathProvider.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfig.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java mercurial-server/src/META-INF/build-server-plugin-mercurial.xml mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfig.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfigImpl.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProvider.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProviderTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java mercurial-tests/src/testng.xml
diffstat 41 files changed, 530 insertions(+), 105 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial-agent/src/META-INF/build-agent-plugin-mercurial.xml	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-agent/src/META-INF/build-agent-plugin-mercurial.xml	Fri Aug 19 15:21:38 2011 +0400
@@ -3,4 +3,6 @@
 
 <beans default-autowire="constructor">
   <bean id="mercurialAgent" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialAgentSideVcsSupport" />
+  <bean id="hgPathProvider" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.AgentHgPathProvider" />
+  <bean id="hgDetector" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgDetector" />
 </beans>
--- a/mercurial-agent/src/build-agent-plugin.xml	Thu Aug 11 09:33:20 2011 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-<!--
-PicoContainer configuration for old fashioned TeamCity agent plugins.
-In TeamCity 4.0 Spring configuration must be created instead.
--->
-<container>
-  <component class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialAgentSideVcsSupport" />
-</container>
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentHgPathProvider.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,32 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.agent.BuildAgentConfiguration;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import jetbrains.buildServer.parameters.ProcessingResult;
+import jetbrains.buildServer.parameters.ValueResolver;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class AgentHgPathProvider implements HgPathProvider {
+
+  private final ValueResolver myResolver;
+
+
+  public AgentHgPathProvider(@NotNull final BuildAgentConfiguration agentConfig) {
+    myResolver = agentConfig.getParametersResolver();
+  }
+
+
+  public String getHgPath(@NotNull final Settings settings) {
+    String pathFromRoot = settings.getHgPath();
+    return resolve(pathFromRoot);
+  }
+
+
+  private String resolve(@NotNull final String value) {
+    ProcessingResult result = myResolver.resolve(value);
+    return result.getResult();
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgDetector.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,90 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import com.intellij.openapi.diagnostic.Logger;
+import jetbrains.buildServer.agent.AgentLifeCycleAdapter;
+import jetbrains.buildServer.agent.AgentLifeCycleListener;
+import jetbrains.buildServer.agent.BuildAgent;
+import jetbrains.buildServer.agent.BuildAgentConfiguration;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommand;
+import jetbrains.buildServer.util.EventDispatcher;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * @author dmitry.neverov
+ */
+public class HgDetector extends AgentLifeCycleAdapter {
+
+  final static String AGENT_HG_PATH_PROPERTY = "teamcity.hg.agent.path";
+  private final static Logger LOG = Logger.getInstance(HgDetector.class.getName());
+  private final static HgVersion LEAST_SUPPORTED_VERSION = new HgVersion(1, 5, 2);
+  private final List<String> myHgPaths = Arrays.asList("hg");
+
+
+  public HgDetector(@NotNull final EventDispatcher<AgentLifeCycleListener> dispatcher) {
+    dispatcher.addListener(this);
+  }
+
+
+  @Override
+  public void beforeAgentConfigurationLoaded(@NotNull final BuildAgent agent) {
+    BuildAgentConfiguration config = agent.getConfiguration();
+    String agentHgPath = config.getConfigurationParameters().get(AGENT_HG_PATH_PROPERTY);
+    File workDir = config.getTempDirectory();
+    if (agentHgPath == null) {
+      String detectedHg = detectHg(workDir);
+      if (detectedHg != null) {
+        LOG.info("Detect installed mercurial at path " + detectedHg + ", provide it as a property " + AGENT_HG_PATH_PROPERTY);
+        config.addConfigurationParameter(AGENT_HG_PATH_PROPERTY, "hg");
+      } else {
+        LOG.info("Cannot detect installed mercurial");
+      }
+    } else {
+      if (!canRunHg(agentHgPath, workDir, true))
+        LOG.warn("Mercurial executable at path " + agentHgPath + " cannot be run or not compatible with TeamCity");
+    }
+  }
+
+
+  @Nullable
+  private String detectHg(@NotNull final File workDir) {
+    for (String path : myHgPaths) {
+      if (canRunHg(path, workDir))
+        return path;
+    }
+    return null;
+  }
+
+
+  private boolean canRunHg(@NotNull final String hgPath, @NotNull final File workDir) {
+    return canRunHg(hgPath, workDir, false);
+  }
+
+  private boolean canRunHg(@NotNull final String hgPath, @NotNull final File workDir, boolean logWarnings) {
+    VersionCommand versionCommand = new VersionCommand(hgPath, workDir);
+    try {
+      HgVersion version = versionCommand.execute();
+      if (isCompatible(version)) {
+        return true;
+      } else {
+        if (logWarnings)
+          LOG.warn("Mercurial version at path " + hgPath + " is " + version + ", required version is " + LEAST_SUPPORTED_VERSION + "+");
+        return false;
+      }
+    } catch (VcsException e) {
+      if (logWarnings)
+        LOG.warn("Error while trying to get hg version, hg path " + hgPath + ", error: " + e.getMessage());
+      return false;
+    }
+  }
+
+
+  private boolean isCompatible(@NotNull final HgVersion version) {
+    return version.isEqualsOrGreaterThan(LEAST_SUPPORTED_VERSION);
+  }
+}
--- a/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-agent/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialAgentSideVcsSupport.java	Fri Aug 19 15:21:38 2011 +0400
@@ -39,19 +39,23 @@
 public class MercurialAgentSideVcsSupport extends AgentVcsSupport implements UpdateByIncludeRules2 {
 
   private final MirrorManager myMirrorManager;
+  private final HgPathProvider myHgPathProvider;
 
-  public MercurialAgentSideVcsSupport(BuildAgentConfiguration agentConfiguration) {
+  public MercurialAgentSideVcsSupport(@NotNull final BuildAgentConfiguration agentConfiguration,
+                                      @NotNull final HgPathProvider hgPathProvider) {
     myMirrorManager = new MirrorManager(agentConfiguration.getCacheDirectory("mercurial"));
+    myHgPathProvider = hgPathProvider;
   }
 
   public IncludeRuleUpdater getUpdater(@NotNull final VcsRoot vcsRoot, @NotNull final CheckoutRules checkoutRules, @NotNull final String toVersion, @NotNull final File checkoutDirectory, @NotNull final AgentRunningBuild build, boolean cleanCheckoutRequested) throws VcsException {
     final BuildProgressLogger logger = build.getBuildLogger();
     final boolean useLocalMirrors = isUseLocalMirrors(build);
+
     return new IncludeRuleUpdater() {
       public void process(@NotNull final IncludeRule includeRule, @NotNull final File workingDir) throws VcsException {
         try {
           checkRuleIsValid(includeRule);
-          Settings settings = new Settings(vcsRoot);
+          Settings settings = new Settings(myHgPathProvider, vcsRoot);
           if (useLocalMirrors) {
             updateLocalMirror(vcsRoot, logger);
           }
@@ -124,7 +128,7 @@
   }
 
   private void updateLocalMirror(VcsRoot root, BuildProgressLogger logger) throws VcsException, IOException {
-    Settings settings = new Settings(root);
+    Settings settings = new Settings(myHgPathProvider, root);
     File mirrorDir = myMirrorManager.getMirrorDir(settings.getRepositoryUrl());
     logger.message("Update local mirror at " + mirrorDir);
     if (!Settings.isValidRepository(mirrorDir)) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgPathProvider.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,13 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public interface HgPathProvider {
+
+  String getHgPath(@NotNull Settings settings);
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfig.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,10 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+/**
+ * @author dmitry.neverov
+ */
+public interface PluginConfig {
+
+  String getHgPath();
+
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -24,25 +24,21 @@
 
 import java.io.File;
 import java.util.Collections;
-import java.util.Set;
 
 /**
  * @author pavel
  */
 public class BaseCommand {
-  private final Settings mySettings;
+
+  private final String myHgPath;
   private final File myWorkDirectory;
 
-  public BaseCommand(@NotNull final Settings settings, @NotNull File workingDir) {
-    mySettings = settings;
+  public BaseCommand(@NotNull final String hgPath, @NotNull File workingDir) {
+    myHgPath = hgPath;
     myWorkDirectory = workingDir;
   }
 
 
-  public Settings getSettings() {
-    return mySettings;
-  }
-
   public File getWorkDirectory() {
     return myWorkDirectory;
   }
@@ -84,7 +80,7 @@
    * @param cli command line in which to setup hg executable
    */
   private void setupExecutable(GeneralCommandLine cli) {
-    if (SystemInfo.isWindows && getSettings().getHgCommandPath().equals("hg")) {
+    if (SystemInfo.isWindows && myHgPath.equals("hg")) {
       setupCmd(cli);
     } else {
       setupHg(cli);
@@ -98,15 +94,15 @@
   }
 
   private void setupHg(GeneralCommandLine cli) {
-    cli.setExePath(getSettings().getHgCommandPath());
+    cli.setExePath(myHgPath);
   }
 
   protected ExecResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException {
-    return CommandUtil.runCommand(cli, getPrivateData());
+    return CommandUtil.runCommand(cli, Collections.<String>emptySet());
   }
 
   protected ExecResult runCommand(@NotNull GeneralCommandLine cli, int executionTimeout) throws VcsException {
-    return CommandUtil.runCommand(cli, executionTimeout, getPrivateData());
+    return CommandUtil.runCommand(cli, executionTimeout, Collections.<String>emptySet());
   }
 
   protected void failIfNotEmptyStdErr(@NotNull GeneralCommandLine cli, @NotNull ExecResult res) throws VcsException {
@@ -120,8 +116,4 @@
       CommandUtil.commandFailed(cli.getCommandLineString(), res);
     }
   }
-
-  public Set<String> getPrivateData() {
-    return Collections.singleton(mySettings.getPassword());
-  }
 }
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BranchesCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -30,7 +30,7 @@
  * @author Pavel.Sher
  *         Date: 26.10.2008
  */
-public class BranchesCommand extends BaseCommand {
+public class BranchesCommand extends VcsRootCommand {
 
   public BranchesCommand(@NotNull Settings settings, @NotNull File workingDir) {
     super(settings, workingDir);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -26,7 +26,7 @@
 import java.util.List;
 import java.util.Queue;
 
-public class CatCommand extends BaseCommand {
+public class CatCommand extends VcsRootCommand {
   private String myRevId;
   private final static int MAX_CMD_LEN = 900;
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ChangedFilesCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -28,7 +28,7 @@
 /**
  * @author Pavel.Sher
  */
-public class ChangedFilesCommand extends BaseCommand {
+public class ChangedFilesCommand extends VcsRootCommand {
   private String myRevId;
 
   public ChangedFilesCommand(@NotNull Settings settings, @NotNull File workingDir) {
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -22,7 +22,7 @@
 
 import java.io.File;
 
-public class CloneCommand extends BaseCommand{
+public class CloneCommand extends VcsRootCommand {
   private String myToId;
   private boolean myUpdateWorkingDir = true;
   private String myRepository;
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -26,7 +26,7 @@
  * @author Pavel.Sher
  *         Date: 16.07.2008
  */
-public class IdentifyCommand extends BaseCommand {
+public class IdentifyCommand extends VcsRootCommand {
 
   private boolean myInLocalRepository = false;
   private ChangeSet myChangeSet;
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Init.java	Fri Aug 19 15:21:38 2011 +0400
@@ -10,7 +10,7 @@
 /**
  * @author dmitry.neverov
  */
-public class Init extends BaseCommand {
+public class Init extends VcsRootCommand {
 
   private final String myDefaultPullUrl;
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/LogCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -29,7 +29,7 @@
 import java.util.List;
 import java.util.Locale;
 
-public class LogCommand extends BaseCommand {
+public class LogCommand extends VcsRootCommand {
 
   private final static Logger LOG = Logger.getInstance(LogCommand.class.getName());
   private final static String ZERO_PARENT_ID = "0000000000000000000000000000000000000000";
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -25,7 +25,7 @@
  * @author Pavel.Sher
  *         Date: 14.07.2008
  */
-public class PullCommand extends BaseCommand {
+public class PullCommand extends VcsRootCommand {
 
   public PullCommand(@NotNull Settings settings, @NotNull File workingDir) {
     super(settings, workingDir);
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -25,7 +25,7 @@
 /**
  * @author pavel
  */
-public class PushCommand extends BaseCommand {
+public class PushCommand extends VcsRootCommand {
   private boolean myForced;
 
   public PushCommand(@NotNull Settings settings, @NotNull File workingDir) {
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/Settings.java	Fri Aug 19 15:21:38 2011 +0400
@@ -16,6 +16,7 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgPathProvider;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.PathUtil;
 import jetbrains.buildServer.log.Loggers;
 import jetbrains.buildServer.util.StringUtil;
@@ -35,6 +36,8 @@
  * Represents Mercurial repository settings
  */
 public class Settings {
+
+  private final HgPathProvider myHgPathProvider;
   private String myRepository;
   private String myHgCommandPath;
   private File myCustomWorkingDir;
@@ -45,7 +48,8 @@
   private static final String DEFAULT_BRANCH_NAME = "default";
   private String myCustomClonePath;
 
-  public Settings(@NotNull VcsRoot vcsRoot) {
+  public Settings(@NotNull final HgPathProvider hgPathProvider, @NotNull final VcsRoot vcsRoot) {
+    myHgPathProvider = hgPathProvider;
     myRepository = vcsRoot.getProperty(Constants.REPOSITORY_PROP);
     myHgCommandPath = vcsRoot.getProperty(Constants.HG_COMMAND_PATH_PROP);
     myBranchName = vcsRoot.getProperty(Constants.BRANCH_NAME_PROP);
@@ -85,11 +89,17 @@
   }
 
   /**
-   * Returns path to hg command
-   * @return path to hg command
+   * @return path to hg command taking into account server-wide/agent-wide settings
    */
   @NotNull
   public String getHgCommandPath() {
+    return myHgPathProvider.getHgPath(this);
+  }
+
+  /**
+   * @return path to hg command as it is set in VCS root settings
+   */
+  public String getHgPath() {
     return myHgCommandPath;
   }
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/StatusCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -24,7 +24,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
-public class StatusCommand extends BaseCommand {
+public class StatusCommand extends VcsRootCommand {
   private String myFromId;
   private String myToId;
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/TagCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -21,7 +21,7 @@
 
 import java.io.File;
 
-public class TagCommand extends BaseCommand {
+public class TagCommand extends VcsRootCommand {
   private String myTag;
   private String myRevId;
 
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -21,7 +21,7 @@
 
 import java.io.File;
 
-public class UpdateCommand extends BaseCommand {
+public class UpdateCommand extends VcsRootCommand {
 
   private static final int UPDATE_TIMEOUT_SECONDS = 8 * 3600;//8 hours
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VcsRootCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,44 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
+
+import com.intellij.execution.configurations.GeneralCommandLine;
+import jetbrains.buildServer.ExecResult;
+import jetbrains.buildServer.vcs.VcsException;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.File;
+import java.util.Collections;
+import java.util.Set;
+
+/**
+ * @author dmitry.neverov
+ */
+public class VcsRootCommand extends BaseCommand {
+
+  private final Settings mySettings;
+
+
+  public VcsRootCommand(@NotNull final Settings settings, @NotNull final File workDir) {
+    super(settings.getHgCommandPath(), workDir);
+    mySettings = settings;
+  }
+
+
+  public Settings getSettings() {
+    return mySettings;
+  }
+
+
+  public Set<String> getPrivateData() {
+    return Collections.singleton(mySettings.getPassword());
+  }
+
+
+  protected ExecResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException {
+    return CommandUtil.runCommand(cli, getPrivateData());
+  }
+
+
+  protected ExecResult runCommand(@NotNull GeneralCommandLine cli, int executionTimeout) throws VcsException {
+    return CommandUtil.runCommand(cli, executionTimeout, getPrivateData());
+  }
+}
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommand.java	Fri Aug 19 15:21:38 2011 +0400
@@ -14,7 +14,12 @@
 public class VersionCommand extends BaseCommand {
 
   public VersionCommand(@NotNull final Settings settings, @NotNull File workingDir) {
-    super(settings, workingDir);
+    super(settings.getHgCommandPath(), workingDir);
+  }
+
+
+  public VersionCommand(@NotNull final String hgPath, @NotNull File workingDir) {
+    super(hgPath, workingDir);
   }
 
 
--- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml	Fri Aug 19 15:21:38 2011 +0400
@@ -3,6 +3,7 @@
 
 <beans default-autowire="constructor">
   <bean id="mercurialServer" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MercurialVcsSupport" />
-  <bean id="config" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.PluginConfigImpl" />
+  <bean id="config" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerPluginConfigImpl" />
   <bean id="commandFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.CommandFactory" />
+  <bean id="hgPathProvider" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProvider"/>
 </beans>
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java	Fri Aug 19 15:21:38 2011 +0400
@@ -59,19 +59,22 @@
   private VcsManager myVcsManager;
   private File myDefaultWorkFolderParent;
   private MirrorManager myMirrorManager;
-  private final PluginConfig myConfig;
+  private final ServerPluginConfig myConfig;
+  private final HgPathProvider myHgPathProvider;
   private final CommandFactory myCommandFactory;
 
   public MercurialVcsSupport(@NotNull final VcsManager vcsManager,
                              @NotNull final ServerPaths paths,
                              @NotNull final SBuildServer server,
                              @NotNull final EventDispatcher<BuildServerListener> dispatcher,
-                             @NotNull final PluginConfig config,
+                             @NotNull final ServerPluginConfig config,
+                             @NotNull final HgPathProvider hgPathProvider,
                              @NotNull final CommandFactory commandFactory) {
     myVcsManager = vcsManager;
     myDefaultWorkFolderParent = new File(paths.getCachesDir(), "mercurial");
     myMirrorManager = new MirrorManager(myDefaultWorkFolderParent);
     myConfig = config;
+    myHgPathProvider = hgPathProvider;
     myCommandFactory = commandFactory;
     dispatcher.addListener(new BuildServerAdapter() {
       @Override
@@ -94,6 +97,15 @@
         });
       }
     });
+    logUsedHg();
+  }
+
+  private void logUsedHg() {
+    String hgPath = myConfig.getHgPath();
+    if (hgPath != null)
+      Loggers.VCS.info("Use server-wide hg path " + hgPath + ", path in the VCS root settings will be ignored");
+    else
+      Loggers.VCS.info("Server-wide hg path is not set, will use path from the VCS root settings");
   }
 
   private void deleteWithLocking(Collection<File> filesForDelete) {
@@ -727,7 +739,7 @@
   }
 
   private Settings createSettings(final VcsRoot root) throws VcsException {
-    Settings settings = new Settings(root);
+    Settings settings = new Settings(myHgPathProvider, root);
     String customClonePath = settings.getCustomClonePath();
     if (!StringUtil.isEmptyOrSpaces(customClonePath) && !myDefaultWorkFolderParent.equals(new File(customClonePath).getAbsoluteFile())) {
       File parentDir = new File(customClonePath);
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfig.java	Thu Aug 11 09:33:20 2011 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial;
-
-/**
- * @author dmitry.neverov
- */
-public interface PluginConfig {
-
-  public boolean isUsePullProtocol();
-
-}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/PluginConfigImpl.java	Thu Aug 11 09:33:20 2011 +0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-package jetbrains.buildServer.buildTriggers.vcs.mercurial;
-
-import jetbrains.buildServer.serverSide.TeamCityProperties;
-
-/**
- * @author dmitry.neverov
- */
-public class PluginConfigImpl implements PluginConfig {
-
-  public boolean isUsePullProtocol() {
-    return TeamCityProperties.getBooleanOrTrue("teamcity.hg.use.pull.protocol");
-  }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProvider.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,39 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ServerHgPathProvider implements HgPathProvider {
+
+  private final ServerPluginConfig myConfig;
+
+
+  public ServerHgPathProvider(@NotNull final ServerPluginConfig config) {
+    myConfig = config;
+  }
+
+
+  public String getHgPath(@NotNull final Settings settings) {
+    String serverWideHgPath = myConfig.getHgPath();
+    if (serverWideHgPath != null) {
+      return serverWideHgPath;
+    } else {
+      String pathFromRoot = settings.getHgPath();
+      if (pathFromRoot.equals(unresolvedAgentHgPath())) {
+        //try to use hg from the PATH:
+        return "hg";
+      } else {
+        return pathFromRoot;
+      }
+    }
+  }
+
+
+  private String unresolvedAgentHgPath() {
+    //Use hard-coded value here in order to not add dependency on agent part of plugin:
+    return "%teamcity.hg.agent.path%";
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfig.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,10 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+/**
+ * @author dmitry.neverov
+ */
+public interface ServerPluginConfig extends PluginConfig {
+
+  public boolean isUsePullProtocol();
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,19 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.serverSide.TeamCityProperties;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ServerPluginConfigImpl implements ServerPluginConfig {
+
+  public boolean isUsePullProtocol() {
+    return TeamCityProperties.getBooleanOrTrue("teamcity.hg.use.pull.protocol");
+  }
+
+  @Nullable
+  public String getHgPath() {
+    return TeamCityProperties.getPropertyOrNull("teamcity.hg.server.path");
+  }
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/AgentSideCheckoutTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -18,11 +18,16 @@
 import jetbrains.buildServer.agent.AgentRunningBuild;
 import jetbrains.buildServer.agent.BuildAgentConfiguration;
 import jetbrains.buildServer.agent.BuildProgressLogger;
+import jetbrains.buildServer.parameters.ProcessingResult;
+import jetbrains.buildServer.parameters.ReferencesResolverUtil;
+import jetbrains.buildServer.parameters.ValueResolver;
 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;
@@ -40,6 +45,8 @@
  */
 @Test
 public class AgentSideCheckoutTest extends BaseMercurialTestCase {
+
+  private final static String HG_PATH_REFERENCE = "%" + HgDetector.AGENT_HG_PATH_PROPERTY + "%";
   private MercurialAgentSideVcsSupport myVcsSupport;
   private File myWorkDir;
   private File myMirrorsRootDir;
@@ -59,9 +66,10 @@
     final BuildAgentConfiguration agentConfig = myContext.mock(BuildAgentConfiguration.class);
     myContext.checking(new Expectations() {{
       allowing(agentConfig).getCacheDirectory("mercurial"); will(returnValue(myMirrorsRootDir));
+      allowing(agentConfig).getParametersResolver(); will(returnValue(new HgPathResolver()));
     }});
 
-    myVcsSupport = new MercurialAgentSideVcsSupport(agentConfig);
+    myVcsSupport = new MercurialAgentSideVcsSupport(agentConfig, new AgentHgPathProvider(agentConfig));
 
     myLogger = myContext.mock(BuildProgressLogger.class);
     myContext.checking(new Expectations() {{
@@ -72,6 +80,14 @@
 
   }
 
+  public void should_work_when_path_to_hg_is_property() throws Exception {
+    VcsRootImpl root = new VcsRootBuilder()
+            .repository(LocalRepositoryUtil.prepareRepository(simpleRepo()).getAbsolutePath())
+            .hgPath(HG_PATH_REFERENCE).build();
+    testUpdate(root, "4:b06a290a363b", "cleanPatch1/after", new IncludeRule(".", ".", null));
+  }
+
+
   public void checkout_on_agent() throws IOException, VcsException {
     testUpdate(createVcsRoot(simpleRepo()), "4:b06a290a363b", "cleanPatch1/after", new IncludeRule(".", ".", null));
   }
@@ -222,4 +238,58 @@
   protected String getTestDataPath() {
     return "mercurial-tests/testData";
   }
+
+
+  private static class HgPathResolver implements ValueResolver {
+    @NotNull
+    public ProcessingResult resolve(@NotNull String value) {
+      if (ReferencesResolverUtil.containsReference(value)) {
+        if (value.equals(HG_PATH_REFERENCE)) {
+          try {
+            return new ResolvedPath(Util.getHgPath());
+          } catch (IOException e) {
+            return new Unresolved(value);
+          }
+        } else {
+          throw new IllegalArgumentException("Value resolver is asked to resolve " + value);
+        }
+      } else {
+        return new ResolvedPath(value);
+      }
+    }
+  }
+
+  private static class ResolvedPath implements ProcessingResult {
+    private final String myPath;
+    ResolvedPath(final @NotNull String path) {
+      myPath = path;
+    }
+    public boolean isModified() {
+      return true;
+    }
+    @NotNull
+    public String getResult() {
+      return myPath;
+    }
+    public boolean isFullyResolved() {
+      return true;
+    }
+  }
+
+  private static class Unresolved implements ProcessingResult {
+    private final String myUnresolvedValue;
+    Unresolved(@NotNull final String value) {
+      myUnresolvedValue = value;
+    }
+    public boolean isModified() {
+      return false;
+    }
+    @NotNull
+    public String getResult() {
+      return myUnresolvedValue;
+    }
+    public boolean isFullyResolved() {
+      return false;
+    }
+  }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -46,7 +46,7 @@
   private MercurialVcsSupport myVcs;
   private ServerPaths myServerPaths;
   private String myRep2Path = new File("mercurial-tests/testData/rep2").getAbsolutePath();
-
+  private ServerPluginConfig myPluginConfig;
 
   @BeforeMethod
   protected void setUp() throws Exception {
@@ -63,7 +63,8 @@
     File systemDir = myTempFiles.createTempDir();
     myServerPaths = new ServerPaths(systemDir.getAbsolutePath(), systemDir.getAbsolutePath(), systemDir.getAbsolutePath());
     assertTrue(new File(myServerPaths.getCachesDir()).mkdirs());
-    myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), myServerPaths, (SBuildServer)serverMock.proxy(), dispatcher, createPluginConfig(), new CommandFactory(myServerPaths));
+    myPluginConfig = createPluginConfig();
+    myVcs = new MercurialVcsSupport((VcsManager)vcsManagerMock.proxy(), myServerPaths, (SBuildServer)serverMock.proxy(), dispatcher, myPluginConfig, new ServerHgPathProvider(myPluginConfig), new CommandFactory(myServerPaths));
   }
 
   protected String getTestDataPath() {
@@ -337,15 +338,15 @@
     VcsRootImpl vcsRoot = createVcsRoot(simpleRepo());
     String repPath = vcsRoot.getProperty(Constants.REPOSITORY_PROP);
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath + "#test_branch");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = new Settings(new ServerHgPathProvider(myPluginConfig), vcsRoot);
     assertEquals("test_branch", settings.getBranchName());
 
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath + "#");
-    settings = new Settings(vcsRoot);
+    settings = new Settings(new ServerHgPathProvider(myPluginConfig), vcsRoot);
     assertEquals("default", settings.getBranchName());
 
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, repPath);
-    settings = new Settings(vcsRoot);
+    settings = new Settings(new ServerHgPathProvider(myPluginConfig), vcsRoot);
     assertEquals("default", settings.getBranchName());
   }
 
@@ -443,7 +444,7 @@
     VcsRootImpl root = new VcsRootImpl(1, Constants.VCS_NAME);
     root.addAllProperties(myVcs.getDefaultVcsProperties());
     root.addProperty(Constants.REPOSITORY_PROP, "http://host.com/path");
-    Settings settings = new Settings(root);
+    Settings settings = new Settings(new ServerHgPathProvider(myPluginConfig), root);
     assertFalse(settings.isUncompressedTransfer());
   }
 
@@ -502,11 +503,15 @@
     assertTrue(myVcs.getCollectChangesPolicy() instanceof CollectChangesByCheckoutRules);
   }
 
-  private PluginConfig createPluginConfig() {
-    return new PluginConfig() {
+  private ServerPluginConfig createPluginConfig() {
+    return new ServerPluginConfig() {
       public boolean isUsePullProtocol() {
         return true;
       }
+
+      public String getHgPath() {
+        return null;
+      }
     };
   }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerHgPathProviderTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,54 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import jetbrains.buildServer.vcs.impl.VcsRootImpl;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+import static org.testng.AssertJUnit.assertEquals;
+
+/**
+ * @author dmitry.neverov
+ */
+@Test
+public class ServerHgPathProviderTest {
+
+  private String myServerWideHgPath;
+  private String myVcsRootHgPath;
+
+
+  @BeforeMethod
+  public void setUp() {
+    myServerWideHgPath = null;
+    myVcsRootHgPath = "/vcs/root/hg/path";
+  }
+
+
+  public void server_should_use_settings_from_vcs_root_if_server_wide_path_is_not_set() throws Exception {
+    myServerWideHgPath = null;
+    HgPathProvider provider = createHgPathProvider();
+    Settings settings = createSettings(provider);
+    assertEquals(myVcsRootHgPath, provider.getHgPath(settings));
+  }
+
+
+  public void server_should_use_server_wide_path_if_it_is_set() throws Exception {
+    myServerWideHgPath = "/server-wide/hg/path";
+    HgPathProvider provider = createHgPathProvider();
+    Settings settings = createSettings(provider);
+    assertEquals(myServerWideHgPath, provider.getHgPath(settings));
+  }
+
+
+  private ServerHgPathProvider createHgPathProvider() {
+    ServerPluginConfig config = new ServerPluginConfigBuilder().hgPath(myServerWideHgPath).build();
+    return new ServerHgPathProvider(config);
+  }
+
+
+  private Settings createSettings(HgPathProvider hgPathProvider) throws Exception {
+    VcsRootImpl root = new VcsRootBuilder().hgPath(myVcsRootHgPath).build();
+    return new Settings(hgPathProvider, root);
+  }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigBuilder.java	Fri Aug 19 15:21:38 2011 +0400
@@ -0,0 +1,37 @@
+package jetbrains.buildServer.buildTriggers.vcs.mercurial;
+
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @author dmitry.neverov
+ */
+public class ServerPluginConfigBuilder {
+
+  private boolean myUsePullProtocol = true;
+  private String myHgPath;
+
+
+  @NotNull
+  public ServerPluginConfig build() {
+    return new ServerPluginConfig() {
+      public boolean isUsePullProtocol() {
+        return myUsePullProtocol;
+      }
+
+      public String getHgPath() {
+        return myHgPath;
+      }
+    };
+  }
+
+
+  public ServerPluginConfigBuilder userPullProtocol(boolean doUse) {
+    myUsePullProtocol = doUse;
+    return this;
+  }
+
+  public ServerPluginConfigBuilder hgPath(String hgPath) {
+    myHgPath = hgPath;
+    return this;
+  }
+}
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/SettingsTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -17,8 +17,10 @@
 
 import jetbrains.buildServer.TempFiles;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.Settings;
+import jetbrains.buildServer.vcs.VcsRoot;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import junit.framework.TestCase;
+import org.jetbrains.annotations.NotNull;
 import org.testng.annotations.AfterMethod;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
@@ -46,64 +48,64 @@
 
   public void test_url_without_credentials() {
     VcsRootImpl vcsRoot = createVcsRoot("http://host.com/path");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("http://user:pwd@host.com/path", settings.getRepositoryUrl());
   }
 
   public void test_url_with_credentials() {
     VcsRootImpl vcsRoot = createVcsRoot("http://user:pwd@host.com/path");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("http://user:pwd@host.com/path", settings.getRepositoryUrl());
   }
 
   public void test_url_with_username() {
     VcsRootImpl vcsRoot = createVcsRoot("http://user@host.com/path");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("http://user:pwd@host.com/path", settings.getRepositoryUrl());
   }
 
   public void test_url_with_at_after_slash() {
     VcsRootImpl vcsRoot = createVcsRoot("http://host.com/path@");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("http://user:pwd@host.com/path@", settings.getRepositoryUrl());
   }
 
   public void test_url_with_at_in_username() {
     VcsRootImpl vcsRoot = createVcsRoot("http://host.com/path", "my.name@gmail.com", "1234");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("http://my.name%40gmail.com:1234@host.com/path", settings.getRepositoryUrl());
   }
 
   /** TW-13768 */
   public void test_underscore_in_host() {
 		VcsRootImpl vcsRoot = createVcsRoot("http://Klekovkin.SDK_GARANT:8000/", "my.name@gmail.com", "1234");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
 		assertEquals("http://my.name%40gmail.com:1234@Klekovkin.SDK_GARANT:8000/", settings.getRepositoryUrl());
 	}
 
   /** TW-13768 */
   public void test_underscore_in_host_with_credentials_in_url() {
     VcsRootImpl vcsRoot = createVcsRoot("http://me:mypass@Klekovkin.SDK_GARANT:8000/");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
 		assertEquals("http://me:mypass@Klekovkin.SDK_GARANT:8000/", settings.getRepositoryUrl());
   }
 
   public void test_windows_path() throws Exception {
     VcsRootImpl vcsRoot = createVcsRoot("c:\\windows\\path");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("c:\\windows\\path", settings.getRepositoryUrl());
   }
 
   public void test_file_scheme_has_no_credentials() {
     VcsRootImpl vcsRoot = createVcsRoot("file:///path/to/repo", "my.name@gmail.com", "1234");
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = createSettings(vcsRoot);
     assertEquals("file:///path/to/repo", settings.getRepositoryUrl());
   }
 
   public void uncompressed_transfer() {
     VcsRootImpl root = createVcsRoot("http://host.com/path");
     root.addProperty(Constants.UNCOMPRESSED_TRANSFER, "true");
-    Settings settings = new Settings(root);
+    Settings settings = createSettings(root);
     assertTrue(settings.isUncompressedTransfer());
   }
 
@@ -119,4 +121,8 @@
     vcsRoot.addProperty(Constants.PASSWORD, password);
     return vcsRoot;
   }
+
+  private Settings createSettings(@NotNull final VcsRoot root) {
+    return new Settings(new ServerHgPathProvider(new ServerPluginConfigImpl()), root);
+  }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/VcsRootBuilder.java	Fri Aug 19 15:21:38 2011 +0400
@@ -15,11 +15,12 @@
   private String myPassword;
   private String myBranch;
   private long myRootId = 1L;
+  private String myHgPath;
 
   public VcsRootImpl build() throws IOException {
     VcsRootImpl vcsRoot = new VcsRootImpl(1, Constants.VCS_NAME);
     vcsRoot.addProperty(Constants.REPOSITORY_PROP, myRepository);
-    vcsRoot.addProperty(Constants.HG_COMMAND_PATH_PROP, Util.getHgPath());
+    vcsRoot.addProperty(Constants.HG_COMMAND_PATH_PROP, myHgPath != null ? myHgPath : Util.getHgPath());
     vcsRoot.addProperty(Constants.USERNAME, myUsername);
     vcsRoot.addProperty(Constants.PASSWORD, myPassword);
     vcsRoot.addProperty(Constants.BRANCH_NAME_PROP, myBranch);
@@ -55,4 +56,10 @@
     myRootId = rootId;
     return this;
   }
+
+
+  public VcsRootBuilder hgPath(String hgPath) {
+    myHgPath = hgPath;
+    return this;
+  }
 }
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -2,8 +2,6 @@
 
 import com.intellij.execution.configurations.GeneralCommandLine;
 import com.intellij.openapi.util.SystemInfo;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder;
-import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import junit.framework.TestCase;
 import org.testng.annotations.Test;
 
@@ -17,11 +15,8 @@
 public class BaseCommandTest extends TestCase {
 
   public void should_quote_command_line_arguments() throws IOException {
-    VcsRootImpl root = new VcsRootBuilder().repository("http://some.org/repo.hg").build();
     File workingDir = new File("some dir");
-    Settings settings = new Settings(root);
-
-    BaseCommand command = new BaseCommand(settings, workingDir);
+    BaseCommand command = new BaseCommand("/path/to/hg", workingDir);
     GeneralCommandLine cl = command.createCommandLine();
     cl.addParameter("param with spaces");
     cl.addParameter("param with quote \" rm -rf /");
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommandTestCase.java	Fri Aug 19 15:21:38 2011 +0400
@@ -17,10 +17,7 @@
 
 import jetbrains.buildServer.BaseTestCase;
 import jetbrains.buildServer.TempFiles;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.LocalRepositoryUtil;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.MirrorManager;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.Util;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.*;
 import jetbrains.buildServer.vcs.VcsException;
 import jetbrains.buildServer.vcs.VcsRoot;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
@@ -76,7 +73,7 @@
 
     MirrorManager mirrorManager = new MirrorManager(parentDir);
     VcsRoot vcsRoot = new VcsRootImpl(1, vcsRootProps);
-    Settings settings = new Settings(vcsRoot);
+    Settings settings = new Settings(new ServerHgPathProvider(new ServerPluginConfigImpl()), vcsRoot);
     final File workingDir = mirrorManager.getMirrorDir(settings.getRepositoryUrl());
     settings.setCustomWorkingDir(workingDir);
     try {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommandTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -2,9 +2,7 @@
 
 import jetbrains.buildServer.BaseTestCase;
 import jetbrains.buildServer.TempFiles;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.Constants;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.LocalRepositoryUtil;
-import jetbrains.buildServer.buildTriggers.vcs.mercurial.Util;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.*;
 import jetbrains.buildServer.vcs.VcsException;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import org.testng.annotations.AfterMethod;
@@ -44,7 +42,7 @@
     root.addProperty(Constants.HG_COMMAND_PATH_PROP, Util.getHgPath());
 
     File workingDir = myTempFiles.createTempDir();
-    Settings settings = new Settings(root);
+    Settings settings = new Settings(new ServerHgPathProvider(new ServerPluginConfigImpl()), root);
     settings.setCustomWorkingDir(workingDir);
 
     CloneCommand clone = new CloneCommand(settings, workingDir);
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/VersionCommandTest.java	Fri Aug 19 15:21:38 2011 +0400
@@ -1,6 +1,8 @@
 package jetbrains.buildServer.buildTriggers.vcs.mercurial.command;
 
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVersion;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProvider;
+import jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerPluginConfigImpl;
 import jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder;
 import jetbrains.buildServer.vcs.impl.VcsRootImpl;
 import junit.framework.TestCase;
@@ -16,7 +18,7 @@
 
   public void test() throws Exception {
     VcsRootImpl root = new VcsRootBuilder().repository("some/repository").build();
-    Settings settings = new Settings(root);
+    Settings settings = new Settings(new ServerHgPathProvider(new ServerPluginConfigImpl()), root);
     VersionCommand versionCommand = new VersionCommand(settings, new File(".."));
     HgVersion version = versionCommand.execute();
     assertNotNull(version);
--- a/mercurial-tests/src/testng.xml	Thu Aug 11 09:33:20 2011 +0400
+++ b/mercurial-tests/src/testng.xml	Fri Aug 19 15:21:38 2011 +0400
@@ -14,6 +14,7 @@
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.MirrorManagerTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVersionTest"/>
       <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.command.VersionCommandTest"/>
+      <class name="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProviderTest"/>
     </classes>
   </test>
 </suite>