# HG changeset patch # User Dmitry Neverov # Date 1329461758 -14400 # Node ID 3b799724b82b04bb7363a32781f9d31a3081db35 # Parent 55c2c88a2d82c4fee990f1ef71c07c9d0ff9462a TW-20304 port fix from branch default diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/ArchiveCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -20,6 +20,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + public class ArchiveCommand extends BaseCommand { private File myDestDir; private String myToId; @@ -45,8 +47,7 @@ setRevision(cli); setDestination(cli); - CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + runCommand(cli, with().failureWhenStderrNotEmpty()); deleteHgArchival(); } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/BaseCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -16,7 +16,6 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; import com.intellij.execution.configurations.GeneralCommandLine; -import jetbrains.buildServer.util.StringUtil; import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; @@ -24,6 +23,8 @@ import java.util.Collections; import java.util.Set; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + /** * @author pavel */ @@ -67,21 +68,11 @@ } protected CommandResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException { - return CommandUtil.runCommand(cli, getPrivateData()); - } - - protected CommandResult runCommand(@NotNull GeneralCommandLine cli, boolean logErrorsInDebug) throws VcsException { - return CommandUtil.runCommand(cli, CommandUtil.DEFAULT_COMMAND_TIMEOUT_SEC, getPrivateData(), logErrorsInDebug); + return CommandUtil.runCommand(cli, with().privateData(getPrivateData())); } - protected CommandResult runCommand(@NotNull GeneralCommandLine cli, int executionTimeout) throws VcsException { - return CommandUtil.runCommand(cli, executionTimeout, getPrivateData()); - } - - protected void failIfNotEmptyStdErr(@NotNull GeneralCommandLine cli, @NotNull CommandResult res) throws VcsException { - if (!StringUtil.isEmpty(res.getStderr())) { - CommandUtil.commandFailed(cli.getCommandLineString(), res); - } + protected CommandResult runCommand(@NotNull GeneralCommandLine cli, @NotNull CommandExecutionSettingsBuilder with) throws VcsException { + return CommandUtil.runCommand(cli, with.privateData(getPrivateData())); } public Set getPrivateData() { diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CatCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -26,10 +26,12 @@ import java.util.List; import java.util.Queue; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + public class CatCommand extends BaseCommand { private String myRevId; private final static int MAX_CMD_LEN = 900; - private boolean myLogErrorsInDebug = false; + private boolean myCheckForFailure = true; public CatCommand(@NotNull Settings settings, @NotNull File workingDir) { super(settings, workingDir); @@ -39,8 +41,8 @@ myRevId = revId; } - public void setLogErrorsInDebug(boolean doLogErrorsInDebug) { - myLogErrorsInDebug = doLogErrorsInDebug; + public void checkForFailure(boolean doCheckFailure) { + myCheckForFailure = doCheckFailure; } public File execute(List relPaths) throws VcsException { @@ -68,7 +70,7 @@ cmdSize += path.length(); } while (cmdSize < MAX_CMD_LEN && !paths.isEmpty()); - runCommand(cli, myLogErrorsInDebug); + runCommand(cli, with().checkForFailure(myCheckForFailure)); } return tempDir; diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CloneCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -21,6 +21,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + public class CloneCommand extends BaseCommand{ private String myToId; private boolean myUpdateWorkingDir = true; @@ -74,6 +76,6 @@ cli.addParameter(myRepository); cli.addParameter(myWorkingDir.getName()); - runCommand(cli, 24*3600); // some repositories are quite large, we set timeout to 24 hours + runCommand(cli, with().timeout(24 * 3600)); // some repositories are quite large, we set timeout to 24 hours } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandExecutionSettings.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandExecutionSettings.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,40 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +/** + * @author dmitry.neverov + */ +public class CommandExecutionSettings { + + private final int myTimeout; + private final Set myPrivateData; + private final boolean myCheckForFailure; + private final boolean myFailWhenStderrNotEmpty; + + CommandExecutionSettings(int timeout, @NotNull Set privateData, boolean checkForFailure, boolean failWhenStderrNotEmpty) { + myTimeout = timeout; + myPrivateData = privateData; + myCheckForFailure = checkForFailure; + myFailWhenStderrNotEmpty = failWhenStderrNotEmpty; + } + + public int timeout() { + return myTimeout; + } + + @NotNull + public Set privateData() { + return myPrivateData; + } + + public boolean shouldCheckForFailure() { + return myCheckForFailure; + } + + public boolean shouldFailWithNonEmptyStderr() { + return myFailWhenStderrNotEmpty; + } +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandExecutionSettingsBuilder.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandExecutionSettingsBuilder.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,47 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.Set; + +/** + * @author dmitry.neverov + */ +public class CommandExecutionSettingsBuilder { + + private static final int DEFAULT_COMMAND_TIMEOUT_SEC = 3600; + + private int myTimeout = DEFAULT_COMMAND_TIMEOUT_SEC; + private Set myPrivateData = Collections.emptySet(); + private boolean myCheckForFailure = true; + private boolean myFailWhenStderrNotEmpty = false; + + public static CommandExecutionSettingsBuilder with() { + return new CommandExecutionSettingsBuilder(); + } + + public CommandExecutionSettings build() { + return new CommandExecutionSettings(myTimeout, myPrivateData, myCheckForFailure, myFailWhenStderrNotEmpty); + } + + public CommandExecutionSettingsBuilder timeout(int timeout) { + myTimeout = timeout; + return this; + } + + public CommandExecutionSettingsBuilder privateData(@NotNull Set privateData) { + myPrivateData = privateData; + return this; + } + + public CommandExecutionSettingsBuilder checkForFailure(boolean checkForFailure) { + myCheckForFailure = checkForFailure; + return this; + } + + public CommandExecutionSettingsBuilder failureWhenStderrNotEmpty() { + myFailWhenStderrNotEmpty = true; + return this; + } +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResult.java Fri Feb 17 10:55:58 2012 +0400 @@ -1,24 +1,47 @@ package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; +import com.intellij.openapi.diagnostic.Logger; import jetbrains.buildServer.ExecResult; +import jetbrains.buildServer.util.StringUtil; +import jetbrains.buildServer.vcs.VcsException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; +import java.util.HashSet; import java.util.Set; +import static com.intellij.openapi.util.text.StringUtil.isEmpty; +import static java.util.Arrays.asList; import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandUtil.removePrivateData; /** - * Decorator for ExecResult that filters out private data from stdout and strerr. + * Mercurial command result. Filters out private data from stdout and detects errors. * * @author dmitry.neverov */ public class CommandResult { + //Mercurial returns -1 in the case of errors (see dispatch.py) + //and on some shells (e.g. windows cmd) it is truncated to 255. + //A non-zero exit code is not always an error: + //http://mercurial.selenic.com/bts/issue186 + //http://mercurial.selenic.com/bts/issue2189 + //e.g. pull command in hg 2.1 exits with 1 if no new changes were pulled. + private static final Set ERROR_EXIT_CODES = setOf(-1, 255); + + private final Logger myLogger; + private final String myCommand; private final ExecResult myDelegate; private final Set myPrivateData; - public CommandResult(@NotNull final ExecResult execResult, @NotNull final Set privateData) { + public CommandResult(@NotNull Logger logger, @NotNull String command, @NotNull ExecResult execResult) { + this(logger, command, execResult, Collections.emptySet()); + } + + public CommandResult(@NotNull Logger logger, @NotNull String command, @NotNull ExecResult execResult, @NotNull Set privateData) { + myLogger = logger; + myCommand = command; myDelegate = execResult; myPrivateData = privateData; } @@ -28,21 +51,133 @@ return removePrivateData(myDelegate.getStdout(), myPrivateData); } - @NotNull - public String getStderr() { - return removePrivateData(myDelegate.getStderr(), myPrivateData); + public void checkCommandFailed() throws VcsException { + checkFailure(false); } - @Nullable - public Throwable getException() { - return myDelegate.getException(); - } - - public int getExitCode() { - return myDelegate.getExitCode(); + public void checkFailure(boolean failWhenStderrIsNonEmpty) throws VcsException { + rethrowDetectedError(); + if (isFailure()) + logAndThrowError(); + String stderr = getStderr(); + if (!isEmpty(stderr)) { + if (failWhenStderrIsNonEmpty) + logAndThrowError(); + else + logStderr(stderr); + } } public byte[] getByteOut() { return myDelegate.getByteOut(); } + + private void logAndThrowError() throws VcsException { + String message = createCommandLogMessage(); + myLogger.warn(message); + if (hasImportantException()) + myLogger.error("Error during executing '" + getCommand() + "'", getException()); + throw new VcsException(message); + } + + private void logStderr(String stderr) { + myLogger.warn("Error output produced by: " + getCommand()); + myLogger.warn(stderr); + } + + @NotNull + private String getStderr() { + return removePrivateData(myDelegate.getStderr(), myPrivateData); + } + + @Nullable + private Throwable getException() { + return myDelegate.getException(); + } + + private boolean isFailure() { + return getException() != null || isErrorExitCode(); + } + + private boolean isErrorExitCode() { + int exitCode = myDelegate.getExitCode(); + return ERROR_EXIT_CODES.contains(exitCode); + } + + private boolean shouldDetectErrors() { + return isFailure() || myDelegate.getExitCode() != 0; + } + + @NotNull + private String getCommand() { + return removePrivateData(myCommand, myPrivateData); + } + + private boolean hasImportantException() { + Throwable exception = getException(); + return exception instanceof NullPointerException; + } + + private String createCommandLogMessage() { + String stderr = getStderr(); + String stdout = getStdout(); + String exceptionMessage = getExceptionMessage(); + return "'" + getCommand() + "' command failed.\n" + + (!StringUtil.isEmpty(stdout) ? "stdout: " + stdout + "\n" : "") + + (!StringUtil.isEmpty(stderr) ? "stderr: " + stderr + "\n" : "") + + (exceptionMessage != null ? "exception: " + exceptionMessage : ""); + } + + @Nullable + private String getExceptionMessage() { + Throwable exception = getException(); + if (exception == null) + return null; + String message = exception.getMessage(); + if (message == null) + message = exception.getClass().getName(); + return message; + } + + private void rethrowDetectedError() throws VcsException { + if (!shouldDetectErrors()) + return; + String stderr = getStderr().trim(); + checkUnrelatedRepository(stderr); + checkUnknownRevision(stderr); + checkFileNotUnderTheRoot(stderr); + } + + private void checkUnrelatedRepository(@NotNull final String stderr) throws UnrelatedRepositoryException { + if (stderr.contains("abort: repository is unrelated")) + throw new UnrelatedRepositoryException(); + } + + private void checkUnknownRevision(@NotNull final String stderr) throws UnknownRevisionException { + final String message = "abort: unknown revision '"; + int idx = stderr.indexOf(message); + if (idx != -1) { + int startIdx = idx + message.length(); + int endIdx = stderr.indexOf("'", startIdx); + String revision = stderr.substring(startIdx, endIdx); + throw new UnknownRevisionException(revision); + } + } + + private void checkFileNotUnderTheRoot(@NotNull final String stderr) throws VcsException { + final String prefix = "abort: "; + int idx = stderr.indexOf("abort: "); + if (idx != -1) { + int startIdx = idx + prefix.length(); + int endIdx = stderr.indexOf(" not under root"); + if (endIdx != -1) { + String path = stderr.substring(startIdx, endIdx); + throw new UnknownFileException(path); + } + } + } + + private static Set setOf(Integer... ints) { + return new HashSet(asList(ints)); + } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandUtil.java Fri Feb 17 10:55:58 2012 +0400 @@ -1,138 +1,73 @@ -/* - * Copyright 2000-2011 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; - -import com.intellij.execution.configurations.GeneralCommandLine; -import jetbrains.buildServer.ExecResult; -import jetbrains.buildServer.SimpleCommandLineProcessRunner; -import jetbrains.buildServer.log.Loggers; -import jetbrains.buildServer.util.StringUtil; -import jetbrains.buildServer.vcs.VcsException; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.Collections; -import java.util.Set; - -public class CommandUtil { - public static final int DEFAULT_COMMAND_TIMEOUT_SEC = 3600; - - public static void checkCommandFailed(@NotNull String cmdName, @NotNull CommandResult res) throws VcsException { - checkCommandFailed(cmdName, res, false); - } - - private static void checkCommandFailed(@NotNull String cmdName, @NotNull CommandResult res, boolean logErrorsInDebug) throws VcsException { - if (logErrorsInDebug) { - if (res.getExitCode() != 0 || res.getException() != null) - Loggers.VCS.debug(createCommandLogMessage(cmdName, res)); - } else { - if (res.getExitCode() != 0 || res.getException() != null) - commandFailed(cmdName, res); - if (res.getStderr().length() > 0) { - Loggers.VCS.warn("Error output produced by: " + cmdName); - Loggers.VCS.warn(res.getStderr()); - } - } - } - - public static void commandFailed(final String cmdName, final CommandResult res) throws VcsException { - final String message = createCommandLogMessage(cmdName, res); - Loggers.VCS.warn(message); - if (hasImportantException(res)) { - Loggers.VCS.error("Error during executing '" + cmdName + "'", res.getException()); - } - throw new VcsException(message); - } - - private static String createCommandLogMessage(final String cmdName, final CommandResult res) { - String stderr = res.getStderr(); - String stdout = res.getStdout(); - String exceptionMessage = getExceptionMessage(res); - return "'" + cmdName + "' command failed.\n" + - (!StringUtil.isEmpty(stdout) ? "stdout: " + stdout + "\n" : "") + - (!StringUtil.isEmpty(stderr) ? "stderr: " + stderr + "\n" : "") + - (exceptionMessage != null ? "exception: " + exceptionMessage : ""); - } - - @Nullable - private static String getExceptionMessage(CommandResult result) { - Throwable exception = result.getException(); - String message = null; - if (exception != null) { - message = exception.getMessage(); - if (message == null) { - message = exception.getClass().getName(); - } - } - return message; - } - - private static boolean hasImportantException(CommandResult result) { - Throwable exception = result.getException(); - if (exception != null) { - return exception instanceof NullPointerException; - } else { - return false; - } - } - - public static CommandResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException { - return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, Collections.emptySet()); - } - - public static CommandResult runCommand(@NotNull GeneralCommandLine cli, @NotNull Set privateData) throws VcsException { - return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, privateData); - } - - public static CommandResult runCommand(@NotNull GeneralCommandLine cli, final int executionTimeout, @NotNull Set privateData) throws VcsException { - return runCommand(cli, executionTimeout, privateData, false); - } - - public static CommandResult runCommand(@NotNull GeneralCommandLine cli, final int executionTimeout, @NotNull Set privateData, boolean logErrorsInDebug) throws VcsException { - final String cmdStr = removePrivateData(cli.getCommandLineString(), privateData); - Loggers.VCS.debug("Run command: " + cmdStr); - CommandResult res = run(cli, executionTimeout, cmdStr, privateData); - CommandUtil.checkCommandFailed(cmdStr, res, logErrorsInDebug); - Loggers.VCS.debug("Command " + cmdStr + " output:\n" + res.getStdout()); - return res; - } - - private static CommandResult run(@NotNull final GeneralCommandLine cli, final int executionTimeout, @NotNull final String cmdStr, @NotNull final Set privateData) { - final long start = System.currentTimeMillis(); - ExecResult res = SimpleCommandLineProcessRunner.runCommand(cli, null, new SimpleCommandLineProcessRunner.RunCommandEventsAdapter() { - @Override - public Integer getOutputIdleSecondsTimeout() { - return executionTimeout; - } - @Override - public void onProcessFinished(Process ps) { - long duration = System.currentTimeMillis() - start; - Loggers.VCS.debug("Command " + cmdStr + " took " + duration + "ms"); - } - }); - return new CommandResult(res, privateData); - } - - public static String removePrivateData(final String str, final Set privateData) { - String result = str; - for (String data: privateData) { - if (data == null || data.length() == 0) continue; - result = result.replace(data, "******"); - } - - return result; - } -} +/* + * Copyright 2000-2011 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; + +import com.intellij.execution.configurations.GeneralCommandLine; +import jetbrains.buildServer.ExecResult; +import jetbrains.buildServer.SimpleCommandLineProcessRunner; +import jetbrains.buildServer.log.Loggers; +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +import java.util.Set; + +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + +public class CommandUtil { + + public static CommandResult runCommand(@NotNull GeneralCommandLine cli) throws VcsException { + return runCommand(cli, with()); + } + + public static CommandResult runCommand(@NotNull GeneralCommandLine cli, @NotNull CommandExecutionSettingsBuilder executionSettingsBuilder) throws VcsException { + return runCommand(cli, executionSettingsBuilder.build()); + } + + private static CommandResult runCommand(@NotNull GeneralCommandLine cli, @NotNull CommandExecutionSettings executionSettings) throws VcsException { + final String command = removePrivateData(cli.getCommandLineString(), executionSettings.privateData()); + Loggers.VCS.debug("Run command: " + command); + CommandResult res = run(cli, executionSettings.timeout(), command, executionSettings.privateData()); + if (executionSettings.shouldCheckForFailure() || executionSettings.shouldFailWithNonEmptyStderr()) + res.checkFailure(executionSettings.shouldFailWithNonEmptyStderr()); + Loggers.VCS.debug("Command " + command + " output:\n" + res.getStdout()); + return res; + } + + private static CommandResult run(@NotNull final GeneralCommandLine cli, final int executionTimeout, @NotNull final String command, @NotNull final Set privateData) { + final long start = System.currentTimeMillis(); + ExecResult res = SimpleCommandLineProcessRunner.runCommand(cli, null, new SimpleCommandLineProcessRunner.RunCommandEventsAdapter() { + @Override + public Integer getOutputIdleSecondsTimeout() { + return executionTimeout; + } + @Override + public void onProcessFinished(Process ps) { + long duration = System.currentTimeMillis() - start; + Loggers.VCS.debug("Command " + command + " took " + duration + "ms"); + } + }); + return new CommandResult(Loggers.VCS, command, res, privateData); + } + + public static String removePrivateData(final String str, final Set privateData) { + String result = str; + for (String data: privateData) { + if (data == null || data.length() == 0) continue; + result = result.replace(data, "******"); + } + return result; + } +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/IdentifyCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -21,6 +21,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + /** * @author Pavel.Sher * Date: 16.07.2008 @@ -54,8 +56,7 @@ cli.addParameter("--rev"); cli.addParameter(myChangeSet.getId()); } - CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + CommandResult res = runCommand(cli, with().failureWhenStderrNotEmpty()); return res.getStdout(); } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PullCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -21,6 +21,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + /** * @author Pavel.Sher * Date: 14.07.2008 @@ -42,6 +44,6 @@ GeneralCommandLine cli = createCommandLine(); cli.addParameter("pull"); cli.addParameter(myPullUrl); - runCommand(cli, timeout); + runCommand(cli, with().timeout(timeout)); } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/PushCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -21,6 +21,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + /** * @author pavel */ @@ -42,7 +44,6 @@ cli.addParameter("-f"); } cli.addParameter(getSettings().getRepositoryUrl()); - CommandResult res = runCommand(cli); - failIfNotEmptyStdErr(cli, res); + runCommand(cli, with().failureWhenStderrNotEmpty()); } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownFileException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownFileException.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,22 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +/** + * @author dmitry.neverov + */ +public class UnknownFileException extends VcsException { + + private final String myPath; + + public UnknownFileException(@NotNull String path) { + super("Unknown file " + path); + myPath = path; + } + + public String getPath() { + return myPath; + } + +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnknownRevisionException.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,23 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import jetbrains.buildServer.vcs.VcsException; +import org.jetbrains.annotations.NotNull; + +/** + * @author dmitry.neverov + */ +public class UnknownRevisionException extends VcsException { + + private final String myRevision; + + public UnknownRevisionException(@NotNull final String revision) { + super("Unknown revision " + revision); + myRevision = revision; + } + + @NotNull + public String getRevision() { + return myRevision; + } + +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UnrelatedRepositoryException.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,10 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import jetbrains.buildServer.vcs.VcsException; + +/** + * @author dmitry.neverov + */ +public class UnrelatedRepositoryException extends VcsException { + +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java --- a/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/UpdateCommand.java Fri Feb 17 10:55:58 2012 +0400 @@ -21,6 +21,8 @@ import java.io.File; +import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandExecutionSettingsBuilder.with; + public class UpdateCommand extends BaseCommand { private static final int UPDATE_TIMEOUT_SECONDS = 8 * 3600;//8 hours @@ -45,6 +47,6 @@ } else { cli.addParameter(getSettings().getBranchName()); } - runCommand(cli, UPDATE_TIMEOUT_SECONDS); + runCommand(cli, with().timeout(UPDATE_TIMEOUT_SECONDS)); } } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupport.java Fri Feb 17 10:55:58 2012 +0400 @@ -204,7 +204,7 @@ File dir = getWorkingDir(settings); CatCommand cat = new CatCommand(settings, dir); cat.setRevId(cset.getId()); - cat.setLogErrorsInDebug(true); + cat.checkForFailure(false); File parentDir = null; try { parentDir = cat.execute(Collections.singletonList(path)); diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java --- a/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/ServerPluginConfigImpl.java Fri Feb 17 10:55:58 2012 +0400 @@ -8,7 +8,7 @@ public class ServerPluginConfigImpl implements ServerPluginConfig { private static final String PULL_TIMEOUT_SECONDS = "teamcity.hg.pull.timeout.seconds"; - private final int DEFAULT_PULL_TIMEOUT_SECONDS = 3600; + public static final int DEFAULT_PULL_TIMEOUT_SECONDS = 3600; public boolean isUsePullProtocol() { return TeamCityProperties.getBooleanOrTrue("teamcity.hg.use.pull.protocol"); diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandResultTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/CommandResultTest.java Wed Feb 15 12:07:12 2012 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,41 +0,0 @@ -package jetbrains.buildServer.buildTriggers.vcs.mercurial; - -import jetbrains.buildServer.ExecResult; -import jetbrains.buildServer.StreamGobbler; -import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommandResult; -import org.jetbrains.annotations.NotNull; -import org.testng.annotations.Test; - -import java.io.ByteArrayInputStream; -import java.util.Collections; - -import static org.testng.AssertJUnit.assertFalse; - -/** - * @author dmitry.neverov - */ -@Test -public class CommandResultTest { - - public void output_should_not_contain_private_data() { - String password = "pass"; - ExecResult result = createExecResult(password, password); - CommandResult commandResult = new CommandResult(result, Collections.singleton(password)); - assertFalse(commandResult.getStdout().contains(password)); - assertFalse(commandResult.getStderr().contains(password)); - } - - private ExecResult createExecResult(@NotNull final String output, @NotNull final String error) { - ExecResult result = new ExecResult(); - result.setOutputGobbler(createStringGobbler(output)); - result.setErrorGobbler(createStringGobbler(error)); - return result; - } - - private StreamGobbler createStringGobbler(@NotNull final String str) { - StreamGobbler gobbler = new StreamGobbler(new ByteArrayInputStream(str.getBytes())); - gobbler.start(); - return gobbler; - } - -} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java --- a/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialVcsSupportTest.java Fri Feb 17 10:55:58 2012 +0400 @@ -517,7 +517,7 @@ return true; } public int getPullTimeout() { - return CommandUtil.DEFAULT_COMMAND_TIMEOUT_SEC; + return ServerPluginConfigImpl.DEFAULT_PULL_TIMEOUT_SECONDS; } }; } diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mercurial-tests/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommandResultTest.java Fri Feb 17 10:55:58 2012 +0400 @@ -0,0 +1,197 @@ +package jetbrains.buildServer.buildTriggers.vcs.mercurial.command; + +import com.intellij.openapi.diagnostic.Logger; +import jetbrains.buildServer.ExecResult; +import jetbrains.buildServer.StreamGobbler; +import jetbrains.buildServer.vcs.VcsException; +import org.apache.log4j.Level; +import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import java.io.ByteArrayInputStream; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import static org.testng.AssertJUnit.*; + +/** + * @author dmitry.neverov + */ +@Test +public class CommandResultTest { + + private RecordingLogger myLogger; + + @BeforeMethod + public void setUp() { + myLogger = new RecordingLogger(); + } + + public void output_should_not_contain_private_data() { + String password = "pass"; + CommandResult commandResult = commandResultFor(execResult().withStdout(password).withStderr(password), password); + assertFalse(commandResult.getStdout().contains(password)); + myLogger.assertLogMessagesDontContain(password); + } + + @DataProvider(name = "exitCodesForErrors") + public static Object[][] exitCodesForErrors() { + return new Object[][] { + new Object[] { -1 }, + new Object[] { 255}}; + } + + @Test(expectedExceptions = VcsException.class, + dataProvider = "exitCodesForErrors") + public void should_detect_error_for_exit_code(int exitCode) throws VcsException { + CommandResult commandResult = commandResultFor(execResult().withExitCode(exitCode)); + commandResult.checkCommandFailed(); + } + + public void exit_code_of_one_is_not_an_error() throws VcsException { + CommandResult commandResult = commandResultFor(execResult().withStdout("pull: no new changes").withExitCode(1)); + commandResult.checkCommandFailed(); + } + + @Test(expectedExceptions = UnrelatedRepositoryException.class) + public void should_detect_unrelated_repository_error() throws VcsException { + String unrelatedRepositoryStderr = "abort: repository is unrelated\n"; + CommandResult commandResult = commandResultFor(execResult().withStderr(unrelatedRepositoryStderr).withExitCode(255)); + commandResult.checkCommandFailed(); + } + + @Test + public void should_detect_unknown_revision_error() throws VcsException { + String unknownRevision = "9c6a6b4aede0"; + String unknownRevisionError = "abort: unknown revision '" + unknownRevision + "'\n"; + CommandResult commandResult = commandResultFor(execResult().withStderr(unknownRevisionError).withExitCode(255)); + try { + commandResult.checkCommandFailed(); + fail("unknown exception should be thrown"); + } catch (UnknownRevisionException e) { + assertEquals(unknownRevision, e.getRevision()); + } + } + + @Test(expectedExceptions = VcsException.class) + public void should_detect_failure_when_delegate_has_exception() throws VcsException { + CommandResult commandResult = commandResultFor(execResult().withException(new RuntimeException())); + commandResult.checkCommandFailed(); + } + + + ExecResultBuilder execResult() { + return new ExecResultBuilder(); + } + + CommandResult commandResultFor(ExecResultBuilder builder, String... privateData) { + return new CommandResult(myLogger, "", builder.build(), new HashSet(Arrays.asList(privateData))); + } + + private class ExecResultBuilder { + private String myStdout = ""; + private String myStderr = ""; + private Throwable myException = null; + private int myExitCode = 0; + + ExecResultBuilder withStdout(String stdout) { + myStdout = stdout; + return this; + } + ExecResultBuilder withStderr(String stderr) { + myStderr = stderr; + return this; + } + ExecResultBuilder withException(Throwable exception) { + myException = exception; + return this; + } + ExecResultBuilder withExitCode(int exitCode) { + myExitCode = exitCode; + return this; + } + + ExecResult build() { + ExecResult result = new ExecResult(); + result.setOutputGobbler(createStringGobbler(myStdout)); + result.setErrorGobbler(createStringGobbler(myStderr)); + if (myException != null) + result.setException(myException); + if (myExitCode != 0) + result.setExitCode(myExitCode); + return result; + } + + private StreamGobbler createStringGobbler(@NotNull final String str) { + StreamGobbler gobbler = new StreamGobbler(new ByteArrayInputStream(str.getBytes())); + gobbler.start(); + try { + Thread.sleep(10);//wait for gobbler to read string + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + return gobbler; + } + } + + private class RecordingLogger extends Logger { + + private List myMessages = new ArrayList(); + + public void assertLogMessagesDontContain(@NotNull String... strs) { + for (String s : strs) { + for (String message : myMessages) + assertFalse("'" + s + "' was logged", message.contains(s)); + } + } + + @Override + public boolean isDebugEnabled() { + return true; + } + + @Override + public void debug(@NonNls String s) { + myMessages.add(s); + } + + @Override + public void debug(Throwable throwable) { + myMessages.add(throwable.getMessage()); + } + + @Override + public void debug(@NonNls String s, Throwable throwable) { + myMessages.add(s); + } + + @Override + public void error(@NonNls String s, Throwable throwable, @NonNls String... strings) { + myMessages.add(s); + } + + @Override + public void info(@NonNls String s) { + myMessages.add(s); + } + + @Override + public void info(@NonNls String s, Throwable throwable) { + myMessages.add(s); + } + + @Override + public void warn(@NonNls String s, Throwable throwable) { + myMessages.add(s); + } + + @Override + public void setLevel(Level level) { + } + } +} diff -r 55c2c88a2d82 -r 3b799724b82b mercurial-tests/src/testng.xml --- a/mercurial-tests/src/testng.xml Wed Feb 15 12:07:12 2012 +0400 +++ b/mercurial-tests/src/testng.xml Fri Feb 17 10:55:58 2012 +0400 @@ -12,7 +12,7 @@ - +