Skip to content

Commit

Permalink
chore: Improve uninstall/--replace if dir in use
Browse files Browse the repository at this point in the history
  • Loading branch information
rsenden committed Jan 23, 2024
1 parent 3b38286 commit 6eed27f
Show file tree
Hide file tree
Showing 12 changed files with 245 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.fortify.cli.sc_sast.scan.helper.SCSastControllerScanJobArtifactState;
import com.fortify.cli.sc_sast.scan.helper.SCSastControllerScanJobState;
import com.fortify.cli.ssc.artifact.helper.SSCArtifactStatus;
import com.fortify.cli.tool._common.helper.ToolUninstaller;

import lombok.AccessLevel;
import lombok.Getter;
Expand All @@ -48,6 +49,7 @@ public final class FortifyCLIStaticInitializer {
private static final FortifyCLIStaticInitializer instance = new FortifyCLIStaticInitializer();

public void initialize() {
ToolUninstaller.deleteAllPending();
initializeTrustStore();
initializeLocale();
initializeFoDProperties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.channels.FileChannel;
import java.nio.file.CopyOption;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.Comparator;
import java.util.stream.Stream;
import java.util.zip.GZIPInputStream;
Expand Down Expand Up @@ -116,12 +119,41 @@ public static final void extractTarGZ(File tgzFile, Path targetDir) {
}
}

/**
* Recursively delete the given path. As a best practice, this method should
* only be invoked if {@link #isDirPathInUse(Path)} returns false. The
* deleteRecursive() method itself doesn't invoke {@link #isDirPathInUse(Path)}
* for performance reasons, as callers may wish to explicitly check whether
* any files are in use in order to perform some alternative action.
* @param path
*/
@SneakyThrows
public static final void deleteRecursive(Path installPath) {
try (Stream<Path> walk = Files.walk(installPath)) {
public static final void deleteRecursive(Path path) {
try (Stream<Path> walk = Files.walk(path)) {
walk.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}

@SneakyThrows
public static final boolean isDirPathInUse(Path path) {
try (Stream<Path> walk = Files.walk(path)) {
return walk.anyMatch(FileUtils::isFilePathInUse);
}
}

@SneakyThrows
public static final boolean isFilePathInUse(Path path) {
if ( path.toFile().isFile() ) {
try ( var fc = FileChannel.open(path, StandardOpenOption.APPEND) ) {
if ( fc.tryLock()==null ) {
return true;
}
} catch ( FileSystemException e ) {
return true;
}
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* Copyright 2023 Open Text.
*
* The only warranties for products and services of Open Text
* and its affiliates and licensors (“Open Text”) are as may
* be set forth in the express warranty statements accompanying
* such products and services. Nothing herein should be construed
* as constituting an additional warranty. Open Text shall not be
* liable for technical or editorial errors or omissions contained
* herein. The information contained herein is subject to change
* without notice.
*/
package com.fortify.cli.common.util;

import java.lang.module.ModuleDescriptor.Version;
import java.util.regex.Pattern;

public final class SemVerHelper {
private static final Pattern semverPattern = Pattern.compile("([1-9]\\d*)\\.(\\d+)\\.(\\d+)(?:-(.*))?");
/**
* Loosely compare two semantic versions, returning -1 if first semver is lower than
* the second, 0 if they are the same, or 1 if first semver is higher than the
* second. Null, blank, or non-semver values are always considered lower than
* true semvers.
*
* @param semver1
* @param semver2
* @return
*/
public static final int compare(String semver1, String semver2) {
var semver1Matcher = semverPattern.matcher(semver1==null?"":semver1);
var semver2Matcher = semverPattern.matcher(semver2==null?"":semver2);
if ( (semver1==null && semver2==null) || semver1.equals(semver2) ) {
return 0;
} else if ( !semver1Matcher.matches() && !semver2Matcher.matches() ) {
return semver1.compareTo(semver2);
} else if ( semver1Matcher.matches() && !semver2Matcher.matches() ) {
return 1;
} else if ( !semver1Matcher.matches() && semver2Matcher.matches() ) {
return -1;
} else {
var version1 = Version.parse(semver1);
var version2 = Version.parse(semver2);
return version1.compareTo(version2);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Copyright 2023 Open Text.
*
* The only warranties for products and services of Open Text
* and its affiliates and licensors (“Open Text”) are as may
* be set forth in the express warranty statements accompanying
* such products and services. Nothing herein should be construed
* as constituting an additional warranty. Open Text shall not be
* liable for technical or editorial errors or omissions contained
* herein. The information contained herein is subject to change
* without notice.
*/
package com.fortify.cli.common.util;

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

import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

/**
*
* @author Ruud Senden
*/
public class SemVerHelperTest {
@ParameterizedTest
@CsvSource({
",,0",
"a,a,0",
"1.2.3,1.2.3,0",
"a,b,-1",
"b,a,1",
"a,1.2.3,-1",
"1.2.3,a,1",
"a,1.2.3-alpha1,-1",
"1.2.3-alpha1,a,1",
"1.2.0,1.2.1,-1",
"1.2.1,1.2.0,1",
"1.2.0,1.3.0,-1",
"1.3.0,1.2.0,1",
"1.0.0,2.0.0,-1",
"2.0.0,1.0.0,1",
"1.2.0,1.2.0-alpha1,1",
"1.2.0-alpha1,1.2.0,-1"
})
public void testSemVerCompare(String semver1, String semver2, int expectedResult) throws Exception {
assertEquals(expectedResult, SemVerHelper.compare(semver1, semver2));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fortify.cli.common.cli.mixin.CommonOptionMixins;
import com.fortify.cli.common.cli.util.CommandGroup;
import com.fortify.cli.common.json.JsonHelper;
Expand All @@ -34,11 +33,10 @@
import com.fortify.cli.common.util.FileUtils;
import com.fortify.cli.common.util.StringUtils;
import com.fortify.cli.tool._common.helper.ToolInstallationDescriptor;
import com.fortify.cli.tool._common.helper.ToolInstallationHelper;
import com.fortify.cli.tool._common.helper.ToolInstallationOutputDescriptor;
import com.fortify.cli.tool._common.helper.ToolInstaller;
import com.fortify.cli.tool._common.helper.ToolInstaller.DigestMismatchAction;
import com.fortify.cli.tool._common.helper.ToolInstaller.ToolInstallationResult;
import com.fortify.cli.tool._common.helper.ToolUninstaller;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionVersionDescriptor;

import lombok.Getter;
Expand Down Expand Up @@ -148,10 +146,12 @@ private Path getBasePath() {
private final class ToolInstallationPreparer implements Consumer<ToolInstaller> {
@Getter private final ArrayNode toolInstallationOutputDescriptors = OBJECTMAPPER.createArrayNode();
private ToolInstaller installer;
private ToolUninstaller uninstaller;

@Override
public void accept(ToolInstaller installer) {
this.installer = installer;
this.uninstaller = new ToolUninstaller(installer.getToolName());
prepare();
}

Expand Down Expand Up @@ -210,12 +210,10 @@ private void deleteRecursive(Path targetPath) {
private void uninstall(ToolDefinitionVersionDescriptor versionDescriptor, ToolInstallationDescriptor installationDescriptor) {
var toolName = installer.getToolName();
var toolVersion = versionDescriptor.getVersion();
var installDir = installationDescriptor.getInstallDir();
installer.getProgressWriter().writeProgress("Uninstalling %s %s from %s", toolName, toolVersion, installDir);
ToolInstallationHelper.uninstall(installer.getToolName(), versionDescriptor, installationDescriptor);
ObjectNode output = OBJECTMAPPER.valueToTree(new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor));
output.put("__action__", "UNINSTALLED");
toolInstallationOutputDescriptors.add(output);
var installPath = installationDescriptor.getInstallPath();
installer.getProgressWriter().writeProgress("Uninstalling %s %s from %s", toolName, toolVersion, installPath);
var outputDescriptor = uninstaller.uninstall(versionDescriptor, installationDescriptor, installer.getVersionDescriptor());
toolInstallationOutputDescriptors.add(OBJECTMAPPER.valueToTree(outputDescriptor));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,6 @@ public final boolean isSingular() {
private ToolInstallationOutputDescriptor createToolOutputDescriptor(ToolDefinitionVersionDescriptor versionDescriptor) {
var toolName = getToolName();
var installationDescriptor = ToolInstallationDescriptor.load(toolName, versionDescriptor);
return new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor);
return new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor, "");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,7 @@
import com.fortify.cli.common.output.cli.cmd.IJsonNodeSupplier;
import com.fortify.cli.common.output.transform.IActionCommandResultSupplier;
import com.fortify.cli.tool._common.helper.ToolInstallationDescriptor;
import com.fortify.cli.tool._common.helper.ToolInstallationHelper;
import com.fortify.cli.tool._common.helper.ToolInstallationOutputDescriptor;
import com.fortify.cli.tool._common.helper.ToolUninstaller;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionVersionDescriptor;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionsHelper;

Expand All @@ -44,8 +43,7 @@ public final JsonNode getJsonNode() {
var installationDescriptor = getInstallationDescriptor(toolName, versionDescriptor);
Path installPath = getInstallPath(installationDescriptor);
requireConfirmation.checkConfirmed(installPath);
ToolInstallationHelper.uninstall(toolName, versionDescriptor, installationDescriptor);
var outputDescriptor = new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor);
var outputDescriptor = new ToolUninstaller(toolName).uninstall(versionDescriptor, installationDescriptor);
return new ObjectMapper().valueToTree(outputDescriptor);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ private static final Path getInstallDescriptorPath(String toolName, String versi
}

private static Path getInstallDescriptorsDirPath(String toolName) {
return FcliDataHelper.getFcliStatePath().resolve("tools").resolve(toolName);
return ToolInstallationHelper.getToolsStatePath().resolve(toolName);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@
*******************************************************************************/
package com.fortify.cli.tool._common.helper;

import com.fortify.cli.common.util.FileUtils;
import com.fortify.cli.tool.definitions.helper.ToolDefinitionVersionDescriptor;
import java.nio.file.Path;

import com.fortify.cli.common.util.FcliDataHelper;

public final class ToolInstallationHelper {
public static final String getToolResourceDir(String toolName) {
Expand All @@ -24,12 +25,7 @@ public static final String getToolResourceFile(String toolName, String fileName)
return String.format("%s/%s", getToolResourceDir(toolName.replace('-', '_')), fileName);
}

public static final void uninstall(String toolName, ToolDefinitionVersionDescriptor versionDescriptor, ToolInstallationDescriptor installationDescriptor) {
var installPath = installationDescriptor.getInstallPath();
try {
FileUtils.deleteRecursive(installPath);
} finally {
ToolInstallationDescriptor.delete(toolName, versionDescriptor);
}
public static Path getToolsStatePath() {
return FcliDataHelper.getFcliStatePath().resolve("tools");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ public class ToolInstallationOutputDescriptor {
private final String installDir;
private final String binDir;
private final String installed;
private final String __action__;

public ToolInstallationOutputDescriptor(String toolName, ToolDefinitionVersionDescriptor versionDescriptor, ToolInstallationDescriptor installationDescriptor) {
public ToolInstallationOutputDescriptor(String toolName, ToolDefinitionVersionDescriptor versionDescriptor, ToolInstallationDescriptor installationDescriptor, String action) {
this.name = toolName;
this.version = versionDescriptor.getVersion();
this.aliases = reverse(versionDescriptor.getAliases());
Expand All @@ -53,6 +54,7 @@ public ToolInstallationOutputDescriptor(String toolName, ToolDefinitionVersionDe
this.installDir = installationDescriptor==null ? null : installationDescriptor.getInstallDir();
this.binDir = installationDescriptor==null ? null : installationDescriptor.getBinDir();
this.installed = StringUtils.isBlank(this.installDir) ? "No" : "Yes";
this.__action__ = action;
}

private static final String[] reverse(String[] array) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ public static final class ToolInstallationResult {
private final ToolInstallationDescriptor installationDescriptor;

public final ToolInstallationOutputDescriptor asOutputDescriptor() {
return new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor);
return new ToolInstallationOutputDescriptor(toolName, versionDescriptor, installationDescriptor, "INSTALLED");
}
}

Expand Down
Loading

0 comments on commit 6eed27f

Please sign in to comment.