Conversation
Codacy's Analysis Summary0 new issue (≤ 1 medium issue) Review Pull Request in Codacy →
|
There was a problem hiding this comment.
Pull request overview
This pull request implements a feature to disable Codacy patterns directly from the IDE. The feature adds a "Disable pattern" quick fix action for Codacy diagnostics, with permission checking and support for both direct pattern disabling and guidance for patterns in coding standards.
Changes:
- Added permission checking logic to verify user access for pattern configuration
- Implemented UI flow for disabling patterns, including webview panel for standards-based patterns
- Integrated the feature into VS Code's quick fix system via CodeActionProvider
- Added setup completion tracking to gate the feature
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| src/views/hasPermissions.ts | New utility function to check if users have required permissions based on organization settings and repository access |
| src/views/PatternInStandardView.ts | New webview panel implementation to guide users through disabling patterns in coding standards |
| src/views/SetupView.ts | Added module-level state tracking for setup completion count |
| src/views/ProblemsDiagnosticCollection.ts | Updated code action provider to include disable pattern option and prefixed action titles with "Codacy:" |
| src/views/IssueDetailsProvider.ts | Implemented main command handler for disabling patterns with permission checks and API integration |
| src/extension.ts | Registered new disable pattern command and updated IssueActionProvider with repository params |
| package.json | Added command definition for disable pattern feature |
| media/styles/pattern-in-standard.css | Styling for the pattern-in-standard webview panel |
| media/scripts/patternInStandardScript.js | Client-side JavaScript for webview interactions |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
e950a85 to
2962164
Compare
2962164 to
02a935e
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
573d94c to
ff6bfa5
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 13 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 8 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const pendingCount = getCurrentPendingCount() | ||
|
|
||
| // If pendingCount is undefined, assume setup is not complete (safer default) | ||
| // If pendingCount > 0, setup is incomplete | ||
| if (pendingCount === undefined || pendingCount > 0) { | ||
| const action = await vscode.window.showInformationMessage( | ||
| 'Complete your Codacy setup to disable patterns.', | ||
| 'Complete setup' | ||
| ) | ||
|
|
||
| if (action === 'Complete setup') { | ||
| await vscode.commands.executeCommand('workbench.view.extension.codacy-main') | ||
| } | ||
| return | ||
| } |
There was a problem hiding this comment.
disablePatternFn blocks when getCurrentPendingCount() is undefined, which happens whenever the Setup webview was never opened/resolved. This makes “Disable pattern” unusable even if the user is fully set up. Use a source of truth independent of the SetupView UI (e.g., CodacyCloud state / Config + params checks) or persist setup completion in context/state, instead of relying on a module-global value updated only by the webview.
| export const disablePatternCommand = async (issue: CommitIssue, params: RepositoryParams, cli?: CodacyCli) => { | ||
| const props: DisablePatternProps = { | ||
| provider: params.provider, | ||
| organization: params.organization, | ||
| repository: params.repository, | ||
| toolUuid: issue.toolInfo.uuid, | ||
| patternId: issue.patternInfo.id, | ||
| commitSHA: issue.commitInfo?.sha, | ||
| } | ||
|
|
||
| await disablePatternFn(props, cli) | ||
| } | ||
|
|
||
| export const disableCliPatternCommand = async ( | ||
| params: RepositoryParams, | ||
| issue?: ProcessedSarifResult, | ||
| cli?: CodacyCli | ||
| ) => { | ||
| const tools = await Api.Tools.listTools() | ||
| const toolUuid = tools.data.find((tool) => tool.name === issue?.tool)?.uuid | ||
|
|
||
| if (!issue || !issue.rule || !toolUuid) { | ||
| vscode.window.showErrorMessage('Unable to show issue details: missing tool or rule information.') | ||
| Logger.error('Unable to show issue details: missing tool or rule information.') | ||
| return | ||
| } | ||
|
|
||
| const props: DisablePatternProps = { | ||
| provider: params.provider, | ||
| organization: params.organization, | ||
| repository: params.repository, | ||
| toolUuid, | ||
| patternId: issue.rule.id, | ||
| // no commit SHA for CLI issues, we should simply re-initialize the CLI | ||
| } | ||
| await disablePatternFn(props, cli) | ||
| } |
There was a problem hiding this comment.
These commands are contributed in package.json, so they can be invoked from the Command Palette without arguments. disablePatternCommand assumes issue and params are always provided and will throw if called directly. Make parameters optional and show a user-facing error when missing, and/or hide these commands from the command palette via a contributes.menus.commandPalette entry with when: "false".
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| undefined, | ||
| [] |
There was a problem hiding this comment.
onDidReceiveMessage is called with an empty disposables array ([]). This means the returned disposable isn’t retained anywhere, making the parameter ineffective and potentially leaking listeners. Either omit the third argument or store the returned disposable and dispose it when the panel is disposed.
| undefined, | |
| [] | |
| undefined |
| constructor( | ||
| private getParams?: () => { provider: string; organization: string; repository: string } | undefined, | ||
| private cli?: CodacyCli | ||
| ) {} |
There was a problem hiding this comment.
IssueActionProvider’s getParams callback is typed as { provider: string; organization: string; repository: string }, but the codebase already defines RepositoryParams (and provider is now typed as Provider). Reuse RepositoryParams here to keep types aligned and avoid accidentally passing invalid provider values.
| export const hasPermission = async ( | ||
| provider: string, | ||
| organization: string, | ||
| allowedByDefault: Permission, | ||
| repositoryPermission?: Permission | ||
| ) => { | ||
| let minimumPermission: Permission | undefined | ||
|
|
||
| try { | ||
| const { data: organizationData } = await Api.Organization.getOrganization(provider, organization) | ||
| minimumPermission = organizationData.analysisConfigurationMinimumPermission | ||
| } catch (error) { | ||
| // If we cannot retrieve the organization or its configuration, deny permission by default. | ||
| return false | ||
| } | ||
|
|
||
| if (!minimumPermission) return false | ||
|
|
||
| switch (repositoryPermission) { | ||
| case 'read': | ||
| return minimumPermission === 'read' || allowedByDefault === 'read' | ||
| case 'write': { | ||
| const writePermissions = ['read', 'write'] | ||
| return writePermissions.includes(minimumPermission) || writePermissions.includes(allowedByDefault) | ||
| } | ||
| case 'admin': { | ||
| const adminPermissions = ['read', 'write', 'admin'] | ||
| return adminPermissions.includes(minimumPermission) || adminPermissions.includes(allowedByDefault) | ||
| } |
There was a problem hiding this comment.
allowedByDefault is currently combined with the organization’s analysisConfigurationMinimumPermission using ||, which can allow callers to bypass the org minimum depending on what they pass (e.g., allowedByDefault='read' would grant access even if the org minimum is admin). Since this function already fetches a minimum permission and returns false when it’s missing, consider removing allowedByDefault entirely or only using it as a fallback when the org minimum cannot be retrieved (instead of OR-ing it with the org minimum).
| export const disablePatternCommand = async (issue: CommitIssue, params: RepositoryParams, cli?: CodacyCli) => { | ||
| const props: DisablePatternProps = { | ||
| provider: params.provider, | ||
| organization: params.organization, | ||
| repository: params.repository, | ||
| toolUuid: issue.toolInfo.uuid, | ||
| patternId: issue.patternInfo.id, | ||
| commitSHA: issue.commitInfo?.sha, | ||
| } | ||
|
|
||
| await disablePatternFn(props, cli) | ||
| } |
There was a problem hiding this comment.
This command is contributed in package.json, so it can be invoked from the Command Palette without arguments. The handler currently requires issue and params and will throw if invoked without them (e.g., issue.toolInfo access). Make the parameters optional and add a guard that shows a user-friendly message and returns when required arguments are missing.
| export const disableCliPatternCommand = async ( | ||
| params: RepositoryParams, | ||
| issue?: ProcessedSarifResult, | ||
| cli?: CodacyCli | ||
| ) => { | ||
| const tools = await Api.Tools.listTools() | ||
| const toolUuid = tools.data.find((tool) => tool.name === issue?.tool)?.uuid | ||
|
|
||
| if (!issue || !issue.rule || !toolUuid) { | ||
| vscode.window.showErrorMessage('Unable to disable pattern: missing tool or rule information.') | ||
| Logger.error('Unable to disable pattern: missing tool or rule information.') | ||
| return | ||
| } |
There was a problem hiding this comment.
This command is contributed in package.json, so it can be invoked without arguments from the Command Palette. params is required here and will cause a runtime exception when undefined. Consider making params optional and validating inputs (showing an error) before proceeding.
| export function getNonce() { | ||
| let text = '' | ||
| const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | ||
| for (let i = 0; i < 32; i++) { | ||
| text += possible.charAt(Math.floor(Math.random() * possible.length)) | ||
| } | ||
| return text |
There was a problem hiding this comment.
getNonce() uses Math.random(), which is not suitable for CSP nonces and can be predictable. Use a cryptographically secure source (e.g., Node's crypto.randomBytes/crypto.randomUUID) to generate the nonce string.
| export function getNonce() { | |
| let text = '' | |
| const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' | |
| for (let i = 0; i < 32; i++) { | |
| text += possible.charAt(Math.floor(Math.random() * possible.length)) | |
| } | |
| return text | |
| import { randomBytes } from 'crypto'; | |
| export function getNonce() { | |
| return randomBytes(16).toString('base64') |
| <button class="copy-pattern-button" data-pattern-title="${escapedTitle}" aria-label="Copy pattern"> | ||
| <svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" class="copy-icon" width="16" height="16" viewBox="0 0 512 512"><rect x="128" y="128" width="336" height="336" rx="57" ry="57" fill="none" stroke="currentColor" stroke-linejoin="round" stroke-width="32"/><path d="M383.5 128l.5-24a56.16 56.16 0 00-56-56H112a64.19 64.19 0 00-64 64v216a56.16 56.16 0 0056 56h24" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="32"/></svg> | ||
| Copy pattern: "${escapedTitle}" | ||
| </button> |
There was a problem hiding this comment.
The data-pattern-title attribute is populated with escapedTitle. HTML escaping is correct for rendering, but getAttribute('data-pattern-title') returns the escaped entity text (e.g. &), so the clipboard/message will contain entities instead of the original title. Store the raw title separately for copying (while still escaping for display/attributes), or decode entities before copying.
Uh oh!
There was an error while loading. Please reload this page.