dbb70026988534629dd34bced602bc559782e447
[teamcity/git-plugin.git] / git-agent / src / jetbrains / buildServer / buildTriggers / vcs / git / agent / command / impl / CommandUtil.java
1 /*
2  * Copyright 2000-2018 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 package jetbrains.buildServer.buildTriggers.vcs.git.agent.command.impl;
18
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;
27
28 import java.io.*;
29
30 import static com.intellij.openapi.util.text.StringUtil.isEmpty;
31
32 public class CommandUtil {
33   public static final int DEFAULT_COMMAND_TIMEOUT_SEC = 3600;
34
35   @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
36   public static void checkCommandFailed(@NotNull String cmdName, @NotNull ExecResult res, String... errorsLogLevel) throws VcsException {
37     if (res.getExitCode() != 0 || res.getException() != null) {
38       commandFailed(cmdName, res);
39     }
40     if (res.getStderr().length() > 0) {
41       logMessage("Error output produced by: " + cmdName, errorsLogLevel);
42       logMessage(res.getStderr().trim(), errorsLogLevel);
43     }
44   }
45
46   @SuppressWarnings({"ThrowableResultOfMethodCallIgnored"})
47   public static void commandFailed(final String cmdName, final ExecResult res, String... errorsLogLevel) throws VcsException {
48     Throwable exception = res.getException();
49     String stderr = res.getStderr().trim();
50     String stdout = res.getStdout().trim();
51     int exitCode = res.getExitCode();
52     String message = "'" + cmdName + "' command failed." +
53                      (exitCode != 0 ? "\nexit code: " + exitCode : "") +
54                      (!StringUtil.isEmpty(stdout) ? "\n" + "stdout: " + stdout : "") +
55                      (!StringUtil.isEmpty(stderr) ? "\n" + "stderr: " + stderr : "");
56     if (exception != null) {
57       message += "\nexception: ";
58       message += exception.getClass().getName();
59       String exceptionMessage = exception.getMessage();
60       if (!StringUtil.isEmpty(exceptionMessage))
61         message += ": " + exceptionMessage;
62     }
63     if (exception != null && isImportant(exception)) {
64       Writer stackWriter = new StringWriter();
65       exception.printStackTrace(new PrintWriter(stackWriter));
66       message += "\n" + stackWriter.toString();
67     }
68     logMessage(message, errorsLogLevel);
69     if (exception != null)
70       throw new VcsException(message, exception);
71     throw new VcsException(message);
72   }
73
74   private static boolean isImportant(Throwable e) {
75     return e instanceof NullPointerException;
76   }
77
78   /**
79    * Log message using level, if level is not set - use WARN
80    *
81    * @param message message to log
82    * @param level   level to use
83    */
84   private static void logMessage(String message, String... level) {
85     String theLevel;
86     if (level.length > 0) {
87       theLevel = level[0];
88     } else {
89       theLevel = "warn";
90     }
91     if (theLevel.equals("warn")) {
92       Loggers.VCS.warn(message);
93     } else if (theLevel.equals("debug")) {
94       Loggers.VCS.debug(message);
95     }
96   }
97
98   public static ExecResult runCommand(@NotNull GitCommandLine cli, final String... errorsLogLevel) throws VcsException {
99     return runCommand(cli, DEFAULT_COMMAND_TIMEOUT_SEC, errorsLogLevel);
100   }
101
102   public static ExecResult runCommand(@NotNull GitCommandLine cli, int timeoutSeconds, final String... errorsLogLevel) throws VcsException {
103     int attemptsLeft = 2;
104     while (true) {
105       try {
106         cli.checkCanceled();
107         String cmdStr = cli.getCommandLineString();
108         File workingDir = cli.getWorkingDirectory();
109         String inDir = workingDir != null ? "[" + workingDir.getAbsolutePath() + "]" : "";
110         String msg = inDir + ": " + cmdStr;
111         Loggers.VCS.info(msg);
112         cli.logStart(cmdStr);
113         ByteArrayOutputStream stdoutBuffer = new ByteArrayOutputStream();
114         ByteArrayOutputStream stderrBuffer = cli.createStderrBuffer();
115         ExecResult res = SimpleCommandLineProcessRunner.runCommandSecure(cli, cli.getCommandLineString(), null, new ProcessTimeoutCallback(timeoutSeconds), stdoutBuffer, stderrBuffer);
116         cli.logFinish(cmdStr);
117         CommandUtil.checkCommandFailed(cmdStr, res, errorsLogLevel);
118         String out = res.getStdout().trim();
119         Loggers.VCS.debug(out);
120         if (!isEmpty(out) || !cli.isRepeatOnEmptyOutput() || attemptsLeft <= 0)
121           return res;
122         Loggers.VCS.warn("Get an unexpected empty output, will repeat command, attempts left: " + attemptsLeft);
123         attemptsLeft--;
124       } finally {
125         for (Runnable action : cli.getPostActions()) {
126           action.run();
127         }
128       }
129     }
130   }
131
132   public static void failIfNotEmptyStdErr(@NotNull GeneralCommandLine cli, @NotNull ExecResult res, String... errorsLogLevel) throws VcsException {
133     if (!isEmpty(res.getStderr()))
134       CommandUtil.commandFailed(cli.getCommandLineString(), res, errorsLogLevel);
135   }
136
137
138   public static boolean isTimeoutError(@NotNull VcsException e) {
139     String msg = e.getMessage();
140     return msg != null && msg.contains("exception: jetbrains.buildServer.ProcessTimeoutException");
141   }
142
143   public static boolean isCanceledError(@NotNull VcsException e) {
144     return e instanceof CheckoutCanceledException || e.getCause() instanceof InterruptedException;
145   }
146 }