diff --git a/src/config.ts b/src/config.ts index 38cebc30..c4983fc6 100644 --- a/src/config.ts +++ b/src/config.ts @@ -201,6 +201,16 @@ class Config { // process.env['VSCEXT_EXHORT_SNYK_TOKEN'] = token; } + /** + * Adds a path to the workspace-local redHatDependencyAnalytics.exclude list. + * @param path The path to add + */ + async addFileToExcludeList(path: string) { + const original = vscode.workspace.getConfiguration('redHatDependencyAnalytics').inspect('exclude'); + const newValues = [...((original?.workspaceValue as string[] | undefined) || []), path]; + await vscode.workspace.getConfiguration('redHatDependencyAnalytics').update('exclude', newValues); + } + /** * Authorizes the RHDA (Red Hat Dependency Analytics) service. * @param context The extension context for authorization. diff --git a/src/constants.ts b/src/constants.ts index d1d9b4ed..11beb97d 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -21,6 +21,7 @@ export enum StatusMessages { export enum PromptText { FULL_STACK_PROMPT_TEXT = `Open Red Hat Dependency Analytics Report`, LSP_FAILURE_TEXT = `Open the output window`, + IGNORE_FILE = 'Ignore this file', } export enum Titles { diff --git a/src/extension.ts b/src/extension.ts index fe76e610..39d7a7c1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -16,6 +16,7 @@ import { applySettingNameMappings, buildLogErrorMessage } from './utils'; import { clearCodeActionsMap, getDiagnosticsCodeActions } from './codeActionHandler'; import { AnalysisMatcher } from './fileHandler'; import { EventEmitter } from 'node:events'; +import { AbstractDiagnosticsPipeline } from './diagnosticsPipeline'; export let outputChannelDep: DepOutputChannel; @@ -105,10 +106,16 @@ export async function activate(context: vscode.ExtensionContext) { const showVulnerabilityFoundPrompt = async (msg: string, filePath: vscode.Uri) => { const fileName = path.basename(filePath.fsPath); - const selection = await vscode.window.showWarningMessage(`${msg}`, PromptText.FULL_STACK_PROMPT_TEXT); + const selection = await vscode.window.showWarningMessage(`${msg}`, PromptText.FULL_STACK_PROMPT_TEXT as string, PromptText.IGNORE_FILE); if (selection === PromptText.FULL_STACK_PROMPT_TEXT) { record(context, TelemetryActions.vulnerabilityReportPopupOpened, { manifest: fileName, fileName: fileName }); vscode.commands.executeCommand(commands.STACK_ANALYSIS_COMMAND, filePath.fsPath); + } else if (selection === PromptText.IGNORE_FILE) { + outputChannelDep.info(`Added "${filePath.fsPath}" to workspace exclude list`); + await globalConfig.addFileToExcludeList(filePath.fsPath); + AbstractDiagnosticsPipeline.diagnosticsCollection.delete(filePath); + clearCodeActionsMap(filePath); + // TODO: need to clear status bar } else { record(context, TelemetryActions.vulnerabilityReportPopupIgnored, { manifest: fileName, fileName: fileName }); } @@ -123,12 +130,19 @@ export async function activate(context: vscode.ExtensionContext) { } }); - notifications.on('caError', (errorData: CANotificationData) => { + notifications.on('caError', async (errorData: CANotificationData) => { const notification = new CANotification(errorData); caStatusBarProvider.setError(); // Since CA is an automated feature, only warning message will be shown on failure - vscode.window.showWarningMessage(`RHDA error while analyzing ${errorData.uri.fsPath}: ${notification.errorMsg()}`); + const selection = await vscode.window.showWarningMessage(`RHDA error while analyzing ${errorData.uri.fsPath}: ${notification.errorMsg()}`, PromptText.IGNORE_FILE); + if (selection === PromptText.IGNORE_FILE) { + outputChannelDep.info(`Added "${errorData.uri.fsPath}" to workspace exclude list`); + await globalConfig.addFileToExcludeList(errorData.uri.fsPath); + AbstractDiagnosticsPipeline.diagnosticsCollection.delete(errorData.uri); + clearCodeActionsMap(errorData.uri); + // TODO: need to clear status bar + } // Record telemetry event record(context, TelemetryActions.componentAnalysisFailed, { manifest: path.basename(notification.origin().fsPath), fileName: path.basename(notification.origin().fsPath), error: notification.errorMsg() }); @@ -151,6 +165,7 @@ export async function activate(context: vscode.ExtensionContext) { vscode.workspace.onDidChangeConfiguration(() => { globalConfig.loadData(); + outputChannelDep.debug(`configuration updated`); }); } @@ -213,8 +228,11 @@ function showRHRepositoryRecommendationNotification() { * @param context - The extension context. */ function registerStackAnalysisCommands(context: vscode.ExtensionContext) { - - const invokeFullStackReport = async (filePath: string) => { + const recordAndInvoke = async (origin: string, uri: vscode.Uri) => { + // TODO: vscode.window.activeTextEditor may be null + const fileUri = uri || vscode.window.activeTextEditor!.document.uri; + const filePath = fileUri.fsPath; + record(context, origin, { manifest: path.basename(filePath), fileName: path.basename(filePath) }); const fileName = path.basename(filePath); try { await generateRHDAReport(context, filePath, outputChannelDep); @@ -227,14 +245,6 @@ function registerStackAnalysisCommands(context: vscode.ExtensionContext) { } }; - const recordAndInvoke = (origin: string, uri: vscode.Uri) => { - // TODO: vscode.window.activeTextEditor may be null - const fileUri = uri || vscode.window.activeTextEditor!.document.uri; - const filePath = fileUri.fsPath; - record(context, origin, { manifest: path.basename(filePath), fileName: path.basename(filePath) }); - invokeFullStackReport(filePath); - }; - const registerCommand = (cmd: string, action: TelemetryActions) => { return vscode.commands.registerCommand(cmd, recordAndInvoke.bind(null, action)); };