diff --git a/src/commands/project/deploy/validate.ts b/src/commands/project/deploy/validate.ts index 579b2303..96b3190f 100644 --- a/src/commands/project/deploy/validate.ts +++ b/src/commands/project/deploy/validate.ts @@ -224,6 +224,15 @@ export default class DeployMetadataValidate extends SfCommand this.logSuccess(messages.getMessage('info.SuccessfulValidation', [deploy.id])); this.log(messages.getMessage('info.suggestedQuickDeploy', [this.config.bin, deploy.id])); } else { + let componentDeployErrors = result.response.errorMessage; + if (!result.response.errorMessage) { + componentDeployErrors = ''; + // gather component deployment errors + const failures = formatter.getFileResponseFailures(); + failures?.map((f) => { + componentDeployErrors += `${f.problemType} in ${f.fullName} - ${f.error}${os.EOL}`; + }); + } throw messages .createError('error.FailedValidation', [ deploy.id, @@ -233,7 +242,7 @@ export default class DeployMetadataValidate extends SfCommand (warning: CodeCoverageWarnings & { name?: string }) => `${warning.name ? `${warning.name} - ` : ''}${warning.message}` ), - result.response.errorMessage, + componentDeployErrors, result.response.numberComponentErrors ? `${result.response.numberComponentErrors} component error(s)` : '', ] .join(os.EOL) diff --git a/src/formatters/deployResultFormatter.ts b/src/formatters/deployResultFormatter.ts index c52f7601..1b38a91a 100644 --- a/src/formatters/deployResultFormatter.ts +++ b/src/formatters/deployResultFormatter.ts @@ -8,7 +8,13 @@ import * as path from 'node:path'; import { EOL } from 'node:os'; import * as fs from 'node:fs'; import { ux } from '@oclif/core'; -import { ComponentStatus, DeployResult, FileResponse, RequestStatus } from '@salesforce/source-deploy-retrieve'; +import { + ComponentStatus, + DeployResult, + FileResponse, + FileResponseFailure, + RequestStatus, +} from '@salesforce/source-deploy-retrieve'; import { Org, SfError, Lifecycle } from '@salesforce/core'; import { Duration, ensureArray, sortBy } from '@salesforce/kit'; import { @@ -131,6 +137,27 @@ export class DeployResultFormatter extends TestResultsFormatter implements Forma return 'normal'; } + public getFileResponseFailures(): FileResponseFailure[] | undefined { + const failures = this.relativeFiles.filter(isSdrFailure); + const deployMessages = ensureArray(this.result.response.details?.componentFailures); + if (deployMessages.length > failures.length) { + const failureKeySet = new Set(failures.map((f) => makeKey(f.type, f.fullName))); + // if there's additional failures in the API response, find the failure and add it to the output + deployMessages + .filter((m) => !m.componentType || !failureKeySet.has(makeKey(m.componentType, m.fullName))) + .map((deployMessage) => { + failures.push({ + fullName: deployMessage.fullName, + type: deployMessage.componentType ?? 'UNKNOWN', + state: ComponentStatus.Failed, + error: deployMessage.problem ?? 'UNKNOWN', + problemType: deployMessage.problemType ?? 'Error', + }); + }); + } + return failures; + } + private maybeCreateRequestedReports(): void { // only generate reports if test results are presented if (this.coverageOptions.reportFormats?.length) { @@ -269,24 +296,8 @@ export class DeployResultFormatter extends TestResultsFormatter implements Forma private displayFailures(): void { if (this.result.response.status === RequestStatus.Succeeded) return; - const failures = this.relativeFiles.filter(isSdrFailure); - const deployMessages = ensureArray(this.result.response.details?.componentFailures); - if (deployMessages.length > failures.length) { - const failureKeySet = new Set(failures.map((f) => makeKey(f.type, f.fullName))); - // if there's additional failures in the API response, find the failure and add it to the output - deployMessages - .filter((m) => !m.componentType || !failureKeySet.has(makeKey(m.componentType, m.fullName))) - .map((deployMessage) => { - failures.push({ - fullName: deployMessage.fullName, - type: deployMessage.componentType ?? 'UNKNOWN', - state: ComponentStatus.Failed, - error: deployMessage.problem ?? 'UNKNOWN', - problemType: deployMessage.problemType ?? 'Error', - }); - }); - } - if (!failures.length) return; + const failures = this.getFileResponseFailures(); + if (!failures?.length) return; const columns = { problemType: { header: 'Type' },