Skip to content
This repository was archived by the owner on Mar 18, 2024. It is now read-only.

Commit ac88604

Browse files
authored
feat(dependency): add support for branch in package depednencies
This feature adds support for branches in package dependency
1 parent ab3398e commit ac88604

File tree

6 files changed

+277
-26
lines changed

6 files changed

+277
-26
lines changed

packages/core/src/package/dependencies/PackageDependencyResolver.ts

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import lodash = require('lodash');
33
import Git from '../../git/Git';
44
import GitTags from '../../git/GitTags';
55
import Package2VersionFetcher, { Package2Version } from '../version/Package2VersionFetcher';
6+
import SFPLogger, { LoggerLevel } from '@dxatscale/sfp-logger';
7+
68

79
/**
810
* Resolves package dependency versions to their exact versions
@@ -39,7 +41,6 @@ export default class PackageDependencyResolver {
3941
if (this.packagesToBeResolved && !this.packagesToBeResolved.includes(packageDirectory.package)) {
4042
continue;
4143
}
42-
4344
if (packageDirectory.dependencies && Array.isArray(packageDirectory.dependencies)) {
4445
for (let i = 0; i < packageDirectory.dependencies.length; i++) {
4546
let dependency = packageDirectory.dependencies[i];
@@ -55,16 +56,45 @@ export default class PackageDependencyResolver {
5556
continue;
5657
}
5758

58-
if (this.packagesToBeSkipped && this.packagesToBeSkipped.includes(dependency.package)) {
59+
if (this.packagesToBeSkipped && this.packagesToBeSkipped.includes(dependency.package) && !dependency.branch) {
5960
// Dependency is part of the same build, will be resolved when new version is created
6061
continue;
6162
}
63+
let package2VersionForDependency: any = '';
64+
if ( dependency.branch && dependency.branch !== '' ) {
65+
SFPLogger.log(`Specified branch: ${dependency.branch} for dependency: ${dependency.package}`, LoggerLevel.INFO);
66+
package2VersionForDependency = await this.getPackage2VersionForDependency(
67+
this.conn,
68+
dependency,
69+
packageVersionId,
70+
dependency.branch
71+
);
72+
SFPLogger.log(`Fetched latest branched package of ${dependency.package},`
73+
+`version: ${package2VersionForDependency.MajorVersion}.`
74+
+`${package2VersionForDependency.MinorVersion}.`
75+
+`${package2VersionForDependency.PatchVersion}.`
76+
+`${package2VersionForDependency.BuildNumber}`, LoggerLevel.INFO);
6277

63-
const package2VersionForDependency = await this.getPackage2VersionForDependency(
64-
this.conn,
65-
dependency,
66-
packageVersionId
67-
);
78+
let branchedPackageAlias = `${dependency.package}@`
79+
+`${package2VersionForDependency.MajorVersion}.`
80+
+`${package2VersionForDependency.MinorVersion}.`
81+
+`${package2VersionForDependency.PatchVersion}.`
82+
+`${package2VersionForDependency.BuildNumber}-`
83+
+`${dependency.branch}`;
84+
dependency.package = branchedPackageAlias;
85+
this.projectConfig.packageAliases[branchedPackageAlias] = package2VersionForDependency.SubscriberPackageVersionId;
86+
delete dependency.versionNumber;
87+
delete dependency.branch;
88+
continue;
89+
90+
}else {
91+
package2VersionForDependency = await this.getPackage2VersionForDependency(
92+
this.conn,
93+
dependency,
94+
packageVersionId
95+
);
96+
}
97+
6898

6999
if (package2VersionForDependency == null) {
70100
packageDirectory.dependencies.splice(i, 1);
@@ -74,7 +104,6 @@ export default class PackageDependencyResolver {
74104
}
75105
}
76106
}
77-
78107
return this.projectConfig;
79108
}
80109

@@ -87,7 +116,8 @@ export default class PackageDependencyResolver {
87116
private async getPackage2VersionForDependency(
88117
conn: Connection,
89118
dependency: { package: string; versionNumber: string },
90-
packageVersionId: string
119+
packageVersionId: string,
120+
branch?: string,
91121
): Promise<Package2Version> {
92122

93123
//Dont hit api's if its only for external dependencies
@@ -111,11 +141,21 @@ export default class PackageDependencyResolver {
111141
);
112142
} else {
113143
const package2VersionFetcher = new Package2VersionFetcher(conn);
114-
const records = await package2VersionFetcher.fetchByPackage2Id(
115-
packageVersionId,
116-
versionNumber,
117-
true
118-
);
144+
let records;
145+
if( branch ){
146+
records = await package2VersionFetcher.fetchByPackageBranchAndName(
147+
branch,
148+
dependency.package,
149+
versionNumber
150+
);
151+
}else{
152+
records = await package2VersionFetcher.fetchByPackage2Id(
153+
packageVersionId,
154+
versionNumber,
155+
true
156+
);
157+
}
158+
119159
this.package2VersionCache.set(
120160
packageVersionId,
121161
versionNumber,
@@ -133,13 +173,12 @@ export default class PackageDependencyResolver {
133173
);
134174
}
135175

136-
if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package)) {
176+
if (this.projectConfig.packageDirectories.find((dir) => dir.package === dependency.package && !branch)) {
137177
package2Version = await this.getPackage2VersionFromCurrentBranch(package2Versions, dependency);
138178
} else {
139179
// Take last validated package for external packages
140180
package2Version = package2Versions[0];
141181
}
142-
143182
return package2Version;
144183
}
145184

packages/core/src/package/version/Package2VersionFetcher.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import semver from 'semver';
77
*/
88
export default class Package2VersionFetcher {
99
private readonly query: string =
10-
'Select SubscriberPackageVersionId, Package2Id, Package2.Name, IsPasswordProtected, IsReleased, MajorVersion, MinorVersion, PatchVersion, BuildNumber, CodeCoverage, HasPassedCodeCoverageCheck from Package2Version ';
10+
'Select SubscriberPackageVersionId, Package2Id, Package2.Name, IsPasswordProtected, IsReleased, MajorVersion, MinorVersion, PatchVersion, BuildNumber, CodeCoverage, HasPassedCodeCoverageCheck, Branch from Package2Version ';
1111

1212
constructor(private conn: Connection) {}
1313

@@ -65,6 +65,32 @@ export default class Package2VersionFetcher {
6565
const records = await QueryHelper.query<Package2Version>(query, this.conn, true);
6666
return records[0];
6767
}
68+
69+
async fetchByPackageBranchAndName(
70+
packageBranch: string,
71+
packageName: string,
72+
versionNumber?: string,
73+
): Promise<Package2Version[]> {
74+
75+
let query = this.query;
76+
77+
let whereClause: string = `where Branch='${packageBranch}' and Package2.Name ='${packageName}' `;
78+
if (versionNumber) {
79+
// TODO: validate version number
80+
const versions = versionNumber.split('.');
81+
if (versions[0]) whereClause += `and MajorVersion=${versions[0]} `;
82+
if (versions[1]) whereClause += `and MinorVersion=${versions[1]} `;
83+
if (versions[2]) whereClause += `and PatchVersion=${versions[2]} `;
84+
}
85+
query += whereClause;
86+
87+
let orderByClause: string = `order by CreatedDate desc`;
88+
query += orderByClause;
89+
90+
const records = await QueryHelper.query<Package2Version>(query, this.conn, true);
91+
return records;
92+
93+
}
6894
}
6995

