diff --git a/packages/code-analyzer-core/package.json b/packages/code-analyzer-core/package.json index 16f8a839..445226d0 100644 --- a/packages/code-analyzer-core/package.json +++ b/packages/code-analyzer-core/package.json @@ -13,7 +13,7 @@ "main": "dist/index.js", "types": "dist/index.d.ts", "dependencies": { - "@salesforce/code-analyzer-engine-api": "0.14.0", + "@salesforce/code-analyzer-engine-api": "0.15.0-SNAPSHOT", "@types/js-yaml": "^4.0.9", "@types/node": "^20.0.0", "@types/sarif": "^2.1.7", diff --git a/packages/code-analyzer-core/src/code-analyzer.ts b/packages/code-analyzer-core/src/code-analyzer.ts index 8f3a099f..6fd33f5a 100644 --- a/packages/code-analyzer-core/src/code-analyzer.ts +++ b/packages/code-analyzer-core/src/code-analyzer.ts @@ -203,11 +203,11 @@ export class CodeAnalyzer { try { apiEngineRunResults = await engine.runRules(rulesToRun, engineRunOptions); } catch (error) { - return new UnexpectedErrorEngineRunResults(engineName, error as Error); + return new UnexpectedErrorEngineRunResults(engineName, await engine.getEngineVersion(), error as Error); } validateEngineRunResults(engineName, apiEngineRunResults, ruleSelection); - const engineRunResults: EngineRunResults = new EngineRunResultsImpl(engineName, apiEngineRunResults, ruleSelection); + const engineRunResults: EngineRunResults = new EngineRunResultsImpl(engineName, await engine.getEngineVersion(), apiEngineRunResults, ruleSelection); this.emitEvent({ type: EventType.EngineRunProgressEvent, timestamp: this.clock.now(), engineName: engineName, percentComplete: 100 diff --git a/packages/code-analyzer-core/src/output-formats/json-output-format.ts b/packages/code-analyzer-core/src/output-formats/json-output-format.ts index f8337440..be339d61 100644 --- a/packages/code-analyzer-core/src/output-formats/json-output-format.ts +++ b/packages/code-analyzer-core/src/output-formats/json-output-format.ts @@ -12,9 +12,14 @@ export type JsonResultsOutput = { sev4: number sev5: number } + versions: JsonVersionOutput violations: JsonViolationOutput[] } +export type JsonVersionOutput = { + [engine: string]: string +} + export type JsonViolationOutput = { rule: string engine: string @@ -53,10 +58,20 @@ export function toJsonResultsOutput(results: RunResults, sanitizeFcn: (text: str sev4: results.getViolationCountOfSeverity(SeverityLevel.Low), sev5: results.getViolationCountOfSeverity(SeverityLevel.Info), }, + versions: toJsonVersionObject(results), violations: toJsonViolationOutputArray(results.getViolations(), results.getRunDirectory(), sanitizeFcn) }; } +function toJsonVersionObject(results: RunResults): JsonVersionOutput { + const versions: JsonVersionOutput = {}; + const engineNames: string[] = results.getEngineNames(); + for (const engineName of engineNames) { + versions[engineName] = results.getEngineRunResults(engineName).getEngineVersion(); + } + return versions; +} + export function toJsonViolationOutputArray(violations: Violation[], runDir: string, sanitizeFcn: (text: string) => string): JsonViolationOutput[] { return violations.map(v => toJsonViolationOutput(v, runDir, sanitizeFcn)); } diff --git a/packages/code-analyzer-core/src/output-formats/sarif-output-format.ts b/packages/code-analyzer-core/src/output-formats/sarif-output-format.ts index d9477211..2d31d925 100644 --- a/packages/code-analyzer-core/src/output-formats/sarif-output-format.ts +++ b/packages/code-analyzer-core/src/output-formats/sarif-output-format.ts @@ -33,6 +33,7 @@ function toSarifRun(engineRunResults: EngineRunResults, runDir: string): sarif.R tool: { driver: { name: engineRunResults.getEngineName(), + semanticVersion: engineRunResults.getEngineVersion(), informationUri: "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/version-5.html", rules: rules.map(toSarifReportingDescriptor), } diff --git a/packages/code-analyzer-core/src/output-formats/xml-output-format.ts b/packages/code-analyzer-core/src/output-formats/xml-output-format.ts index 23b32ba2..9fdb7acd 100644 --- a/packages/code-analyzer-core/src/output-formats/xml-output-format.ts +++ b/packages/code-analyzer-core/src/output-formats/xml-output-format.ts @@ -18,6 +18,12 @@ export class XmlOutputFormatter implements OutputFormatter { violationCountsNode.node('sev4').text(`${resultsOutput.violationCounts.sev4}`); violationCountsNode.node('sev5').text(`${resultsOutput.violationCounts.sev5}`); + const versionsNode: xmlbuilder.XMLElement = resultsNode.node('versions'); + const engineNames: string[] = results.getEngineNames(); + for (const engineName of engineNames) { + versionsNode.node(engineName).text(results.getEngineRunResults(engineName).getEngineVersion()); + } + const violationsNode: xmlbuilder.XMLElement = resultsNode.node('violations'); for (const violationOutput of resultsOutput.violations) { const violationNode: xmlbuilder.XMLElement = violationsNode.node('violation'); diff --git a/packages/code-analyzer-core/src/results.ts b/packages/code-analyzer-core/src/results.ts index 0ee668d7..4a9a3978 100644 --- a/packages/code-analyzer-core/src/results.ts +++ b/packages/code-analyzer-core/src/results.ts @@ -24,6 +24,7 @@ export interface Violation { export interface EngineRunResults { getEngineName(): string + getEngineVersion(): string getViolationCount(): number getViolationCountOfSeverity(severity: SeverityLevel): number getViolations(): Violation[] @@ -169,11 +170,13 @@ export class UnexpectedEngineErrorViolation implements Violation { export class EngineRunResultsImpl implements EngineRunResults { private readonly engineName: string; + private readonly engineVersion: string; private readonly apiEngineRunResults: engApi.EngineRunResults; private readonly ruleSelection: RuleSelection; - constructor(engineName: string, apiEngineRunResults: engApi.EngineRunResults, ruleSelection: RuleSelection) { + constructor(engineName: string, engineVersion: string, apiEngineRunResults: engApi.EngineRunResults, ruleSelection: RuleSelection) { this.engineName = engineName; + this.engineVersion = engineVersion; this.apiEngineRunResults = apiEngineRunResults; this.ruleSelection = ruleSelection; } @@ -182,6 +185,10 @@ export class EngineRunResultsImpl implements EngineRunResults { return this.engineName; } + getEngineVersion(): string { + return this.engineVersion; + } + getViolationCount(): number { return this.apiEngineRunResults.violations.length; } @@ -198,10 +205,12 @@ export class EngineRunResultsImpl implements EngineRunResults { export class UnexpectedErrorEngineRunResults implements EngineRunResults { private readonly engineName: string; + private readonly engineVersion: string; private readonly violation: Violation; - constructor(engineName: string, error: Error) { + constructor(engineName: string, engineVersion: string, error: Error) { this.engineName = engineName; + this.engineVersion = engineVersion; this.violation = new UnexpectedEngineErrorViolation(engineName, error); } @@ -209,6 +218,10 @@ export class UnexpectedErrorEngineRunResults implements EngineRunResults { return this.engineName; } + getEngineVersion(): string { + return this.engineVersion; + } + getViolationCount(): number { return 1; } diff --git a/packages/code-analyzer-core/test/run.test.ts b/packages/code-analyzer-core/test/run.test.ts index cd2f2e0f..a65992ed 100644 --- a/packages/code-analyzer-core/test/run.test.ts +++ b/packages/code-analyzer-core/test/run.test.ts @@ -261,6 +261,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const stubEngine1Results = overallResults.getEngineRunResults('stubEngine1'); expect(stubEngine1Results.getEngineName()).toEqual('stubEngine1'); + expect(stubEngine1Results.getEngineVersion()).toEqual('0.0.1'); expect(stubEngine1Results.getViolationCount()).toEqual(0); for (const severityLevel of getAllSeverityLevels()) { expect(stubEngine1Results.getViolationCountOfSeverity(severityLevel)).toEqual(0); @@ -269,6 +270,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const stubEngine2Results = overallResults.getEngineRunResults('stubEngine2'); expect(stubEngine2Results.getEngineName()).toEqual('stubEngine2'); + expect(stubEngine2Results.getEngineVersion()).toEqual('0.1.0'); expect(stubEngine2Results.getViolationCount()).toEqual(0); for (const severityLevel of getAllSeverityLevels()) { expect(stubEngine2Results.getViolationCountOfSeverity(severityLevel)).toEqual(0); @@ -277,6 +279,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const stubEngine3Results = overallResults.getEngineRunResults('stubEngine3'); expect(stubEngine3Results.getEngineName()).toEqual('stubEngine3'); + expect(stubEngine3Results.getEngineVersion()).toEqual('1.0.0'); expect(stubEngine3Results.getViolationCount()).toEqual(0); for (const severityLevel of getAllSeverityLevels()) { expect(stubEngine3Results.getViolationCountOfSeverity(severityLevel)).toEqual(0); @@ -303,6 +306,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const engine1Results = overallResults.getEngineRunResults('stubEngine1'); expect(engine1Results.getEngineName()).toEqual('stubEngine1'); + expect(engine1Results.getEngineVersion()).toEqual('0.0.1'); expect(engine1Results.getViolationCount()).toEqual(2); expect(engine1Results.getViolationCountOfSeverity(SeverityLevel.Critical)).toEqual(0); expect(engine1Results.getViolationCountOfSeverity(SeverityLevel.High)).toEqual(0); @@ -332,6 +336,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const engine2Results = overallResults.getEngineRunResults('stubEngine2'); expect(engine2Results.getEngineName()).toEqual('stubEngine2'); + expect(engine2Results.getEngineVersion()).toEqual('0.1.0'); expect(engine2Results.getViolationCount()).toEqual(1); expect(engine2Results.getViolationCountOfSeverity(SeverityLevel.Critical)).toEqual(0); expect(engine2Results.getViolationCountOfSeverity(SeverityLevel.High)).toEqual(1); @@ -495,6 +500,7 @@ describe("Tests for the run method of CodeAnalyzer", () => { const violations: Violation[] = overallResults.getViolations(); expect(violations).toHaveLength(1); const engineRunResults: EngineRunResults = overallResults.getEngineRunResults('throwingEngine'); + expect(engineRunResults.getEngineVersion()).toEqual('3.0.0'); expect(engineRunResults.getViolations()).toEqual(violations); expect(violations[0].getRule()).toEqual(new UnexpectedEngineErrorRule('throwingEngine')); expect(violations[0].getRule().getDescription()).toEqual(getMessage('UnexpectedEngineErrorRuleDescription', 'throwingEngine')); diff --git a/packages/code-analyzer-core/test/stubs.ts b/packages/code-analyzer-core/test/stubs.ts index f507bbe4..61c2fca4 100644 --- a/packages/code-analyzer-core/test/stubs.ts +++ b/packages/code-analyzer-core/test/stubs.ts @@ -80,6 +80,10 @@ export class StubEngine1 extends engApi.Engine { return "stubEngine1"; } + getEngineVersion(): Promise { + return Promise.resolve('0.0.1'); + } + async describeRules(describeOptions: DescribeOptions): Promise { this.describeRulesCallHistory.push({describeOptions}); this.emitDescribeRulesProgressEvent(20); @@ -157,6 +161,10 @@ export class StubEngine2 extends engApi.Engine { return "stubEngine2"; } + getEngineVersion(): Promise { + return Promise.resolve('0.1.0'); + } + async describeRules(describeOptions: DescribeOptions): Promise { this.describeRulesCallHistory.push({describeOptions}); this.emitDescribeRulesProgressEvent(30); @@ -211,6 +219,10 @@ export class StubEngine3 extends engApi.Engine { return 'stubEngine3'; } + getEngineVersion(): Promise { + return Promise.resolve('1.0.0'); + } + async describeRules(describeOptions: DescribeOptions): Promise { this.describeRulesCallHistory.push({describeOptions}); this.emitDescribeRulesProgressEvent(50); @@ -376,6 +388,10 @@ class FutureEngine extends engApi.Engine { return "future"; } + getEngineVersion(): Promise { + return Promise.resolve('2.0.0'); + } + async describeRules(_describeOptions: DescribeOptions): Promise { return []; } @@ -483,6 +499,10 @@ class ThrowingEngine extends StubEngine1 { return "throwingEngine"; } + getEngineVersion(): Promise { + return Promise.resolve('3.0.0'); + } + async runRules(_ruleNames: string[], _runOptions: engApi.RunOptions): Promise { throw new Error('SomeErrorMessageFromThrowingEngine'); } @@ -509,6 +529,10 @@ class RepeatedRuleNameEngine extends engApi.Engine { return 'repeatedRuleNameEngine'; } + getEngineVersion(): Promise { + return Promise.resolve('4.0.0'); + } + async describeRules(_describeOptions: DescribeOptions): Promise { return [ { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json index 06ace3d2..1e1f5cb2 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.json @@ -8,6 +8,11 @@ "sev4": 1, "sev5": 0 }, + "versions": { + "stubEngine1": "0.0.1", + "stubEngine2": "0.1.0", + "stubEngine3": "1.0.0" + }, "violations": [ { "rule": "stub1RuleA", diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.sarif b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.sarif index 014314e8..ca3cf85b 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.sarif +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.sarif @@ -6,6 +6,7 @@ "tool": { "driver": { "name": "stubEngine1", + "semanticVersion": "0.0.1", "informationUri": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/version-5.html", "rules": [ { @@ -169,6 +170,7 @@ "tool": { "driver": { "name": "stubEngine2", + "semanticVersion": "0.1.0", "informationUri": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/version-5.html", "rules": [ { @@ -255,6 +257,7 @@ "tool": { "driver": { "name": "stubEngine3", + "semanticVersion": "1.0.0", "informationUri": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/version-5.html", "rules": [ { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml index 55a51afc..86074149 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/multipleViolations.goldfile.xml @@ -9,6 +9,11 @@ 1 0 + + 0.0.1 + 0.1.0 + 1.0.0 + stub1RuleA diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json index 5c156a19..1d566c26 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.json @@ -8,6 +8,9 @@ "sev4": 0, "sev5": 0 }, + "versions": { + "throwingEngine": "3.0.0" + }, "violations": [ { "rule": "UnexpectedEngineError", diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.sarif b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.sarif index cf1ce1e5..284708e2 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.sarif +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.sarif @@ -6,6 +6,7 @@ "tool": { "driver": { "name": "throwingEngine", + "semanticVersion": "3.0.0", "informationUri": "https://developer.salesforce.com/docs/platform/salesforce-code-analyzer/guide/version-5.html", "rules": [ { diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml index 7a1425d9..2772c19c 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/unexpectedEngineErrorViolation.goldfile.xml @@ -9,6 +9,9 @@ 0 0 + + 3.0.0 + UnexpectedEngineError diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json index bb6c8bf6..75669f27 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.json @@ -8,5 +8,6 @@ "sev4": 0, "sev5": 0 }, + "versions": {}, "violations": [] } \ No newline at end of file diff --git a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml index 54d274b7..b9c8ab42 100644 --- a/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml +++ b/packages/code-analyzer-core/test/test-data/expectedOutputFiles/zeroViolations.goldfile.xml @@ -9,5 +9,6 @@ 0 0 + \ No newline at end of file