From d22ee914123b5930b1397ead071ed00969b1e87c Mon Sep 17 00:00:00 2001 From: Nikhil Mittal Date: Thu, 20 Aug 2026 15:35:08 +0530 Subject: [PATCH 1/2] apex guru severity changes --- .../src/apexguru-rules.ts | 92 ++++++++----------- 1 file changed, 38 insertions(+), 54 deletions(-) diff --git a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts index b0da6033..37c9b70b 100644 --- a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts +++ b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts @@ -10,7 +10,7 @@ import { COMMON_TAGS, RuleDescription, SeverityLevel } from '@salesforce/code-an */ export const APEXGURU_RULES: RuleDescription[] = [ // ================================================================================================================= - // PERFORMANCE RULES - HIGH SEVERITY (CRITICAL - RECOMMENDED) + // PERFORMANCE RULES - HIGH SEVERITY // ================================================================================================================= { @@ -29,18 +29,6 @@ export const APEXGURU_RULES: RuleDescription[] = [ resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_dml_in_loop.htm&type=5'] }, - // ================================================================================================================= - // PERFORMANCE RULES - HIGH SEVERITY (PERFORMANCE ONLY - NOT RECOMMENDED) - // ================================================================================================================= - - { - name: 'SoqlInALoopOneHop', - severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'SOQL query reached one method-hop away inside a loop causes performance issues and can hit governor limits', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop_one_hop.htm&type=5'] - }, - { name: 'ExpensiveMethods', severityLevel: SeverityLevel.High, @@ -49,38 +37,34 @@ export const APEXGURU_RULES: RuleDescription[] = [ resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_methods.htm&type=5'] }, - // ================================================================================================================= - // PERFORMANCE RULES - MODERATE SEVERITY (CRITICAL - RECOMMENDED) - // ================================================================================================================= - { - name: 'SoqlWithoutAWhereClauseOrLimitStatement', - severityLevel: SeverityLevel.Moderate, + name: 'SchemaGetGlobalDescribeNotEfficient', + severityLevel: SeverityLevel.High, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5'] + description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5'] }, { - name: 'SoqlWithWildcardFilter', - severityLevel: SeverityLevel.Moderate, + name: 'SoqlWithoutPlatformCache', + severityLevel: SeverityLevel.High, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5'] + description: 'Frequently executed SOQL query whose results could be served from Platform Cache to reduce database load', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_platform_cache.htm&type=5'] }, + // ================================================================================================================= + // PERFORMANCE RULES - MODERATE SEVERITY + // ================================================================================================================= + { - name: 'SchemaGetGlobalDescribeNotEfficient', + name: 'SoqlInALoopOneHop', severityLevel: SeverityLevel.Moderate, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5'] + description: 'SOQL query reached one method-hop away inside a loop causes performance issues and can hit governor limits', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop_one_hop.htm&type=5'] }, - // ================================================================================================================= - // PERFORMANCE RULES - MODERATE SEVERITY (PERFORMANCE ONLY - NOT RECOMMENDED) - // ================================================================================================================= - { name: 'Soql Aggregation', severityLevel: SeverityLevel.Moderate, @@ -114,32 +98,40 @@ export const APEXGURU_RULES: RuleDescription[] = [ }, { - name: 'SoqlWithNegativeExpressions', + name: 'SObjectMapInAForLoop', severityLevel: SeverityLevel.Moderate, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5'] + description: 'Building Map using .put() in a for loop is inefficient - use map constructor or putAll()', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5'] }, + // ================================================================================================================= + // PERFORMANCE RULES - LOW SEVERITY + // ================================================================================================================= + { - name: 'SObjectMapInAForLoop', - severityLevel: SeverityLevel.Moderate, + name: 'SoqlWithoutAWhereClauseOrLimitStatement', + severityLevel: SeverityLevel.Low, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'Building Map using .put() in a for loop is inefficient - use map constructor or putAll()', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5'] + description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5'] }, { - name: 'SoqlWithoutPlatformCache', - severityLevel: SeverityLevel.Moderate, + name: 'SoqlWithWildcardFilter', + severityLevel: SeverityLevel.Low, tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], - description: 'Frequently executed SOQL query whose results could be served from Platform Cache to reduce database load', - resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_platform_cache.htm&type=5'] + description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5'] }, - // ================================================================================================================= - // PERFORMANCE RULES - LOW SEVERITY (PERFORMANCE ONLY - NOT RECOMMENDED) - // ================================================================================================================= + { + name: 'SoqlWithNegativeExpressions', + severityLevel: SeverityLevel.Low, + tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans', + resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5'] + }, { name: 'LimitsGetHeapsizeMethods', @@ -165,10 +157,6 @@ export const APEXGURU_RULES: RuleDescription[] = [ resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_debug_statements.htm&type=5'] }, - // ================================================================================================================= - // BEST PRACTICES - LOW SEVERITY (RECOMMENDED) - // ================================================================================================================= - { name: 'UsingTheTestMethodKeyword', severityLevel: SeverityLevel.Low, @@ -177,10 +165,6 @@ export const APEXGURU_RULES: RuleDescription[] = [ resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_using_testmethod.htm&type=5'] }, - // ================================================================================================================= - // BEST PRACTICES - LOW SEVERITY (NOT RECOMMENDED) - // ================================================================================================================= - { name: 'SortingInApex', severityLevel: SeverityLevel.Low, From ae45627a11173c81b4ee00c1749ecf2adff28b07 Mon Sep 17 00:00:00 2001 From: Nikhil Mittal Date: Thu, 20 Aug 2026 17:41:55 +0530 Subject: [PATCH 2/2] NEW @W-21910054@ Make ApexGuru engine opt-in via apex-guru tag and reduce API timeout to 5 minutes - ApexGuru rules no longer run in default scans. They are excluded from the "Recommended" default, "all", and severity-based selectors, and now run only when explicitly requested by engine name (apexguru) or the apex-guru tag (e.g. --rule-selector apex-guru). - Replaced the removed DevPreviewApexGuru opt-in tag with apex-guru. - Reduced the ApexGuru API timeout default from 10 minutes to 5 minutes. --- .../src/apexguru-rules.ts | 58 +++++++++++-------- .../src/config.ts | 8 +-- .../test/ApexGuruEngine.test.ts | 17 +++--- .../test/apexguru-rules.test.ts | 13 ++++- packages/code-analyzer-core/src/rules.ts | 6 +- .../test/rule-selection.test.ts | 43 ++++++++------ packages/code-analyzer-core/test/stubs.ts | 24 ++++---- 7 files changed, 96 insertions(+), 73 deletions(-) diff --git a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts index 37c9b70b..a8b61b1e 100644 --- a/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts +++ b/packages/code-analyzer-apexguru-engine/src/apexguru-rules.ts @@ -1,6 +1,16 @@ -import { COMMON_TAGS, RuleDescription, SeverityLevel } from '@salesforce/code-analyzer-engine-api'; +import { RuleDescription, SeverityLevel } from '@salesforce/code-analyzer-engine-api'; + +/** + * Tag carried by every ApexGuru rule. + * + * ApexGuru rules are opt-in: they are excluded from the default ("Recommended"), "all", and severity-based + * rule selection. They only run when explicitly requested by the engine name ('apexguru') or by this tag + * (e.g. via '--rule-selector apex-guru'). The opt-in exclusion is enforced in code-analyzer-core's + * rule selection logic, which special-cases this tag. + */ +export const APEXGURU_TAG: string = 'apex-guru'; /** * Known ApexGuru rules with descriptions and metadata. @@ -16,7 +26,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlInALoop', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL query inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop.htm&type=5'] }, @@ -24,7 +34,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'DmlInALoop', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'DML statement inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_dml_in_loop.htm&type=5'] }, @@ -32,7 +42,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveMethods', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Method accounts for a large share of observed Apex CPU time and is a hotspot for performance work', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_methods.htm&type=5'] }, @@ -40,7 +50,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SchemaGetGlobalDescribeNotEfficient', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Using Schema.getGlobalDescribe() causes unnecessary overhead and decreases performance', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_schema_getglobaldescribe_not_efficient.htm&type=5'] }, @@ -48,7 +58,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithoutPlatformCache', severityLevel: SeverityLevel.High, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Frequently executed SOQL query whose results could be served from Platform Cache to reduce database load', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_platform_cache.htm&type=5'] }, @@ -60,7 +70,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlInALoopOneHop', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL query reached one method-hop away inside a loop causes performance issues and can hit governor limits', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_in_loop_one_hop.htm&type=5'] }, @@ -68,7 +78,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'Soql Aggregation', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Manual aggregation in Apex instead of using SOQL aggregate functions causes performance issues', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_aggregating_in_apex.htm&type=5'] }, @@ -76,7 +86,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithApexFilter', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Filtering records in Apex instead of using SOQL WHERE clause causes performance issues', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_apex_filter.htm&type=5'] }, @@ -84,7 +94,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'CopyingListOrSetElementsUsingAForLoop', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Copying list or set elements using a for loop is inefficient - use addAll() instead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_copying_elements_with_for_loop.htm&type=5'] }, @@ -92,7 +102,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'Redundant Soql', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Multiple identical SOQL queries cause unnecessary database round trips', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_redundant_soql.htm&type=5'] }, @@ -100,7 +110,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SObjectMapInAForLoop', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Building Map using .put() in a for loop is inefficient - use map constructor or putAll()', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sobject_map_in_for_loop.htm&type=5'] }, @@ -112,7 +122,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithoutAWhereClauseOrLimitStatement', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL query without WHERE clause or LIMIT statement can cause performance issues and heap size exceptions', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_without_where_clause_or_limit_statement.htm&type=5'] }, @@ -120,7 +130,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithWildcardFilter', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL query using LIKE with leading wildcard is inefficient and cannot use indexes', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_wildcard_filter.htm&type=5'] }, @@ -128,7 +138,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithNegativeExpressions', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL queries using negative expressions (NOT IN, !=) don\'t use indexes and cause full table scans', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_negative_expressions.htm&type=5'] }, @@ -136,7 +146,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'LimitsGetHeapsizeMethods', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Frequent Limits.getHeapSize() calls add runtime overhead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_limits_getheapsize_methods.htm&type=5'] }, @@ -144,7 +154,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveStringComparison', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Inefficient string comparison wastes CPU time', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_string_comparison.htm&type=5'] }, @@ -152,7 +162,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'ExpensiveDebugStatements', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Expensive System.debug() statements add runtime overhead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_expensive_debug_statements.htm&type=5'] }, @@ -160,7 +170,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'UsingTheTestMethodKeyword', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'The testMethod keyword is deprecated - use @isTest annotation instead', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_using_testmethod.htm&type=5'] }, @@ -168,7 +178,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SortingInApex', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Sorting records in Apex wastes CPU time and can exceed governor limits - use ORDER BY in SOQL', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_sorting_in_apex.htm&type=5'] }, @@ -176,7 +186,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'BusyLoopDelay', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Using empty loops to delay execution wastes CPU time - use System.enqueueJob with delay parameter', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_busy_loop_delay.htm&type=5'] }, @@ -184,7 +194,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'SoqlWithUnusedFields', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'SOQL query selecting unused fields increases resource consumption unnecessarily', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_antipattern_soql_with_unused_fields.htm&type=5'] }, @@ -192,7 +202,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'WritingFillerStatements', severityLevel: SeverityLevel.Low, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Filler statements written to inflate code coverage instead of testing real behavior', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru_test_case_antipattern_filler_statements.htm&type=5'] }, @@ -204,7 +214,7 @@ export const APEXGURU_RULES: RuleDescription[] = [ { name: 'apexguru-other', severityLevel: SeverityLevel.Moderate, - tags: [COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE], + tags: [APEXGURU_TAG], description: 'Other ApexGuru rules - covers new rules added by Salesforce that are not yet explicitly declared', resourceUrls: ['https://help.salesforce.com/s/articleView?id=xcloud.apexguru.htm'] } diff --git a/packages/code-analyzer-apexguru-engine/src/config.ts b/packages/code-analyzer-apexguru-engine/src/config.ts index f2bc0eeb..a51216bc 100644 --- a/packages/code-analyzer-apexguru-engine/src/config.ts +++ b/packages/code-analyzer-apexguru-engine/src/config.ts @@ -13,7 +13,7 @@ export type ApexGuruEngineConfig = { /** * Maximum time to wait for ApexGuru API response (in milliseconds) - * Default: 600000 (10 minutes) + * Default: 300000 (5 minutes) */ api_timeout_ms: number; @@ -42,7 +42,7 @@ export type ApexGuruEngineConfig = { * Default configuration values */ export const DEFAULT_APEXGURU_ENGINE_CONFIG: ApexGuruEngineConfig = { - api_timeout_ms: 600000, // 10 minutes + api_timeout_ms: 300000, // 5 minutes api_initial_retry_ms: 2000, // 2 seconds api_max_retry_ms: 60000, // 60 seconds api_backoff_multiplier: 2 // 2x exponential backoff @@ -60,9 +60,9 @@ export const APEXGURU_ENGINE_CONFIG_DESCRIPTION: ConfigDescription = { defaultValue: null }, api_timeout_ms: { - descriptionText: 'Maximum time to wait for ApexGuru API response (in milliseconds). Default: 600000 (10 minutes)', + descriptionText: 'Maximum time to wait for ApexGuru API response (in milliseconds). Default: 300000 (5 minutes)', valueType: 'number', - defaultValue: 600000 + defaultValue: 300000 }, api_initial_retry_ms: { descriptionText: 'Initial retry delay for polling ApexGuru API (in milliseconds). Default: 2000 (2 seconds)', diff --git a/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts b/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts index bd9512ce..ec834773 100644 --- a/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts +++ b/packages/code-analyzer-apexguru-engine/test/ApexGuruEngine.test.ts @@ -1,5 +1,6 @@ import { ApexGuruEngine } from '../src/engine'; import { ApexGuruService } from '../src/services/ApexGuruService'; +import { APEXGURU_TAG } from '../src/apexguru-rules'; import { LogLevel, RunOptions, Workspace, COMMON_TAGS } from '@salesforce/code-analyzer-engine-api'; import * as fs from 'node:fs/promises'; import * as fsSync from 'node:fs'; @@ -299,12 +300,12 @@ describe('ApexGuruEngine', () => { status: 'skipped', error: { code: 'SCAN_TIMEOUT', - message: 'Code Analyzer skipped ApexGuru scan because the workspace scan timed out after 600 seconds. ' + + message: 'Code Analyzer skipped ApexGuru scan because the workspace scan timed out after 300 seconds. ' + 'Increase the timeout setting in the Code Analyzer configuration file.', remediation: '' } }); - expect(logSpy).toHaveBeenCalledWith(LogLevel.Warn, expect.stringContaining('workspace scan timed out after 600 seconds')); + expect(logSpy).toHaveBeenCalledWith(LogLevel.Warn, expect.stringContaining('workspace scan timed out after 300 seconds')); expect(mockApexGuruService.cleanup).toHaveBeenCalled(); }); @@ -677,24 +678,24 @@ describe('ApexGuruEngine', () => { expect(engine.getName()).toBe('apexguru'); }); - it('should describe rules selectable by the Recommended tag', async () => { + it('should describe rules selectable by the apex-guru tag', async () => { const rules = await engine.describeRules({ logFolder: '/tmp/logs', workingFolder: '/tmp/working' }); - const recommendedRules = rules.filter(r => r.tags.includes(COMMON_TAGS.RECOMMENDED)); - expect(recommendedRules).toHaveLength(rules.length); + const apexGuruRules = rules.filter(r => r.tags.includes(APEXGURU_TAG)); + expect(apexGuruRules).toHaveLength(rules.length); }); - it('should describe rules selectable by the Performance tag', async () => { + it('should not carry the Recommended tag so rules do not run by default', async () => { const rules = await engine.describeRules({ logFolder: '/tmp/logs', workingFolder: '/tmp/working' }); - const performanceRules = rules.filter(r => r.tags.includes(COMMON_TAGS.CATEGORIES.PERFORMANCE)); - expect(performanceRules).toHaveLength(rules.length); + const recommendedRules = rules.filter(r => r.tags.includes(COMMON_TAGS.RECOMMENDED)); + expect(recommendedRules).toHaveLength(0); }); it('should describe individual rules by name for explicit selection', async () => { diff --git a/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts b/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts index d9082b15..dbc4c1f9 100644 --- a/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts +++ b/packages/code-analyzer-apexguru-engine/test/apexguru-rules.test.ts @@ -1,5 +1,5 @@ import { COMMON_TAGS } from '@salesforce/code-analyzer-engine-api'; -import { APEXGURU_RULES, isKnownRule, FALLBACK_RULE_NAME } from '../src/apexguru-rules'; +import { APEXGURU_RULES, APEXGURU_TAG, isKnownRule, FALLBACK_RULE_NAME } from '../src/apexguru-rules'; describe('apexguru-rules', () => { @@ -8,9 +8,16 @@ describe('apexguru-rules', () => { expect(APEXGURU_RULES).toHaveLength(23); }); - it('every rule should carry exactly the Recommended and Performance tags', () => { + it('every rule should carry exactly the apex-guru tag', () => { for (const rule of APEXGURU_RULES) { - expect(rule.tags).toEqual([COMMON_TAGS.RECOMMENDED, COMMON_TAGS.CATEGORIES.PERFORMANCE]); + expect(rule.tags).toEqual([APEXGURU_TAG]); + } + }); + + it('no rule should carry the Recommended or Performance tags (ApexGuru is opt-in, not run by default)', () => { + for (const rule of APEXGURU_RULES) { + expect(rule.tags).not.toContain(COMMON_TAGS.RECOMMENDED); + expect(rule.tags).not.toContain(COMMON_TAGS.CATEGORIES.PERFORMANCE); } }); diff --git a/packages/code-analyzer-core/src/rules.ts b/packages/code-analyzer-core/src/rules.ts index 5a83bf59..d6d07332 100644 --- a/packages/code-analyzer-core/src/rules.ts +++ b/packages/code-analyzer-core/src/rules.ts @@ -109,10 +109,10 @@ export class RuleImpl implements Rule { const sevNumber: number = this.getSeverityLevel().valueOf(); const sevName: string = SeverityLevel[sevNumber]; const tags: string[] = this.getTags().map(t => t.toLowerCase()); - const isDevPreviewApexGuru: boolean = tags.includes('devpreviewapexguru'); + const isApexGuru: boolean = tags.includes('apex-guru'); let selectables: string[]; - if (isDevPreviewApexGuru) { - // DevPreviewApexGuru rules are opt-in: only selectable by engine name, rule name, or explicit tag. + if (isApexGuru) { + // apex-guru rules are opt-in: only selectable by engine name, rule name, or explicit tag. // 'all' and severity are intentionally excluded so broad selectors (e.g. 'all', '4', 'Low') don't pull them in. selectables = [ this.getEngineName().toLowerCase(), diff --git a/packages/code-analyzer-core/test/rule-selection.test.ts b/packages/code-analyzer-core/test/rule-selection.test.ts index ed2db3c8..7104390e 100644 --- a/packages/code-analyzer-core/test/rule-selection.test.ts +++ b/packages/code-analyzer-core/test/rule-selection.test.ts @@ -610,45 +610,50 @@ describe('Tests for selecting rules', () => { getMessage('InstructionsToIgnoreErrorAndDisableEngine', 'someEngine')); }) - describe('DevPreviewApexGuru rule selection behavior', () => { + describe('apex-guru rule selection behavior', () => { beforeEach(async () => { codeAnalyzer = createCodeAnalyzer(); - await codeAnalyzer.addEnginePlugin(new stubs.DevPreviewEnginePlugin()); + await codeAnalyzer.addEnginePlugin(new stubs.ApexGuruEnginePlugin()); }); - it('DevPreviewApexGuru rules are NOT selected by severity number selector', async () => { + it('apex-guru rules are NOT selected by the default Recommended selector', async () => { + const selection: RuleSelection = await codeAnalyzer.selectRules(['Recommended']); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual([]); + }); + + it('apex-guru rules are NOT selected by severity number selector', async () => { const selection: RuleSelection = await codeAnalyzer.selectRules(['4']); // Low - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual([]); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual([]); }); - it('DevPreviewApexGuru rules are NOT selected by severity name selector', async () => { + it('apex-guru rules are NOT selected by severity name selector', async () => { const selection: RuleSelection = await codeAnalyzer.selectRules(['Low']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual([]); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual([]); }); - it('DevPreviewApexGuru rules are NOT selected by all', async () => { + it('apex-guru rules are NOT selected by all', async () => { const selection: RuleSelection = await codeAnalyzer.selectRules(['all']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual([]); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual([]); }); - it('DevPreviewApexGuru rules ARE selected by engine name', async () => { - const selection: RuleSelection = await codeAnalyzer.selectRules(['devPreviewEngine']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual(['devPreviewRule1', 'devPreviewRule2']); + it('apex-guru rules ARE selected by engine name', async () => { + const selection: RuleSelection = await codeAnalyzer.selectRules(['apexGuruEngine']); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual(['apexGuruRule1', 'apexGuruRule2']); }); - it('DevPreviewApexGuru rules ARE selected by rule name', async () => { - const selection: RuleSelection = await codeAnalyzer.selectRules(['devPreviewRule1']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual(['devPreviewRule1']); + it('apex-guru rules ARE selected by rule name', async () => { + const selection: RuleSelection = await codeAnalyzer.selectRules(['apexGuruRule1']); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual(['apexGuruRule1']); }); - it('DevPreviewApexGuru rules ARE selected by DevPreviewApexGuru tag', async () => { - const selection: RuleSelection = await codeAnalyzer.selectRules(['DevPreviewApexGuru']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual(['devPreviewRule1', 'devPreviewRule2']); + it('apex-guru rules ARE selected by the apex-guru tag', async () => { + const selection: RuleSelection = await codeAnalyzer.selectRules(['apex-guru']); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual(['apexGuruRule1', 'apexGuruRule2']); }); - it('DevPreviewApexGuru rules ARE selected by other tag they carry', async () => { + it('apex-guru rules ARE selected by other tag they carry', async () => { const selection: RuleSelection = await codeAnalyzer.selectRules(['Performance']); - expect(ruleNamesFor(selection, 'devPreviewEngine')).toEqual(['devPreviewRule1']); + expect(ruleNamesFor(selection, 'apexGuruEngine')).toEqual(['apexGuruRule1']); }); }); diff --git a/packages/code-analyzer-core/test/stubs.ts b/packages/code-analyzer-core/test/stubs.ts index 080c7105..e24c0d1c 100644 --- a/packages/code-analyzer-core/test/stubs.ts +++ b/packages/code-analyzer-core/test/stubs.ts @@ -602,21 +602,21 @@ class EmptyTagEngine extends engApi.Engine { } /** - * DevPreviewEnginePlugin - A plugin to help with testing DevPreview rule selection behavior + * ApexGuruEnginePlugin - A plugin to help with testing apex-guru (opt-in) rule selection behavior */ -export class DevPreviewEnginePlugin extends engApi.EnginePluginV1 { +export class ApexGuruEnginePlugin extends engApi.EnginePluginV1 { getAvailableEngineNames(): string[] { - return ["devPreviewEngine"]; + return ["apexGuruEngine"]; } async createEngine(_engineName: string, _config: engApi.ConfigObject): Promise { - return new DevPreviewEngine(); + return new ApexGuruEngine(); } } -class DevPreviewEngine extends engApi.Engine { +class ApexGuruEngine extends engApi.Engine { getName(): string { - return 'devPreviewEngine'; + return 'apexGuruEngine'; } getEngineVersion(): Promise { @@ -626,17 +626,17 @@ class DevPreviewEngine extends engApi.Engine { async describeRules(_describeOptions: engApi.DescribeOptions): Promise { return [ { - name: "devPreviewRule1", + name: "apexGuruRule1", severityLevel: engApi.SeverityLevel.Low, - tags: ['DevPreviewApexGuru', 'Performance'], - description: 'A DevPreview rule with Low severity', + tags: ['apex-guru', 'Performance'], + description: 'An apex-guru rule with Low severity', resourceUrls: [] }, { - name: "devPreviewRule2", + name: "apexGuruRule2", severityLevel: engApi.SeverityLevel.High, - tags: ['DevPreviewApexGuru'], - description: 'A DevPreview rule with High severity', + tags: ['apex-guru'], + description: 'An apex-guru rule with High severity', resourceUrls: [] } ];