From 7d09db0abb1ed370993f770f1f70d2625eca7f92 Mon Sep 17 00:00:00 2001 From: Dawson David Date: Wed, 11 Jun 2025 15:41:17 -0600 Subject: [PATCH 1/3] feat: created bundle list command near copy and paste of package list command unit test based off of bundle create --- command-snapshot.json | 8 ++++ messages/bundle_list.md | 61 ++++++++++++++++++++++++ schemas/package-bundles-list.json | 58 +++++++++++++++++++++++ src/commands/package/bundles/list.ts | 63 +++++++++++++++++++++++++ test/commands/bundle/bundleList.test.ts | 56 ++++++++++++++++++++++ 5 files changed, 246 insertions(+) create mode 100644 messages/bundle_list.md create mode 100644 schemas/package-bundles-list.json create mode 100644 src/commands/package/bundles/list.ts create mode 100644 test/commands/bundle/bundleList.test.ts diff --git a/command-snapshot.json b/command-snapshot.json index c0d0b8db..0f72226a 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -63,6 +63,14 @@ "flags": ["api-version", "description", "flags-dir", "json", "loglevel", "name", "target-dev-hub"], "plugin": "@salesforce/plugin-packaging" }, + { + "alias": ["force:bundle:list"], + "command": "package:bundles:list", + "flagAliases": ["apiversion", "target-hub-org", "targetdevhubusername"], + "flagChars": ["v"], + "flags": ["api-version", "flags-dir", "json", "loglevel", "target-dev-hub", "verbose"], + "plugin": "@salesforce/plugin-packaging" + }, { "alias": ["force:package:convert"], "command": "package:convert", diff --git a/messages/bundle_list.md b/messages/bundle_list.md new file mode 100644 index 00000000..31784e0a --- /dev/null +++ b/messages/bundle_list.md @@ -0,0 +1,61 @@ +# summary + +List all bundles in the Dev Hub org. + +# description + +You can view the namespace, IDs, and other details for each bundle. + +# examples + +- List all bundles in the specified Dev Hub org: + + <%= config.bin %> <%= command.id %> --target-dev-hub devhub@example.com + +- List all bundles details in the specified Dev Hub org, and show extended details about each bundle: + + <%= config.bin %> <%= command.id %> --target-dev-hub devhub@example.com --verbose + +# namespace + +Namespace Prefix + +# name + +Name + +# id + +Id + +# bundle-id + +Subscriber bundle Id + +# alias + +Alias + +# description + +Description + +# flags.verbose.summary + +Display extended bundle detail. + +# convertedFrombundleId + +Converted From bundle Id + +# isOrgDependent + +Org-Dependent Unlocked bundle + +# error-notification-username + +Error Notification Username + +# createdBy + +Created By diff --git a/schemas/package-bundles-list.json b/schemas/package-bundles-list.json new file mode 100644 index 00000000..ca27992a --- /dev/null +++ b/schemas/package-bundles-list.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/BundleListCommandResults", + "definitions": { + "BundleListCommandResults": { + "type": "array", + "items": { + "$ref": "#/definitions/BundleListCommandResult" + } + }, + "BundleListCommandResult": { + "$ref": "#/definitions/BundleSObjects.Bundle" + }, + "BundleSObjects.Bundle": { + "type": "object", + "properties": { + "BundleName": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "Id": { + "type": "string" + }, + "IsDeleted": { + "type": "boolean" + }, + "CreatedDate": { + "type": "string" + }, + "CreatedById": { + "type": "string" + }, + "LastModifiedDate": { + "type": "string" + }, + "LastModifiedById": { + "type": "string" + }, + "SystemModstamp": { + "type": "string" + } + }, + "required": [ + "BundleName", + "Id", + "IsDeleted", + "CreatedDate", + "CreatedById", + "LastModifiedDate", + "LastModifiedById", + "SystemModstamp" + ], + "additionalProperties": false + } + } +} diff --git a/src/commands/package/bundles/list.ts b/src/commands/package/bundles/list.ts new file mode 100644 index 00000000..b0864970 --- /dev/null +++ b/src/commands/package/bundles/list.ts @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2023, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ + +import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core'; +import { Messages } from '@salesforce/core/messages'; +import { PackageBundle, BundleSObjects } from '@salesforce/packaging'; +import chalk from 'chalk'; +import { requiredHubFlag } from '../../../utils/hubFlag.js'; + +// This is a near copy of the package list command, but with the package bundle class and messages. +// If you are looking to copy this command, mabye make an abstract class for the commands that are similar.(please) +Messages.importMessagesDirectoryFromMetaUrl(import.meta.url); +const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'bundle_list'); + +export type BundleListCommandResult = BundleSObjects.Bundle; +export type BundleListCommandResults = BundleListCommandResult[]; + +export class BundleListCommand extends SfCommand { + public static readonly summary = messages.getMessage('summary'); + public static readonly description = messages.getMessage('description'); + public static readonly examples = messages.getMessages('examples'); + public static readonly deprecateAliases = true; + public static readonly aliases = ['force:bundle:list']; + public static readonly flags = { + loglevel, + 'target-dev-hub': requiredHubFlag, + 'api-version': orgApiVersionFlagWithDeprecations, + verbose: Flags.boolean({ + summary: messages.getMessage('flags.verbose.summary'), + }), + }; + + public async run(): Promise { + const { flags } = await this.parse(BundleListCommand); + const connection = flags['target-dev-hub'].getConnection(flags['api-version']); + const results = await PackageBundle.list(connection); + this.displayResults(results, flags.verbose); + return results; + } + + private displayResults(results: BundleListCommandResults, verbose = false): void { + const data = results.map((r) => ({ + 'Bundle Name': r.BundleName, + Id: r.Id, + Description: r.Description, + ...(verbose + ? { + 'Created Date': r.CreatedDate, + 'Created By': r.CreatedById, + 'Last Modified Date': r.LastModifiedDate, + 'Last Modified By': r.LastModifiedById, + 'System Modstamp': r.SystemModstamp, + 'Is Deleted': r.IsDeleted, + } + : {}), + })); + this.table({ data, title: chalk.blue(`Package Bundles [${results.length}]`) }); + } +} diff --git a/test/commands/bundle/bundleList.test.ts b/test/commands/bundle/bundleList.test.ts new file mode 100644 index 00000000..e99b9233 --- /dev/null +++ b/test/commands/bundle/bundleList.test.ts @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2025, salesforce.com, inc. + * All rights reserved. + * Licensed under the BSD 3-Clause license. + * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause + */ +import { Config } from '@oclif/core'; +import { TestContext, MockTestOrgData } from '@salesforce/core/testSetup'; +import { expect } from 'chai'; +import { PackageBundle } from '@salesforce/packaging'; +import { stubSfCommandUx } from '@salesforce/sf-plugins-core'; +import sinon from 'sinon'; +import { BundleListCommand } from '../../../src/commands/package/bundles/list.js'; +describe('force:bundle:list - tests', () => { + const $$ = new TestContext(); + const testOrg = new MockTestOrgData(); + let sfCommandStubs: ReturnType; + let listStub: sinon.SinonStub; + const config = new Config({ root: import.meta.url }); + + beforeEach(async () => { + await $$.stubAuths(testOrg); + await config.load(); + sfCommandStubs = stubSfCommandUx($$.SANDBOX); + + listStub = $$.SANDBOX.stub(PackageBundle, 'list'); + }); + + afterEach(() => { + $$.restore(); + }); + + it('should list a bundle', async () => { + const cmd = new BundleListCommand(['-v', testOrg.username], config); + + listStub.resolves([{ BundleName: 'test-bundle', Id: 'test-id', Description: 'test-description' }]); + + await cmd.run(); + + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(sfCommandStubs.table.calledOnce).to.be.true; + }); + + it('should throw error when test org flag is missing', async () => { + const cmd = new BundleListCommand([], config); + + listStub.resolves([{ BundleName: 'test-bundle', Id: 'test-id', Description: 'test-description' }]); + + try { + await cmd.run(); + expect.fail('Expected error was not thrown'); + } catch (error) { + expect((error as Error).message).to.include('No default dev hub found'); + } + }); +}); From 3147864a82c5a4a3e74fce62e9c7512bb99e310b Mon Sep 17 00:00:00 2001 From: Dawson David Date: Fri, 13 Jun 2025 09:50:14 -0600 Subject: [PATCH 2/3] refactor: cahnged folder name for proper command changed bundles to bundle in order to generate proper command --- command-snapshot.json | 8 +-- messages/bundle_list.md | 8 --- schemas/package-bundle-create.json | 16 +++++ schemas/package-bundle-list.json | 58 +++++++++++++++++++ .../package/{bundles => bundle}/create.ts | 2 +- .../package/{bundles => bundle}/list.ts | 2 +- test/commands/bundle/bundleCreate.test.ts | 2 +- test/commands/bundle/bundleList.test.ts | 2 +- 8 files changed, 82 insertions(+), 16 deletions(-) create mode 100644 schemas/package-bundle-create.json create mode 100644 schemas/package-bundle-list.json rename src/commands/package/{bundles => bundle}/create.ts (96%) rename src/commands/package/{bundles => bundle}/list.ts (97%) diff --git a/command-snapshot.json b/command-snapshot.json index 0f72226a..e20e55e2 100644 --- a/command-snapshot.json +++ b/command-snapshot.json @@ -56,16 +56,16 @@ "plugin": "@salesforce/plugin-packaging" }, { - "alias": ["force:bundle:create"], - "command": "package:bundles:create", + "alias": ["force:package:bundle:create"], + "command": "package:bundle:create", "flagAliases": ["apiversion", "target-hub-org", "targetdevhubusername"], "flagChars": ["d", "n", "v"], "flags": ["api-version", "description", "flags-dir", "json", "loglevel", "name", "target-dev-hub"], "plugin": "@salesforce/plugin-packaging" }, { - "alias": ["force:bundle:list"], - "command": "package:bundles:list", + "alias": ["force:package:bundle:list"], + "command": "package:bundle:list", "flagAliases": ["apiversion", "target-hub-org", "targetdevhubusername"], "flagChars": ["v"], "flags": ["api-version", "flags-dir", "json", "loglevel", "target-dev-hub", "verbose"], diff --git a/messages/bundle_list.md b/messages/bundle_list.md index 31784e0a..98b3b5d2 100644 --- a/messages/bundle_list.md +++ b/messages/bundle_list.md @@ -44,14 +44,6 @@ Description Display extended bundle detail. -# convertedFrombundleId - -Converted From bundle Id - -# isOrgDependent - -Org-Dependent Unlocked bundle - # error-notification-username Error Notification Username diff --git a/schemas/package-bundle-create.json b/schemas/package-bundle-create.json new file mode 100644 index 00000000..2049f257 --- /dev/null +++ b/schemas/package-bundle-create.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/BundleCreate", + "definitions": { + "BundleCreate": { + "type": "object", + "properties": { + "Id": { + "type": "string" + } + }, + "required": ["Id"], + "additionalProperties": false + } + } +} diff --git a/schemas/package-bundle-list.json b/schemas/package-bundle-list.json new file mode 100644 index 00000000..ca27992a --- /dev/null +++ b/schemas/package-bundle-list.json @@ -0,0 +1,58 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$ref": "#/definitions/BundleListCommandResults", + "definitions": { + "BundleListCommandResults": { + "type": "array", + "items": { + "$ref": "#/definitions/BundleListCommandResult" + } + }, + "BundleListCommandResult": { + "$ref": "#/definitions/BundleSObjects.Bundle" + }, + "BundleSObjects.Bundle": { + "type": "object", + "properties": { + "BundleName": { + "type": "string" + }, + "Description": { + "type": "string" + }, + "Id": { + "type": "string" + }, + "IsDeleted": { + "type": "boolean" + }, + "CreatedDate": { + "type": "string" + }, + "CreatedById": { + "type": "string" + }, + "LastModifiedDate": { + "type": "string" + }, + "LastModifiedById": { + "type": "string" + }, + "SystemModstamp": { + "type": "string" + } + }, + "required": [ + "BundleName", + "Id", + "IsDeleted", + "CreatedDate", + "CreatedById", + "LastModifiedDate", + "LastModifiedById", + "SystemModstamp" + ], + "additionalProperties": false + } + } +} diff --git a/src/commands/package/bundles/create.ts b/src/commands/package/bundle/create.ts similarity index 96% rename from src/commands/package/bundles/create.ts rename to src/commands/package/bundle/create.ts index 42dab556..d7a837ae 100644 --- a/src/commands/package/bundles/create.ts +++ b/src/commands/package/bundle/create.ts @@ -18,7 +18,7 @@ export class PackageBundlesCreate extends SfCommand { public static readonly summary = messages.getMessage('summary'); public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); - public static readonly aliases = ['force:bundle:create']; + public static readonly aliases = ['force:package:bundle:create']; public static readonly requiresProject = true; public static readonly flags = { loglevel, diff --git a/src/commands/package/bundles/list.ts b/src/commands/package/bundle/list.ts similarity index 97% rename from src/commands/package/bundles/list.ts rename to src/commands/package/bundle/list.ts index b0864970..99196fff 100644 --- a/src/commands/package/bundles/list.ts +++ b/src/commands/package/bundle/list.ts @@ -24,7 +24,7 @@ export class BundleListCommand extends SfCommand { public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); public static readonly deprecateAliases = true; - public static readonly aliases = ['force:bundle:list']; + public static readonly aliases = ['force:package:bundle:list']; public static readonly flags = { loglevel, 'target-dev-hub': requiredHubFlag, diff --git a/test/commands/bundle/bundleCreate.test.ts b/test/commands/bundle/bundleCreate.test.ts index fcdf8402..3d91c07a 100644 --- a/test/commands/bundle/bundleCreate.test.ts +++ b/test/commands/bundle/bundleCreate.test.ts @@ -11,7 +11,7 @@ import { expect } from 'chai'; import { BundleCreateOptions, PackageBundle } from '@salesforce/packaging'; import { stubSfCommandUx } from '@salesforce/sf-plugins-core'; import { Connection, SfProject } from '@salesforce/core'; -import { PackageBundlesCreate } from '../../../src/commands/package/bundles/create.js'; +import { PackageBundlesCreate } from '../../../src/commands/package/bundle/create.js'; describe('force:bundle:create - tests', () => { const $$ = new TestContext(); diff --git a/test/commands/bundle/bundleList.test.ts b/test/commands/bundle/bundleList.test.ts index e99b9233..128336bf 100644 --- a/test/commands/bundle/bundleList.test.ts +++ b/test/commands/bundle/bundleList.test.ts @@ -10,7 +10,7 @@ import { expect } from 'chai'; import { PackageBundle } from '@salesforce/packaging'; import { stubSfCommandUx } from '@salesforce/sf-plugins-core'; import sinon from 'sinon'; -import { BundleListCommand } from '../../../src/commands/package/bundles/list.js'; +import { BundleListCommand } from '../../../src/commands/package/bundle/list.js'; describe('force:bundle:list - tests', () => { const $$ = new TestContext(); const testOrg = new MockTestOrgData(); From 74a16a03b4774eadf89d763cec54194df7e0d6b6 Mon Sep 17 00:00:00 2001 From: Dawson David Date: Fri, 13 Jun 2025 11:58:51 -0600 Subject: [PATCH 3/3] docs: renamed bundles -> bundle additionally updates messages --- messages/bundle_create.md | 18 ++++----- messages/bundle_list.md | 16 ++------ schemas/package-bundles-create.json | 16 -------- schemas/package-bundles-list.json | 58 ----------------------------- src/commands/package/bundle/list.ts | 3 +- 5 files changed, 13 insertions(+), 98 deletions(-) delete mode 100644 schemas/package-bundles-create.json delete mode 100644 schemas/package-bundles-list.json diff --git a/messages/bundle_create.md b/messages/bundle_create.md index be02d894..3ffdb7a9 100644 --- a/messages/bundle_create.md +++ b/messages/bundle_create.md @@ -1,24 +1,22 @@ # summary -Create a bundle. +Create a package bundle in the Dev Hub org. # description -First, use this command to create a bunle. Then create a bundle version. - -Your --name value must be unique within your namespace. - -Run '<%= config.bin %> bundle list to list all bundles in the Dev Hub org. +A package bundle is an artifact that contains one or more 2GP managed packages. +A bundle can be listed on AppExchange, installed, or upgraded as a single artifact. # examples -- Default Use Case - <%= config.bin %> <%= command.id %> --name --description "" --target-dev-hub ` +Create a package bundle in the Dev Hub org; uses the Dev Hub org with the username devhub@example.com: + +sf package bundle create --name “Your bundle name” --description "Your bundle description" --target-dev-hub devhub@example.com # flags.name.summary -Name of the bundle to create. +Name of the package bundle. # flags.description.summary -Description of the bundle +Description of the package bundle. diff --git a/messages/bundle_list.md b/messages/bundle_list.md index 98b3b5d2..26bd0921 100644 --- a/messages/bundle_list.md +++ b/messages/bundle_list.md @@ -1,20 +1,12 @@ # summary -List all bundles in the Dev Hub org. - -# description - -You can view the namespace, IDs, and other details for each bundle. +List all package bundles in the Dev Hub org. # examples -- List all bundles in the specified Dev Hub org: - - <%= config.bin %> <%= command.id %> --target-dev-hub devhub@example.com - -- List all bundles details in the specified Dev Hub org, and show extended details about each bundle: +List all package bundles in the specified Dev Hub org; uses the Dev Hub org with the username devhub@example.com: - <%= config.bin %> <%= command.id %> --target-dev-hub devhub@example.com --verbose +sf package bundle list --target-dev-hub # namespace @@ -30,7 +22,7 @@ Id # bundle-id -Subscriber bundle Id +Package Bundle Id # alias diff --git a/schemas/package-bundles-create.json b/schemas/package-bundles-create.json deleted file mode 100644 index 2049f257..00000000 --- a/schemas/package-bundles-create.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/BundleCreate", - "definitions": { - "BundleCreate": { - "type": "object", - "properties": { - "Id": { - "type": "string" - } - }, - "required": ["Id"], - "additionalProperties": false - } - } -} diff --git a/schemas/package-bundles-list.json b/schemas/package-bundles-list.json deleted file mode 100644 index ca27992a..00000000 --- a/schemas/package-bundles-list.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "$ref": "#/definitions/BundleListCommandResults", - "definitions": { - "BundleListCommandResults": { - "type": "array", - "items": { - "$ref": "#/definitions/BundleListCommandResult" - } - }, - "BundleListCommandResult": { - "$ref": "#/definitions/BundleSObjects.Bundle" - }, - "BundleSObjects.Bundle": { - "type": "object", - "properties": { - "BundleName": { - "type": "string" - }, - "Description": { - "type": "string" - }, - "Id": { - "type": "string" - }, - "IsDeleted": { - "type": "boolean" - }, - "CreatedDate": { - "type": "string" - }, - "CreatedById": { - "type": "string" - }, - "LastModifiedDate": { - "type": "string" - }, - "LastModifiedById": { - "type": "string" - }, - "SystemModstamp": { - "type": "string" - } - }, - "required": [ - "BundleName", - "Id", - "IsDeleted", - "CreatedDate", - "CreatedById", - "LastModifiedDate", - "LastModifiedById", - "SystemModstamp" - ], - "additionalProperties": false - } - } -} diff --git a/src/commands/package/bundle/list.ts b/src/commands/package/bundle/list.ts index 99196fff..33b53046 100644 --- a/src/commands/package/bundle/list.ts +++ b/src/commands/package/bundle/list.ts @@ -21,7 +21,6 @@ export type BundleListCommandResults = BundleListCommandResult[]; export class BundleListCommand extends SfCommand { public static readonly summary = messages.getMessage('summary'); - public static readonly description = messages.getMessage('description'); public static readonly examples = messages.getMessages('examples'); public static readonly deprecateAliases = true; public static readonly aliases = ['force:package:bundle:list']; @@ -44,7 +43,7 @@ export class BundleListCommand extends SfCommand { private displayResults(results: BundleListCommandResults, verbose = false): void { const data = results.map((r) => ({ - 'Bundle Name': r.BundleName, + 'Package Bundle Name': r.BundleName, Id: r.Id, Description: r.Description, ...(verbose