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

Fix FSHRunner command execution #714

Merged
merged 2 commits into from
Jun 28, 2023
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.hl7.fhir.igtools.publisher;

import com.google.common.collect.ImmutableList;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.exec.ExecuteWatchdog;
import org.apache.commons.exec.PumpStreamHandler;
Expand All @@ -10,12 +12,15 @@
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.settings.FhirSettings;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;

public class FSHRunner {

Expand Down Expand Up @@ -64,25 +69,22 @@ protected void runFsh(File file, Publisher.IGBuildMode mode) throws IOException
exec.setWorkingDirectory(file);
ExecuteWatchdog watchdog = new ExecuteWatchdog(fshTimeout);
exec.setWatchdog(watchdog);

//FIXME This construction is a mess, and should use CommandLine(...).addArgument(...) construction. -Dotasek
String cmd = fshVersion == null ? "sushi" : "npx fsh-sushi@"+fshVersion;
if (mode == Publisher.IGBuildMode.PUBLICATION || mode == Publisher.IGBuildMode.AUTOBUILD) {
cmd += " --require-latest";
}

try {
final String execString;
if (SystemUtils.IS_OS_WINDOWS) {
exec.execute(org.apache.commons.exec.CommandLine.parse("cmd /C "+cmd+" . -o ."));
exec.execute(getWindowsCommandLine(fshVersion, mode));
} else if (FhirSettings.hasNpmPath()) {
ProcessBuilder processBuilder = new ProcessBuilder(new String("bash -c "+cmd));
ProcessBuilder processBuilder = new ProcessBuilder(new String("bash -c "+ getSushiCommandString(fshVersion,mode)));
Map<String, String> env = processBuilder.environment();
Map<String, String> vars = new HashMap<>();
vars.putAll(env);
String path = FhirSettings.getNpmPath()+":"+env.get("PATH");
vars.put("PATH", path);
exec.execute(org.apache.commons.exec.CommandLine.parse("bash -c "+cmd+" . -o ."), vars);

exec.execute(getNpmPathCommandLine(fshVersion, mode), vars);
} else {
exec.execute(org.apache.commons.exec.CommandLine.parse(cmd+" . -o ."));
exec.execute(getDefaultCommandLine(fshVersion, mode));
}
} catch (IOException ioex) {
log("Sushi couldn't be run. Complete output from running Sushi : " + pumpHandler.getBufferString());
Expand All @@ -99,6 +101,52 @@ protected void runFsh(File file, Publisher.IGBuildMode mode) throws IOException
}
}

@Nonnull
protected CommandLine getDefaultCommandLine(String fshVersion, Publisher.IGBuildMode mode) {
final List<String> sushiCommandList = getSushiCommandList(fshVersion, mode);
CommandLine commandLine = new CommandLine(sushiCommandList.get(0));
for (int i = 1; i < sushiCommandList.size(); i++) {
commandLine.addArgument(sushiCommandList.get(i));
}
commandLine.addArgument(".").addArgument("-o").addArgument(".");
return commandLine;
}

@Nonnull
protected CommandLine getNpmPathCommandLine(String fshVersion, Publisher.IGBuildMode mode) {
CommandLine commandLine = new CommandLine("bash").addArgument("-c");
for (String argument : getSushiCommandList(fshVersion,mode)) {
commandLine.addArgument(argument);
}
commandLine.addArgument(".").addArgument("-o").addArgument(".");
return commandLine;
}

@Nonnull
protected CommandLine getWindowsCommandLine(String fshVersion, Publisher.IGBuildMode mode) {
CommandLine commandLine = new CommandLine("cmd").addArgument("/C");
for (String argument : getSushiCommandList(fshVersion,mode)) {
commandLine.addArgument(argument);
}
commandLine.addArgument(".").addArgument("-o").addArgument(".");
return commandLine;
}

