A fast and powerful Salesforce CLI plugin with utilities for metadata formatting, sorting, and more
npm install sf-swiftA fast and powerful Salesforce CLI plugin with utilities for metadata formatting, sorting, integrity checks and more 🎯
``bash`
sf plugins install sf-swift
`bashOptional: Init .swiftrc config to have full control for metadata adjustment rules inside your SF project
sf swift config init
---
Commands
- sf swift config init
- sf swift metadata adjust
- sf swift metadata integrity
- sf swift detect git conflicts
- sf swift detect fileInstallation
$3
`bash
Install from npm
sf plugins install sf-swift
`- Make sure that you have SF CLI:
sf -v
- Install SF CLI if missing or outdated: npm install @salesforce/cli -gCommand:
sf swift metadata adjustSorts and normalizes Salesforce metadata XML files with type-aware rules, entity preservation, and optional CI/CD optimized efficiency. ⚡
- 🎯 Smart Metadata Sorting - Understands PermissionSet, Profile, and other metadata structures
- 💾 Automatic Backups - Creates timestamped backups before processing (opt-in)
- 📊 Detailed Reporting - Shows which files were modified vs already okay
- 🔄 Recursive Processing - Handles nested directory structures
- 🔍 CI/CD optimization - Process only files changed in recent commits
- 🛡️ Safety Whitelist - Only processes safe metadata types by default (can be bypassed with
--all)
- ⏭️ Exclude Filter - Skip specific metadata types (e.g., --exclude field,object)
- 🎯 Include Filter - Target only specific metadata types (e.g., --include permissionset,profile)
- 🧹 Clean Formatting - Consistent indentation and XML formatting
- ⏱️ Execution Timer - Shows how long processing took overall$3
`bash
Process current directory
sf swift metadata adjustProcess specific directory
sf swift metadata adjust ./force-app/main/defaultProcess with backup (disabled by default)
sf swift metadata adjust --backupProcess only files changed in last 3 commits
sf swift metadata adjust --git-depth 3Process only files changed in last 5 commits with backup
sf swift metadata adjust --git-depth 5 --backupProcess only PermissionSet files
sf swift metadata adjust --include permissionsetProcess only PermissionSet and Profile files
sf swift metadata adjust --include permissionset,profileCombine with git-depth to process only specific types from recent commits
sf swift metadata adjust --git-depth 3 --include permissionsetExclude specific types (overrides defaults)
sf swift metadata adjust --exclude profile,permissionsetInclude with custom exclusions
sf swift metadata adjust --include permissionset,field --exclude profileProcess ALL metadata types (bypass safety whitelist)
sf swift metadata adjust --allProcess ALL types with backup
sf swift metadata adjust --all --backupProcess ALL types changed in last 5 commits
sf swift metadata adjust --all --git-depth 5Get help
sf swift metadata adjust --help
`
$3
- PATH - Path to the SF project directory containing metadata files to process$3
| Flag | Short | Description | Default |
|------|-------|-------------|---------|
|
--target-dir | -d | Target directory to process | . (current) |
| --config | -c | Path to custom config file | .swiftrc or built-in |
| --git-depth | -g | Process only N commits | 0 (all files) |
| --include | -i | Only process specific types | All whitelisted types |
| --exclude | -e | Exclude specific types | reportType,flexipage,layout |
| --all | -a | Process ALL types (bypass whitelist) | Disabled |
| --backup | - | Create backup before processing | Disabled |
| --help | -h | Show help information | - |$3
`
🎯 Including only: permissionset, profile, translation
🔍 Found 371 changed *-meta.xml files in last 100 commits
📋 Processing 25 specific metadata files
🔤 Processing specified metadata files...✏️ Modified: permissionsets/Admin.permissionset-meta.xml
✏️ Modified: profiles/Admin.profile-meta.xml
...
============================================================
📊 ADJUSTMENT SUMMARY
============================================================
📁 Total files checked: 25 files
✏️ Modified: 23 files
✅ Already good: 2 files
⏭️ Skipped: 346 files
⚠️ Errors encountered: 0 files
🎉 Successfully adjusted 23 metadata files!
⏱️ Completed in 3.10 seconds
`$3
By default, the tool excludes certain Salesforce metadata file types that should not be sorted:
-
reportType-meta.xml - Report Type metadata files
- flexipage-meta.xml - Lightning Page (FlexiPage) metadata files
- layout-meta.xml - Page Layout metadata filesYou can override these defaults with the
--exclude flag:`bash
Only exclude profiles (process everything else including layouts, flexipages, etc.)
sf swift metadata adjust --exclude profileExclude nothing (process all files)
sf swift metadata adjust --exclude ""Custom exclusions
sf swift metadata adjust --exclude labels,field
`These files are counted in the summary statistics but never modified.
$3
By default, the tool uses a safety whitelist to only process metadata types that are known to be safe for XML sorting. This prevents potential issues with complex metadata types that may have specific ordering requirements.
#### Whitelisted types (safe by default)
The following metadata types are whitelisted and will be processed by default:
-
cls-meta.xml - Apex classes
- field-meta.xml - Fields
- globalValueSet-meta.xml - Global Value Sets
- labels-meta.xml - Labels
- listView-meta.xml - List views
- object-meta.xml - Standard Objects
- permissionset-meta.xml - Permission Sets
- profile-meta.xml - User Profiles
- settings-meta.xml - Org Settings (various types)
- trigger-meta.xml - Triggers
- validationRule-meta.xml - Validation Rules#### Always excluded types
Some types are always excluded due to special handling requirements:
-
flow-meta.xml - Flows (require special key ordering logic)#### Using the whitelist
`bash
Default: Only process whitelisted types
sf swift metadata adjustSpecific whitelisted types only
sf swift metadata adjust --include permissionset,profileError: reportType is not whitelisted
sf swift metadata adjust --include reportType
❌ Invalid configuration: The following types are not in the allowed whitelist: reportType-meta.xml
Use --all flag to process all metadata types without whitelist restrictions.
`#### Bypassing the whitelist
Use the
--all flag to process any metadata type, bypassing whitelist restrictions:`bash
Process ALL metadata types (use with caution)
sf swift metadata adjust --allProcess specific non-whitelisted types
sf swift metadata adjust --all --include reportType,customFieldProcess ALL types from recent commits
sf swift metadata adjust --all --git-depth 10Process ALL with backup (recommended when experimenting)
sf swift metadata adjust --all --backup
`⚠️ Important: When using
--all, be aware that some complex metadata types may have specific ordering requirements that standard alphabetical sorting doesn't preserve. Always:
- Test in a non-production environment first
- Use --backup flag for safety
- Review changes carefully before committing
- Check that metadata still deploys correctly$3
The adjust command supports a YAML configuration file (
.swiftrc) in your project root. This allows you to customize formatting rules, cleanup rules, and exclusions without command-line flags.#### Default behavior
- If no
.swiftrc file exists, the tool uses built-in defaults
- If a .swiftrc file is found in your project root, it is loaded and used
- Use --config path/to/file.yaml to specify a custom configuration fileTo customize the configuration, copy the sample config below to
.swiftrc in your project root.#### Configuration structure
`yaml
.swiftrc - SF Swift Configuration File
metadata:
adjust:
# Formatting rules define how XML elements should be sorted for each file type
# Files are whitelisted implicitly by having a formatting rule
formatting:
- filePattern: "field-meta.xml"
elementPriority:
- fullName
- filePattern: "listView-meta.xml"
elementPriority:
- fullName
unsortedArrays:
- filters
- filePattern: "permissionset-meta.xml"
elementPriority:
- label
- description
- editable
- readable
- filePattern: "profile-meta.xml"
elementPriority:
- editable
- readable
# Element cleanup rules for removing default/empty values
cleanup:
field-meta.xml:
- elementName: externalId
removeValues:
- "false"
conditions:
- elementName: type
values:
- Picklist
- elementName: description
removeValues:
- ""
# File types that are always excluded (cannot be processed)
alwaysExcluded:
- flow-meta.xml
`#### Configuration options
| Section | Description |
|---------|-------------|
|
metadata.adjust.formatting | Array of rules defining how to sort XML elements per file type |
| metadata.adjust.formatting[].filePattern | File suffix to match (e.g., field-meta.xml) |
| metadata.adjust.formatting[].elementPriority | Keys that appear first within each object, in order |
| metadata.adjust.formatting[].sortedByElements | Keys to use for sorting array elements (first match wins) |
| metadata.adjust.formatting[].unsortedArrays | Array keys that preserve original order |
| metadata.adjust.formatting[].condensedElements | Elements formatted on a single line for better diffs |
| metadata.adjust.cleanup | Rules for removing default/empty values per metadata type |
| metadata.adjust.alwaysExcluded | File types that can never be processed |#### Implicit whitelist
Files are whitelisted implicitly by having a
metadata.adjust.formatting rule. Only files matching a metadata.adjust.formatting[].filePattern will be processed (unless --all flag is used).#### No merging
User configuration is used exactly as-is with no merging with defaults. This ensures predictable behavior—what you configure is exactly what you get.
#### Full configuration reference
For detailed documentation with before/after examples for each formatting option, see CONFIGURATION.md.
#### Project root detection
The
.swiftrc file is searched for by walking up the directory tree from the target directory, checking for:1.
.swiftrc file (highest priority)
2. .git directory
3. package.json fileThis allows the config to work from any subdirectory in your project.
$3
1. Use git-depth for large repos: Only process changed files
2. Backup disabled by default: Already optimized for CI/CD
3. Run before commit: Catch issues early with git-depth 1
4. Ignore backup folders: Add
.backup-* to .gitignore (when using --backup)Command:
sf swift config initCreates a
.swiftrc file in the current directory using the built-in default configuration. If a .swiftrc already exists, it is backed up as .swiftrc.backup.YYYYMMDD before being overwritten.$3
`bash
Create a .swiftrc file in the current directory
sf swift config init
`$3
When a backup is created, the command prints the backup filename before confirming the new file was written.
Command:
sf swift detect git conflictsScans your repository for pending
.rej files generated by GIT merges$3
`bash
Scan current directory for reject files
sf swift detect git conflictsEmit machine-readable JSON output
sf swift detect git conflicts --jsonCheck a specific directory
sf swift detect git conflicts --target-dir force-app/main/default
`$3
| Flag | Short | Description | Default |
|------|-------|-------------|---------|
|
--target-dir | -d | Directory to scan for .rej files | . (current) |
| --json | - | Return machine-readable output | Disabled |$3
Default output is human-readable and includes a summary plus any
.rej file paths. Use --json to integrate with automation (e.g., GitHub Actions).`json
{
"status": 1,
"result": {
"count": 2,
"conflictFiles": [
"force-app/main/default/classes/Foo.cls-meta.xml.rej",
"force-app/main/default/objects/Bar__c.object-meta.xml.rej"
]
},
"warnings": []
}
`$3
- Fail CI checks whenever metadata merges leave behind
.rej files
- Provide actionable feedback in PR comments (see workflows below)
- Run locally before committing to ensure no conflict leftovers are stagedCommand:
sf swift detect fileScans any directory tree for arbitrary file suffixes (e.g.,
.rej, .log, .tmp) so CI workflows can halt when unwanted artifacts slip into pull requests.$3
`bash
Look for reject and log files in the current directory
sf swift detect file --type .rej --type .logScan a subdirectory and stop after the first match
sf swift detect file ./force-app --type .tmp --max 1Emit machine-readable JSON output
sf swift detect file --type .rej --json
`$3
| Flag | Short | Description | Default |
|------|-------|-------------|---------|
|
--target-dir | -d | Directory to scan when no positional path is supplied | . (current) |
| --type | -t | Repeatable suffix filter (e.g., .rej, .log). Required. | – |
| --max | – | Stop the scan once this many matches have been collected | unlimited |
| --json | – | Return machine-readable output | Disabled |$3
The human-readable output mirrors the git conflict detector: emoji headings, elapsed time, and a numbered list of matching files relative to the scan root. Use
--json to integrate with automation.`json
{
"status": 1,
"result": {
"count": 3,
"types": [".rej", ".log"],
"files": [
"force-app/main/default/classes/Foo.cls-meta.xml.rej",
"scripts/tmp/apex.log",
"scripts/tmp/trace.log"
]
},
"warnings": []
}
`$3
- Reuse the same detector for
.rej, .tmp, .log, or any other unwanted artifacts
- Speed up scans in large repos with --max 1 when you only need to know a file exists
- Augment GitHub Actions by feeding the JSON payload into custom comment bots or gating logicCommand:
sf swift metadata integrityCross-checks recent Git history for deleted metadata (Apex classes, Visualforce pages, custom fields) and reports lingering references across Profiles, Permission Sets, source code, flows, formulas, layouts, and more.
➡️ For a detailed coverage matrix, CLI tips, and CI examples see METADATA-INTEGRITY.md.
$3
`bash
Analyze the latest 5 commits (default depth)
sf swift metadata integrityTarget a specific package directory with deeper history
sf swift metadata integrity ./force-app/main/default --git-depth 10Emit machine-readable results
sf swift metadata integrity --json
`$3
| Flag | Short | Description | Default |
|------|-------|-------------|---------|
|
--target-dir | -d | Directory to analyze when no positional path is given | . (current) |
| --git-depth | -g | Number of commits to inspect for deletions (clamped to history) | 5 |
| --test-with-class | - | Treat provided Apex class names as removed metadata (repeatable). Useful when testing class-related rules without deleting code. | Disabled |
| --test-with-field | - | Treat provided field API names (Object.Field__c) as removed metadata (repeatable). Useful for auditing field access without Git changes. | Disabled |
- Temporarily simulate deletions with --test-with-class or --test-with-field to audit specific classes or fields without modifying Git history$3
Returns a summary of deleted metadata and outstanding references. Use
--json to integrate with CI or PR bots.`json
{
"status": 1,
"result": {
"gitDepthUsed": 5,
"removedItems": [
{
"type": "ApexClass",
"name": "ObsoleteService",
"referenceKey": "ObsoleteService",
"sourceFile": "force-app/main/default/classes/ObsoleteService.cls"
}
],
"issues": [
{
"type": "MissingApexClassReference",
"missingItem": "ObsoleteService",
"referencingFile": "profiles/Admin.profile-meta.xml",
"detail": "Class access still enabled for removed Apex class 'ObsoleteService'"
}
]
},
"warnings": []
}
`$3
- Deleted Apex classes that are still granted access via
classAccesses
- Deleted custom fields that remain in fieldPermissions
- Deleted Visualforce pages that remain enabled via pageAccesses
- Apex classes and triggers that reference removed Apex classes
- Lightning Web Components (lwc/) and Aura components (aura/) that import or declare removed Apex classes
- Visualforce pages and components (.page, .component) whose controllers or markup reference removed Apex classes
- Flow definitions (*.flow-meta.xml) that invoke removed Apex classes or reference removed custom fields
- Formula fields (*.field-meta.xml) whose formulas reference removed custom fields
- Field sets (*.fieldSet-meta.xml) that list removed custom fields
- Layouts (*.layout-meta.xml) that still list removed custom fields
- Compact layouts (*.compactLayout-meta.xml) that display removed custom fields
- Record types (*.recordType-meta.xml) that reference removed custom fields in picklists or field selections
- Validation rules in object metadata (*.object-meta.xml) that reference removed custom fields
- Profiles and Permission Sets located anywhere within the target directory$3
- Clean up dangling permissions after deleting code or fields
- Block deployments or merges that would leave broken references in user access metadata
- Audit refactors to ensure no obsolete classes or fields linger in security artifacts
Integration Examples
$3
This plugin ships with ready-to-use workflows:
-
.github/workflows/pr-metadata-adjust.yml adjusts metadata files in pull requests
- .github/workflows/pr-detect-issues.yml fails the PR when .rej files are found and comments with the list
- .github/workflows/pr-check-adjust.yml gates formatting jobs by running conflict and metadata-integrity checks up front
- .github/workflows/pr-code-adjust.yml runs Prettier-based Apex formatting and auto-commits the resultspr-check-adjust.yml overview:- check-rej-files: runs
sf swift detect git conflicts, comments with a reject-file list, and fails on leftover .rej files.
- check-integrity: inspects every commit in the PR with sf swift metadata integrity, comments on detected issues, and blocks subsequent jobs when problems remain.
- adjust-code / adjust-metadata: formatting stages that only run after both guard jobs succeed, ensuring they never hide metadata issues under auto-fixes.pr-code-adjust.yml overview:- adjust: calculates the PR commit count, executes
prettier-fix-delta.sh to reformat Apex classes and triggers with Prettier, commits updated .cls/.trigger files when changes occur, retries the push up to three times, and posts a summary comment on the pull request.#### Metadata adjust workflow (
.github/workflows/pr-metadata-adjust.yml)This workflow automatically adjusts metadata files on pull requests and commits the changes back to the PR branch.
##### Setup
1. The workflow file is already included:
.github/workflows/pr-metadata-adjust.yml
2. Configure which metadata types to process by editing the INCLUDED_TYPES environment variable:`yaml
env:
# Process all whitelisted types
INCLUDED_TYPES: '' # Process only defined types
INCLUDED_TYPES: 'profile,permissionset'
# Only process files changed in PR (recommended)
ADJUST_DELTA_ONLY: 'true'
# Or process all files in directory
ADJUST_DELTA_ONLY: 'false'
`##### Features
- ✅ Automatic Triggering - Runs when metadata files change in PRs
- 🤖 Auto-Commit - Commits formatting changes back to PR branch
- 💬 PR Comments - Notifies about formatting status
- 🎯 Configurable - Choose which metadata types to process
- ⚡ Delta Mode - Optionally process only files changed in the PR
##### Workflow behavior
1. Triggered when a PR is opened, synchronized, or reopened with metadata file changes
2. Delta Mode (when
ADJUST_DELTA_ONLY: 'true'):
- Automatically calculates the number of commits in the PR
- Uses --git-depth to process only files changed in the PR
- PR comment indicates: "Changed files only (X commits)"
3. Full Mode (when ADJUST_DELTA_ONLY: 'false' or unset):
- Processes all metadata files in the configured directory
- PR comment indicates: "All files in directory"
4. Formats metadata files based on INCLUDED_TYPES configuration
5. Commits changes automatically if any files were modified
6. Comments on the PR with the formatting status and scope
##### Customization
Edit
.github/workflows/pr-metadata-adjust.yml to:
- Change INCLUDED_TYPES to process different metadata types
- Set ADJUST_DELTA_ONLY: 'true' for PR-only processing (recommended)
- Set ADJUST_DELTA_ONLY: 'false' to process all files in directory
- Adjust commit message format
- Adjust PR comment templates
- Change trigger conditions#### Detect issues workflow (
.github/workflows/pr-detect-issues.yml)This workflow installs the plugin, runs
sf swift detect git conflicts --json, and fails the PR when .rej files are present. It also comments on the pull request with the full list of conflict files so authors can resolve them quickly.Key points
- Requires no configuration—runs on every pull request
- Comment body includes a code block listing each
.rej file returned by the command
- Failing step signals reviewers that conflicts must be addressed before mergingTroubleshooting
$3
- Check you're in the right directory
- Verify files end with -meta.xml$3
- Ensure you're in a Git repository
- Check git-depth doesn't exceed commit count$3
- You're trying to include a non-whitelisted metadata type
- Use --all flag to bypass whitelist restrictions
- Review the whitelisted types in the error message
- Example: sf swift metadata adjust --all --include reportType$3
- Check file permissions
- Use --backup to preserve originals if needed$3
- Only created when using --backup flag
- Add .backup-* to .gitignore
- Clean old backups: rm -rf .backup-*Best Practices
✅ DO:
- Run before committing code
- Use git-depth for incremental checks
- Review modified files in summary
- Add to pre-commit hooks
- Use
--backup when testing major changes
- Stick to whitelisted metadata types for safety
- Test with --all flag in non-production first
- Use --all --backup when processing new metadata types❌ DON'T:
- Process backup folders (add to gitignore)
- Ignore errors in CI/CD
- Forget to commit adjusted files
- Use
--all` flag in production without thorough testing1. Fork the repository
2. Create a feature branch
3. Make your changes
4. Add tests if applicable
5. Submit a pull request
This project is licensed under the ISC License.