Skip to content

feat: granular decomposed PermissionSet child handling#1238

Closed
scolladon wants to merge 8 commits intomainfrom
feat/decomposed-permissionset-granular
Closed

feat: granular decomposed PermissionSet child handling#1238
scolladon wants to merge 8 commits intomainfrom
feat/decomposed-permissionset-granular

Conversation

@scolladon
Copy link
Owner

@scolladon scolladon commented Mar 4, 2026

Explain your changes

Generate granular package.xml entries for decomposed PermissionSet child types instead of copying the entire PermissionSet folder.

  • Before: <members>Admin</members><name>PermissionSet</name> + copies entire folder
  • After: <members>Admin.Account.MyField__c</members><name>FieldPermission</name> + copies only the changed child file

Supports both decomposed formats:

  • Beta (decomposePermissionSetBeta): subdirectory layout (permissionsets/Admin/fieldPermissions/...)
  • Beta2 (decomposePermissionSetBeta2): flat layout (permissionsets/Admin.Account.MyField__c.fieldPermission-meta.xml) and objectSettings grouping

Key changes:

  • Register 14 PermissionSet child types in internal registry with decomposition: 'containedDecomposed' discriminant
  • Add PermissionSetChildHandler (extends StandardHandler, overrides _getElementName() with segment-counting logic)
  • Route child types via dynamic resolution in TypeHandlerFactory.resolveHandler()
  • Handle CustomPermission suffix/dir collision via path-context override (not registered as child)
  • Remove ContainedDecomposedHandler entirely — monolithic .permissionset-meta.xml falls to StandardHandler
  • Add 15 child xmlNames to SUB_OBJECT_TYPES for rename detection dedup

Manual Testing Findings

Protocol

  • Scratch org with SGD_Test_PermSet (applicationVisibilities, objectPermissions, fieldPermissions, tabSettings, userPermissions)
  • Two projects: decomposePermissionSetBeta (folder-per-type) and decomposePermissionSetBeta2 (flat + objectSettings)
  • Changes: modify a field permission, delete a user permission, add a new field permission

Findings

  1. Registry: 14/15 child types are in the SF CLI metadata registry. Only ObjectSettings is NOT.

  2. Manifest deploy (--manifest): Child type entries (e.g. FieldPermission: PSName.Object.Field) result in numberComponentsDeployed: 0. The CLI accepts the type but silently deploys nothing — it cannot resolve child member names to source files. Only PermissionSet: PSName works.

  3. Source-dir deploy (--source-dir): Always reconstructs the full PermissionSet from all decomposed files in the project, regardless of which single child file is specified. Not granular.

  4. Delta deploy (-d): Without parent .permissionset-meta.xml, deploy fails. With parent added, deploy succeeds but is destructive — the CLI replaces the full PS with only the files present in delta (all missing children are erased).

Conclusion

Child types are not independently deployable. Decomposition is purely a local source format. The only working deployment strategy is:

  • package.xml: use PermissionSet: PSName (not child types)
  • Delta (-d): copy the entire PS directory (all decomposed files), not just changed children

Comparison with main branch (ContainedDecomposedHandler)

The old implementation on main already produces the correct output:

  • Emits PermissionSet: PSName in package.xml (deployable)
  • Copies the entire PS directory for delta (deployable, non-destructive)
  • Does NOT track granular deletions in destructiveChanges.xml

Does this close any currently open issues?

closes #1221

  • Jest tests added to cover the fix.
  • NUT tests added to cover the fix.
  • E2E tests added to cover the fix.

Any particular element that can be tested locally

Test with a decomposed PermissionSet project (Beta or Beta2 preset) and verify that package.xml contains granular child type entries (e.g., FieldPermission, ObjectSettings) instead of PermissionSet.

Any other comments

Important: Manual testing revealed that granular child type entries in package.xml are NOT deployable by the Salesforce CLI. The old ContainedDecomposedHandler on main already produces the correct deployable output (PermissionSet: PSName + full PS directory copy). The granular approach needs to be revised to emit PermissionSet instead of child types while still supporting detection of individual child file changes.

E2E expected files need updating to match output changes.

@scolladon scolladon requested a review from mehdicherf as a code owner March 4, 2026 15:14
@github-actions
Copy link

github-actions bot commented Mar 4, 2026

Published under dev-1238 npm channel.

$ sf plugins install sfdx-git-delta@dev-1238

@scolladon
Copy link
Owner Author

scolladon commented Mar 4, 2026

Hi @Lintlinger !

Could you test on your laptop and help us validate this PR please ?
You can follow those steps to easily do it locally

Ensure the result produced is deployable please

@codecov
Copy link

codecov bot commented Mar 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aad4d50) to head (6469122).

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #1238   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files           58        58           
  Lines         1574      1566    -8     
  Branches       204       205    +1     
=========================================
- Hits          1574      1566    -8     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Lintlinger
Copy link

@scolladon
Works as expected! Thanks for this quick enhancement!

@scolladon
Copy link
Owner Author

@scolladon Works as expected! Thanks for this quick enhancement!

For both decomposePermissionSetBeta and decomposePermissionSetBeta2 @Lintlinger ?
Both deployable ?

@scolladon
Copy link
Owner Author

scolladon commented Mar 5, 2026

Closing — granular approach is not viable

Manual testing on a scratch org revealed that decomposed PermissionSet child types are not independently deployable by the Salesforce CLI. The granular approach this PR implements (emitting FieldPermission: PSName.Object.Field in package.xml + copying only the changed child file) produces non-deployable output.

Key findings

  1. 14/15 child types are in the CLI metadata registry (only ObjectSettings is not), but using them in --manifest deploys 0 components — the CLI cannot resolve child member names to source files.
  2. --source-dir always reconstructs the full PermissionSet from all decomposed files regardless of which single file is specified.
  3. Partial delta is destructive — deploying only changed children via --source-dir wipes all unchanged children from the org.
  4. The only working deployment strategy is PermissionSet: PSName in package.xml + copying the entire PS directory for delta — which is exactly what the old ContainedDecomposedHandler on main already does.

What's worth fixing

The existing ContainedDecomposedHandler._getElementName() on main has a nested folder bug: for paths like permissionsets/marketing/AdminMarketing/fieldPermissions/..., it uses the intermediate folder name (marketing) instead of the actual PS name (AdminMarketing). This will be fixed in a separate, focused PR.

@scolladon scolladon closed this Mar 5, 2026
@scolladon scolladon deleted the feat/decomposed-permissionset-granular branch March 5, 2026 13:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

decomposedPermissionSetBeta2 is supported

2 participants