Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Exit code not propagated from the daemon to mvnd client #222

Merged
merged 3 commits into from Nov 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 34 additions & 19 deletions client/src/main/java/org/mvndaemon/mvnd/client/DefaultClient.java
Expand Up @@ -41,6 +41,7 @@
import org.mvndaemon.mvnd.common.Environment;
import org.mvndaemon.mvnd.common.Message;
import org.mvndaemon.mvnd.common.Message.BuildException;
import org.mvndaemon.mvnd.common.Message.BuildFinished;
import org.mvndaemon.mvnd.common.OsUtils;
import org.mvndaemon.mvnd.common.TimeUtils;
import org.mvndaemon.mvnd.common.logging.ClientOutput;
Expand Down Expand Up @@ -79,16 +80,20 @@ public static void main(String[] argv) throws Exception {
}

DaemonParameters parameters = new DaemonParameters();
int exitCode = 0;
try (TerminalOutput output = new TerminalOutput(batchMode || parameters.noBuffering(), parameters.rollingWindowSize(),
logFile)) {
try {
new DefaultClient(parameters).execute(output, args);
final ExecutionResult result = new DefaultClient(parameters).execute(output, args);
exitCode = result.getExitCode();
} catch (DaemonException.InterruptedException e) {
final AttributedStyle s = new AttributedStyle().bold().foreground(AttributedStyle.RED);
String str = new AttributedString(System.lineSeparator() + "Canceled by user", s).toAnsi();
output.accept(Message.display(str));
exitCode = 130;
}
}
System.exit(exitCode);
}

public DefaultClient(DaemonParameters parameters) {
Expand Down Expand Up @@ -176,7 +181,7 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) {
d.getJavaHome())));
}
}
return new DefaultResult(argv, null);
return DefaultResult.success(argv);
}
boolean stop = args.remove("--stop");
if (stop) {
Expand All @@ -193,13 +198,13 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) {
}
}
}
return new DefaultResult(argv, null);
return DefaultResult.success(argv);
}
boolean purge = args.remove("--purge");
if (purge) {
String result = purgeLogs();
output.accept(Message.display(result != null ? result : "Nothing to purge"));
return new DefaultResult(argv, null);
return DefaultResult.success(argv);
}

