From 35b7fd71951a0b4213f66839624abd763baec2cc Mon Sep 17 00:00:00 2001 From: Willie Ruemmele Date: Mon, 24 Aug 2026 16:30:22 -0600 Subject: [PATCH 1/2] feat: display deploy-level notifications in CLI output @W-23939851@ --- src/formatters/deployResultFormatter.ts | 18 +++++ test/utils/output.test.ts | 90 +++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/src/formatters/deployResultFormatter.ts b/src/formatters/deployResultFormatter.ts index e2cb99c9..82158bf2 100644 --- a/src/formatters/deployResultFormatter.ts +++ b/src/formatters/deployResultFormatter.ts @@ -20,6 +20,7 @@ import { join } from 'node:path'; import { Ux } from '@salesforce/sf-plugins-core'; import { ComponentStatus, + DeployNotification, DeployResult, FileResponse, FileResponseFailure, @@ -154,6 +155,7 @@ export class DeployResultFormatter extends TestResultsFormatter implements Forma this.displayTestResults(); this.maybeCreateRequestedReports(); this.displayReplacements(); + this.displayNotifications(); } public determineVerbosity(): Verbosity { @@ -297,6 +299,22 @@ export class DeployResultFormatter extends TestResultsFormatter implements Forma } } + private displayNotifications(): void { + const notifications: DeployNotification[] = ensureArray(this.result.response.notifications); + if (!notifications.length) return; + + ux.log(); + ux.table({ + data: notifications, + columns: [ + { key: 'messageCode', name: 'Code' }, + { key: 'messageText', name: 'Message' }, + ], + title: tableHeader('Notifications'), + overflow: 'wrap', + }); + } + private displaySuccesses(): void { const successes = this.relativeFiles.filter(isSdrSuccess); diff --git a/test/utils/output.test.ts b/test/utils/output.test.ts index ceafc332..f6cee39a 100644 --- a/test/utils/output.test.ts +++ b/test/utils/output.test.ts @@ -254,6 +254,96 @@ describe('deployResultFormatter', () => { }); }); + describe('displayNotifications', () => { + let tableStub: sinon.SinonStub; + + beforeEach(() => { + tableStub = sandbox.stub(Ux.prototype, 'table'); + }); + + it('displays notifications table when notifications array is present', () => { + const deployResult = getDeployResult('successSync', { + notifications: [ + { + messageCode: 'ApexApiVersionRetirement', + messageText: 'This deployment contains 2 Apex classes with warnings.', + }, + { + messageCode: 'ApexApiVersionRetirement', + messageText: 'This deployment contains 1 Apex triggers with warnings.', + }, + ], + }); + const formatter = new DeployResultFormatter(deployResult, { verbose: true }); + formatter.display(); + + const notificationsTableCall = tableStub.getCalls().find((call) => { + const callArg = call.args[0] as { title?: string }; + return callArg?.title?.includes('Notifications'); + }); + + expect(notificationsTableCall).to.exist; + if (notificationsTableCall) { + const tableArg = notificationsTableCall.args[0] as { + data: Array<{ messageCode: string; messageText: string }>; + }; + expect(tableArg.data).to.have.lengthOf(2); + expect(tableArg.data[0].messageCode).to.equal('ApexApiVersionRetirement'); + } + }); + + it('displays notifications table when a single notification object is present', () => { + const deployResult = getDeployResult('successSync', { + notifications: { + messageCode: 'ApexApiVersionRetirement', + messageText: 'This deployment contains 1 Apex triggers with warnings.', + }, + }); + const formatter = new DeployResultFormatter(deployResult, { verbose: true }); + formatter.display(); + + const notificationsTableCall = tableStub.getCalls().find((call) => { + const callArg = call.args[0] as { title?: string }; + return callArg?.title?.includes('Notifications'); + }); + + expect(notificationsTableCall).to.exist; + if (notificationsTableCall) { + const tableArg = notificationsTableCall.args[0] as { + data: Array<{ messageCode: string; messageText: string }>; + }; + expect(tableArg.data).to.have.lengthOf(1); + expect(tableArg.data[0].messageCode).to.equal('ApexApiVersionRetirement'); + } + }); + + it('does not display notifications table when notifications are absent', () => { + const deployResult = getDeployResult('successSync'); + const formatter = new DeployResultFormatter(deployResult, { verbose: true }); + formatter.display(); + + const notificationsTableCall = tableStub.getCalls().find((call) => { + const callArg = call.args[0] as { title?: string }; + return callArg?.title?.includes('Notifications'); + }); + + expect(notificationsTableCall).to.not.exist; + }); + + it('includes notifications in JSON output', async () => { + const notifications = [ + { + messageCode: 'ApexApiVersionRetirement', + messageText: 'This deployment contains 2 Apex classes with warnings.', + }, + ]; + const deployResult = getDeployResult('successSync', { notifications }); + const formatter = new DeployResultFormatter(deployResult, {}); + const json = await formatter.getJson(); + expect(json).to.have.property('notifications').deep.equal(notifications); + }); + }); + describe('coverage functions', () => { describe('getCoverageFormattersOptions', () => { it('clover, json', () => { From 839a13e733c112cd0447086b0d8edc94ce2c001b Mon Sep 17 00:00:00 2001 From: Willie Ruemmele Date: Wed, 26 Aug 2026 09:17:08 -0600 Subject: [PATCH 2/2] chore: bump deps, regen snapshots --- command-snapshot.json | 2 +- package.json | 6 ++-- schemas/project-delete-source.json | 39 ++++++++++++++++++++++ schemas/project-deploy-cancel.json | 39 ++++++++++++++++++++++ schemas/project-deploy-quick.json | 39 ++++++++++++++++++++++ schemas/project-deploy-report.json | 39 ++++++++++++++++++++++ schemas/project-deploy-resume.json | 39 ++++++++++++++++++++++ schemas/project-deploy-start.json | 39 ++++++++++++++++++++++ schemas/project-deploy-validate.json | 39 ++++++++++++++++++++++ yarn.lock | 50 ++++++++++++++-------------- 10 files changed, 302 insertions(+), 29 deletions(-) diff --git a/command-snapshot.json b/command-snapshot.json index 44ee8527..22cc92ed 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -286,12 +286,12 @@ "api-version", "flags-dir", "ignore-conflicts", - "root-type-with-dependencies", "json", "manifest", "metadata", "output-dir", "package-name", + "root-type-with-dependencies", "single-package", "source-dir", "target-metadata-dir", diff --git a/package.json b/package.json index 010729e3..f205705a 100644 --- a/package.json +++ b/package.json @@ -11,12 +11,12 @@ "@oclif/core": "^4.13.3", "@oclif/multi-stage-output": "^0.8.42", "@salesforce/apex-node": "^9.0.0", - "@salesforce/core": "^9.1.2", + "@salesforce/core": "^9.1.7", "@salesforce/kit": "^4.0.0", "@salesforce/plugin-info": "^4.0.0", "@salesforce/sf-plugins-core": "^13.0.0", - "@salesforce/source-deploy-retrieve": "^13.1.1", - "@salesforce/source-tracking": "^8.1.0", + "@salesforce/source-deploy-retrieve": "^13.3.0", + "@salesforce/source-tracking": "^8.1.1", "@salesforce/ts-types": "^3.0.0", "ansis": "^3.17.0", "terminal-link": "^3.0.0" diff --git a/schemas/project-delete-source.json b/schemas/project-delete-source.json index 48dc2a31..f73ed238 100644 --- a/schemas/project-delete-source.json +++ b/schemas/project-delete-source.json @@ -84,6 +84,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "deletedSource": { "type": "array", "items": { @@ -442,6 +455,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "FileResponse": { "anyOf": [ { @@ -586,6 +612,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ diff --git a/schemas/project-deploy-cancel.json b/schemas/project-deploy-cancel.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-cancel.json +++ b/schemas/project-deploy-cancel.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/schemas/project-deploy-quick.json b/schemas/project-deploy-quick.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-quick.json +++ b/schemas/project-deploy-quick.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/schemas/project-deploy-report.json b/schemas/project-deploy-report.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-report.json +++ b/schemas/project-deploy-report.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/schemas/project-deploy-resume.json b/schemas/project-deploy-resume.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-resume.json +++ b/schemas/project-deploy-resume.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/schemas/project-deploy-start.json b/schemas/project-deploy-start.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-start.json +++ b/schemas/project-deploy-start.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/schemas/project-deploy-validate.json b/schemas/project-deploy-validate.json index 58e21739..f68f1903 100644 --- a/schemas/project-deploy-validate.json +++ b/schemas/project-deploy-validate.json @@ -109,6 +109,19 @@ }, "stateDetail": { "type": "string" + }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] } }, "required": [ @@ -490,6 +503,19 @@ "required": ["id", "methodName", "name", "time"], "additionalProperties": false }, + "DeployNotification": { + "type": "object", + "properties": { + "messageCode": { + "type": "string" + }, + "messageText": { + "type": "string" + } + }, + "required": ["messageCode", "messageText"], + "additionalProperties": false + }, "AsyncDeployResultJson": { "type": "object", "additionalProperties": false, @@ -590,6 +616,19 @@ "stateDetail": { "type": "string" }, + "notifications": { + "anyOf": [ + { + "$ref": "#/definitions/DeployNotification" + }, + { + "type": "array", + "items": { + "$ref": "#/definitions/DeployNotification" + } + } + ] + }, "id": { "type": "string" }, diff --git a/yarn.lock b/yarn.lock index c1198311..c1148b99 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1097,10 +1097,10 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@jsforce/jsforce-node@^3.10.17", "@jsforce/jsforce-node@^3.10.19": - version "3.10.19" - resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.10.19.tgz#ccbc539c12f4f7dff9cfdcc6cfb8f07bd840f731" - integrity sha512-k7i2Tntu1fLvkMtRcKDFU64/Fr2M692ECtbwIGX6hcOh5mj+jrMa1tlvcdwffxAMl+lPYCXnY2bjErxWmP84zA== +"@jsforce/jsforce-node@^3.10.17", "@jsforce/jsforce-node@^3.10.19", "@jsforce/jsforce-node@^3.10.23": + version "3.10.23" + resolved "https://registry.yarnpkg.com/@jsforce/jsforce-node/-/jsforce-node-3.10.23.tgz#464be5621ee638a29d3bd43d97ade5b16470118f" + integrity sha512-sepBNb9Bt0vSdXqZqq97p/EP8NJjjOnDQoRmiH2Lcm/nsznQsrsUttoNmNxfxrRubuJTIL9m4H1I7uOb8HrgRg== dependencies: "@sindresorhus/is" "^4" base64url "^3.0.1" @@ -1374,12 +1374,12 @@ ts-retry-promise "^0.8.1" zod "^4.1.12" -"@salesforce/core@^9.0.0", "@salesforce/core@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-9.1.2.tgz#c31777c0e74407085557ef8ca15d795d10451a2e" - integrity sha512-ezAsmwfmUYSinGtJjqRfgioKXQsVdC0bc+KSJgMSLihysG6osjhkvbGtFqHwhoFs2kLu4evJRDERthsmfEW7nA== +"@salesforce/core@^9.0.0", "@salesforce/core@^9.1.4", "@salesforce/core@^9.1.7": + version "9.1.7" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-9.1.7.tgz#6084959bfab927d062cf4ef39d261b9a6e655cb0" + integrity sha512-6ecm8k3RQ5lLZ8ecv4kuY9ORpdZ5vceQO/QEDkEIfg+XbT3k10lMztX6/Q0lM6EmeNGT8xqwbdHaYvcKhCwyDQ== dependencies: - "@jsforce/jsforce-node" "^3.10.17" + "@jsforce/jsforce-node" "^3.10.23" "@salesforce/kit" "^4.0.0" "@salesforce/ts-types" "^3.0.0" ajv "^8.18.0" @@ -1548,12 +1548,12 @@ proxy-agent "^6.5.0" yaml "^2.9.0" -"@salesforce/source-deploy-retrieve@^13.1.1": - version "13.1.1" - resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-13.1.1.tgz#1f23b52f1630143b9b5770b55f5d65cef35279d1" - integrity sha512-MjA3sJrjUBIK948x+frmc+/s/Pzc/sakvilBsxXbg5ITXexcmDi25nWM1h90RXdLwS3ejT7GIZCwxlzrnDzugw== +"@salesforce/source-deploy-retrieve@^13.2.0", "@salesforce/source-deploy-retrieve@^13.3.0": + version "13.3.0" + resolved "https://registry.yarnpkg.com/@salesforce/source-deploy-retrieve/-/source-deploy-retrieve-13.3.0.tgz#2d1164233243ff40d7e760a5ceaeb837c77b9182" + integrity sha512-mwiffD4Z2VnmU9OqCZr/E7SjhkVhgRv8SkVcG3AneRiMZI2nb9DHyJZhqDm/yzDuoCCBZnxEXTBj8h6onX0QqQ== dependencies: - "@salesforce/core" "^9.0.0" + "@salesforce/core" "^9.1.7" "@salesforce/kit" "^4.0.0" "@salesforce/ts-types" "^3.0.0" "@salesforce/types" "^1.6.0" @@ -1584,15 +1584,15 @@ shelljs "^0.10.0" sinon "^10.0.0" -"@salesforce/source-tracking@^8.1.0": - version "8.1.0" - resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-8.1.0.tgz#d79b62c6fbacdfe3afcc6e3bcbaaa4d804fb47b9" - integrity sha512-ookx5YVI4ddEkNWwSkqPrXU5MKXkcuGig0rW7FzBtDnpIZU8q3+elilIJ5PI/YXBAIpUZl69Alsjh6+Bl7ZqeA== +"@salesforce/source-tracking@^8.1.1": + version "8.1.1" + resolved "https://registry.yarnpkg.com/@salesforce/source-tracking/-/source-tracking-8.1.1.tgz#4f2a7e72a48b420571d184ea440da6d1736b2b1b" + integrity sha512-ih8btm91ig3CLHiWXx52mgYF1KalFONH/AQDMMpG1L+svIiPM0HYY0k4JiT4Hulz/0f0trj0SAqcuj2aFiz9KQ== dependencies: - "@salesforce/core" "^9.1.2" + "@salesforce/core" "^9.1.4" "@salesforce/kit" "^4.0.0" - "@salesforce/source-deploy-retrieve" "^13.1.1" - "@salesforce/ts-types" "^3.0.0" + "@salesforce/source-deploy-retrieve" "^13.2.0" + "@salesforce/ts-types" "^3.0.1" fast-xml-parser "^5.5.7" graceful-fs "^4.2.11" isomorphic-git "^1.34.2" @@ -1612,10 +1612,10 @@ resolved "https://registry.yarnpkg.com/@salesforce/ts-types/-/ts-types-2.0.12.tgz#60420622812a7ec7e46d220667bc29b42dc247ff" integrity sha512-BIJyduJC18Kc8z+arUm5AZ9VkPRyw1KKAm+Tk+9LT99eOzhNilyfKzhZ4t+tG2lIGgnJpmytZfVDZ0e2kFul8g== -"@salesforce/ts-types@^3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@salesforce/ts-types/-/ts-types-3.0.0.tgz#375b13e92f594bc9d0a09716d77fc753bd31e6a6" - integrity sha512-lTCoI1NtQWiMDyn7B9N/CfWK9WmSpRnbYWqKTZYstn3mZ2DWSYwJXTh5Oh2wAAS9LAD/vaoU3F/TrV3q7wfnYw== +"@salesforce/ts-types@^3.0.0", "@salesforce/ts-types@^3.0.1": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@salesforce/ts-types/-/ts-types-3.1.0.tgz#d1255d79f01f8a64a5ce142677b8d75a157adc4c" + integrity sha512-ynzIcFj6JVL3PcbxKlHfJ9dtGQUqZvND3APrfPjUPB63FeSN+HK12pYhpJV49wrjPiig0FYFFwhmSYq2OSQoRA== "@salesforce/types@^1.6.0": version "1.8.0"