Skip to content

Commit 31ec6f0

Browse files
committed
feat: add bundle version to sfdx project
1 parent 28a2e18 commit 31ec6f0

2 files changed

Lines changed: 204 additions & 2 deletions

File tree

src/package/packageBundleVersion.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import { Connection, Lifecycle, Messages, PollingClient, SfError, StatusResult } from '@salesforce/core';
1818
import { SfProject } from '@salesforce/core';
19-
import { Duration } from '@salesforce/kit';
19+
import { Duration, env } from '@salesforce/kit';
2020
import { Schema } from '@jsforce/jsforce-node';
2121
import {
2222
BundleVersionCreateOptions,
@@ -45,7 +45,7 @@ export class PackageBundleVersion {
4545
);
4646

4747
if (options.polling) {
48-
return PackageBundleVersion.pollCreateStatus(createResult.Id, options.connection, options.project, options.polling).catch(
48+
const finalResult = await PackageBundleVersion.pollCreateStatus(createResult.Id, options.connection, options.project, options.polling).catch(
4949
(error: SfError) => {
5050
if (error.name === 'PollingClientTimeout') {
5151
const modifiedError = new SfError(error.message);
@@ -56,6 +56,20 @@ export class PackageBundleVersion {
5656
throw applyErrorAction(massageErrorMessage(error));
5757
}
5858
);
59+
60+
// Add bundle version alias to sfdx-project.json after successful creation
61+
if (finalResult.RequestStatus === BundleSObjects.PkgBundleVersionCreateReqStatus.success && finalResult.PackageBundleVersionId) {
62+
await PackageBundleVersion.addBundleVersionAlias(options.project, finalResult);
63+
}
64+
65+
return finalResult;
66+
}
67+
68+
// Add bundle version alias to sfdx-project.json after successful creation (non-polling case)
69+
// Note: In the non-polling case, the bundle version may not be created yet (status is 'Queued' or 'InProgress')
70+
// So we only add the alias if the status is already 'Success'
71+
if (createResult.RequestStatus === BundleSObjects.PkgBundleVersionCreateReqStatus.success && createResult.PackageBundleVersionId) {
72+
await PackageBundleVersion.addBundleVersionAlias(options.project, createResult);
5973
}
6074

6175
return createResult;
@@ -309,4 +323,33 @@ export class PackageBundleVersion {
309323
SystemModstamp: packageBundle?.SystemModstamp ?? '',
310324
};
311325
}
326+
327+
/**
328+
* Add a bundle version alias to the sfdx-project.json file after successful bundle version creation.
329+
* Creates an alias in the format: <BundleName>@<MajorVersion>.<MinorVersion>
330+
*
331+
* @param project The SfProject instance
332+
* @param result The bundle version create result containing bundle information
333+
*/
334+
private static async addBundleVersionAlias(
335+
project: SfProject,
336+
result: BundleSObjects.PackageBundleVersionCreateRequestResult
337+
): Promise<void> {
338+
// Skip if auto-update is disabled
339+
if (env.getBoolean('SF_PROJECT_AUTOUPDATE_DISABLE_FOR_PACKAGE_CREATE')) {
340+
return;
341+
}
342+
343+
// Ensure we have the necessary information to create the alias
344+
if (!result.PackageBundleVersionId || !result.VersionName || !result.MajorVersion || !result.MinorVersion) {
345+
return;
346+
}
347+
348+
// Create alias in format: BundleName@MajorVersion.MinorVersion
349+
const alias = `${result.VersionName}@${result.MajorVersion}.${result.MinorVersion}`;
350+
351+
// Add the alias to the sfdx-project.json file
352+
project.getSfProjectJson().addPackageBundleAlias(alias, result.PackageBundleVersionId);
353+
await project.getSfProjectJson().write();
354+
}
312355
}

test/package/bundleVersionCreate.test.ts

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -843,5 +843,164 @@ describe('PackageBundleVersion.create', () => {
843843
`SELECT BundleName FROM PackageBundle WHERE Id = '${testBundleId}'`
844844
);
845845
});
846+
847+
it('should add bundle version alias to sfdx-project.json after successful creation with polling', async () => {
848+
const componentsPath = path.join(project.getPath(), 'bundle-components.json');
849+
const components = [{ packageVersion: 'pkgA@1.1' }];
850+
fs.writeFileSync(componentsPath, JSON.stringify(components));
851+
852+
// Mock the connection for polling scenario
853+
let callCount = 0;
854+
Object.assign(connection.tooling, {
855+
sobject: () => ({
856+
create: () =>
857+
Promise.resolve({
858+
success: true,
859+
id: '0Ho000000000000',
860+
}),
861+
}),
862+
query: () =>
863+
Promise.resolve({
864+
records: [{ BundleName: 'MyTestBundle' }],
865+
}),
866+
});
867+
868+
// Mock autoFetchQuery for getCreateStatus
869+
Object.assign(connection, {
870+
autoFetchQuery: () => {
871+
callCount++;
872+
if (callCount === 1) {
873+
return Promise.resolve({
874+
records: [
875+
{
876+
Id: '0Ho000000000000',
877+
RequestStatus: BundleSObjects.PkgBundleVersionCreateReqStatus.queued,
878+
PackageBundle: { Id: '1Fl000000000001' },
879+
PackageBundleVersion: { Id: '' },
880+
VersionName: 'MyTestBundle',
881+
MajorVersion: '1',
882+
MinorVersion: '0',
883+
BundleVersionComponents: JSON.stringify(['04t000000000001']),
884+
CreatedDate: new Date().toISOString(),
885+
CreatedById: 'testUser',
886+
ValidationError: '',
887+
},
888+
],
889+
});
890+
} else {
891+
return Promise.resolve({
892+
records: [
893+
{
894+
Id: '0Ho000000000000',
895+
RequestStatus: BundleSObjects.PkgBundleVersionCreateReqStatus.success,
896+
PackageBundle: { Id: '1Fl000000000001' },
897+
PackageBundleVersion: { Id: '1Q8000000000001' },
898+
VersionName: 'MyTestBundle',
899+
MajorVersion: '1',
900+
MinorVersion: '0',
901+
BundleVersionComponents: JSON.stringify(['04t000000000001']),
902+
CreatedDate: new Date().toISOString(),
903+
CreatedById: 'testUser',
904+
ValidationError: '',
905+
},
906+
],
907+
});
908+
}
909+
},
910+
});
911+
912+
const options: BundleVersionCreateOptions = {
913+
connection,
914+
project,
915+
PackageBundle: 'MyTestBundle',
916+
MajorVersion: '1',
917+
MinorVersion: '0',
918+
Ancestor: null,
919+
BundleVersionComponentsPath: componentsPath,
920+
polling: {
921+
timeout: Duration.seconds(10),
922+
frequency: Duration.seconds(1),
923+
},
924+
};
925+
926+
const result = await PackageBundleVersion.create(options);
927+
928+
// Verify the result is successful
929+
expect(result).to.have.property('RequestStatus', BundleSObjects.PkgBundleVersionCreateReqStatus.success);
930+
expect(result).to.have.property('PackageBundleVersionId', '1Q8000000000001');
931+
932+
// Verify that the bundle version alias was added to sfdx-project.json
933+
const packageBundleAliases = project.getSfProjectJson().getPackageBundleAliases();
934+
expect(packageBundleAliases).to.have.property('MyTestBundle@1.0', '1Q8000000000001');
935+
936+
// Clean up
937+
fs.unlinkSync(componentsPath);
938+
});
939+
940+
it('should add bundle version alias to sfdx-project.json after successful creation without polling', async () => {
941+
const componentsPath = path.join(project.getPath(), 'bundle-components.json');
942+
const components = [{ packageVersion: 'pkgA@1.1' }];
943+
fs.writeFileSync(componentsPath, JSON.stringify(components));
944+
945+
// Mock the connection for immediate success
946+
Object.assign(connection.tooling, {
947+
sobject: () => ({
948+
create: () =>
949+
Promise.resolve({
950+
success: true,
951+
id: '0Ho000000000000',
952+
}),
953+
}),
954+
query: () =>
955+
Promise.resolve({
956+
records: [{ BundleName: 'AnotherTestBundle' }],
957+
}),
958+
});
959+
960+
// Mock autoFetchQuery for getCreateStatus - immediate success
961+
Object.assign(connection, {
962+
autoFetchQuery: () =>
963+
Promise.resolve({
964+
records: [
965+
{
966+
Id: '0Ho000000000000',
967+
RequestStatus: BundleSObjects.PkgBundleVersionCreateReqStatus.success,
968+
PackageBundle: { Id: '1Fl000000000002' },
969+
PackageBundleVersion: { Id: '1Q8000000000002' },
970+
VersionName: 'AnotherTestBundle',
971+
MajorVersion: '2',
972+
MinorVersion: '3',
973+
BundleVersionComponents: JSON.stringify(['04t000000000001']),
974+
CreatedDate: new Date().toISOString(),
975+
CreatedById: 'testUser',
976+
ValidationError: '',
977+
},
978+
],
979+
}),
980+
});
981+
982+
const options: BundleVersionCreateOptions = {
983+
connection,
984+
project,
985+
PackageBundle: 'AnotherTestBundle',
986+
MajorVersion: '2',
987+
MinorVersion: '3',
988+
Ancestor: null,
989+
BundleVersionComponentsPath: componentsPath,
990+
};
991+
992+
const result = await PackageBundleVersion.create(options);
993+
994+
// Verify the result is successful
995+
expect(result).to.have.property('RequestStatus', BundleSObjects.PkgBundleVersionCreateReqStatus.success);
996+
expect(result).to.have.property('PackageBundleVersionId', '1Q8000000000002');
997+
998+
// Verify that the bundle version alias was added to sfdx-project.json
999+
const packageBundleAliases = project.getSfProjectJson().getPackageBundleAliases();
1000+
expect(packageBundleAliases).to.have.property('AnotherTestBundle@2.3', '1Q8000000000002');
1001+
1002+
// Clean up
1003+
fs.unlinkSync(componentsPath);
1004+
});
8461005
});
8471006
});

0 commit comments

Comments
 (0)