Skip to content

Commit

Permalink
Generate a file hardis-report/apex-coverage-results.json with Apex co…
Browse files Browse the repository at this point in the history
…de coverage details

- Generate a file **hardis-report/apex-coverage-results.json** with Apex code coverage details for the following commands:
  - [hardis:project:deploy:smart](https://sfdx-hardis.cloudity.com/hardis/project/deploy/smart/) (only if `COVERAGE_FORMATTER_JSON=true` environment varriable is defined)
  - [hardis:org:test:apex](https://sfdx-hardis.cloudity.com/hardis/org/test/apex/) (always)
  - [SF Cli deployment wrapper commands](https://sfdx-hardis.cloudity.com/salesforce-deployment-assistant-setup/#using-custom-cicd-pipeline)
  • Loading branch information
nvuillam committed Jan 9, 2025
1 parent cc17d10 commit 32d4b43
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 3 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@

Note: Can be used with `sfdx plugins:install sfdx-hardis@beta` and docker image `hardisgroupcom/sfdx-hardis@beta`

## [5.14.1] 2025-01-09

- Generate a file **hardis-report/apex-coverage-results.json** with Apex code coverage details for the following commands:
- [hardis:project:deploy:smart](https://sfdx-hardis.cloudity.com/hardis/project/deploy/smart/) (only if `COVERAGE_FORMATTER_JSON=true` environment variable is defined)
- [hardis:org:test:apex](https://sfdx-hardis.cloudity.com/hardis/org/test/apex/) (always)
- [SF Cli deployment wrapper commands](https://sfdx-hardis.cloudity.com/salesforce-deployment-assistant-setup/#using-custom-cicd-pipeline)

## [5.14.0] 2025-01-09

- Add ability to replace ApiVersion on specific Metadata Types file using `sf hardis:project:audit:apiversion`
Expand Down
3 changes: 3 additions & 0 deletions src/commands/hardis/org/test/apex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { execCommand, extractRegexMatchesMultipleGroups, uxLog } from '../../../
import { getNotificationButtons, getOrgMarkdown } from '../../../../common/utils/notifUtils.js';
import { CONSTANTS, getConfig, getReportDirectory } from '../../../../config/index.js';
import { NotifProvider, NotifSeverity } from '../../../../common/notifProvider/index.js';
import { generateApexCoverageOutputFile } from '../../../../common/utils/deployUtils.js';

Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
const messages = Messages.loadMessages('sfdx-hardis', 'org');
Expand Down Expand Up @@ -145,6 +146,7 @@ This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/sales
// Parse outcome value from logs with Regex
this.testRunOutcome = (/Outcome *(.*) */.exec(execCommandRes.stdout + execCommandRes.stderr) || '')[1].trim();
this.testRunOutputString = execCommandRes.stdout + execCommandRes.stderr;
await generateApexCoverageOutputFile(this.testRunOutputString || '');
} catch (e) {
// No Apex in the org
if (
Expand All @@ -156,6 +158,7 @@ This command is part of [sfdx-hardis Monitoring](${CONSTANTS.DOC_URL_ROOT}/sales
// Failing Apex tests
this.testRunOutputString = (e as Error).message;
this.testRunOutcome = 'Failed';
await generateApexCoverageOutputFile(e);
}
}
}
Expand Down
41 changes: 38 additions & 3 deletions src/common/utils/deployUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
replaceJsonInString,
uxLog,
} from './index.js';
import { CONSTANTS, getConfig, setConfig } from '../../config/index.js';
import { CONSTANTS, getConfig, getReportDirectory, setConfig } from '../../config/index.js';
import { GitProvider } from '../gitProvider/index.js';
import { deployCodeCoverageToMarkdown } from '../gitProvider/utilsMarkdown.js';
import { MetadataUtils } from '../metadata-utils/index.js';
Expand Down Expand Up @@ -282,24 +282,30 @@ export async function smartDeploy(
(options.postDestructiveChanges ? ` --post-destructive-changes ${options.postDestructiveChanges}` : '') +
(options.targetUsername ? ` -o ${options.targetUsername}` : '') +
(testlevel === 'NoTestRun' || branchConfig?.skipCodeCoverage === true ? '' : ' --coverage-formatters json-summary') +
((testlevel === 'NoTestRun' || branchConfig?.skipCodeCoverage === true) && process.env?.COVERAGE_FORMATTER_JSON === "true" ? '' : ' --coverage-formatters json') +
(debugMode ? ' --verbose' : '') +
` --wait ${process.env.SFDX_DEPLOY_WAIT_MINUTES || '120'}` +
(process.env.SFDX_DEPLOY_DEV_DEBUG ? ' --dev-debug' : '') +
` --json`;
let deployRes;
try {
deployRes = await execCommand(deployCommand, commandThis, {
output: true,
output: false,
debug: debugMode,
fail: true,
retry: deployment.retry || null,
});
} catch (e) {
if (deployRes.status === 0) {
uxLog(commandThis, c.grey(shortenLogLines(deployRes.stdout + deployRes.stderr)));
}
} catch (e: any) {
await generateApexCoverageOutputFile(e);
deployRes = await handleDeployError(e, check, branchConfig, commandThis, options, deployment);
}
if (typeof deployRes === 'object') {
deployRes.stdout = JSON.stringify(deployRes);
}
await generateApexCoverageOutputFile(deployRes.stdout + deployRes.stderr || '');

// Set deployment id
await getDeploymentId(deployRes.stdout + deployRes.stderr || '');
Expand Down Expand Up @@ -1311,3 +1317,32 @@ async function updatePullRequestResultCoverage(
}
globalThis.pullRequestData = Object.assign(globalThis.pullRequestData || {}, prDataCodeCoverage);
}

export async function generateApexCoverageOutputFile(commandOutput: string | any): Promise<void> {
try {
const outputString =
typeof commandOutput === 'string' ? commandOutput :
typeof commandOutput === 'object' && commandOutput.stdout && commandOutput.stderr ? commandOutput.stdout + commandOutput.stderr :
typeof commandOutput === 'object' && commandOutput.stdout ? commandOutput.stdout :
typeof commandOutput === 'object' && commandOutput.stderr ? commandOutput.stderr :
JSON.stringify(commandOutput);
const reportDir = await getReportDirectory();
const coverageFileName = path.join(reportDir, "apex-coverage-results.json");
let coverageObject: any = null;
const jsonLog = findJsonInString(outputString);
// Output from sf project deploy start or similar: extract from JSON
if (jsonLog && jsonLog?.result?.details?.runTestResult?.codeCoverage?.length > 0) {
coverageObject = jsonLog.result.details.runTestResult.codeCoverage;
}
// Output from apex run tests: get locally generated file
else if (fs.existsSync(path.join(reportDir, "test-result-codecoverage.json"))) {
coverageObject = JSON.parse(fs.readFileSync(path.join(reportDir, "test-result-codecoverage.json"), 'utf8'));
}
if (coverageObject !== null) {
await fs.writeFile(coverageFileName, JSON.stringify(coverageObject, null, 2), 'utf8');
uxLog(this, c.cyan(`Written Apex coverage results in file ${coverageFileName}`));
}
} catch (e: any) {
uxLog(this, c.red(`Error while generating Apex coverage output file: ${e.message}`));
}
}
3 changes: 3 additions & 0 deletions src/common/utils/wrapUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { SfCommand } from "@salesforce/sf-plugins-core";
import c from "chalk";
import { execCommand, uxLog } from "./index.js";
import { analyzeDeployErrorLogs } from "./deployTips.js";
import { generateApexCoverageOutputFile } from "./deployUtils.js";

export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], commandThis: SfCommand<any>, debug = false): Promise<any> {
const endArgs = [...argv].map((arg) => {
Expand Down Expand Up @@ -44,7 +45,9 @@ export async function wrapSfdxCoreCommand(commandBase: string, argv: string[], c
fail: true,
});
process.exitCode = 0;
await generateApexCoverageOutputFile(deployRes);
} catch (e) {
await generateApexCoverageOutputFile(e);
// Add deployment tips in error logs
const { errLog } = await analyzeDeployErrorLogs((e as any).stdout + (e as any).stderr, true, { check: endArgs.includes("--checkonly") });
uxLog(commandThis, c.red(c.bold("Sadly there has been error(s)")));
Expand Down

0 comments on commit 32d4b43

Please sign in to comment.