7096
export interface Package2Version {
@@ -79,4 +105,5 @@ export interface Package2Version {
79105
BuildNumber: number;
80106
CodeCoverage: { apexCodeCoveragePercentage: number };
81107
HasPassedCodeCoverageCheck: boolean;
108+
Branch: string;
82109
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { expect } from '@jest/globals';
2+
import { MockTestOrgData, testSetup } from '@salesforce/core/lib/testSetup';
3+
import { Connection, AuthInfo } from '@salesforce/core';
4+
import Package2VersionFetcher from '../../src/package/version/Package2VersionFetcher';
5+
import { AnyJson } from '@salesforce/ts-types';
6+
7+
const $$ = testSetup();
8+
9+
let conn: Connection;
10+
11+
describe("Given a PackageDependencyResolver", () => {
12+
13+
beforeEach(async () => {
14+
const testData = new MockTestOrgData();
15+
16+
testData.makeDevHub();
17+
$$.setConfigStubContents('AuthInfoConfig', {
18+
contents: await testData.getConfig(),
19+
});
20+
21+
let records: any = {
22+
records: [
23+
{
24+
attributes: {
25+
type: 'Package2Version',
26+
url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNPWAA4'
27+
},
28+
SubscriberPackageVersionId: '04t5i000000V2DiAAK',
29+
Package2Id: '0Ho5i000000sYaWCAU',
30+
Package2: { attributes: [Object], Name: 'core' },
31+
IsPasswordProtected: false,
32+
IsReleased: false,
33+
MajorVersion: 0,
34+
MinorVersion: 1,
35+
PatchVersion: 0,
36+
BuildNumber: 17,
37+
CodeCoverage: { apexCodeCoveragePercentage: 100 },
38+
HasPassedCodeCoverageCheck: true,
39+
Branch: 'king'
40+
},
41+
{
42+
attributes: {
43+
type: 'Package2Version',
44+
url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNOiAAO'
45+
},
46+
SubscriberPackageVersionId: '04t5i000000UyCJAA0',
47+
Package2Id: '0Ho5i000000sYaWCAU',
48+
Package2: { attributes: [Object], Name: 'core' },
49+
IsPasswordProtected: false,
50+
IsReleased: false,
51+
MajorVersion: 0,
52+
MinorVersion: 1,
53+
PatchVersion: 0,
54+
BuildNumber: 16,
55+
CodeCoverage: { apexCodeCoveragePercentage: 100 },
56+
HasPassedCodeCoverageCheck: true,
57+
Branch: 'king'
58+
}
59+
],
60+
};
61+
$$.fakeConnectionRequest = (request: any): Promise<any> => {
62+
return Promise.resolve(records);
63+
};
64+
conn = await Connection.create({
65+
authInfo: await AuthInfo.create({username: testData.username})
66+
});
67+
})
68+
it('should return an array of Package2Version records if matching records found', async () => {
69+
// Mock the query method in QueryHelper to return some dummy data
70+
const package2VersionFetcher = new Package2VersionFetcher(conn);
71+
72+
const result = await package2VersionFetcher.fetchByPackageBranchAndName('king', 'core');
73+
74+
expect(result[0].Package2.Name).toEqual('core');
75+
expect(result[0].Branch).toEqual('king');
76+
});
77+
78+
});
79+

packages/core/tests/package/dependencies/PackageDependencyResolver.test.ts

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ const setupFakeConnection = async () => {
1111
$$.setConfigStubContents('AuthInfoConfig', {
1212
contents: await testData.getConfig(),
1313
});
14-
$$.fakeConnectionRequest = (request) => {
14+
15+
$$.fakeConnectionRequest = (request: any): Promise<any> => {
1516
return Promise.resolve(response);
16-
};
17+
};
1718

1819
const conn = await Connection.create({
1920
authInfo: await AuthInfo.create({username: testData.username})
@@ -120,6 +121,53 @@ describe("Given a PackageDependencyResolver", () => {
120121
const packageDependencyResolver = new PackageDependencyResolver(conn, falseProjectConfig, ["contact-management"]);
121122
expect(() => {return packageDependencyResolver.resolvePackageDependencyVersions()}).rejects.toThrow();
122123
});
124+
it('should return the latest branched package version id if matching records found', async () => {
125+
// Mock the query method in QueryHelper to return some dummy data
126+
response = {
127+
records: [
128+
{
129+
attributes: {
130+
type: 'Package2Version',
131+
url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNPWAA4'
132+
},
133+
SubscriberPackageVersionId: '04t5i000000V2DiAAK',
134+
Package2Id: '0Ho5i000000sYaWCAU',
135+
Package2: { attributes: [Object], Name: 'core' },
136+
IsPasswordProtected: false,
137+
IsReleased: false,
138+
MajorVersion: 0,
139+
MinorVersion: 1,
140+
PatchVersion: 0,
141+
BuildNumber: 17,
142+
CodeCoverage: { apexCodeCoveragePercentage: 100 },
143+
HasPassedCodeCoverageCheck: true,
144+
Branch: 'inspection'
145+
},
146+
{
147+
attributes: {
148+
type: 'Package2Version',
149+
url: '/services/data/v57.0/tooling/sobjects/Package2Version/05i5i000000TNOiAAO'
150+
},
151+
SubscriberPackageVersionId: '04t5i000000UyCJAA0',
152+
Package2Id: '0Ho5i000000sYaWCAU',
153+
Package2: { attributes: [Object], Name: 'core' },
154+
IsPasswordProtected: false,
155+
IsReleased: false,
156+
MajorVersion: 0,
157+
MinorVersion: 1,
158+
PatchVersion: 0,
159+
BuildNumber: 16,
160+
CodeCoverage: { apexCodeCoveragePercentage: 100 },
161+
HasPassedCodeCoverageCheck: true,
162+
Branch: 'inspection'
163+
}
164+
],
165+
};
166+
const packageDependencyResolver = new PackageDependencyResolver(conn, projectConfig, ["inspections"]);
167+
const resolvedProjectConfig = await packageDependencyResolver.resolvePackageDependencyVersions();
168+
169+
expect(resolvedProjectConfig.packageAliases['[email protected]']).toEqual('04t5i000000V2DiAAK');
170+
});
123171

124172
// TODO: test cache
125173
});
@@ -198,7 +246,7 @@ const projectConfig = {
198246
},
199247
{
200248
package: 'core',
201-
versionNumber: '1.0.0.LATEST'
249+
versionNumber: '1.0.0.LATEST',
202250
}
203251
]
204252
},
@@ -214,7 +262,21 @@ const projectConfig = {
214262
versionNumber: '1.0.0.LATEST'
215263
}
216264
]
217-
}
265+
},
266+
{
267+
path: 'packages/inspections',
268+
package: 'inspections',
269+
default: false,
270+
versionName: 'inspections-1.0.0',
271+
versionNumber: '1.0.0.NEXT',
272+
dependencies: [
273+
{
274+
package: 'core',
275+
versionNumber: '1.0.0.LATEST',
276+
branch: 'inspection'
277+
}
278+
]
279+
}
218280
],
219281
namespace: '',
220282
sfdcLoginUrl: 'https://login.salesforce.com',

