Skip to content

Commit

Permalink
DCA11Y-1145: refactor: use shell only if node version manager require…
Browse files Browse the repository at this point in the history
…s shell
  • Loading branch information
flipatlas committed Oct 28, 2024
1 parent b418b20 commit d8b40b6
Show file tree
Hide file tree
Showing 9 changed files with 329 additions and 173 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# ignoring windows for now
invoker.os.family=!windows,unix,mac
invoker.environmentVariables.SHELL=bash
invoker.environmentVariables.SHELL=bash
invoker.environmentVariables.FNM_DIR=$HOME
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -22,15 +23,17 @@
public final class ProcessExecutor {
private final static String PATH_ENV_VAR = "PATH";

private final Map<String, String> environment;
private Map<String, String> environment;
private CommandLine commandLine;
private final Executor executor;
private final Platform platform;

public ProcessExecutor(File workingDirectory, List<String> paths, List<String> command, Platform platform, Map<String, String> additionalEnvironment){
this(workingDirectory, paths, command, platform, additionalEnvironment, 0);
}

public ProcessExecutor(File workingDirectory, List<String> paths, List<String> command, Platform platform, Map<String, String> additionalEnvironment, long timeoutInSeconds) {
this.platform = platform;
this.environment = createEnvironment(paths, platform, additionalEnvironment);
this.commandLine = createCommandLine(command);
this.executor = createExecutor(workingDirectory, timeoutInSeconds);
Expand All @@ -53,6 +56,13 @@ public int executeAndRedirectOutput(final Logger logger) throws ProcessExecution
return execute(logger, stdout, stdout);
}

public int execute(List<String> command, List<String> paths, final Logger logger, final OutputStream stdout, final OutputStream stderr)
throws ProcessExecutionException {
this.commandLine = createCommandLine(command);
this.environment = createEnvironment(paths, platform, Collections.emptyMap());
return execute(logger, stdout, stderr);
}

public int execute(final Logger logger, final OutputStream stdout, final OutputStream stderr)
throws ProcessExecutionException {
logger.debug("Executing command line {}", commandLine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,24 +12,30 @@
import java.util.Collections;
import java.util.List;

public class ShellExecutor {
public class CommandExecutor {

private final Logger logger = LoggerFactory.getLogger(getClass());
private final InstallConfig config;

private String shell;
private ProcessExecutor executor;

public ShellExecutor(InstallConfig config) {
private String fileToSource;
private String pathToInclude;

public CommandExecutor(InstallConfig config) {
this.config = config;
}

public String executeAndCatchErrors(List<String> command, List<String> paths) {
public String executeAndCatchErrors(List<String> command) {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
List<String> profiledShellCommand = getShellCommand(command);
if (shell != null) {
command = getShellCommand(command);
}

try {
int exitValue = execute(profiledShellCommand, stdout, stderr, paths);
int exitValue = execute(command, Collections.singletonList(pathToInclude), stdout, stderr);
if (exitValue != 0) {
logger.debug("Command finished with an error exit code {}", exitValue);
}
Expand All @@ -42,18 +48,16 @@ public String executeAndCatchErrors(List<String> command, List<String> paths) {
return output;
}

public String executeAndCatchErrors(List<String> command) {
return executeAndCatchErrors(command, Collections.emptyList());
}

public String executeOrFail(List<String> command, List<String> paths) {
public String executeOrFail(List<String> command) {
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
ByteArrayOutputStream stderr = new ByteArrayOutputStream();
List<String> profiledShellCommand = getShellCommand(command);
if (shell != null) {
command = getShellCommand(command);
}

boolean hasExecutionFailed = false;
try {
int exitValue = execute(profiledShellCommand, stdout, stderr, paths);
int exitValue = execute(command, Collections.singletonList(pathToInclude), stdout, stderr);
if (exitValue != 0) {
hasExecutionFailed = true;
}
Expand All @@ -62,7 +66,7 @@ public String executeOrFail(List<String> command, List<String> paths) {
}

if (hasExecutionFailed) {
String commandText = String.join(" ", profiledShellCommand);
String commandText = String.join(" ", command);
throw new RuntimeException(String.format("Execution of `%s` has failed" +
"\nstdout: `%s`" +
"\nstderr: `%s`", commandText, parseOutput(stdout), parseOutput(stderr)));
Expand All @@ -73,68 +77,67 @@ public String executeOrFail(List<String> command, List<String> paths) {
}
}

public String executeOrFail(List<String> command) {
return executeOrFail(command, Collections.emptyList());
public CommandExecutor withShell() {
setCurrentUnixShell();
return this;
}

private int execute(List<String> command, ByteArrayOutputStream stdout, ByteArrayOutputStream stderr, List<String> paths) throws ProcessExecutionException {
ProcessExecutor executor = new ProcessExecutor(
config.getWorkingDirectory(),
paths,
command,
config.getPlatform(),
Collections.emptyMap());

return executor.execute(logger, stdout, stderr);
public CommandExecutor withSourced(String file) {
fileToSource = file;
return this;
}

private List<String> getShellCommand(List<String> command) {
List<String> profiledShellCommand = new ArrayList<>();
public CommandExecutor withPath(String path) {
pathToInclude = path;
return this;
}

// FIXME
if (config.getPlatform().isWindows() || true) {
logger.warn("Windows is currently not supported");
profiledShellCommand.addAll(command);
} else {
String shell = getCurrentUnixShell();
profiledShellCommand.add(shell);
profiledShellCommand.add("-c");
profiledShellCommand.add(getCommandWithSourcedProfile(shell, command));
public void initializeProcessExecutor(List<String> paths) {
if (executor == null) {
executor = new ProcessExecutor(
config.getWorkingDirectory(),
paths,
Arrays.asList("echo", "running empty command..."),
config.getPlatform(),
Collections.emptyMap());
}
}

return profiledShellCommand;
private int execute(List<String> command, List<String> paths, ByteArrayOutputStream stdout, ByteArrayOutputStream stderr) throws ProcessExecutionException {
initializeProcessExecutor(Collections.emptyList());
return executor.execute(command, paths, logger, stdout, stderr);
}

private String parseOutput(ByteArrayOutputStream stream) {
return stream.toString().trim();
}

private String getCommandWithSourcedProfile(String shell, List<String> commandParts) {
String flagCommand = String.join(" ", commandParts);
String sourceProfile = "";
private List<String> getShellCommand(List<String> commandParts) {
String flatCommand = String.join(" ", commandParts);

List<String> commandWithSourcedProfile = new ArrayList<>();
commandWithSourcedProfile.add(shell);
commandWithSourcedProfile.add("-c");

if (shell.endsWith("zsh")) {
sourceProfile = "source ~/.zshrc";
} else if (shell.endsWith("bash")) {
sourceProfile = "source ~/.bashrc";
} else if (shell.endsWith("fish")) {
sourceProfile = "source ~/.config/fish/config.fish";
if (fileToSource != null) {
String sourceCommand = String.format(". %s", fileToSource);
commandWithSourcedProfile.add(String.format("%s; %s", sourceCommand, flatCommand));
} else {
sourceProfile = "source ~/.profile";
commandWithSourcedProfile.add(flatCommand);
}

return String.format("%s; %s", sourceProfile, flagCommand);
return commandWithSourcedProfile;
}

private String getCurrentUnixShell() {
if (shell != null) return shell;
private void setCurrentUnixShell() {
if (shell != null) return;

String shellFromEV = System.getenv("SHELL");
if (shellFromEV == null || shellFromEV.isEmpty()) {
logger.debug("SHELL variable couldn't be found. Falling back on reading the variable from /bin/sh.");
ByteArrayOutputStream stdout = new ByteArrayOutputStream();
try {
execute(Arrays.asList("/bin/sh", "-c", "echo $SHELL"), stdout, stdout, Collections.emptyList());
execute(Arrays.asList("/bin/sh", "-c", "echo $SHELL"), Collections.emptyList(), stdout, stdout);
String shellFromSh = parseOutput(stdout);
logger.debug("SHELL from /bin/sh: {}", shellFromSh);

Expand All @@ -149,6 +152,5 @@ private String getCurrentUnixShell() {
} else {
shell = shellFromEV;
}
return shell;
}
}
Original file line number Diff line number Diff line change
@@ -1,53 +1,94 @@
package com.github.eirslett.maven.plugins.frontend.lib.version.manager.client;

import com.github.eirslett.maven.plugins.frontend.lib.version.manager.ShellExecutor;
import com.github.eirslett.maven.plugins.frontend.lib.version.manager.CommandExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

public class AsdfClient implements VersionManagerClient {
final Logger logger = LoggerFactory.getLogger(getClass());
final ShellExecutor shellExecutor;
final CommandExecutor commandExecutor;

private static final String EXECUTABLE = "asdf";

public AsdfClient(ShellExecutor shellExecutor) {
this.shellExecutor = shellExecutor;
public AsdfClient(CommandExecutor commandExecutor) {
this.commandExecutor = commandExecutor;
}

@Override
public boolean isInstalled() {
String version = shellExecutor.executeAndCatchErrors(Arrays.asList(
EXECUTABLE, "--version"
));

return version.matches("v\\d+\\.\\d+\\.\\d+-[0-9a-z]+");
String asdfDir = getAsdfDir();
logger.debug("Checking if ASDF installation directory exists: {}", asdfDir);
return asdfDir != null;
}

@Override
public void installNode() {

shellExecutor.executeOrFail(Arrays.asList(
EXECUTABLE, "plugin", "add", "nodejs"
));
shellExecutor.executeOrFail(Arrays.asList(
EXECUTABLE, "install", "nodejs"
));
commandExecutor
.withShell()
.withSourced(getAsdfScript())
.executeOrFail(Arrays.asList(
EXECUTABLE, "plugin", "add", "nodejs"
));
commandExecutor
.withShell()
.withSourced(getAsdfScript())
.executeOrFail(Arrays.asList(
EXECUTABLE, "install", "nodejs"
));
}

@Override
public File getNodeExecutable() {
return new File(shellExecutor.executeOrFail(Arrays.asList(
EXECUTABLE, "which", "node"
)));
String nodePath = commandExecutor
.withShell()
.withSourced(getAsdfScript())
.executeOrFail(Arrays.asList(
EXECUTABLE, "which", "node"
));
return new File(nodePath);
}

@Override
public File getNpmExecutable() {
File nodeExec = getNodeExecutable();
return Paths.get(nodeExec.getParent(), "npm").toFile();
}

private String getAsdfScript() {
String asdfDir = getAsdfDir();
String asdfScript = Paths.get(asdfDir, "asdf.sh").toString();

return asdfScript;
}

private String getAsdfDir() {
String asdfDir = System.getenv("ASDF_DIR");
if (asdfDir != null) {
Path path = Paths.get(asdfDir);
if (Files.exists(path)) {
return path.toString();
}
}

String home = System.getenv("HOME");
if (home != null) {
Path path = Paths.get(home, ".asdf");
if (Files.exists(path)) {
return path.toString();
}

path = Paths.get(home, ".local", "share", "asdf");
if (Files.exists(path)) {
return path.toString();
}
}

return null;
}
}
Loading

0 comments on commit d8b40b6

Please sign in to comment.