diff --git a/package.json b/package.json index b524b112d..556b3a0dd 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,7 @@ ], "dependencies": { "@jsforce/jsforce-node": "^3.10.16", - "@salesforce/core": "^8.31.1", + "@salesforce/core": "^8.31.2", "@salesforce/kit": "^3.2.6", "@salesforce/schemas": "^1.10.3", "@salesforce/source-deploy-retrieve": "^12.36.2", diff --git a/src/package/packageProfileApi.ts b/src/package/packageProfileApi.ts index 443696a43..5d0a2cf2b 100644 --- a/src/package/packageProfileApi.ts +++ b/src/package/packageProfileApi.ts @@ -123,7 +123,10 @@ export class PackageProfileApi extends AsyncCreatable { // Look for profiles in all package directories private findAllProfiles(excludedDirectories: string[] = []): string[] { - const ignore = excludedDirectories.map((dir) => `**/${dir.split(path.sep).join(path.posix.sep)}/**`); + const ignore = excludedDirectories.map((dir) => { + const normalized = path.normalize(dir).split(path.sep).filter(Boolean).join(path.posix.sep); + return `**/${normalized}/**`; + }); const patterns = this.project .getUniquePackageDirectories() .map((pDir) => pDir.fullPath) diff --git a/src/package/packageVersionCreate.ts b/src/package/packageVersionCreate.ts index bb29cc64e..a13a5d98b 100644 --- a/src/package/packageVersionCreate.ts +++ b/src/package/packageVersionCreate.ts @@ -587,9 +587,10 @@ export class PackageVersionCreate { profileExcludeDirs ); + const excludedProfileNames = excludedProfiles?.map((p) => path.basename(p)); packageXmlAsJson.types = typesArr.map((type) => { if (type.name !== 'Profile') return type; - return { ...type, members: type.members.filter((m) => !excludedProfiles?.includes(m)) }; + return { ...type, members: type.members.filter((m) => !excludedProfileNames?.includes(m)) }; }); const xml = packageXmlJsonToXmlString(packageXmlAsJson); diff --git a/test/package/packageVersionCreate.test.ts b/test/package/packageVersionCreate.test.ts index 6e221bd9e..ca7008d61 100644 --- a/test/package/packageVersionCreate.test.ts +++ b/test/package/packageVersionCreate.test.ts @@ -836,6 +836,66 @@ describe('Package Version Create', () => { expect(pkgTypeMembers[1].members).to.deep.equal(['Test Profile']); }); + it('should include profiles from all package directories when scopeProfiles is false', async () => { + project.getSfProjectJson().set('packageDirectories', [ + { path: './src-access-management', package: 'ACCESS', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT' }, + { path: 'force-app', package: 'TEST', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT', default: true }, + ]); + await project.getSfProjectJson().write(); + + const siblingDir = path.join(project.getPath(), 'src-access-management'); + await fs.promises.mkdir(siblingDir, { recursive: true }); + const fileContents = ''; + await fs.promises.writeFile(path.join(siblingDir, 'Sibling Profile.profile-meta.xml'), fileContents); + await fs.promises.writeFile( + path.join(project.getPath(), 'force-app', 'Target Profile.profile-meta.xml'), + fileContents + ); + + const pkgProfileApi = await PackageProfileApi.create({ project, includeUserLicenses: false }); + const types = [ + { name: 'Layout', members: ['Test Layout'] }, + { name: 'Profile', members: ['Sibling Profile', 'Target Profile'] }, + ]; + + // With no excludedDirectories (scopeProfiles=false), all profiles from all dirs are included + const pkgTypeMembers = pkgProfileApi.filterAndGenerateProfilesForManifest(types); + expect(pkgTypeMembers.find((t) => t.name === 'Profile')?.members).to.deep.equal([ + 'Sibling Profile', + 'Target Profile', + ]); + }); + + it('should exclude profiles from ./-prefixed sibling package directories when scopeProfiles is true', async () => { + // Configure a multi-directory project where sibling uses ./ prefix in path + project.getSfProjectJson().set('packageDirectories', [ + { path: './src-access-management', package: 'ACCESS', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT' }, + { path: 'force-app', package: 'TEST', versionName: 'ver 0.1', versionNumber: '0.1.0.NEXT', default: true }, + ]); + await project.getSfProjectJson().write(); + + const siblingDir = path.join(project.getPath(), 'src-access-management'); + await fs.promises.mkdir(siblingDir, { recursive: true }); + const fileContents = ''; + await fs.promises.writeFile(path.join(siblingDir, 'Sibling Profile.profile-meta.xml'), fileContents); + await fs.promises.writeFile( + path.join(project.getPath(), 'force-app', 'Target Profile.profile-meta.xml'), + fileContents + ); + + const pkgProfileApi = await PackageProfileApi.create({ project, includeUserLicenses: false }); + const types = [ + { name: 'Layout', members: ['Test Layout'] }, + { name: 'Profile', members: ['Sibling Profile', 'Target Profile'] }, + ]; + + // When scopeProfiles is true, packageVersionCreate passes sibling package dir paths + // as excludedDirectories. These retain the ./ prefix from sfdx-project.json. + const excludedDirectories = ['./src-access-management']; + const pkgTypeMembers = pkgProfileApi.filterAndGenerateProfilesForManifest(types, excludedDirectories); + expect(pkgTypeMembers.find((t) => t.name === 'Profile')?.members).to.deep.equal(['Target Profile']); + }); + describe('validateAncestorId', () => { it('should throw if the explicitUseNoAncestor is true and highestReleasedVersion is not undefined', () => { const ancestorId = 'ancestorId'; diff --git a/yarn.lock b/yarn.lock index 0452a707b..c55cf3bf1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -551,7 +551,7 @@ strip-ansi "6.0.1" ts-retry-promise "^0.8.1" -"@salesforce/core@^8.23.1", "@salesforce/core@^8.30.3", "@salesforce/core@^8.31.0", "@salesforce/core@^8.31.1": +"@salesforce/core@^8.23.1", "@salesforce/core@^8.30.3", "@salesforce/core@^8.31.0": version "8.31.1" resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.31.1.tgz#a0057e46568b5aeb6d838c461d7c98105ec11dc2" integrity sha512-dnBfLI0v/Ucsh/QrpYPGeo39qsvvglWMRSifx1lmAwLc9QAnL3Hhp9zUxJyX5icD9jj1uMftsAtIOGyjC2+KXA== @@ -576,6 +576,31 @@ ts-retry-promise "^0.8.1" zod "^4.1.12" +"@salesforce/core@^8.31.2": + version "8.31.2" + resolved "https://registry.yarnpkg.com/@salesforce/core/-/core-8.31.2.tgz#968448f423b553f726f42c27da6b55c4ec7eb93a" + integrity sha512-naqnq7Z+gbl1LdnyNvrGrNUoeMUQtCOsnrS6DfqeuLMJTFqcL9Dq0/od+xcuqi0+l7HTyH0/gU1BQitWpd1rag== + dependencies: + "@jsforce/jsforce-node" "^3.10.13" + "@salesforce/kit" "^3.2.4" + "@salesforce/ts-types" "^2.0.12" + ajv "^8.18.0" + change-case "^4.1.2" + fast-levenshtein "^3.0.0" + faye "^1.4.1" + form-data "^4.0.5" + js2xmlparser "^4.0.1" + jsonwebtoken "9.0.3" + jszip "3.10.1" + memfs "4.38.1" + pino "^9.7.0" + pino-abstract-transport "^1.2.0" + pino-pretty "^11.3.0" + proper-lockfile "^4.1.2" + semver "^7.8.0" + ts-retry-promise "^0.8.1" + zod "^4.1.12" + "@salesforce/dev-config@^4.3.1": version "4.3.2" resolved "https://registry.yarnpkg.com/@salesforce/dev-config/-/dev-config-4.3.2.tgz#10047e2b8d289c93f157ab4243a1b1de57f2d6a2"