From c231897a21bcd9d433c3ca68ec256cbdabe1e827 Mon Sep 17 00:00:00 2001 From: Noy Shabtay <70848358+noyshabtay@users.noreply.github.com> Date: Wed, 20 Dec 2023 10:55:46 +0200 Subject: [PATCH] Fixed 'go' Commands Issue in Windows Paths with Spaces (#772) --- .../jfrog/build/extractor/go/GoDriver.java | 49 ++++++++++++++++++- .../build/extractor/go/GoDriverTest.java | 19 +++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/build-info-extractor-go/src/main/java/org/jfrog/build/extractor/go/GoDriver.java b/build-info-extractor-go/src/main/java/org/jfrog/build/extractor/go/GoDriver.java index c2307970a..1cc8aad99 100644 --- a/build-info-extractor-go/src/main/java/org/jfrog/build/extractor/go/GoDriver.java +++ b/build-info-extractor-go/src/main/java/org/jfrog/build/extractor/go/GoDriver.java @@ -1,6 +1,8 @@ package org.jfrog.build.extractor.go; +import com.google.common.collect.Maps; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; import org.jfrog.build.api.util.Log; import org.jfrog.build.extractor.executor.CommandExecutor; import org.jfrog.build.extractor.executor.CommandResults; @@ -24,6 +26,7 @@ public class GoDriver implements Serializable { private static final String GO_GET_CMD = "get"; private static final String GO_LIST_MODULE_CMD = "list -m"; private static final String GO_VERSION_CMD = "version"; + private static final String DEFAULT_EXECUTABLE_NAME = "go"; private static final long serialVersionUID = 1L; private final CommandExecutor commandExecutor; @@ -31,11 +34,52 @@ public class GoDriver implements Serializable { private final Log logger; public GoDriver(String executablePath, Map env, File workingDirectory, Log logger) { - this.commandExecutor = new CommandExecutor(StringUtils.defaultIfEmpty(executablePath, "go"), env); + this.commandExecutor = generateCommandExecutor(executablePath, env); this.workingDirectory = workingDirectory; this.logger = logger; } + /** + * Generate a new mutable copy of environment variables map with the Go executable directory path inserted to the beginning of the Path. + * + * @param executablePath Go executable path + * @param env Environment variables map + * @return a new Environment variables map + */ + static Map generateWindowsEnv(String executablePath, Map env) { + // If executablePath ends with "go" or "go.exe" - remove it from the directory path + executablePath = StringUtils.removeEnd(executablePath, ".exe"); + executablePath = StringUtils.removeEnd(executablePath, DEFAULT_EXECUTABLE_NAME); + + // Insert the Go executable directory path to the beginning of the Path environment variable + // Make sure to copy the environment variables map to avoid changing the original map or in case it is immutable + Map newEnv = Maps.newHashMap(env); + String windowsPathEnvKey = "Path"; + if (newEnv.containsKey(windowsPathEnvKey)) { + newEnv.put(windowsPathEnvKey, executablePath + File.pathSeparator + newEnv.get(windowsPathEnvKey)); + } else { + newEnv.put(windowsPathEnvKey, executablePath); + } + return newEnv; + } + + /** + * Create a CommandExecutor with the given executable path and environment variables. + * + * @param executablePath Go executable path + * @param env Environment variables map + * @return CommandExecutor + */ + private static CommandExecutor generateCommandExecutor(String executablePath, Map env) { + if (!SystemUtils.IS_OS_WINDOWS || StringUtils.isBlank(executablePath) || StringUtils.equals(executablePath, DEFAULT_EXECUTABLE_NAME) || env == null) { + return new CommandExecutor(StringUtils.defaultIfEmpty(executablePath, DEFAULT_EXECUTABLE_NAME), env); + } + // Handling Windows case with a given executable path: + // A bug was identified for the Go executable in Windows where the executable path may be incorrectly parsed + // as two command arguments when the path contains spaces (e.g., "C:\Program Files\Go\bin\go.exe"). + return new CommandExecutor(DEFAULT_EXECUTABLE_NAME, generateWindowsEnv(executablePath, env)); + } + public CommandResults runCmd(String args, boolean verbose) throws IOException { List argsList = new ArrayList<>(Arrays.asList(args.split(" "))); return runCmd(argsList, verbose); @@ -109,8 +153,9 @@ public void modTidy(boolean verbose, boolean ignoreErrors) throws IOException { /** * Run go get. + * * @param componentId - Component ID string. ( Example: github.com/jfrog/build-info-go@v1.8.7 ) - * @param verbose - True if should print the results to the log + * @param verbose - True if should print the results to the log * @throws IOException - in case of any I/O error. */ public void get(String componentId, boolean verbose) throws IOException { diff --git a/build-info-extractor-go/src/test/java/org/jfrog/build/extractor/go/GoDriverTest.java b/build-info-extractor-go/src/test/java/org/jfrog/build/extractor/go/GoDriverTest.java index eaa77580c..5acc9ff13 100644 --- a/build-info-extractor-go/src/test/java/org/jfrog/build/extractor/go/GoDriverTest.java +++ b/build-info-extractor-go/src/test/java/org/jfrog/build/extractor/go/GoDriverTest.java @@ -2,9 +2,11 @@ import com.google.common.collect.Sets; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.SystemUtils; import org.jfrog.build.api.util.NullLog; import org.jfrog.build.extractor.executor.CommandResults; import org.testng.Assert; +import org.testng.SkipException; import org.testng.annotations.Test; import java.io.File; @@ -13,6 +15,7 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; +import java.util.Map; import java.util.Set; import java.util.stream.Collectors; @@ -114,4 +117,20 @@ public void testGoGet() throws IOException { FileUtils.deleteDirectory(projectDir); } } + + @Test + public void testGoDriverWindowsInit() throws IOException { + if (!SystemUtils.IS_OS_WINDOWS) { + throw new SkipException("Skipping non-windows test"); + } + File projectDir = Files.createTempDirectory("").toFile(); + try { + Map systemEnv = System.getenv(); + String executablePath = "C:\\Program Files\\Go\\bin\\go"; + Map executorEnv = GoDriver.generateWindowsEnv(executablePath, systemEnv); + assertTrue(executorEnv.get("Path").startsWith("C:\\Program Files\\Go\\bin")); + } finally { + FileUtils.deleteDirectory(projectDir); + } + } }