Skip to content

Commit 40b7e57

Browse files
authored
Merge pull request #887 from forcedotcom/wr/fixScopeProfilesExcludeGlob
fix: scopeProfiles excludes profiles correctly with ./-prefixed paths @W-22149938@
2 parents 3ad2c3a + 43e9b55 commit 40b7e57

5 files changed

Lines changed: 93 additions & 4 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
],
4444
"dependencies": {
4545
"@jsforce/jsforce-node": "^3.10.16",
46-
"@salesforce/core": "^8.31.1",
46+
"@salesforce/core": "^8.31.2",
4747
"@salesforce/kit": "^3.2.6",
4848
"@salesforce/schemas": "^1.10.3",
4949
"@salesforce/source-deploy-retrieve": "^12.36.2",

src/package/packageProfileApi.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,10 @@ export class PackageProfileApi extends AsyncCreatable<ProfileApiOptions> {
123123

124124
// Look for profiles in all package directories
125125
private findAllProfiles(excludedDirectories: string[] = []): string[] {
126-
const ignore = excludedDirectories.map((dir) => `**/${dir.split(path.sep).join(path.posix.sep)}/**`);
126+
const ignore = excludedDirectories.map((dir) => {
127+
const normalized = path.normalize(dir).split(path.sep).filter(Boolean).join(path.posix.sep);
128+
return `**/${normalized}/**`;
129+
});
127130
const patterns = this.project
128131
.getUniquePackageDirectories()
129132
.map((pDir) => pDir.fullPath)

src/package/packageVersionCreate.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -587,9 +587,10 @@ export class PackageVersionCreate {
587587
profileExcludeDirs
588588
);
589589

590+
const excludedProfileNames = excludedProfiles?.map((p) => path.basename(p));
590591
packageXmlAsJson.types = typesArr.map((type) => {
591592
if (type.name !== 'Profile') return type;
592-
return { ...type, members: type.members.filter((m) => !excludedProfiles?.includes(m)) };
593+
return { ...type, members: type.members.filter((m) => !excludedProfileNames?.includes(m)) };
593594
});
594595

595596
const xml = packageXmlJsonToXmlString(packageXmlAsJson);

test/package/packageVersionCreate.test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,6 +836,66 @@ describe('Package Version Create', () => {
836836
expect(pkgTypeMembers[1].members).to.deep.equal(['Test Profile']);
837837
});
838838

839+
it('should include profiles from all package directories when scopeProfiles is false', async () => {
840+
project.getSfProjectJson().set('packageDirectories', [
841+
{ path: './src-access-management', package: 'ACCESS', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT' },
842+
{ path: 'force-app', package: 'TEST', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT', default: true },
843+
]);
844+
await project.getSfProjectJson().write();
845+
846+
const siblingDir = path.join(project.getPath(), 'src-access-management');
847+
await fs.promises.mkdir(siblingDir, { recursive: true });
848+
const fileContents = '<?xml version="1.0" encoding="UTF-8"?>';
849+
await fs.promises.writeFile(path.join(siblingDir, 'Sibling Profile.profile-meta.xml'), fileContents);
850+
await fs.promises.writeFile(
851+
path.join(project.getPath(), 'force-app', 'Target Profile.profile-meta.xml'),
852+
fileContents
853+
);
854+
855+
const pkgProfileApi = await PackageProfileApi.create({ project, includeUserLicenses: false });
856+
const types = [
857+
{ name: 'Layout', members: ['Test Layout'] },
858+
{ name: 'Profile', members: ['Sibling Profile', 'Target Profile'] },
859+
];
860+
861+
// With no excludedDirectories (scopeProfiles=false), all profiles from all dirs are included
862+
const pkgTypeMembers = pkgProfileApi.filterAndGenerateProfilesForManifest(types);
863+
expect(pkgTypeMembers.find((t) => t.name === 'Profile')?.members).to.deep.equal([
864+
'Sibling Profile',
865+
'Target Profile',
866+
]);
867+
});
868+
869+
it('should exclude profiles from ./-prefixed sibling package directories when scopeProfiles is true', async () => {
870+
// Configure a multi-directory project where sibling uses ./ prefix in path
871+
project.getSfProjectJson().set('packageDirectories', [
872+
{ path: './src-access-management', package: 'ACCESS', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT' },
873+
{ path: 'force-app', package: 'TEST', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT', default: true },
874+
]);
875+
await project.getSfProjectJson().write();
876+
877+
const siblingDir = path.join(project.getPath(), 'src-access-management');
878+
await fs.promises.mkdir(siblingDir, { recursive: true });
879+
const fileContents = '<?xml version="1.0" encoding="UTF-8"?>';
880+
await fs.promises.writeFile(path.join(siblingDir, 'Sibling Profile.profile-meta.xml'), fileContents);
881+
await fs.promises.writeFile(
882+
path.join(project.getPath(), 'force-app', 'Target Profile.profile-meta.xml'),
883+
fileContents
884+
);
885+
886+
const pkgProfileApi = await PackageProfileApi.create({ project, includeUserLicenses: false });
887+
const types = [
888+
{ name: 'Layout', members: ['Test Layout'] },
889+
{ name: 'Profile', members: ['Sibling Profile', 'Target Profile'] },
890+
];
891+
892+
// When scopeProfiles is true, packageVersionCreate passes sibling package dir paths
893+
// as excludedDirectories. These retain the ./ prefix from sfdx-project.json.
894+
const excludedDirectories = ['./src-access-management'];
895+
const pkgTypeMembers = pkgProfileApi.filterAndGenerateProfilesForManifest(types, excludedDirectories);
896+
expect(pkgTypeMembers.find((t) => t.name === 'Profile')?.members).to.deep.equal(['Target Profile']);
897+
});
898+
839899
describe('validateAncestorId', () => {
840900
it('should throw if the explicitUseNoAncestor is true and highestReleasedVersion is not undefined', () => {
841901
const ancestorId = 'ancestorId';

yarn.lock

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@
551551
strip-ansi "6.0.1"
552552
ts-retry-promise "^0.8.1"
553553

554-
"@salesforce/core@^8.23.1", "@salesforce/core@^8.30.3", "@salesforce/core@^8.31.0", "@salesforce/core@^8.31.1":
554+
"@salesforce/core@^8.23.1", "@salesforce/core@^8.30.3", "@salesforce/core@^8.31.0":
555555
version "8.31.1"
556556
resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.31.1.tgz#a0057e46568b5aeb6d838c461d7c98105ec11dc2"
557557
integrity sha512-dnBfLI0v/Ucsh/QrpYPGeo39qsvvglWMRSifx1lmAwLc9QAnL3Hhp9zUxJyX5icD9jj1uMftsAtIOGyjC2+KXA==
@@ -576,6 +576,31 @@
576576
ts-retry-promise "^0.8.1"
577577
zod "^4.1.12"
578578

579+
"@salesforce/core@^8.31.2":
580+
version "8.31.2"
581+
resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.31.2.tgz#968448f423b553f726f42c27da6b55c4ec7eb93a"
582+
integrity sha512-naqnq7Z+gbl1LdnyNvrGrNUoeMUQtCOsnrS6DfqeuLMJTFqcL9Dq0/od+xcuqi0+l7HTyH0/gU1BQitWpd1rag==
583+
dependencies:
584+
"@jsforce/jsforce-node" "^3.10.13"
585+
"@salesforce/kit" "^3.2.4"
586+
"@salesforce/ts-types" "^2.0.12"
587+
ajv "^8.18.0"
588+
change-case "^4.1.2"
589+
fast-levenshtein "^3.0.0"
590+
faye "^1.4.1"
591+
form-data "^4.0.5"
592+
js2xmlparser "^4.0.1"
593+
jsonwebtoken "9.0.3"
594+
jszip "3.10.1"
595+
memfs "4.38.1"
596+
pino "^9.7.0"
597+
pino-abstract-transport "^1.2.0"
598+
pino-pretty "^11.3.0"
599+
proper-lockfile "^4.1.2"
600+
semver "^7.8.0"
601+
ts-retry-promise "^0.8.1"
602+
zod "^4.1.12"
603+
579604
"@salesforce/dev-config@^4.3.1":
580605
version "4.3.2"
581606
resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-4.3.2.tgz#10047e2b8d289c93f157ab4243a1b1de57f2d6a2"

0 commit comments

Comments
 (0)