From d8854be925004716dfd76b1558b8717a1311947e Mon Sep 17 00:00:00 2001 From: Ruud Senden <8635138+rsenden@users.noreply.github.com> Date: Thu, 1 Feb 2024 02:05:16 +0100 Subject: [PATCH] chore: Improve tool commands --- .../helper/ToolInstallationDescriptor.java | 12 +++++++--- .../ToolInstallationOutputDescriptor.java | 2 ++ .../tool/_common/helper/ToolInstaller.java | 17 +++++++++++--- .../fcli/cli/cmd/ToolFcliInstallCommand.java | 22 ++++++++++++++++++- .../com/fortify/cli/ftest/_common/Fcli.groovy | 7 ++++++ .../tool/ToolBugTrackerUtilitySpec.groovy | 2 +- .../cli/ftest/tool/ToolDebrickedSpec.groovy | 2 +- .../cli/ftest/tool/ToolFcliSpec.groovy | 2 +- .../cli/ftest/tool/ToolFoDUploaderSpec.groovy | 8 +++---- .../cli/ftest/tool/ToolScClientSpec.groovy | 2 +- .../ftest/tool/ToolVulnExporterSpec.groovy | 2 +- 11 files changed, 62 insertions(+), 16 deletions(-) diff --git a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationDescriptor.java b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationDescriptor.java index 339bac2d20..be8b304a24 100644 --- a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationDescriptor.java +++ b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationDescriptor.java @@ -38,10 +38,12 @@ public class ToolInstallationDescriptor { private String installDir; private String binDir; + private String globalBinDir; - public ToolInstallationDescriptor(Path installPath, Path binPath) { - this.installDir = installPath.toAbsolutePath().toString(); - this.binDir = binPath.toAbsolutePath().toString(); + public ToolInstallationDescriptor(Path installPath, Path binPath, Path globalBinPath) { + this.installDir = installPath==null ? null : installPath.toAbsolutePath().normalize().toString(); + this.binDir = binPath==null ? null : binPath.toAbsolutePath().normalize().toString(); + this.globalBinDir = globalBinPath==null ? null : globalBinPath.toAbsolutePath().normalize().toString(); } public static final ToolInstallationDescriptor load(String toolName, ToolDefinitionVersionDescriptor versionDescriptor) { @@ -70,6 +72,10 @@ public Path getBinPath() { return asPath(binDir); } + public Path getGlobalBinPath() { + return asPath(binDir); + } + private static final Path asPath(String dir) { return StringUtils.isNotBlank(dir) ? Paths.get(dir) : null; } diff --git a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationOutputDescriptor.java b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationOutputDescriptor.java index ee4233c6f1..c8f83b7ebc 100644 --- a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationOutputDescriptor.java +++ b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstallationOutputDescriptor.java @@ -41,6 +41,7 @@ public class ToolInstallationOutputDescriptor { private Map binaries; private final String installDir; private final String binDir; + private final String globalBinDir; private final String installed; private final String __action__; @@ -53,6 +54,7 @@ public ToolInstallationOutputDescriptor(String toolName, ToolDefinitionVersionDe this.binaries = versionDescriptor.getBinaries(); this.installDir = installationDescriptor==null ? null : installationDescriptor.getInstallDir(); this.binDir = installationDescriptor==null ? null : installationDescriptor.getBinDir(); + this.globalBinDir = installationDescriptor==null ? null : installationDescriptor.getGlobalBinDir(); this.installed = StringUtils.isBlank(this.installDir) ? "No" : "Yes"; this.__action__ = action; } diff --git a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstaller.java b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstaller.java index 148048c5c9..932f9fd528 100644 --- a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstaller.java +++ b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/_common/helper/ToolInstaller.java @@ -205,6 +205,7 @@ private final ToolInstallationResult install(ToolDefinitionArtifactDescriptor ar progressWriter.writeProgress("Running post-install actions"); postInstallAction.accept(this, result); updateBinPermissions(result.getInstallationDescriptor().getBinPath()); + writeInstallationInfo(result); return result; } catch ( IOException e ) { throw new RuntimeException("Error installing "+toolName, e); @@ -244,7 +245,8 @@ private final void copyOrExtract(ToolDefinitionArtifactDescriptor artifactDescri private final ToolInstallationDescriptor createAndSaveInstallationDescriptor() { var installPath = getTargetPath(); var binPath = getBinPath(); - var installationDescriptor = new ToolInstallationDescriptor(installPath, binPath); + var globalBinPath = getGlobalBinPath(); + var installationDescriptor = new ToolInstallationDescriptor(installPath, binPath, globalBinPath); installationDescriptor.save(toolName, getVersionDescriptor()); return installationDescriptor; } @@ -269,6 +271,13 @@ private final void warnIfDifferentTargetPath() { } } + private final void writeInstallationInfo(ToolInstallationResult installationResult) { + var globalBinDir = installationResult.getInstallationDescriptor().getGlobalBinDir(); + var binDir = installationResult.getInstallationDescriptor().getBinDir(); + progressWriter.writeWarning("INFO: Add the following directory to PATH for easy tool invocation:\n %s\n", + globalBinDir==null ? binDir : globalBinDir); + } + private final void checkEmptyTargetPath() throws IOException { var targetPath = getTargetPath(); if ( Files.exists(targetPath) && Files.list(targetPath).findFirst().isPresent() ) { @@ -277,8 +286,10 @@ private final void checkEmptyTargetPath() throws IOException { } private static final void updateBinPermissions(Path binPath) throws IOException { - try (Stream walk = Files.walk(binPath)) { - walk.forEach(ToolInstaller::updateFilePermissions); + if ( binPath!=null ) { + try (Stream walk = Files.walk(binPath)) { + walk.forEach(ToolInstaller::updateFilePermissions); + } } } diff --git a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/fcli/cli/cmd/ToolFcliInstallCommand.java b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/fcli/cli/cmd/ToolFcliInstallCommand.java index 1bf2926ef5..d02f720c25 100644 --- a/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/fcli/cli/cmd/ToolFcliInstallCommand.java +++ b/fcli-core/fcli-tool/src/main/java/com/fortify/cli/tool/fcli/cli/cmd/ToolFcliInstallCommand.java @@ -12,8 +12,10 @@ *******************************************************************************/ package com.fortify.cli.tool.fcli.cli.cmd; +import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.StandardCopyOption; import com.fortify.cli.common.output.cli.mixin.OutputHelperMixins; import com.fortify.cli.common.util.FileUtils; @@ -39,13 +41,31 @@ protected String getDefaultArtifactType() { @Override @SneakyThrows protected void postInstall(ToolInstaller installer, ToolInstallationResult installationResult) { + installBinariesAndScripts(installer); + installFcliCompletion(installer); + } + + private void installFcliCompletion(ToolInstaller installer) throws IOException { + var globalBinPath = installer.getGlobalBinPath(); + var originalFcliCompletionScript = installer.getBinPath().resolve("fcli_completion"); + if ( Files.exists(originalFcliCompletionScript) ) { + var pw = installer.getProgressWriter(); + Path targetFcliCompletionScript = originalFcliCompletionScript; + if ( globalBinPath!=null ) { + targetFcliCompletionScript = globalBinPath.resolve(originalFcliCompletionScript.getFileName()); + Files.copy(originalFcliCompletionScript, targetFcliCompletionScript, StandardCopyOption.REPLACE_EXISTING); + } + pw.writeWarning("INFO: Run the following command to update fcli auto-completion:\n source %s", targetFcliCompletionScript.toAbsolutePath().normalize()); + } + } + + private void installBinariesAndScripts(ToolInstaller installer) { Path installPath = installer.getTargetPath(); FileUtils.moveFiles(installPath, installer.getBinPath(), "fcli(_completion)?(\\.exe)?"); if ( Files.exists(installPath.resolve("fcli.jar")) ) { installer.installJavaBinScripts("fcli", "fcli.jar"); } else { installer.installGlobalBinScript(BinScriptType.bash, "fcli", "bin/fcli"); - installer.installGlobalBinScript(BinScriptType.bash, "fcli_completion", "bin/fcli_completion"); installer.installGlobalBinScript(BinScriptType.bat, "fcli.bat", "bin/fcli.exe"); } } diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/_common/Fcli.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/_common/Fcli.groovy index b23a8f8ef5..0d4db840fe 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/_common/Fcli.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/_common/Fcli.groovy @@ -150,6 +150,13 @@ public class Fcli { } return this } + final FcliResult expectZeroExitCode() { + if ( nonZeroExitCode ) { + throw new UnexpectedFcliResultException("Fcli unexpectedly terminated unsuccessfully\n " + +stderr.join("\n "), this) + } + return this + } } public static class UnexpectedFcliResultException extends RuntimeException { diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolBugTrackerUtilitySpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolBugTrackerUtilitySpec.groovy index f1f8d91c43..228f5a0d34 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolBugTrackerUtilitySpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolBugTrackerUtilitySpec.groovy @@ -32,7 +32,7 @@ class ToolBugTrackerUtilitySpec extends FcliBaseSpec { def "install"() { def args = "tool bugtracker-utility install -y -v=${version} --progress=none -b ${baseDir}" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolDebrickedSpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolDebrickedSpec.groovy index 6df8205b2c..5fa5d0201f 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolDebrickedSpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolDebrickedSpec.groovy @@ -33,7 +33,7 @@ class ToolDebrickedSpec extends FcliBaseSpec { def "install"() { def args = "tool debricked-cli install -y -v=${version} --progress=none -b ${baseDir} --platform windows/x64" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFcliSpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFcliSpec.groovy index c739b1cbb9..8ef958c4b7 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFcliSpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFcliSpec.groovy @@ -33,7 +33,7 @@ class ToolFcliSpec extends FcliBaseSpec { def "install"() { def args = "tool fcli install -y -v=${version} --progress=none -b ${baseDir} --platform windows/x64" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFoDUploaderSpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFoDUploaderSpec.groovy index baa6f08bbc..56afd0966f 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFoDUploaderSpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolFoDUploaderSpec.groovy @@ -33,7 +33,7 @@ class ToolFoDUploaderSpec extends FcliBaseSpec { def "installLatest"() { def args = "tool fod-uploader install -y -v=${version} --progress=none -b ${baseDir}" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 @@ -74,7 +74,7 @@ class ToolFoDUploaderSpec extends FcliBaseSpec { def "installV5"() { def args = "tool fod-uploader install -y -v=5 --progress=none" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 @@ -87,7 +87,7 @@ class ToolFoDUploaderSpec extends FcliBaseSpec { def "installV50"() { def args = "tool fod-uploader install -y -v=5.0 --progress=none" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 @@ -100,7 +100,7 @@ class ToolFoDUploaderSpec extends FcliBaseSpec { def "installV500"() { def args = "tool fod-uploader install -y -v=5.0.0 --progress=none" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolScClientSpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolScClientSpec.groovy index a3c02a6ea4..b2ca6b3e2a 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolScClientSpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolScClientSpec.groovy @@ -33,7 +33,7 @@ class ToolScClientSpec extends FcliBaseSpec { def "install"() { def args = "tool sc-client install -y -v=${version} --progress=none -b ${baseDir}" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0 diff --git a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolVulnExporterSpec.groovy b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolVulnExporterSpec.groovy index 704d03a313..6eb717504c 100644 --- a/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolVulnExporterSpec.groovy +++ b/fcli-other/fcli-functional-test/src/ftest/groovy/com/fortify/cli/ftest/tool/ToolVulnExporterSpec.groovy @@ -33,7 +33,7 @@ class ToolVulnExporterSpec extends FcliBaseSpec { def "install"() { def args = "tool vuln-exporter install -y -v=${version} --progress=none -b ${baseDir}" when: - def result = Fcli.run(args) + def result = Fcli.run(args, {it.expectZeroExitCode()}) then: verifyAll(result.stdout) { size()>0