Skip to content

Commit bf9becf

Browse files
Merge pull request #974 from salesforcecli/t/packaging-distribution/W-18671326/Package-Bundle-Create-command
feat: added bundle create
2 parents c696a47 + f4061d3 commit bf9becf

5 files changed

Lines changed: 177 additions & 0 deletions

File tree

command-snapshot.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,14 @@
5555
"flags": ["api-version", "flags-dir", "json", "loglevel", "package-id", "target-org"],
5656
"plugin": "@salesforce/plugin-packaging"
5757
},
58+
{
59+
"alias": ["force:bundle:create"],
60+
"command": "package:bundles:create",
61+
"flagAliases": ["apiversion", "target-hub-org", "targetdevhubusername"],
62+
"flagChars": ["d", "n", "v"],
63+
"flags": ["api-version", "description", "flags-dir", "json", "loglevel", "name", "target-dev-hub"],
64+
"plugin": "@salesforce/plugin-packaging"
65+
},
5866
{
5967
"alias": ["force:package:convert"],
6068
"command": "package:convert",

messages/bundle_create.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# summary
2+
3+
Create a bundle.
4+
5+
# description
6+
7+
First, use this command to create a bunle. Then create a bundle version.
8+
9+
Your --name value must be unique within your namespace.
10+
11+
Run '<%= config.bin %> bundle list to list all bundles in the Dev Hub org.
12+
13+
# examples
14+
15+
- Default Use Case
16+
<%= config.bin %> <%= command.id %> --name <bundle_name> --description "<bundle_description>" --target-dev-hub <dev_hub_alias>`
17+
18+
# flags.name.summary
19+
20+
Name of the bundle to create.
21+
22+
# flags.description.summary
23+
24+
Description of the bundle
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"$ref": "#/definitions/BundleCreate",
4+
"definitions": {
5+
"BundleCreate": {
6+
"type": "object",
7+
"properties": {
8+
"Id": {
9+
"type": "string"
10+
}
11+
},
12+
"required": ["Id"],
13+
"additionalProperties": false
14+
}
15+
}
16+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { Flags, loglevel, orgApiVersionFlagWithDeprecations, SfCommand } from '@salesforce/sf-plugins-core';
9+
import { BundleCreateOptions, PackageBundle } from '@salesforce/packaging';
10+
import { Messages } from '@salesforce/core';
11+
import { requiredHubFlag } from '../../../utils/hubFlag.js';
12+
13+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
14+
const messages = Messages.loadMessages('@salesforce/plugin-packaging', 'bundle_create');
15+
export type BundleCreate = { Id: string };
16+
17+
export class PackageBundlesCreate extends SfCommand<BundleCreate> {
18+
public static readonly summary = messages.getMessage('summary');
19+
public static readonly description = messages.getMessage('description');
20+
public static readonly examples = messages.getMessages('examples');
21+
public static readonly aliases = ['force:bundle:create'];
22+
public static readonly requiresProject = true;
23+
public static readonly flags = {
24+
loglevel,
25+
name: Flags.string({
26+
char: 'n',
27+
summary: messages.getMessage('flags.name.summary'),
28+
required: true,
29+
}),
30+
description: Flags.string({
31+
char: 'd',
32+
summary: messages.getMessage('flags.description.summary'),
33+
}),
34+
'target-dev-hub': requiredHubFlag,
35+
'api-version': orgApiVersionFlagWithDeprecations,
36+
};
37+
38+
public async run(): Promise<BundleCreate> {
39+
const { flags } = await this.parse(PackageBundlesCreate);
40+
41+
const options: BundleCreateOptions = {
42+
Description: flags.description ?? '',
43+
BundleName: flags.name,
44+
};
45+
this.spinner.start(`Creating Bundle with name ${options.BundleName}`);
46+
const result = await PackageBundle.create(
47+
flags['target-dev-hub'].getConnection(flags['api-version']),
48+
this.project!,
49+
options
50+
);
51+
52+
this.spinner.stop();
53+
this.table({ data: [{ name: 'Bundle Id', value: result.Id }], title: 'Ids' });
54+
55+
return result;
56+
}
57+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2025, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import { Config } from '@oclif/core';
8+
import { TestContext, MockTestOrgData } from '@salesforce/core/testSetup';
9+
import * as sinon from 'sinon';
10+
import { expect } from 'chai';
11+
import { BundleCreateOptions, PackageBundle } from '@salesforce/packaging';
12+
import { stubSfCommandUx } from '@salesforce/sf-plugins-core';
13+
import { Connection, SfProject } from '@salesforce/core';
14+
import { PackageBundlesCreate } from '../../../src/commands/package/bundles/create.js';
15+
16+
describe('force:bundle:create - tests', () => {
17+
const $$ = new TestContext();
18+
const testOrg = new MockTestOrgData();
19+
let sfCommandStubs: ReturnType<typeof stubSfCommandUx>;
20+
let createStub: sinon.SinonStub<[Connection, SfProject, BundleCreateOptions], Promise<{ Id: string }>>;
21+
const config = new Config({ root: import.meta.url });
22+
23+
beforeEach(async () => {
24+
await $$.stubAuths(testOrg);
25+
await config.load();
26+
sfCommandStubs = stubSfCommandUx($$.SANDBOX);
27+
28+
createStub = $$.SANDBOX.stub(PackageBundle, 'create');
29+
});
30+
31+
afterEach(() => {
32+
$$.restore();
33+
});
34+
35+
it('should create a bundle', async () => {
36+
const bundleName = 'dummyPackageId';
37+
const cmd = new PackageBundlesCreate(['-v', testOrg.username, '--name', bundleName], config);
38+
39+
createStub.resolves({ Id: 'test-id' });
40+
41+
await cmd.run();
42+
43+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
44+
expect(sfCommandStubs.table.calledOnce).to.be.true;
45+
});
46+
47+
it('should throw error when name flag is missing', async () => {
48+
const cmd = new PackageBundlesCreate(['-v', testOrg.username], config);
49+
50+
createStub.resolves({ Id: 'test-id' });
51+
52+
try {
53+
await cmd.run();
54+
expect.fail('Expected error was not thrown');
55+
} catch (error) {
56+
expect((error as Error).message).to.include('Missing required flag name');
57+
}
58+
});
59+
60+
it('should throw error when test org flag is missing', async () => {
61+
const cmd = new PackageBundlesCreate(['--name', 'dummyPackageId'], config);
62+
63+
createStub.resolves({ Id: 'test-id' });
64+
65+
try {
66+
await cmd.run();
67+
expect.fail('Expected error was not thrown');
68+
} catch (error) {
69+
expect((error as Error).message).to.include('No default dev hub found');
70+
}
71+
});
72+
});

0 commit comments

Comments
 (0)