packages/sfpowerscripts-cli/resources/schemas/sfdx-project.schema.json

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@
3232
"releaseNotesUrl": ["package", "versionNumber"],
3333
"versionDescription": ["package", "versionNumber"],
3434
"versionName": ["package", "versionNumber"],
35-
"versionNumber": ["package"]
35+
"versionNumber": ["package"],
36+
"branch": ["package"]
3637
},
3738
"required": ["path"],
3839
"additionalProperties": true,
@@ -141,6 +142,9 @@
141142
},
142143
"enablePicklist": {
143144
"$ref": "#/definitions/packageDirectory.enablePicklist"
145+
},
146+
"branch": {
147+
"$ref": "#/definitions/packageDirectory.branch"
144148
}
145149

146150
}
@@ -251,6 +255,9 @@
251255
},
252256
"versionNumber": {
253257
"type": "string"
258+
},
259+
"branch": {
260+
"type": "string"
254261
}
255262
}
256263
}
@@ -471,6 +478,11 @@
471478
"title": "Enable Picklist patching for Unlocked Packages",
472479
"description": "Enable automated patching of picklist for unlocked packages as unlocked packages ignore changes"
473480
},
481+
"packageDirectory.branch": {
482+
"type": "string",
483+
"title": "Package branch",
484+
"description": "branched package for the specific dev team"
485+
},
474486
"plugins.sfpowerscripts": {
475487
"type": "object",
476488
"title": "sfpowerscripts plugin configuration",

0 commit comments

Comments
 (0)