protected List<String> getSushiCommandList(String fshVersion, Publisher.IGBuildMode mode) {
final List<String> cmd = fshVersion == null ? List.of("sushi"): List.of("npx", "fsh-sushi@"+fshVersion);
if (mode == Publisher.IGBuildMode.PUBLICATION || mode == Publisher.IGBuildMode.AUTOBUILD) {
return new ImmutableList.Builder<String>().addAll(cmd).add("--require-latest").build();
}
return cmd;
}
protected String getSushiCommandString(String fshVersion, Publisher.IGBuildMode mode) {

StringJoiner stringJoiner = new StringJoiner(" ");
for (String argument : getSushiCommandList(fshVersion,mode)) {
stringJoiner.add(argument);
}
return stringJoiner.toString();
}
public class MySushiHandler extends OutputStream {

private byte[] buffer;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package org.hl7.fhir.igtools.publisher;

import org.apache.commons.exec.CommandLine;
import org.hl7.fhir.r5.context.IWorkerContext;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.mockito.Mockito;

import java.util.List;
import java.util.stream.Stream;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;


public class FSHRunnerTests {



public static Stream<Arguments> defaultExecStringParams () {
List<Arguments> output = List.of(
Arguments.of(Publisher.IGBuildMode.PUBLICATION, null, "sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, null, "sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, null, "sushi . -o ."),
Arguments.of(Publisher.IGBuildMode.PUBLICATION, "1.2.3", "npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, "1.2.3", "npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, "1.2.3", "npx fsh-sushi@1.2.3 . -o .")
);
return output.stream();
}

@ParameterizedTest
@MethodSource("defaultExecStringParams")
public void testDefaultExecString(Publisher.IGBuildMode mode, String fshVersion, String expectedExecString) {
FSHRunner fshRunner = new FSHRunner(Mockito.mock(IWorkerContext.ILoggingService.class));
final CommandLine actualCommandLine = fshRunner.getDefaultCommandLine(fshVersion, mode);
assertIsEqual(CommandLine.parse(expectedExecString), actualCommandLine);
}

public static Stream<Arguments> sushiCommandParams () {
List<Arguments> output = List.of(
Arguments.of(Publisher.IGBuildMode.PUBLICATION, null, "sushi --require-latest"),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, null, "sushi --require-latest"),
Arguments.of(Publisher.IGBuildMode.MANUAL, null, "sushi"),
Arguments.of(Publisher.IGBuildMode.PUBLICATION, "1.2.3", "npx fsh-sushi@1.2.3 --require-latest"),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, "1.2.3", "npx fsh-sushi@1.2.3 --require-latest"),
Arguments.of(Publisher.IGBuildMode.MANUAL, "1.2.3", "npx fsh-sushi@1.2.3")
);
return output.stream();
}

@ParameterizedTest
@MethodSource("sushiCommandParams")
public void testGetSushiCommand(Publisher.IGBuildMode mode, String fshVersion, String expectedSushiCommand) {
FSHRunner fshRunner = new FSHRunner(Mockito.mock(IWorkerContext.ILoggingService.class));
assertEquals(expectedSushiCommand, fshRunner.getSushiCommandString(fshVersion,mode));
}

private void assertIsEqual(CommandLine a, CommandLine b) {
assertEquals(a.getExecutable(), b.getExecutable());
assertArrayEquals(a.getArguments(), b.getArguments());
}

public static Stream<Arguments> windowsExecStringParams () {
List<Arguments> output = List.of(
Arguments.of(Publisher.IGBuildMode.PUBLICATION, null, "cmd /C sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, null, "cmd /C sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, null, "cmd /C sushi . -o ."),
Arguments.of(Publisher.IGBuildMode.PUBLICATION, "1.2.3", "cmd /C npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, "1.2.3", "cmd /C npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, "1.2.3", "cmd /C npx fsh-sushi@1.2.3 . -o .")
);
return output.stream();
}
@ParameterizedTest
@MethodSource("windowsExecStringParams")
public void testWindowsExecString(Publisher.IGBuildMode mode, String fshVersion, String expectedExecString) {
FSHRunner fshRunner = new FSHRunner(Mockito.mock(IWorkerContext.ILoggingService.class));
final CommandLine actualCommandLine = fshRunner.getWindowsCommandLine(fshVersion, mode);
assertIsEqual(CommandLine.parse(expectedExecString), actualCommandLine);
}

public static Stream<Arguments> npmPathExecStringParams () {
List<Arguments> output = List.of(
Arguments.of(Publisher.IGBuildMode.PUBLICATION, null, "bash -c sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, null, "bash -c sushi --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, null, "bash -c sushi . -o ."),
Arguments.of(Publisher.IGBuildMode.PUBLICATION, "1.2.3", "bash -c npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.AUTOBUILD, "1.2.3", "bash -c npx fsh-sushi@1.2.3 --require-latest . -o ."),
Arguments.of(Publisher.IGBuildMode.MANUAL, "1.2.3", "bash -c npx fsh-sushi@1.2.3 . -o .")
);
return output.stream();
}
@ParameterizedTest
@MethodSource("npmPathExecStringParams")
public void testNpmPathExecString(Publisher.IGBuildMode mode, String fshVersion, String expectedExecString) {
FSHRunner fshRunner = new FSHRunner(Mockito.mock(IWorkerContext.ILoggingService.class));
final CommandLine actualCommandLine = fshRunner.getNpmPathCommandLine(fshVersion, mode);
assertIsEqual(CommandLine.parse(expectedExecString), actualCommandLine);
}
}
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,12 @@
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.8.1</version>
<scope>test</scope>
</dependency>
<!-- plantuml -->
<dependency>
<groupId>net.sourceforge.plantuml</groupId>
Expand Down