Salesforce metadata namespace auditing and fail-closed refactoring
Project description
NamespaceForge CLI
NamespaceForge audits and safely refactors namespace references in Salesforce metadata source. Version 0.2 inventories Apex, LWC, Aura, and XML metadata, resolves package-owned symbols, previews add/remove/replace operations, and can execute an approved plan with integrity checks, recoverable backups, and rollback support.
Installation
Python 3.11 or newer is required.
cd namespaceforge-cli
python -m venv .venv
Windows:
.venv\Scripts\activate
pip install -e ".[dev]"
Linux/macOS:
source .venv/bin/activate
pip install -e ".[dev]"
Audit and planning
namespaceforge --help
namespaceforge --version
namespaceforge scan ../force-app/main/default
namespaceforge scan ../force-app/main/default --format json
namespaceforge audit ../force-app/main/default --namespace zPFX
namespaceforge audit ../force-app/main/default --format json --output report.json
namespaceforge audit ../force-app/main/default --fail-on-issues
namespaceforge plan replace ../force-app/main/default --from oldPFX --to zPFX
namespaceforge plan add ../force-app/main/default --namespace zPFX
namespaceforge plan remove ../force-app/main/default --namespace zPFX
All plan commands are strictly read-only. Review their safe, review, and protected classifications before authorizing a transformation.
Applying transformations
Run the corresponding plan first, commit or otherwise preserve the current source state, then use an explicit apply command:
namespaceforge apply replace ../force-app/main/default --from oldPFX --to zPFX --yes
namespaceforge apply add ../force-app/main/default --namespace zPFX --yes
namespaceforge apply remove ../force-app/main/default --namespace zPFX --yes
Use --backup-dir PATH to select a persistent backup root outside the scanned tree. Without it, NamespaceForge uses .namespaceforge/backups at the Salesforce project or Git repository root when one can be discovered. Each successful run prints its unique manifest and backup paths.
Machine-readable transformation output is available without mixing diagnostics into JSON stdout:
namespaceforge apply replace ../force-app/main/default \
--from oldPFX --to zPFX --yes --format json --output transform-report.json
Fail-closed safety model
NamespaceForge applies a transformation only when all of these conditions hold:
--yesexplicitly authorizes source modification.- The scan completed without unreadable files or scanner errors.
- The plan contains no
reviewoccurrences; protected declarations and comments remain untouched. - Every target remains inside the resolved scan root and matches the content hash captured during planning.
- Backups and a prepared manifest are written outside the scanned tree before the first source write.
- Source bytes are checked again immediately before each atomic file replacement.
If a later write fails, already modified files are restored from the in-memory originals and the manifest records the rollback state. UTF-8 BOMs, CRLF/LF line endings, and file modes are preserved.
Rollback
Restore a completed transformation using its manifest:
namespaceforge rollback .namespaceforge/backups/<run-id>/manifest.json --yes
Rollback verifies both the backup hash and each current source hash before writing anything. If a file was edited after the transformation, the entire rollback aborts without overwriting that work. Rollback is idempotent.
Configuration
Create namespaceforge.toml in the working directory, or pass --config PATH:
[namespaceforge]
namespace = "zPFX"
source_path = "force-app/main/default"
exclude = ["**/.git/**", "**/node_modules/**"]
[audit]
fail_on_issues = false
minimum_confidence = 0.6
Without a path, commands use force-app/main/default or the configured source_path. CLI arguments override configuration values.
Reports and schemas
Text reports are optimized for people. JSON reports use stable schema version 1.0. Standalone Draft 2020-12 contracts are packaged for:
- scan reports
- audit reports
- read-only plan reports
- transformation results
- durable transformation manifests
The schemas remain accessible from namespaceforge/schemas through importlib.resources after wheel installation.
For source safety, any explicit --output report must resolve outside the scanned directory. NamespaceForge returns exit code 2 before transforming when an apply report targets the source tree.
Exit codes
| Code | Meaning |
|---|---|
| 0 | Successful command |
| 1 | Warnings or errors found with audit --fail-on-issues |
| 2 | Invalid CLI usage, configuration, confirmation, or output path |
| 3 | Scan path missing or inaccessible |
| 4 | Unexpected internal failure |
| 5 | Transformation or rollback safely aborted |
Development and release validation
pytest
pytest --cov=namespaceforge
python -m build
python -m pip install dist/namespaceforge-0.2.0-py3-none-any.whl
namespaceforge --version
Tests use isolated metadata trees and require no Salesforce org. CI runs on Windows and Linux with Python 3.11, 3.12, and 3.13.
Demo
The tracked demo is outside force-app, so it cannot affect the managed package. Plan commands can run directly against it. To test an apply command, first copy the demo to an untracked temporary directory and transform only that copy.
.\namespaceforge-cli\.venv\Scripts\namespaceforge.exe scan .\namespaceforge-cli\examples\demo-metadata
.\namespaceforge-cli\.venv\Scripts\namespaceforge.exe audit .\namespaceforge-cli\examples\demo-metadata --namespace zPFX
.\namespaceforge-cli\.venv\Scripts\namespaceforge.exe plan replace .\namespaceforge-cli\examples\demo-metadata --from oldPFX --to zPFX
Current limitations
- Reference discovery remains token based; XML ownership extraction uses structured parsing with a recovery fallback.
- Apply commands intentionally refuse every review-required plan. There is no override for partial or ambiguous transformations.
- Unnamespaced custom tokens become high-confidence findings only when ownership can be established from declarations in the scanned tree.
- Third-party namespaces remain non-blocking unless the referenced symbol is owned by the scanned package.
- Dynamic Apex, generated JavaScript, encoded references, and uncommon metadata forms can require manual review or remain undiscovered.
- Source
<fullName>declarations for objects, fields, and labels are protected and remain unmodified. - Correct expected-namespace references are reported as informational findings.
- Always inspect the plan and use version control before applying a transformation.
Roadmap
The next milestone is parser-backed Apex and JavaScript symbol resolution, followed by Salesforce API/OAuth integration and a VS Code workflow. Those capabilities provide the foundation for an AppExchange API or hybrid solution.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file namespaceforge-0.2.0.tar.gz.
File metadata
- Download URL: namespaceforge-0.2.0.tar.gz
- Upload date:
- Size: 39.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
80c39370b3458a1ae197d95d7fecdb21d4b6d5648df93c5d40b2426ec21fd9e3
|
|
| MD5 |
abe616cfcabdbe5cd3dc06b75ce8c97a
|
|
| BLAKE2b-256 |
d5b59e6a6ba365f7ee03d34a77e347a8e1d94d55ac04758ca513d9fb6c537acd
|
Provenance
The following attestation bundles were made for namespaceforge-0.2.0.tar.gz:
Publisher:
publish-pypi.yml on AbdelGahffarZakaria/NamespaceForge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
namespaceforge-0.2.0.tar.gz -
Subject digest:
80c39370b3458a1ae197d95d7fecdb21d4b6d5648df93c5d40b2426ec21fd9e3 - Sigstore transparency entry: 2257288742
- Sigstore integration time:
-
Permalink:
AbdelGahffarZakaria/NamespaceForge@f47aaf3d69dc50876da637040ba521aed4b82ef4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/AbdelGahffarZakaria
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@f47aaf3d69dc50876da637040ba521aed4b82ef4 -
Trigger Event:
workflow_dispatch
-
Statement type:
File details
Details for the file namespaceforge-0.2.0-py3-none-any.whl.
File metadata
- Download URL: namespaceforge-0.2.0-py3-none-any.whl
- Upload date:
- Size: 37.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ca39ece10f2dec937a5502d64f3c60cb7dfc4dae620521008894ece9d3dae37
|
|
| MD5 |
6639373819e5375ad47895ca43716163
|
|
| BLAKE2b-256 |
ec985c273e361bdfaa01eda119577e96d40b0eb731b393940c63e7a86fa0a329
|
Provenance
The following attestation bundles were made for namespaceforge-0.2.0-py3-none-any.whl:
Publisher:
publish-pypi.yml on AbdelGahffarZakaria/NamespaceForge
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
namespaceforge-0.2.0-py3-none-any.whl -
Subject digest:
5ca39ece10f2dec937a5502d64f3c60cb7dfc4dae620521008894ece9d3dae37 - Sigstore transparency entry: 2257288751
- Sigstore integration time:
-
Permalink:
AbdelGahffarZakaria/NamespaceForge@f47aaf3d69dc50876da637040ba521aed4b82ef4 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/AbdelGahffarZakaria
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@f47aaf3d69dc50876da637040ba521aed4b82ef4 -
Trigger Event:
workflow_dispatch
-
Statement type: