Mercurial > hg > mercurial
changeset 430:3600b68a4c0c
TW-21403 better error message when hg not found
line wrap: on
line diff
--- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Fri May 11 16:37:00 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Fri May 11 19:33:48 2012 +0400 @@ -1,16 +1,15 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; +import com.intellij.execution.process.ProcessNotCreatedException; import com.intellij.openapi.diagnostic.Logger; import jetbrains.buildServer.ExecResult; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.ConnectionRefusedException; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownFileException; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnknownRevisionException; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.UnrelatedRepositoryException; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.*; import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.io.IOException; import java.util.HashSet; import java.util.Set; @@ -33,6 +32,9 @@ //e.g. pull command in hg 2.1 exits with 1 if no new changes were pulled. private static final Set<Integer> ERROR_EXIT_CODES = setOf(-1, 255); + private static final String MERCURIAL_NOT_FOUND_MESSAGE_PREFIX = "Cannot run program \""; + private static final String MERCURIAL_NOT_FOUND_MESSAGE_SUFFIX = "No such file or directory"; + private final Logger myLogger; private final String myCommand; private final ExecResult myDelegate; @@ -75,9 +77,30 @@ myLogger.warn(message); if (hasImportantException()) myLogger.error("Error during executing '" + getCommand() + "'", getException()); + throwVcsException(message); + } + + private void throwVcsException(@NotNull String message) throws VcsException { + //noinspection ThrowableResultOfMethodCallIgnored + Throwable e = getException(); + if (isMercurialNotFoundException(e)) { + assert e != null; + throw new MercurialNotFoundException(myCommand, e); + } throw new VcsException(message); } + private boolean isMercurialNotFoundException(@Nullable Throwable e) { + if (e == null) + return false; + final String message = e.getMessage(); + return e instanceof ProcessNotCreatedException && + e.getCause() instanceof IOException && + message != null && + message.startsWith(MERCURIAL_NOT_FOUND_MESSAGE_PREFIX) && + message.endsWith(MERCURIAL_NOT_FOUND_MESSAGE_SUFFIX); + } + private void logStderr(String stderr) { myLogger.warn("Error output produced by: " + getCommand()); myLogger.warn(stderr); @@ -94,6 +117,7 @@ } private boolean isFailure() { + //noinspection ThrowableResultOfMethodCallIgnored return getException() != null || isErrorExitCode(); } @@ -112,6 +136,7 @@ } private boolean hasImportantException() { + //noinspection ThrowableResultOfMethodCallIgnored Throwable exception = getException(); return exception instanceof NullPointerException; } @@ -128,6 +153,7 @@ @Nullable private String getExceptionMessage() { + //noinspection ThrowableResultOfMethodCallIgnored Throwable exception = getException(); if (exception == null) return null;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/exception/MercurialNotFoundException.java Fri May 11 19:33:48 2012 +0400 @@ -0,0 +1,15 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception; + +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +/** + * @author dmitry.neverov + */ +public class MercurialNotFoundException extends VcsException { + + public MercurialNotFoundException(@NotNull String failedHgCommand, Throwable cause) { + super("'" + failedHgCommand + "' command failed: mercurial executable not found", cause); + } + +}
--- a/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Fri May 11 16:37:00 2012 +0400 +++ b/mercurial-server/src/META-INF/build-server-plugin-mercurial.xml Fri May 11 19:33:48 2012 +0400 @@ -7,4 +7,6 @@ <bean id="repoFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.RepoFactory" /> <bean id="hgPathProvider" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.ServerHgPathProvider"/> <bean id="mirrorManager" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.MirrorManagerImpl" /> + <bean id="hgVcsRootFactory" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgVcsRootFactory" /> + <bean id="testConnection" class="jetbrains.buildServer.buildTriggers.vcs.mercurial.HgTestConnectionSupport" /> </beans>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgTestConnectionSupport.java Fri May 11 19:33:48 2012 +0400 @@ -0,0 +1,59 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot; +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.exception.MercurialNotFoundException; +import jetbrains.buildServer.vcs.TestConnectionSupport; +import jetbrains.buildServer.vcs.VcsException; +import jetbrains.buildServer.vcs.VcsRoot; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * @author dmitry.neverov + */ +public class HgTestConnectionSupport implements TestConnectionSupport { + + private final HgVcsRootFactory myHgVcsRootFactory; + private final RepoFactory myRepoFactory; + private final MirrorManager myMirrorManager; + private final HgPathProvider myHgPathProvider; + + public HgTestConnectionSupport(@NotNull HgVcsRootFactory hgVcsRootFactory, + @NotNull RepoFactory repoFactory, + @NotNull MirrorManager mirrorManager, + @NotNull HgPathProvider hgPathProvider) { + myHgVcsRootFactory = hgVcsRootFactory; + myRepoFactory = repoFactory; + myMirrorManager = mirrorManager; + myHgPathProvider = hgPathProvider; + } + + + public String testConnection(@NotNull VcsRoot vcsRoot) throws VcsException { + HgVcsRoot root = myHgVcsRootFactory.createHgRoot(vcsRoot); + HgRepo repo = createRepo(root); + try { + repo.id().repository(root.getRepository()) + .withAuthSettings(root.getAuthSettings()) + .call(); + return null; + } catch (MercurialNotFoundException e) { + throw friendlyException(root, e); + } + } + + + private VcsException friendlyException(HgVcsRoot root, MercurialNotFoundException e) { + return new VcsException("Cannot find mercurial executable at path '" + myHgPathProvider.getHgPath(root) + "'", e); + } + + private ServerHgRepo createRepo(HgVcsRoot root) throws VcsException { + return myRepoFactory.create(getWorkingDir(root), myHgPathProvider.getHgPath(root), root.getAuthSettings()); + } + + private File getWorkingDir(HgVcsRoot root) { + File customDir = root.getCustomWorkingDir(); + return customDir != null ? customDir : myMirrorManager.getMirrorDir(root.getRepository()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/HgVcsRootFactory.java Fri May 11 19:33:48 2012 +0400 @@ -0,0 +1,48 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial; + +import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot; +import jetbrains.buildServer.util.StringUtil; +import jetbrains.buildServer.vcs.VcsException; +import jetbrains.buildServer.vcs.VcsRoot; +import org.jetbrains.annotations.NotNull; + +import java.io.File; + +/** + * @author dmitry.neverov + */ +public class HgVcsRootFactory { + + private File myDefaultWorkFolderParent; + + public HgVcsRootFactory(@NotNull final ServerPluginConfig config) { + myDefaultWorkFolderParent = config.getCachesDir(); + } + + + public HgVcsRoot createHgRoot(@NotNull VcsRoot root) throws VcsException { + HgVcsRoot hgRoot = new HgVcsRoot(root); + String customClonePath = hgRoot.getCustomClonePath(); + if (!StringUtil.isEmptyOrSpaces(customClonePath) && !myDefaultWorkFolderParent.equals(new File(customClonePath).getAbsoluteFile())) { + File parentDir = new File(customClonePath); + createClonedRepositoryParentDir(parentDir); + + // take last part of repository path + String repPath = hgRoot.getRepositoryUrlWithCredentials(); + String[] splitted = repPath.split("[/\\\\]"); + if (splitted.length > 0) { + repPath = splitted[splitted.length-1]; + } + + File customWorkingDir = new File(parentDir, repPath); + hgRoot.setCustomWorkingDir(customWorkingDir); + } + return hgRoot; + } + + private void createClonedRepositoryParentDir(final File parentDir) throws VcsException { + if (!parentDir.exists() && !parentDir.mkdirs()) + throw new VcsException("Failed to create parent directory for cloned repository: " + parentDir.getAbsolutePath()); + } + +}
--- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Fri May 11 16:37:00 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Fri May 11 19:33:48 2012 +0400 @@ -25,7 +25,6 @@ import jetbrains.buildServer.serverSide.*; import jetbrains.buildServer.util.EventDispatcher; import jetbrains.buildServer.util.FileUtil; -import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.util.cache.ResetCacheRegister; import jetbrains.buildServer.vcs.*; import jetbrains.buildServer.vcs.impl.VcsRootImpl; @@ -53,13 +52,14 @@ public class MercurialVcsSupport extends ServerVcsSupport implements LabelingSupport, VcsFileContentProvider, BranchSupport, CollectChangesBetweenRoots, BuildPatchByCheckoutRules { private final VcsManager myVcsManager; - private final File myDefaultWorkFolderParent; private final MirrorManager myMirrorManager; private final ServerPluginConfig myConfig; private final HgPathProvider myHgPathProvider; private final RepoFactory myRepoFactory; + private final HgVcsRootFactory myHgVcsRootFactory; private final FileFilter myIgnoreDotHgFilter = new IgnoreDotHgFilter(); private final FileFilter myAcceptAllFilter = new AcceptAllFilter(); + private final HgTestConnectionSupport myTestConnection; public MercurialVcsSupport(@NotNull final VcsManager vcsManager, @NotNull final SBuildServer server, @@ -68,13 +68,16 @@ @NotNull final ServerPluginConfig config, @NotNull final HgPathProvider hgPathProvider, @NotNull final RepoFactory repoFactory, - @NotNull final MirrorManager mirrorManager) { + @NotNull final MirrorManager mirrorManager, + @NotNull final HgVcsRootFactory hgVcsRootFactory, + @NotNull final HgTestConnectionSupport testConnection) { myVcsManager = vcsManager; myConfig = config; - myDefaultWorkFolderParent = myConfig.getCachesDir(); myMirrorManager = mirrorManager; myHgPathProvider = hgPathProvider; myRepoFactory = repoFactory; + myHgVcsRootFactory = hgVcsRootFactory; + myTestConnection = testConnection; resetCacheHandlerManager.registerHandler(new MercurialResetCacheHandler(myMirrorManager)); dispatcher.addListener(new BuildServerAdapter() { @Override @@ -164,7 +167,7 @@ @NotNull public byte[] getContent(@NotNull final String filePath, @NotNull final VcsRoot vcsRoot, @NotNull final String version) throws VcsException { ChangeSet cset = new ChangeSet(version); - HgVcsRoot root = createHgRoot(vcsRoot); + HgVcsRoot root = myHgVcsRootFactory.createHgRoot(vcsRoot); syncRepository(root, cset); HgRepo repo = createRepo(root); File parentDir = repo.cat().files(filePath).atRevision(cset).call(); @@ -212,7 +215,7 @@ @NotNull public String getCurrentVersion(@NotNull final VcsRoot root) throws VcsException { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); syncRepository(hgRoot); HgRepo repo = createRepo(hgRoot); Map<String, ChangeSet> result = repo.branches().call(); @@ -233,28 +236,7 @@ @Override public TestConnectionSupport getTestConnectionSupport() { - return new TestConnectionSupport() { - public String testConnection(@NotNull final VcsRoot vcsRoot) throws VcsException { - HgVcsRoot root = createHgRoot(vcsRoot); - String idResult = createRepo(root).id() - .repository(root.getRepository()) - .withAuthSettings(root.getAuthSettings()) - .call(); - StringBuilder res = new StringBuilder(); - res.append(quoteIfNeeded(myHgPathProvider.getHgPath(root))); - res.append(" identify "); - res.append(quoteIfNeeded(root.getAuthSettings().getRepositoryUrlWithHiddenPassword(root.getRepository()))); - res.append('\n').append(idResult); - return res.toString(); - } - }; - } - - private String quoteIfNeeded(@NotNull String str) { - if (str.indexOf(' ') != -1) { - return "\"" + str + "\""; - } - return str; + return myTestConnection; } @Nullable @@ -475,7 +457,7 @@ @NotNull private Map<String, String> getBranchesRevisions(@NotNull VcsRoot root) throws VcsException { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); syncRepository(hgRoot); HgRepo repo = createRepo(hgRoot); Map<String, String> result = new HashMap<String, String>(); @@ -495,7 +477,7 @@ @Nullable public PersonalBranchDescription getPersonalBranchDescription(@NotNull VcsRoot root, @NotNull String branchName) throws VcsException { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); VcsRoot branchRoot = createBranchRoot(root, branchName); String baseVersion = getCurrentVersion(root); String branchVersion = getCurrentVersion(branchRoot); @@ -529,7 +511,7 @@ public List<ModificationData> collectChanges(@NotNull VcsRoot fromRoot, @NotNull String fromRootRevision, @NotNull VcsRoot toRoot, @Nullable String toRootRevision, @NotNull CheckoutRules checkoutRules) throws VcsException { - HgVcsRoot hgRoot = createHgRoot(toRoot); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(toRoot); syncRepository(hgRoot); String toRevision = toRootRevision != null ? toRootRevision : getCurrentVersion(toRoot); String mergeBase = getMergeBase(hgRoot, fromRootRevision, toRevision); @@ -571,7 +553,7 @@ } public List<ModificationData> collectChanges(@NotNull VcsRoot root, @NotNull String fromVersion, @Nullable String currentVersion, @NotNull CheckoutRules checkoutRules) throws VcsException { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); syncRepository(hgRoot); List<ModificationData> result = new ArrayList<ModificationData>(); for (ChangeSet cset : getChangesets(hgRoot, fromVersion, currentVersion)) { @@ -627,7 +609,7 @@ } public void buildPatch(@NotNull VcsRoot root, @Nullable String fromVersion, @NotNull String toVersion, @NotNull PatchBuilder builder, @NotNull CheckoutRules checkoutRules) throws IOException, VcsException { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); syncRepository(hgRoot); ChangeSet to = new ChangeSet(toVersion); if (fromVersion == null) { @@ -668,7 +650,7 @@ File tmpDir = null; try { tmpDir = createLabelingTmpDir(); - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); hgRoot.setCustomWorkingDir(tmpDir); syncRepository(hgRoot); HgRepo repo = createRepo(hgRoot); @@ -703,31 +685,6 @@ return customDir != null ? customDir : myMirrorManager.getMirrorDir(root.getRepository()); } - private HgVcsRoot createHgRoot(@NotNull VcsRoot root) throws VcsException { - HgVcsRoot hgRoot = new HgVcsRoot(root); - String customClonePath = hgRoot.getCustomClonePath(); - if (!StringUtil.isEmptyOrSpaces(customClonePath) && !myDefaultWorkFolderParent.equals(new File(customClonePath).getAbsoluteFile())) { - File parentDir = new File(customClonePath); - createClonedRepositoryParentDir(parentDir); - - // take last part of repository path - String repPath = hgRoot.getRepositoryUrlWithCredentials(); - String[] splitted = repPath.split("[/\\\\]"); - if (splitted.length > 0) { - repPath = splitted[splitted.length-1]; - } - - File customWorkingDir = new File(parentDir, repPath); - hgRoot.setCustomWorkingDir(customWorkingDir); - } - return hgRoot; - } - - private void createClonedRepositoryParentDir(final File parentDir) throws VcsException { - if (!parentDir.exists() && !parentDir.mkdirs()) { - throw new VcsException("Failed to create parent directory for cloned repository: " + parentDir.getAbsolutePath()); - } - } public boolean isAgentSideCheckoutAvailable() { return true; @@ -777,7 +734,7 @@ @NotNull public String getBranchName(@NotNull final VcsRoot root) { try { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); return hgRoot.getBranchName(); } catch (VcsException e) { return "default"; @@ -797,7 +754,7 @@ @Override public ListFilesPolicy getListFilesPolicy(@NotNull VcsRoot root) { try { - HgVcsRoot hgRoot = createHgRoot(root); + HgVcsRoot hgRoot = myHgVcsRootFactory.createHgRoot(root); HgRepo repo = createRepo(hgRoot); return new ListFilesSupport(this, hgRoot, repo); } catch (VcsException e) {
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Fri May 11 16:37:00 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Fri May 11 19:33:48 2012 +0400 @@ -35,6 +35,7 @@ import java.util.*; import static com.intellij.openapi.util.io.FileUtil.copyDir; +import static com.intellij.openapi.util.io.FileUtil.delete; import static com.intellij.openapi.util.io.FileUtil.moveDirWithContent; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.VcsRootBuilder.vcsRoot; @@ -234,6 +235,22 @@ } } + + public void should_throw_friendly_exception_when_cannot_run_hg() throws Exception { + VcsRootImpl root = createVcsRoot(simpleRepo()); + File nonExistingHg = myTempFiles.createTempFile(); + delete(nonExistingHg); + String nonExistingHgPath = nonExistingHg.getAbsolutePath(); + root.addProperty(Constants.HG_COMMAND_PATH_PROP, nonExistingHgPath); + try { + myVcs.getTestConnectionSupport().testConnection(root); + fail("Exception expected"); + } catch (VcsException e) { + assertEquals(e.getMessage(), "Cannot find mercurial executable at path '" + nonExistingHgPath + "'"); + } + } + + public void test_tag() throws IOException, VcsException { VcsRootImpl vcsRoot = createVcsRoot(simpleRepo()); cleanRepositoryAfterTest(simpleRepo());
--- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Fri May 11 16:37:00 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/Util.java Fri May 11 19:33:48 2012 +0400 @@ -36,7 +36,7 @@ } - public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, @NotNull ServerPluginConfig config, @NotNull RepoFactory commandFactory) throws IOException { + public static MercurialVcsSupport createMercurialServerSupport(@NotNull Mockery context, @NotNull ServerPluginConfig config, @NotNull RepoFactory repoFactory) throws IOException { VcsManager vcsManager = context.mock(VcsManager.class); final SBuildServer server = context.mock(SBuildServer.class); final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); @@ -44,6 +44,11 @@ allowing(server).getExecutor(); will(returnValue(executor)); }}); EventDispatcher<BuildServerListener> dispatcher = EventDispatcher.create(BuildServerListener.class); - return new MercurialVcsSupport(vcsManager, server, dispatcher, new ResetCacheRegister(), config, new ServerHgPathProvider(config), commandFactory, new MirrorManagerImpl(config)); + HgVcsRootFactory hgVcsRootFactory = new HgVcsRootFactory(config); + MirrorManagerImpl mirrorManager = new MirrorManagerImpl(config); + ServerHgPathProvider hgPathProvider = new ServerHgPathProvider(config); + HgTestConnectionSupport testConnection = new HgTestConnectionSupport(hgVcsRootFactory, repoFactory, mirrorManager, hgPathProvider); + return new MercurialVcsSupport(vcsManager, server, dispatcher, new ResetCacheRegister(), config, hgPathProvider, + repoFactory, mirrorManager, hgVcsRootFactory, testConnection); } }