Allow live output + output capture thanks to zt-exec (#184)
This commit is contained in:
committed by
Marcin Grzejszczak
parent
0d5460e051
commit
353cbc1ab7
1
pom.xml
1
pom.xml
@@ -45,6 +45,7 @@
|
||||
<javax.json.version>1.0.4</javax.json.version>
|
||||
<jopt-simple.version>5.0.3</jopt-simple.version>
|
||||
<fliptables.version>1.0.2</fliptables.version>
|
||||
<zt.exec.version>1.11</zt.exec.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
||||
@@ -135,6 +135,11 @@
|
||||
<version>${javax.json.version}</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.zeroturnaround</groupId>
|
||||
<artifactId>zt-exec</artifactId>
|
||||
<version>${zt.exec.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
|
||||
@@ -18,7 +18,6 @@ package releaser.internal.project;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.FileVisitResult;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
@@ -28,8 +27,8 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -37,6 +36,9 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.zeroturnaround.exec.ProcessExecutor;
|
||||
import org.zeroturnaround.exec.ProcessResult;
|
||||
import org.zeroturnaround.exec.stream.slf4j.Slf4jStream;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.ReleaserPropertiesAware;
|
||||
|
||||
@@ -179,8 +181,8 @@ public class ProjectCommandExecutor implements ReleaserPropertiesAware {
|
||||
return executor(projectRoot).runCommand(substitutedCommands, waitTimeInMinutes);
|
||||
}
|
||||
|
||||
ProcessExecutor executor(String workDir) {
|
||||
return new ProcessExecutor(workDir);
|
||||
ReleaserProcessExecutor executor(String workDir) {
|
||||
return new ReleaserProcessExecutor(workDir);
|
||||
}
|
||||
|
||||
public void publishDocs(ProjectVersion originalVersion,
|
||||
@@ -264,63 +266,64 @@ public class ProjectCommandExecutor implements ReleaserPropertiesAware {
|
||||
|
||||
}
|
||||
|
||||
class ProcessExecutor implements ReleaserPropertiesAware {
|
||||
class ReleaserProcessExecutor implements ReleaserPropertiesAware {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(ProcessExecutor.class);
|
||||
private static final Logger log = LoggerFactory
|
||||
.getLogger(ReleaserProcessExecutor.class);
|
||||
|
||||
private static String[] OS_OPERATORS = { "|", "<", ">", "||", "&&" };
|
||||
|
||||
private String workingDir;
|
||||
|
||||
ProcessExecutor(String workingDir) {
|
||||
ReleaserProcessExecutor(String workingDir) {
|
||||
this.workingDir = workingDir;
|
||||
}
|
||||
|
||||
String runCommand(String[] commands, long waitTimeInMinutes) {
|
||||
String workingDir = this.workingDir;
|
||||
log.info("Will run the command from [{}] and wait for result for [{}] minutes",
|
||||
workingDir, waitTimeInMinutes);
|
||||
|
||||
try {
|
||||
String workingDir = this.workingDir;
|
||||
log.info(
|
||||
"Will run the command from [{}] and wait for result for [{}] minutes",
|
||||
workingDir, waitTimeInMinutes);
|
||||
ProcessBuilder builder = builder(commands, workingDir);
|
||||
Process process = startProcess(builder);
|
||||
boolean finished = process.waitFor(waitTimeInMinutes, TimeUnit.MINUTES);
|
||||
if (!finished) {
|
||||
log.error("The command hasn't managed to finish in [{}] minutes",
|
||||
waitTimeInMinutes);
|
||||
process.destroyForcibly();
|
||||
throw new IllegalStateException("Process waiting time of ["
|
||||
+ waitTimeInMinutes + "] minutes exceeded");
|
||||
}
|
||||
if (process.exitValue() != 0) {
|
||||
ProcessExecutor processExecutor = processExecutor(commands, workingDir)
|
||||
.timeout(waitTimeInMinutes, TimeUnit.MINUTES);
|
||||
final ProcessResult processResult = doExecute(processExecutor);
|
||||
|
||||
int processExitValue = processResult.getExitValue();
|
||||
if (processExitValue != 0) {
|
||||
throw new IllegalStateException("The process has exited with exit code ["
|
||||
+ process.exitValue() + "]");
|
||||
+ processExitValue + "]");
|
||||
}
|
||||
return convertStreamToString(process.getInputStream());
|
||||
return processResult.outputUTF8();
|
||||
}
|
||||
catch (InterruptedException | IOException e) {
|
||||
throw new IllegalStateException(e);
|
||||
throw new IllegalStateException("Process execution failed", e);
|
||||
}
|
||||
catch (TimeoutException e) {
|
||||
log.error("The command hasn't managed to finish in [{}] minutes",
|
||||
waitTimeInMinutes);
|
||||
throw new IllegalStateException("Process waiting time of ["
|
||||
+ waitTimeInMinutes + "] minutes exceeded", e);
|
||||
}
|
||||
}
|
||||
|
||||
private String convertStreamToString(InputStream is) {
|
||||
Scanner scanner = new Scanner(is).useDelimiter("\\A");
|
||||
return scanner.hasNext() ? scanner.next() : "";
|
||||
ProcessResult doExecute(ProcessExecutor processExecutor)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return processExecutor.execute();
|
||||
}
|
||||
|
||||
Process startProcess(ProcessBuilder builder) throws IOException {
|
||||
return builder.start();
|
||||
}
|
||||
|
||||
ProcessBuilder builder(String[] commands, String workingDir) {
|
||||
ProcessExecutor processExecutor(String[] commands, String workingDir) {
|
||||
String[] commandsToRun = commands;
|
||||
String lastArg = String.join(" ", commands);
|
||||
if (Arrays.stream(OS_OPERATORS).anyMatch(lastArg::contains)) {
|
||||
commandsToRun = commandToExecute(lastArg);
|
||||
}
|
||||
log.info("Will run the command [{}]", Arrays.toString(commandsToRun));
|
||||
return new ProcessBuilder(commandsToRun).directory(new File(workingDir))
|
||||
.inheritIO();
|
||||
return new ProcessExecutor().command(commandsToRun).destroyOnExit()
|
||||
.readOutput(true)
|
||||
.redirectOutputAlsoTo(
|
||||
Slf4jStream.of(ReleaserProcessExecutor.class).asInfo())
|
||||
.directory(new File(workingDir));
|
||||
}
|
||||
|
||||
String[] commandToExecute(String lastArg) {
|
||||
|
||||
@@ -16,18 +16,22 @@
|
||||
|
||||
package releaser.internal.project;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.zeroturnaround.exec.ProcessExecutor;
|
||||
import org.zeroturnaround.exec.ProcessResult;
|
||||
import releaser.internal.PomUpdateAcceptanceTests;
|
||||
import releaser.internal.ReleaserProperties;
|
||||
import releaser.internal.buildsystem.TestUtils;
|
||||
@@ -62,7 +66,7 @@ public class ProjectCommandExecutorTests {
|
||||
ProjectCommandExecutor projectBuilder(ReleaserProperties properties) {
|
||||
return new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return testExecutor(workingDir);
|
||||
}
|
||||
};
|
||||
@@ -281,7 +285,7 @@ public class ProjectCommandExecutorTests {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getMaven().setDeployCommand("echo foo");
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
new ProcessExecutor(properties.getWorkingDir())
|
||||
new ReleaserProcessExecutor(properties.getWorkingDir())
|
||||
.runCommand(new String[] { "touch", "pom.xml" }, 1);
|
||||
ProjectCommandExecutor builder = projectBuilder(properties);
|
||||
|
||||
@@ -311,7 +315,7 @@ public class ProjectCommandExecutorTests {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getMaven().setDeployCommand("echo foo");
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
new ProcessExecutor(properties.getWorkingDir())
|
||||
new ReleaserProcessExecutor(properties.getWorkingDir())
|
||||
.runCommand(new String[] { "touch", "pom.xml" }, 1);
|
||||
ProjectCommandExecutor builder = projectBuilder(properties);
|
||||
|
||||
@@ -340,7 +344,7 @@ public class ProjectCommandExecutorTests {
|
||||
ReleaserProperties properties = new ReleaserProperties();
|
||||
properties.getMaven().setDeployCommand("echo foo");
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
new ProcessExecutor(properties.getWorkingDir())
|
||||
new ReleaserProcessExecutor(properties.getWorkingDir())
|
||||
.runCommand(new String[] { "touch", "pom.xml" }, 1);
|
||||
ProjectCommandExecutor builder = projectBuilder(properties);
|
||||
|
||||
@@ -412,10 +416,10 @@ public class ProjectCommandExecutorTests {
|
||||
properties.getBash().setPublishDocsCommands(new String[] { "ls -al",
|
||||
"echo {{version}} {{oldVersion}} {{nextVersion}}" });
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return executor;
|
||||
}
|
||||
};
|
||||
@@ -435,10 +439,10 @@ public class ProjectCommandExecutorTests {
|
||||
new String[] { "echo {{systemProps}} 1", "echo {{systemProps}} 2" });
|
||||
properties.getBash().setSystemProperties("-Dhello=world -Dfoo=bar");
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return executor;
|
||||
}
|
||||
};
|
||||
@@ -457,10 +461,10 @@ public class ProjectCommandExecutorTests {
|
||||
properties.getBash()
|
||||
.setPublishDocsCommands(new String[] { "echo '{{version}}'" });
|
||||
properties.setWorkingDir(tmpFile("/builder/resolved").getPath());
|
||||
TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return executor;
|
||||
}
|
||||
};
|
||||
@@ -478,10 +482,10 @@ public class ProjectCommandExecutorTests {
|
||||
properties.getBash().setGenerateReleaseTrainDocsCommand("echo '{{version}}'");
|
||||
File resolved = tmpFile("/builder/resolved");
|
||||
properties.setWorkingDir(resolved.getPath());
|
||||
TestProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
TestReleaserProcessExecutor executor = testExecutor(properties.getWorkingDir());
|
||||
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return executor;
|
||||
}
|
||||
};
|
||||
@@ -513,11 +517,12 @@ public class ProjectCommandExecutorTests {
|
||||
properties.setWorkingDir(tmpFile("/builder/unresolved").getPath());
|
||||
ProjectCommandExecutor builder = new ProjectCommandExecutor(properties) {
|
||||
@Override
|
||||
ProcessExecutor executor(String workingDir) {
|
||||
return new ProcessExecutor(properties.getWorkingDir()) {
|
||||
ReleaserProcessExecutor executor(String workingDir) {
|
||||
return new ReleaserProcessExecutor(properties.getWorkingDir()) {
|
||||
@Override
|
||||
Process startProcess(ProcessBuilder builder) {
|
||||
return processWithInvalidExitCode();
|
||||
ProcessResult doExecute(ProcessExecutor processExecutor)
|
||||
throws IOException, InterruptedException, TimeoutException {
|
||||
return new ProcessResult(1, null);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -528,42 +533,8 @@ public class ProjectCommandExecutorTests {
|
||||
"The process has exited with exit code [1]");
|
||||
}
|
||||
|
||||
private Process processWithInvalidExitCode() {
|
||||
return new Process() {
|
||||
@Override
|
||||
public OutputStream getOutputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getErrorStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int waitFor() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int exitValue() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private TestProcessExecutor testExecutor(String workingDir) {
|
||||
return new TestProcessExecutor(workingDir);
|
||||
private TestReleaserProcessExecutor testExecutor(String workingDir) {
|
||||
return new TestReleaserProcessExecutor(workingDir);
|
||||
}
|
||||
|
||||
private File tmpFile(String relativePath) {
|
||||
@@ -582,19 +553,35 @@ public class ProjectCommandExecutorTests {
|
||||
return new ProjectVersion("foo", "0.100.0.BUILD-SNAPSHOT");
|
||||
}
|
||||
|
||||
class TestProcessExecutor extends ProcessExecutor {
|
||||
class TestReleaserProcessExecutor extends ReleaserProcessExecutor {
|
||||
|
||||
int counter = 0;
|
||||
|
||||
TestProcessExecutor(String workingDir) {
|
||||
TestReleaserProcessExecutor(String workingDir) {
|
||||
super(workingDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
ProcessBuilder builder(String[] commands, String workingDir) {
|
||||
ProcessExecutor processExecutor(String[] commands, String workingDir) {
|
||||
this.counter++;
|
||||
return super.builder(commands, workingDir)
|
||||
.redirectOutput(tmpFile("/builder/resolved/resolved.log"));
|
||||
final ProcessExecutor processExecutor = super.processExecutor(commands,
|
||||
workingDir);
|
||||
|
||||
File tempFile = tmpFile("/builder/resolved/resolved.log");
|
||||
try {
|
||||
tempFile.createNewFile();
|
||||
OutputStream fos = new BufferedOutputStream(
|
||||
new FileOutputStream(tempFile));
|
||||
|
||||
return processExecutor
|
||||
// use redirectOutputAlsoTo to avoid overriding all output
|
||||
// destinations
|
||||
.redirectOutput(fos);
|
||||
}
|
||||
catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return processExecutor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user