if (args.stream().noneMatch(arg -> arg.startsWith("-T") || arg.equals("--threads"))) {
Expand Down Expand Up @@ -250,13 +255,14 @@ public ExecutionResult execute(ClientOutput output, List<String> argv) {
switch (m.getType()) {
case Message.CANCEL_BUILD:
return new DefaultResult(argv,
new InterruptedException("The build was canceled"));
new InterruptedException("The build was canceled"), 130);
case Message.BUILD_EXCEPTION:
final BuildException e = (BuildException) m;
return new DefaultResult(argv,
new Exception(e.getClassName() + ": " + e.getMessage() + "\n" + e.getStackTrace()));
case Message.BUILD_STOPPED:
return new DefaultResult(argv, null);
new Exception(e.getClassName() + ": " + e.getMessage() + "\n" + e.getStackTrace()),
1);
case Message.BUILD_FINISHED:
return new DefaultResult(argv, null, ((BuildFinished) m).getExitCode());
}
}
}
Expand Down Expand Up @@ -344,41 +350,50 @@ private static class DefaultResult implements ExecutionResult {

private final Exception exception;
private final List<String> args;
private final int exitCode;

private DefaultResult(List<String> args, Exception exception) {
public static DefaultResult success(List<String> args) {
return new DefaultResult(args, null, 0);
}

private DefaultResult(List<String> args, Exception exception, int exitCode) {
super();
this.args = args;
this.exception = exception;
this.exitCode = exitCode;
}

@Override
public ExecutionResult assertSuccess() {
if (exception != null) {
throw new AssertionError(appendCommand(new StringBuilder("Build failed: ")).toString(), exception);
throw new AssertionError(ExecutionResult.appendCommand(new StringBuilder("Build failed: "), args).toString(), exception);
}
if (exitCode != 0) {
throw new AssertionError(
ExecutionResult.appendCommand(new StringBuilder("Build exited with non-zero exit code " + exitCode + ": "), args).toString(),
exception);
}
return this;
}

@Override
public ExecutionResult assertFailure() {
if (exception == null) {
throw new AssertionError(appendCommand(new StringBuilder("Build did not fail: ")));
if (exception == null && exitCode == 0) {
throw new AssertionError(ExecutionResult.appendCommand(new StringBuilder("Build did not fail: "), args));
}
return this;
}

@Override
public int getExitCode() {
return exitCode;
}

@Override
public boolean isSuccess() {
return exception == null;
}

StringBuilder appendCommand(StringBuilder sb) {
sb.append("mvnd");
for (String arg : args) {
sb.append(" \"").append(arg).append('"');
}
return sb;
}
}

}
Expand Up @@ -15,6 +15,8 @@
*/
package org.mvndaemon.mvnd.client;

import java.util.List;

/**
* A result of a {@code mvnd} build.
*/
Expand All @@ -26,4 +28,14 @@ public interface ExecutionResult {

ExecutionResult assertSuccess();

int getExitCode();

public static StringBuilder appendCommand(StringBuilder sb, List<String> args) {
sb.append("mvnd");
for (String arg : args) {
sb.append(" \"").append(arg).append('"');
}
return sb;
}

}
37 changes: 32 additions & 5 deletions common/src/main/java/org/mvndaemon/mvnd/common/Message.java
Expand Up @@ -31,7 +31,7 @@
public abstract class Message {
public static final int BUILD_REQUEST = 0;
public static final int BUILD_STARTED = 1;
public static final int BUILD_STOPPED = 2;
public static final int BUILD_FINISHED = 2;
public static final int PROJECT_STARTED = 3;
public static final int PROJECT_STOPPED = 4;
public static final int MOJO_STARTED = 5;
Expand All @@ -49,7 +49,6 @@ public abstract class Message {

public static final BareMessage KEEP_ALIVE_SINGLETON = new BareMessage(KEEP_ALIVE);
public static final BareMessage STOP_SINGLETON = new BareMessage(STOP);
public static final BareMessage BUILD_STOPPED_SINGLETON = new BareMessage(BUILD_STOPPED);
public static final BareMessage CANCEL_BUILD_SINGLETON = new BareMessage(CANCEL_BUILD);

final int type;
Expand All @@ -68,8 +67,8 @@ public static Message read(DataInputStream input) throws IOException {
return BuildRequest.read(input);
case BUILD_STARTED:
return BuildStarted.read(input);
case BUILD_STOPPED:
return BareMessage.BUILD_STOPPED_SINGLETON;
case BUILD_FINISHED:
return BuildFinished.read(input);
case PROJECT_STARTED:
case PROJECT_STOPPED:
case MOJO_STARTED:
Expand Down Expand Up @@ -300,6 +299,34 @@ public void write(DataOutputStream output) throws IOException {
}
}

public static class BuildFinished extends Message {
final int exitCode;

public static Message read(DataInputStream input) throws IOException {
return new BuildFinished(input.readInt());
}

public BuildFinished(int exitCode) {
super(BUILD_FINISHED);
this.exitCode = exitCode;
}

@Override
public String toString() {
return "BuildRequest{exitCode=" + exitCode + '}';
}

@Override
public void write(DataOutputStream output) throws IOException {
super.write(output);
output.writeInt(exitCode);
}

public int getExitCode() {
return exitCode;
}
}

public static class BuildException extends Message {
final String message;
final String className;
Expand Down Expand Up @@ -474,7 +501,7 @@ public String toString() {
switch (type) {
case KEEP_ALIVE:
return "KeepAlive";
case BUILD_STOPPED:
case BUILD_FINISHED:
return "BuildStopped";
case STOP:
return "Stop";
Expand Down
Expand Up @@ -229,7 +229,7 @@ private boolean doAccept(Message entry) {
this.buildStatus = ((StringMessage) entry).getMessage();
break;
}
case Message.BUILD_STOPPED: {
case Message.BUILD_FINISHED: {
projects.values().stream().flatMap(p -> p.log.stream()).forEach(log);
clearDisplay();
try {
Expand Down
10 changes: 5 additions & 5 deletions daemon/src/main/java/org/mvndaemon/mvnd/daemon/Server.java
Expand Up @@ -529,13 +529,13 @@ public <T extends Message> T request(Message request, Class<T> responseType, Pre
}
}
});
cli.main(
int exitCode = cli.main(
buildRequest.getArgs(),
buildRequest.getWorkingDir(),
buildRequest.getProjectDir(),
buildRequest.getEnv());
LOGGER.info("Build finished, finishing message dispatch");
loggingSpy.finish();
loggingSpy.finish(exitCode);
} catch (Throwable t) {
LOGGER.error("Error while building project", t);
loggingSpy.fail(t);
Expand Down Expand Up @@ -574,7 +574,7 @@ int getClassOrder(Message m) {
return 51;
case Message.PROJECT_STOPPED:
return 95;
case Message.BUILD_STOPPED:
case Message.BUILD_FINISHED:
return 96;
case Message.BUILD_EXCEPTION:
return 97;
Expand Down Expand Up @@ -636,8 +636,8 @@ public DaemonLoggingSpy(BlockingQueue<Message> queue) {
this.queue = queue;
}

public void finish() throws Exception {
queue.add(Message.BUILD_STOPPED_SINGLETON);
public void finish(int exitCode) throws Exception {
queue.add(new Message.BuildFinished(exitCode));
queue.add(Message.STOP_SINGLETON);
}

Expand Down
@@ -0,0 +1,91 @@
/*
* Copyright 2019 the original author or authors.
*
* 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 org.mvndaemon.mvnd.it;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.stream.Stream;
import javax.inject.Inject;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mvndaemon.mvnd.assertj.TestClientOutput;
import org.mvndaemon.mvnd.client.Client;
import org.mvndaemon.mvnd.common.DaemonInfo;
import org.mvndaemon.mvnd.junit.ClientFactory;
import org.mvndaemon.mvnd.junit.MvndNativeTest;
import org.mvndaemon.mvnd.junit.TestParameters;
import org.mvndaemon.mvnd.junit.TestRegistry;

@MvndNativeTest(projectDir = "src/test/projects/new-managed-module")
public class NewManagedModuleNativeIT {

@Inject
TestParameters parameters;

@Inject
TestRegistry registry;

@Inject
ClientFactory clientFactory;

@Test
void upgrade() throws IOException, InterruptedException {
Assertions.assertThat(registry.getAll().size()).isEqualTo(0);

/* Build the initial state of the test project */
final Path parentDir = parameters.getTestDir().resolve("project/parent");
final Client cl = clientFactory.newClient(parameters.cd(parentDir));
{
final TestClientOutput output = new TestClientOutput();
cl.execute(output, "clean", "install", "-e", "-B", "-ntp").assertSuccess();
}
Assertions.assertThat(registry.getAll().size()).isEqualTo(1);

final DaemonInfo d = registry.getAll().get(0);
/* Wait, till the instance becomes idle */
registry.awaitIdle(d.getUid());

/* Do the changes */
final Path srcDir = parentDir.resolve("../changes").normalize();
try (Stream<Path> files = Files.walk(srcDir)) {
files.forEach(source -> {
final Path relPath = srcDir.relativize(source);
final Path dest = parentDir.resolve(relPath);
try {
if (Files.isDirectory(source)) {
Files.createDirectories(dest);
} else {
Files.createDirectories(dest.getParent());
Files.copy(source, dest, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}

/* Build again */
{
final TestClientOutput output = new TestClientOutput();
cl.execute(output, "clean", "install", "-e", "-B", "-ntp")
.assertFailure(); // Switch back to assertSuccess() once https://github.com/mvndaemon/mvnd/issues/218 is fixed
}
Assertions.assertThat(registry.getAll().size()).isEqualTo(1);

}
}
@@ -0,0 +1,23 @@
/*
* Copyright 2019 the original author or authors.
*
* 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 org.mvndaemon.mvnd.it;

import org.mvndaemon.mvnd.junit.MvndTest;

@MvndTest(projectDir = "src/test/projects/new-managed-module")
public class NewManagedModuleTest extends NewManagedModuleNativeIT {

}