2 * Copyright 2000-2018 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl;
19 import com.intellij.execution.configurations.GeneralCommandLine;
20 import jetbrains.buildServer.ExecResult;
21 import jetbrains.buildServer.SimpleCommandLineProcessRunner;
22 import jetbrains.buildServer.buildTriggers.vcs.git.agent.GitCommandLine;
23 import jetbrains.buildServer.log.Loggers;
24 import jetbrains.buildServer.util.StringUtil;
25 import jetbrains.buildServer.vcs.VcsException;
26 import org.jetbrains.annotations.NotNull;
30 import static com.intellij.openapi.util.text.StringUtil.isEmpty;
32 public class CommandUtil {
33 public static final int DEFAULT_COMMAND_TIMEOUT_SEC = 3600;
35 @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
36 public static void checkCommandFailed(@NotNull String cmdName, @NotNull ExecResult res, String... errorsLogLevel) throws VcsException {
37 if ("info".equals(logLevel(errorsLogLevel)) && res.getExitCode() != 0 && res.getException() == null) {
38 logMessage("'" + cmdName + "' exit code: " + res.getExitCode() + ". It is expected behaviour.", "info");
40 if (res.getExitCode() != 0 || res.getException() != null) {
41 commandFailed(cmdName, res);
43 if (res.getStderr().length() > 0) {
44 logMessage("Error output produced by: " + cmdName, errorsLogLevel);
45 logMessage(res.getStderr().trim(), errorsLogLevel);
50 @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
51 public static void commandFailed(final String cmdName, final ExecResult res, String... errorsLogLevel) throws VcsException {
52 Throwable exception = res.getException();
53 String stderr = res.getStderr().trim();
54 String stdout = res.getStdout().trim();
55 int exitCode = res.getExitCode();
56 String message = "'" + cmdName + "' command failed." +
57 (exitCode != 0 ? "\nexit code: " + exitCode : "") +
58 (!StringUtil.isEmpty(stdout) ? "\n" + "stdout: " + stdout : "") +
59 (!StringUtil.isEmpty(stderr) ? "\n" + "stderr: " + stderr : "");
60 if (exception != null) {
61 message += "\nexception: ";
62 message += exception.getClass().getName();
63 String exceptionMessage = exception.getMessage();
64 if (!StringUtil.isEmpty(exceptionMessage))
65 message += ": " + exceptionMessage;
67 if (exception != null && isImportant(exception)) {
68 Writer stackWriter = new StringWriter();
69 exception.printStackTrace(new PrintWriter(stackWriter));
70 message += "\n" + stackWriter.toString();
72 logMessage(message, errorsLogLevel);
73 if (exception != null)
74 throw new VcsException(message, exception);
75 throw new VcsException(message);
78 private static boolean isImportant(Throwable e) {
79 return e instanceof NullPointerException;
83 * Log message using level, if level is not set - use WARN
85 * @param message message to log
86 * @param level level to use
88 private static void logMessage(String message, String... level) {
89 final String theLevel = logLevel(level);
90 if (theLevel.equals("warn")) {
91 Loggers.VCS.warn(message);
92 } else if (theLevel.equals("debug")) {
93 Loggers.VCS.debug(message);
94 } else if (theLevel.equals("info")) {
95 Loggers.VCS.info(message);
99 private static String logLevel(String... level) {
100 return level.length > 0 ? level[0] : "warn";
103 public static ExecResult runCommand(@NotNull GitCommandLine cli, final String... errorsLogLevel) throws VcsException {
104 return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, errorsLogLevel);
107 public static ExecResult runCommand(@NotNull GitCommandLine cli, byte[] input, final String... errorsLogLevel) throws VcsException {
108 return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, input, errorsLogLevel);
111 public static ExecResult runCommand(@NotNull GitCommandLine cli, int timeoutSeconds, final String... errorsLogLevel)
112 throws VcsException {
113 return runCommand(cli, timeoutSeconds, null, errorsLogLevel);
116 public static ExecResult runCommand(@NotNull GitCommandLine cli, int timeoutSeconds, byte[] input, final String... errorsLogLevel)
117 throws VcsException {
118 int attemptsLeft = 2;
122 String cmdStr = cli.getCommandLineString();
123 File workingDir = cli.getWorkingDirectory();
124 String inDir = workingDir != null ? "[" + workingDir.getAbsolutePath() + "]" : "";
125 String msg = inDir + ": " + cmdStr;
126 Loggers.VCS.info(msg);
127 cli.logStart(cmdStr);
128 ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
129 ByteArrayOutputStream stderrBuffer = cli.createStderrBuffer();
130 ExecResult res = SimpleCommandLineProcessRunner
131 .runCommandSecure(cli, cli.getCommandLineString(), input, new ProcessTimeoutCallback(timeoutSeconds), stdoutBuffer, stderrBuffer);
132 cli.logFinish(cmdStr);
133 CommandUtil.checkCommandFailed(cmdStr, res, errorsLogLevel);
134 String out = res.getStdout().trim();
135 Loggers.VCS.debug(out);
136 if (!isEmpty(out) || !cli.isRepeatOnEmptyOutput() || attemptsLeft <= 0)
138 Loggers.VCS.warn("Get an unexpected empty output, will repeat command, attempts left: " + attemptsLeft);
141 for (Runnable action : cli.getPostActions()) {
148 public static void failIfNotEmptyStdErr(@NotNull GeneralCommandLine cli, @NotNull ExecResult res, String... errorsLogLevel) throws VcsException {
149 if (!isEmpty(res.getStderr()))
150 CommandUtil.commandFailed(cli.getCommandLineString(), res, errorsLogLevel);
154 public static boolean isTimeoutError(@NotNull VcsException e) {
155 String msg = e.getMessage();
156 return msg != null && msg.contains("exception: jetbrains.buildServer.ProcessTimeoutException");
159 public static boolean isCanceledError(@NotNull VcsException e) {
160 return e instanceof CheckoutCanceledException || e.getCause() instanceof InterruptedException;