diff --git a/documentation/src/main/markdown/currentreleasenotes.md b/documentation/src/main/markdown/currentreleasenotes.md index 4b537153e2..9c2b783bce 100644 --- a/documentation/src/main/markdown/currentreleasenotes.md +++ b/documentation/src/main/markdown/currentreleasenotes.md @@ -28,6 +28,13 @@ * Ivy CLI Detector, leveraging the `ivy:dependencytree` Ant task to extract direct and transitive dependencies for Ant + Ivy projects. For further information, see [Ivy (Ant) support](packagemgrs/ivy.md). * The `detect.uv.dependency.groups.included` property, supported for UV Build Detector in CLI mode, allows users to specify which UV dependency groups to scan. See [UV Package Manager](packagemgrs/python.md#uv-package-manager) for details. +### Changed features + +* The default output directory of the Quack Patch feature has been updated to use [detect_product_short] scan output directory. For more information, see [Quack Patch Documentation](runningdetect/quack-patch.md). + ### Resolved issues +* (IDETECT-5140) Changed the default output directory of the Quack Patch feature to use [detect_product_short] scan output directory instead of the current working directory. +* (IDETECT-5121) Include Quack Patch output directory as part of diagnostic zip when the feature is enabled. + ### Dependency Updates \ No newline at end of file diff --git a/documentation/src/main/markdown/runningdetect/quack-patch.md b/documentation/src/main/markdown/runningdetect/quack-patch.md index 1aba4eac79..4eb7fa2dfb 100644 --- a/documentation/src/main/markdown/runningdetect/quack-patch.md +++ b/documentation/src/main/markdown/runningdetect/quack-patch.md @@ -32,7 +32,7 @@ Quack Patch assists developers in automatically generating code patches for pack * Set the LLM Gateway URL with the detect.llm.api.endpoint property: `--detect.llm.api.endpoint=https://your-llm-gateway.com`. * Set the LLM Gateway API key with the detect.llm.api.key property: `--detect.llm.api.key=your-llm-api-key`. * Set the LLM model with the detect.llm.name property: `--detect.llm.name=gpt-4`. -* (Optional) Set the `detect.quack.patch.output` property to specify a custom directory for generated patches. The default is the `quack-patch` folder in the current working directory. +* (Optional) Set the `detect.quack.patch.output` property to specify a custom directory for generated patches. If the directory doesn't exist, [detect_product_short] tries to create it and fails if unable to create the output directory. The default is the `quack-patch` folder located within the scan output directory. ## Example Usage @@ -64,17 +64,20 @@ java -jar detect.jar --blackduck.url=https://your-blackduck-url.com \ ## Output -Output patches appear in the `quack-patch` folder inside the current working directory or a custom output directory if set via the `detect.quack.patch.output` property. For example, `/quack-patch/`. +Output patches appear in the quack-patch folder inside the scan output directory or a custom output directory if set via the `detect.quack.patch.output` property. + +For example, `runs//scan/quack-patch/`. ``` -/ -├── quack-patch - ├── 3grh7-build.gradle.modified # Modified build.gradle file with overrides - ├── 3grh7-build.gradle.patch # Patch file containing the changes -    ├── 3grh7-transitive-upgrade-guidance.txt # Extracted component upgrade guidance -    ├── invokedDetectorsAndTheirRelevantFiles.json # List of invoked package managers and associated source files -   ├── rapidFullResults.json # Full rapid scan results -    └── summary.json # Summary of the patches generated through Quack Patch +/runs/2026-01-22-15-40-43-082 +├── scan +│   └── quack-patch +│   ├── 3grh7-build.gradle.modified # Modified build.gradle file with overrides +│   ├── 3grh7-build.gradle.patch # Patch file containing the changes +│   ├── 3grh7-transitive-upgrade-guidance.txt # Extracted component upgrade guidance +│   ├── invokedDetectorsAndTheirRelevantFiles.json # List of invoked package managers and associated source files +│   ├── rapidFullResults.json # Full rapid scan results +│   └── summary.json # Summary of the patches generated through Quack Patch ``` ## Steps to apply the patch diff --git a/src/main/java/com/blackduck/integration/detect/configuration/DetectConfigurationFactory.java b/src/main/java/com/blackduck/integration/detect/configuration/DetectConfigurationFactory.java index fc8b052f69..3922efd0f5 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectConfigurationFactory.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectConfigurationFactory.java @@ -2,10 +2,12 @@ import java.io.IOException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.regex.Pattern; @@ -58,13 +60,14 @@ import com.blackduck.integration.detect.workflow.bdio.BdioOptions; import com.blackduck.integration.detect.workflow.blackduck.BlackDuckPostOptions; import com.blackduck.integration.detect.workflow.blackduck.developer.RapidScanOptions; -import com.blackduck.integration.detect.workflow.blackduck.settings.DetectPropertiesSetting; import com.blackduck.integration.detect.workflow.blackduck.project.customfields.CustomFieldDocument; import com.blackduck.integration.detect.workflow.blackduck.project.options.FindCloneOptions; import com.blackduck.integration.detect.workflow.blackduck.project.options.ParentProjectMapOptions; import com.blackduck.integration.detect.workflow.blackduck.project.options.ProjectGroupOptions; import com.blackduck.integration.detect.workflow.blackduck.project.options.ProjectSyncOptions; import com.blackduck.integration.detect.workflow.blackduck.project.options.ProjectVersionLicenseOptions; +import com.blackduck.integration.detect.workflow.blackduck.settings.DetectPropertiesSetting; +import com.blackduck.integration.detect.workflow.file.DirectoryManager; import com.blackduck.integration.detect.workflow.file.DirectoryOptions; import com.blackduck.integration.detect.workflow.phonehome.PhoneHomeOptions; import com.blackduck.integration.detect.workflow.project.ProjectNameVersionOptions; @@ -233,14 +236,16 @@ public Boolean isQuackPatchEnabled() { } public boolean isQuackPatchPossible() { - boolean allQuackPatchPropertiesSet = Boolean.TRUE.equals(!detectConfiguration.getValue(DetectProperties.DETECT_LLM_NAME).isEmpty() - && !detectConfiguration.getValue(DetectProperties.DETECT_LLM_API_ENDPOINT).isEmpty() - && !detectConfiguration.getValue(DetectProperties.DETECT_LLM_API_KEY).isEmpty()); + if (Boolean.TRUE.equals(isQuackPatchEnabled())) { + boolean allQuackPatchPropertiesSet = Boolean.TRUE.equals(!detectConfiguration.getValue(DetectProperties.DETECT_LLM_NAME).isEmpty() + && !detectConfiguration.getValue(DetectProperties.DETECT_LLM_API_ENDPOINT).isEmpty() + && !detectConfiguration.getValue(DetectProperties.DETECT_LLM_API_KEY).isEmpty()); - if (Boolean.TRUE.equals(isQuackPatchEnabled()) && allQuackPatchPropertiesSet) { - return true; + if (allQuackPatchPropertiesSet) { + return true; + } + logger.info("Quack Patch cannot run because not all required properties are set. Please check your configuration."); } - logger.info("Quack Patch cannot run because not all required properties are set. Please check your configuration."); return false; } @@ -679,4 +684,11 @@ public Optional getContainerScanFilePath() { return Optional.ofNullable(detectConfiguration.getNullableValue(DetectProperties.DETECT_CONTAINER_SCAN_FILE)); } + public String getQuackPatchOutputDirectory(DirectoryManager directoryManager) { + String quackPatchOutput = detectConfiguration.getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT); + if (Objects.isNull(quackPatchOutput) || quackPatchOutput.isEmpty()) { + return directoryManager.getScanOutputDirectory().getAbsolutePath(); + } + return Paths.get(quackPatchOutput.trim()).toAbsolutePath().toString(); + } } diff --git a/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java b/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java index 2f6720ee45..1561215519 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectProperties.java @@ -1,7 +1,6 @@ package com.blackduck.integration.detect.configuration; import java.lang.reflect.Field; -import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -1148,13 +1147,14 @@ private DetectProperties() { .build(); public static final StringProperty DETECT_QUACK_PATCH_OUTPUT = - StringProperty.newBuilder("detect.quack.patch.output", Paths.get("").toAbsolutePath().toString()) + StringProperty.newBuilder("detect.quack.patch.output", "") .setInfo("Quack Patch Output Directory", DetectPropertyFromVersion.VERSION_11_4_0) .setHelp( "Specifies the output directory for Quack Patch results.", - "If not set, the Quack Patch results are placed in a 'quack-patch' subdirectory of the present working directory." + "If not set, the Quack Patch results are placed in a 'quack-patch' subdirectory under scan output directory." ) .setGroups(DetectGroup.QUACKPATCH) + .setDeprecated("This property is deprecated and will be renamed to 'detect.quack.patch.output.path' in Detect release 12.0.", new ProductMajorVersion(12)) .build(); public static final StringProperty DETECT_LLM_API_KEY = diff --git a/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java b/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java index 46eb48e42b..d6c8f09dbb 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/DetectableOptionFactory.java @@ -7,6 +7,7 @@ import java.util.Map; import java.util.Set; +import com.blackduck.integration.detect.workflow.file.DirectoryManager; import org.jetbrains.annotations.Nullable; import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON; @@ -69,12 +70,16 @@ public class DetectableOptionFactory { private final DetectPropertyConfiguration detectConfiguration; + private final DetectConfigurationFactory detectConfigurationFactory; + private final DirectoryManager directoryManager; @Nullable private final DiagnosticSystem diagnosticSystem; private final ProxyInfo proxyInfo; - public DetectableOptionFactory(DetectPropertyConfiguration detectConfiguration, @Nullable DiagnosticSystem diagnosticSystem, ProxyInfo proxyInfo) { + public DetectableOptionFactory(DetectPropertyConfiguration detectConfiguration, DetectConfigurationFactory detectConfigurationFactory, DirectoryManager directoryManager, @Nullable DiagnosticSystem diagnosticSystem, ProxyInfo proxyInfo) { this.detectConfiguration = detectConfiguration; + this.detectConfigurationFactory = detectConfigurationFactory; + this.directoryManager = directoryManager; this.diagnosticSystem = diagnosticSystem; this.proxyInfo = proxyInfo; } @@ -354,12 +359,9 @@ public NugetInspectorOptions createNugetInspectorOptions() { Path nugetConfigPath = detectConfiguration.getPathOrNull(DetectProperties.DETECT_NUGET_CONFIG_PATH); Set nugetExcludedDependencyTypes = detectConfiguration.getValue(DetectProperties.DETECT_NUGET_DEPENDENCY_TYPES_EXCLUDED).representedValueSet(); Path nugetArtifactsPath = detectConfiguration.getPathOrNull(DetectProperties.DETECT_NUGET_ARTIFACTS_PATH); - Path relevantDetectorsAndFilesInfoPath = null; - if (detectConfiguration.getValue(DetectProperties.DETECT_QUACK_PATCH_ENABLED)) { - relevantDetectorsAndFilesInfoPath = Paths.get(detectConfiguration.getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim()) - .resolve(QUACKPATCH_SUBDIRECTORY_NAME) - .resolve(INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); - } + Path relevantDetectorsAndFilesInfoPath = detectConfigurationFactory.isQuackPatchEnabled() + ? Paths.get(detectConfigurationFactory.getQuackPatchOutputDirectory(directoryManager), QUACKPATCH_SUBDIRECTORY_NAME, INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON) + : null; Path nugetInspectorPath = detectConfiguration.getPathOrNull(DetectProperties.DETECT_NUGET_INSPECTOR_PATH); File nugetInspectorPathFile = null; if (nugetInspectorPath != null) { diff --git a/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java b/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java index f93f51af6f..b0b812969d 100644 --- a/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java +++ b/src/main/java/com/blackduck/integration/detect/configuration/validation/DetectConfigurationBootManager.java @@ -20,7 +20,6 @@ import com.blackduck.integration.configuration.property.base.TypedProperty; import com.blackduck.integration.configuration.property.deprecation.DeprecatedValueUsage; import com.blackduck.integration.detect.configuration.DetectProperties; -import com.blackduck.integration.detect.configuration.DetectPropertyConfiguration; import com.blackduck.integration.detect.configuration.DetectUserFriendlyException; import com.blackduck.integration.detect.configuration.enumeration.ExitCodeType; import com.blackduck.integration.detect.workflow.event.EventSystem; @@ -108,27 +107,18 @@ public Optional validateForPropertyParseErrors() th } // Method to validate Quack Patch output path and return an Optional containing a DetectUserFriendlyException if the validation fails, or an empty Optional if it passes. - public Optional validateQuackPatchOutputPath(DetectPropertyConfiguration detectConfiguration) { - String quackPatchOutput = detectConfiguration.getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(); - - // Fail for empty string since that would cause issues later on when we try to write to it, and it's likely the user just forgot to set it if they enabled Quack Patch but left this blank. - if (quackPatchOutput.isEmpty()) { - return Optional.of(new DetectUserFriendlyException( - "Invalid value for Quack Patch output path: " + quackPatchOutput, - ExitCodeType.FAILURE_CONFIGURATION - )); - } + public Optional validateQuackPatchOutputPath(String quackPatchOutputPath) { // Validate the path exists and is a directory - Path path = Paths.get(quackPatchOutput); + Path path = Paths.get(quackPatchOutputPath); if (!Files.exists(path)) { // create the directory if it doesn't exist, since that would cause issues later on when we try to write to it, and it's likely the user just forgot to create it if they enabled Quack Patch and set a path that doesn't exist. try { - logger.debug("Creating quack patch output path: {}", quackPatchOutput); + logger.debug("Creating quack patch output path: {}", quackPatchOutputPath); Files.createDirectories(path); } catch (Exception e) { return Optional.of(new DetectUserFriendlyException( - "Quack Patch output path does not exist and could not be created: " + quackPatchOutput, + "Quack Patch output path does not exist and could not be created: " + quackPatchOutputPath, ExitCodeType.FAILURE_CONFIGURATION )); } @@ -136,14 +126,14 @@ public Optional validateQuackPatchOutputPath(Detect if (!Files.isDirectory(path)) { return Optional.of(new DetectUserFriendlyException( - "Quack Patch output path is not a directory: " + quackPatchOutput, + "Quack Patch output path is not a directory: " + quackPatchOutputPath, ExitCodeType.FAILURE_CONFIGURATION )); } if (!Files.isWritable(path)) { return Optional.of(new DetectUserFriendlyException( - "Quack Patch output path is not writable: " + quackPatchOutput, + "Quack Patch output path is not writable: " + quackPatchOutputPath, ExitCodeType.FAILURE_CONFIGURATION )); } diff --git a/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java b/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java index 20e67dcc41..fb1f550b40 100644 --- a/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java +++ b/src/main/java/com/blackduck/integration/detect/lifecycle/boot/DetectBoot.java @@ -14,14 +14,11 @@ import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; -import java.util.Collections; -import java.util.LinkedHashMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.blackduck.integration.configuration.config.MaskedRawValueResult; -import com.blackduck.integration.configuration.property.base.TypedProperty; import com.blackduck.integration.configuration.config.PropertyConfiguration; import com.blackduck.integration.configuration.property.base.TypedProperty; import com.blackduck.integration.configuration.property.types.enumallnone.list.AllEnumList; @@ -56,7 +53,6 @@ import com.blackduck.integration.detect.lifecycle.boot.product.ProductBoot; import com.blackduck.integration.detect.lifecycle.run.data.ProductRunData; import com.blackduck.integration.detect.lifecycle.run.singleton.BootSingletons; -import com.blackduck.integration.detect.workflow.blackduck.settings.DetectPropertiesSetting; import com.blackduck.integration.detect.tool.cache.InstalledToolLocator; import com.blackduck.integration.detect.tool.cache.InstalledToolManager; import com.blackduck.integration.detect.util.filter.DetectToolFilter; @@ -168,26 +164,19 @@ public Optional boot(String detectVersion, String detectBuildD Configuration freemarkerConfiguration = detectBootFactory.createFreemarkerConfiguration(); DetectPropertyConfiguration detectConfiguration = new DetectPropertyConfiguration(propertyConfiguration, new SimplePathResolver()); - // If quack patch is enabled, we need to validate the output path before doing anything else since it could cause Detect to fail later on if it's not valid, and we want to fail as early as possible with a clear message about what the issue is. - Optional quackPatchError = detectConfigurationBootManager.validateQuackPatchOutputPath(detectConfiguration); - if (quackPatchError.isPresent()) { - return Optional.of(DetectBootResult.exception(quackPatchError.get(), propertyConfiguration)); - } - DetectConfigurationFactory detectConfigurationFactory = new DetectConfigurationFactory(detectConfiguration, gson); + DirectoryManager directoryManager = detectBootFactory.createDirectoryManager(detectConfigurationFactory); - // If quack patch is enabled, we need to validate the output path before doing anything else since it could cause Detect to fail later on if it's not valid, and we want to fail as early as possible with a clear message about what the issue is. + // If quack patch is enabled, we need to validate the output path before doing anything else since it could cause Detect to fail later on if it's not valid, and we want to fail as early as possible with a clear message about what the issue is. if (Boolean.TRUE.equals(detectConfigurationFactory.isQuackPatchEnabled())) { - Optional quackPatchError = detectConfigurationBootManager.validateQuackPatchOutputPath(detectConfiguration); - if (quackPatchError.isPresent()) { - return Optional.of(DetectBootResult.exception(quackPatchError.get(), propertyConfiguration)); - } + Optional quackPatchError = detectConfigurationBootManager.validateQuackPatchOutputPath(detectConfigurationFactory.getQuackPatchOutputDirectory(directoryManager)); + if (quackPatchError.isPresent()) { + return Optional.of(DetectBootResult.exception(quackPatchError.get(), propertyConfiguration)); + } } boolean autonomousScanEnabled = detectConfiguration.getValue(DetectProperties.DETECT_AUTONOMOUS_SCAN_ENABLED); - DirectoryManager directoryManager = detectBootFactory.createDirectoryManager(detectConfigurationFactory); - // TODO Scan settings model obtained below is to be used by the delta-checking operations AutonomousManager autonomousManager = new AutonomousManager(directoryManager, detectConfiguration, autonomousScanEnabled, maskedRawPropertyValues); @@ -222,7 +211,7 @@ public Optional boot(String detectVersion, String detectBuildD DetectableOptionFactory detectableOptionFactory; try { ProxyInfo detectableProxyInfo = detectConfigurationFactory.createBlackDuckProxyInfo(); - detectableOptionFactory = new DetectableOptionFactory(detectConfiguration, diagnosticSystem, detectableProxyInfo); + detectableOptionFactory = new DetectableOptionFactory(detectConfiguration, detectConfigurationFactory, directoryManager, diagnosticSystem, detectableProxyInfo); hasImageOrTar = detectableOptionFactory.createDockerDetectableOptions().hasDockerImageOrTar(); oneRequiresTheOther( detectConfigurationFactory.createDetectTarget() == DetectTargetType.IMAGE, diff --git a/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java b/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java index f0b1447628..2b54004fe7 100644 --- a/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java +++ b/src/main/java/com/blackduck/integration/detect/lifecycle/run/operation/OperationRunner.java @@ -69,7 +69,6 @@ import com.blackduck.integration.componentlocator.beans.Component; import com.blackduck.integration.detect.configuration.DetectConfigurationFactory; import com.blackduck.integration.detect.configuration.DetectInfo; -import com.blackduck.integration.detect.configuration.DetectProperties; import com.blackduck.integration.detect.configuration.DetectUserFriendlyException; import com.blackduck.integration.detect.configuration.DetectorToolOptions; import com.blackduck.integration.detect.configuration.connection.ConnectionFactory; @@ -80,8 +79,6 @@ import com.blackduck.integration.detect.lifecycle.OperationException; import com.blackduck.integration.detect.lifecycle.autonomous.AutonomousManager; import com.blackduck.integration.detect.lifecycle.boot.decision.CorrelatedScanningDecision; -import com.blackduck.integration.detect.lifecycle.boot.decision.CorrelatedScanningDecision; -import com.blackduck.integration.detect.workflow.DetectRunId; import com.blackduck.integration.detect.lifecycle.run.DetectFontLoaderFactory; import com.blackduck.integration.detect.lifecycle.run.data.BlackDuckRunData; import com.blackduck.integration.detect.lifecycle.run.data.DockerTargetData; @@ -402,7 +399,7 @@ public final DetectorToolResult executeDetectors() throws OperationException { if (detectConfigurationFactory.isQuackPatchPossible()) { try { - detectorTool.saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult); + detectorTool.saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult, directoryManager); } catch (IOException e) { throw new RuntimeException("Something went wrong writing relevant files: " + e.getMessage()); } @@ -873,7 +870,7 @@ public final File generateFullRapidJsonFile(List scanResults) throws O return auditLog.namedPublic( "Generate Rapid Full Json File", "RapidScan", - () -> new RapidModeGenerateJsonOperation(htmlEscapeDisabledGson, directoryManager).generateJsonFileFromString(scanResults.get(0).getContentString(), detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim()) + () -> new RapidModeGenerateJsonOperation(htmlEscapeDisabledGson, directoryManager).generateJsonFileFromString(scanResults.get(0).getContentString(), detectConfigurationFactory.getQuackPatchOutputDirectory(directoryManager)) ); } @@ -887,7 +884,7 @@ public void runQuackPatch(File rapidFullResultsJson) throws OperationException { () -> { publishResult( new GenerateComponentLocationAnalysisOperation(detectConfigurationFactory, statusEventPublisher, exitCodePublisher) - .runQuackPatch(rapidFullResultsJson, detectConfigurationFactory) + .runQuackPatch(rapidFullResultsJson, detectConfigurationFactory, detectConfigurationFactory.getQuackPatchOutputDirectory(directoryManager)) ); } ); diff --git a/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java b/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java index f62c7b004a..e848959c6c 100644 --- a/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java +++ b/src/main/java/com/blackduck/integration/detect/tool/detector/DetectorTool.java @@ -23,7 +23,6 @@ import com.blackduck.integration.blackduck.bdio2.model.GitInfo; import com.blackduck.integration.common.util.finder.FileFinder; import com.blackduck.integration.detect.configuration.DetectConfigurationFactory; -import com.blackduck.integration.detect.configuration.DetectProperties; import com.blackduck.integration.detect.configuration.ExcludeIncludeEnumFilter; import com.blackduck.integration.detect.configuration.enumeration.ExitCodeType; import com.blackduck.integration.detect.lifecycle.shutdown.ExitCodePublisher; @@ -96,10 +95,10 @@ public DetectorTool( this.detectConfigurationFactory = detectConfigurationFactory; } - public void saveExtractedDetectorsAndTheirRelevantFilePaths(DetectorToolResult toolResult) throws IOException { + public void saveExtractedDetectorsAndTheirRelevantFilePaths(DetectorToolResult toolResult, DirectoryManager directoryManager) throws IOException { // Create map of extracted detectors and their relevant files Map> detectorsAndFiles = new HashMap<>(); - Path quackDir = Paths.get(detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(), QUACKPATCH_SUBDIRECTORY_NAME); + Path quackDir = Paths.get(detectConfigurationFactory.getQuackPatchOutputDirectory(directoryManager), QUACKPATCH_SUBDIRECTORY_NAME); ObjectMapper mapper = new ObjectMapper(); Path jsonFile = quackDir.resolve(INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); diff --git a/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java b/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java index aea3f1fedf..1a19d78b1b 100644 --- a/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java +++ b/src/main/java/com/blackduck/integration/detect/workflow/componentlocationanalysis/GenerateComponentLocationAnalysisOperation.java @@ -85,15 +85,13 @@ public ComponentLocatorResult locateComponents(Set componentsSet, Fil return new ComponentLocatorResult(outputFilepath); } - public QuackPatchResult runQuackPatch(File rapidFullResultsFile, DetectConfigurationFactory configFactory) { + public QuackPatchResult runQuackPatch(File rapidFullResultsFile, DetectConfigurationFactory configFactory, String quackPatchOutputDir) { logger.info("Attempting Quack Patch."); - // if the detect.quack.patch.output property value set use that as output folder, otherwise default it to current working directory - String quackPatchOutputDir = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT).trim(); logger.debug("Quack Patch output directory set to: {}", quackPatchOutputDir); Map> relevantDetectorsAndFiles = loadDetectorsAndFiles(quackPatchOutputDir + File.separator + QUACKPATCH_SUBDIRECTORY_NAME + File.separator + INVOKED_DETECTORS_AND_RELEVANT_FILES_JSON); String llmKey = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_KEY); String llmName = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_NAME); - String llmURL = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_ENDPOINT); + String llmURL = configFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_LLM_API_ENDPOINT); ComponentLocator.runQuackPatch(rapidFullResultsFile, relevantDetectorsAndFiles, llmKey, llmName, llmURL, quackPatchOutputDir); return new QuackPatchResult(getQuackPatchOutputDirectory(new File(quackPatchOutputDir))); } diff --git a/src/main/java/com/blackduck/integration/detect/workflow/diagnostic/DiagnosticSystem.java b/src/main/java/com/blackduck/integration/detect/workflow/diagnostic/DiagnosticSystem.java index 97bfb62b15..6a2999e6d1 100644 --- a/src/main/java/com/blackduck/integration/detect/workflow/diagnostic/DiagnosticSystem.java +++ b/src/main/java/com/blackduck/integration/detect/workflow/diagnostic/DiagnosticSystem.java @@ -1,18 +1,22 @@ package com.blackduck.integration.detect.workflow.diagnostic; import java.io.File; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.SortedMap; +import org.apache.commons.io.FileUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.blackduck.integration.configuration.config.PropertyConfiguration; import com.blackduck.integration.detect.configuration.DetectInfo; +import com.blackduck.integration.detect.configuration.DetectProperties; import com.blackduck.integration.detect.workflow.DetectRunId; +import static com.blackduck.integration.detect.workflow.componentlocationanalysis.GenerateComponentLocationAnalysisOperation.QUACKPATCH_SUBDIRECTORY_NAME; import com.blackduck.integration.detect.workflow.event.EventSystem; import com.blackduck.integration.detect.workflow.file.DirectoryManager; @@ -134,9 +138,23 @@ public void appendBlackDuckServerProperties(Map serverProperties } private boolean createZip() { + // If quack patch is enabled, then add quack patch output directory to the zip + if (propertyConfiguration.getValueOrDefault(DetectProperties.DETECT_QUACK_PATCH_ENABLED)) { + String quackPatchOutputDirPath = propertyConfiguration.getValueOrDefault(DetectProperties.DETECT_QUACK_PATCH_OUTPUT); + // If quack patch output path is customized, then explicitly include it in the diagnostic zip. Otherwise, default behaviour will automatically pack it. + if (!quackPatchOutputDirPath.isEmpty()) { + quackPatchOutputDirPath = quackPatchOutputDirPath.trim() + File.separator + QUACKPATCH_SUBDIRECTORY_NAME; + logger.info("Adding quack patch output dir {} to the diagnostic zip.", quackPatchOutputDirPath); + // Copy directory quackPatchOutputDirPath to directoryManager.getRunsOutputDirectory() + try { + FileUtils.copyDirectory(new File(quackPatchOutputDirPath), new File(directoryManager.getScanOutputDirectory().getAbsolutePath() + File.separator + QUACKPATCH_SUBDIRECTORY_NAME)); + } catch (IOException e) { + logger.warn("Failed to copy the quack patch output directory to the runs directory. Skipping the addition of quack patch output directory to diagnostics zip file. Error: {}.", e.getMessage()); + } + } + } List directoriesToCompress = new ArrayList<>(); directoriesToCompress.add(directoryManager.getRunHomeDirectory()); - DiagnosticZipCreator zipper = new DiagnosticZipCreator(); return zipper.createDiagnosticZip(detectRunId.getRunId(), directoryManager.getRunsOutputDirectory(), directoriesToCompress); } diff --git a/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java b/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java index 830de8e066..94399bf579 100644 --- a/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java +++ b/src/test/java/com/blackduck/integration/detect/workflow/report/DetectorToolTest.java @@ -82,7 +82,7 @@ void testSaveExtractedDetectorsAndTheirRelevantFilePaths_gradle() throws Excepti DetectConfigurationFactory detectConfigurationFactory = DetectConfigurationFactoryTestUtils.factoryOf(Pair.of(DetectProperties.DETECT_QUACK_PATCH_OUTPUT, tempDir.toString())); new DetectorTool(null, null, null, null, null, null, null, detectConfigurationFactory) - .saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult); + .saveExtractedDetectorsAndTheirRelevantFilePaths(toolResult, directoryManager); File outputFile = new File( new File(detectConfigurationFactory.getDetectPropertyConfiguration().getValue(DetectProperties.DETECT_QUACK_PATCH_OUTPUT), QUACKPATCH_SUBDIRECTORY_NAME),