SFDX-Git-Delta (SGD)

repository·main·Indexed 20 days ago

https://github.com/scolladon/sfdx-git-delta

A Salesforce CLI plugin that generates incremental deployment manifests (package.xml and destructiveChanges.xml) by comparing two Git commits. It is designed to speed up deployments and automate the creation of destructive change manifests in CI/CD pipelines for projects using the Source (DX) format and unmanaged metadata.

Tokens
19.1K
Snippets
49
Records
81
Agent score
65%

What's inside sfdx-git-delta

  1. What is SFDX-Git-Delta (SGD)?

    main

    SFDX-Git-Delta is a Salesforce CLI plugin designed to optimize source deployments by:

    1. Speeding up deployments: It identifies only the metadata that has changed since a specific reference commit, allowing for incremental deployments instead of full deployments.
    2. Automating destructive deployments: It automatically builds the destructiveChanges.xml manifest based on metadata that has been deleted or renamed in the Git history.

    Ideal Use Cases

    SGD is best suited for Technical Architects and Developers who meet these three conditions:

    • The Salesforce project uses a Git repository as the source of truth.
    • The project uses the Source (DX) format.
    • The metadata is unmanaged (not building managed or unlocked packages).

    SGD is primarily intended to be used within a CI/CD pipeline (e.g., GitHub Actions, GitLab CI, Jenkins, Azure DevOps) to handle incremental deployments to Salesforce orgs.

  2. How Git Diff Collection and Rename Detection work

    main

    SGD streams diff lines using an AsyncGenerator, meaning it processes changes as they arrive from git rather than buffering the entire diff upfront.

    Rename Detection: Rename detection is gated on the --changes-manifest flag.

    • By default, detectRenames is false.
    • When --changes-manifest is provided, SGD enables rename detection. It expands rename lines (R) into synthetic Addition (A) and Deletion (D) pairs. This allows the RenameResolver to track component moves.

    Filtering and Ignoring:

    • Scoping: SGD replicates --diff-filter=AMD(R) logic, skipping gitlinks (submodules) and type-change/copy entries.
    • Whitespace: If config.ignoreWhitespace is set, SGD uses ignoreWhitespace: 'all' and ignoreBlankLines: true in the underlying git call.
    • Ignore System: SGD uses a dual-instance ignore pattern:
      • globalIgnore: Applied to all diff lines.
      • destructiveIgnore: Applied only to deletions. It falls back to globalIgnore if --ignore-destructive-file is not provided. It always hard-codes recordTypes/ to avoid Salesforce API limitations.
  3. Integrate SGD with Apex test determination plugins

    main

    SFDX-Git-Delta (SGD) can be used in conjunction with specialized plugins to automate the identification of Apex tests required for deployment based on the delta generated by SGD.

    Two complementary plugins are available:

    1. apex-test-list: Determines required Apex tests by reading test annotations within your Apex classes. You can point this plugin to scan the package.xml file created by SGD to identify the necessary tests.
    2. apex-tests-git-delta: Determines required Apex tests by reading commit messages within a specific commit range. To ensure consistency, use the same --from and --to commit hashes for both SGD and this plugin.
  4. How the GitAdapter works

    main

    The GitAdapter is the core engine for Git operations in SGD. Unlike many tools, it does not require a git binary or shell out to subprocesses. Instead, it uses @scolladon/tsgit to read the repository's object store directly in-process.

    Key Characteristics:

    • No Subprocesses: Operations like rev-parse, diff, and grep are performed by reading the object store directly.
    • Tree Index: Uses a path-segment trie (TreeIndex) per revision for $O(path-depth)$ lookups of file existence and directory listings. This index is built via preBuildTreeIndex and is only used for metadata types requiring deep-path resolution.
    • Memory Management: To prevent high memory usage, getBufferContentOrEscalate monitors buffer size. If a buffer exceeds SIZE_THRESHOLD (1 MB), it throws an EscalateToStreamingSignal, forcing the system to switch to a memory-bounded stream-based approach.
    • LFS Support: The streaming implementation peeks at the first LFS_MAGIC.length bytes to detect and handle Git LFS objects automatically.
  5. Configure source folder focus with `--source`

    main

    The --source flag allows you to restrict the delta generation to specific folders. This is useful in monorepos or large projects where you only want to track changes in specific Salesforce directories.

    • You can use the --source flag multiple times to include different folders.
    • All paths provided must be relative to the --repo-dir.
    • Behavior for existing folders: If the folder exists, its contents are processed.
    • Behavior for non-existent folders: If the folder does not exist, it usually produces no output, unless the folder was recently deleted and is part of the Git diff, in which case changes may still be captured.
  6. Understand the Post-Processing Chain

    main

    After handlers generate results, SFDX-Git-Delta executes a two-phase post-processing chain to transform and finalize the output.

    1. Collectors Phase: Runs via collectAll(). These components (like FlowTranslationProcessor and IncludeProcessor) produce additional HandlerResult data that is merged into the main result set. Collectors are used for tasks like finding translation files or applying --include filters.

    2. Processors Phase: Runs via executeRemaining() in registration order. These components (like BundleRollupProcessor, PackageGenerator, and ChangesManifestProcessor) perform final transformations, such as rolling up Digital Experience Bundles, generating package.xml and destructiveChanges.xml files, or serializing a JSON changes manifest.

    Each processor is wrapped in error isolation, meaning a failure in one processor will produce a warning rather than crashing the entire pipeline.

  7. How the Metadata Registry works

    main

    The MetadataRepository is a central lookup table that maps file paths to Salesforce metadata type definitions. It uses a priority-based merging strategy to resolve metadata types.

    Registry Priority Chain (Highest to Lowest):

    1. Internal registry: SGD-specific overrides and additions (overrides SDR by xmlName).
    2. Additional registry: User-provided custom types via the --additional-metadata-registry flag.
    3. SDR registry: Standard Salesforce metadata types from @salesforce/source-deploy-retrieve.

    Lookup Indexes: To ensure fast resolution, the registry maintains three indexes:

    • extIndex: Primary lookup by file extension (e.g., .cls).
    • dirIndex: Fallback lookup by directory name (e.g., classes/) when extensions are ambiguous. It picks the deepest match and respects container types (like bundle or digitalExperience).
    • xmlNameIndex: Direct lookup by the Salesforce API type name (e.g., ApexClass).
  8. Understand SFDX-Git-Delta error handling philosophy

    main

    SGD follows a warnings-not-exceptions philosophy to ensure that a single broken file does not abort the entire diff processing pipeline. Errors are handled differently depending on the layer:

    • Config validation: Fatal errors. Throws ConfigError or MetadataRegistryError which propagate to the CLI.
    • Handlers (collect()): Errors are caught and converted into warnings within the HandlerResult.
    • Post-processors: Each is wrapped in _safeProcess; failures result in warnings.
    • Git operations: Errors are debug-logged and result in silent degradation (returning empty/false).
    • XML parsing: Produces a MalformedXML warning including the file path and revision.

    Error Hierarchy:

    • SgdError: The base error class; wraps the original error as cause.
    • ConfigError: Occurs due to invalid user configuration.
    • MetadataRegistryError: Occurs due to an invalid additional metadata registry.
  9. Understand the SFDX-Git-Delta execution pipeline

    main

    SFDX-Git-Delta (SGD) processes git diffs through a linear six-stage pipeline. A key design principle is that collection is separated from execution: handlers write manifest entries to a shared ChangeSet sink and accumulate CopyOperation plans, but nothing is written to disk until the final IOExecutor stage. This allows for deduplication and conflict resolution before any I/O occurs.

    The Pipeline Stages:

    1. Config Validation: Validates and normalizes user inputs (SHAs, API versions, file paths).
    2. Metadata Registry: Builds a lookup table mapping file paths to Salesforce metadata type definitions.
    3. Git Diff Collection: Streams diff lines between the from and to commits.
    4. Diff Interpretation: Uses handlers to interpret diff lines.
    5. Post-Processing: Manages post-processing tasks.
    6. I/O Execution: Executes the final I/O operations and generates packages.
  10. Understand the Diff Interpretation and Handler Hierarchy

    main

    SFDX-Git-Delta uses a tiered handler system to interpret git diff lines and determine how metadata should be represented in the resulting manifest and delta files. When a diff line is processed, the TypeHandlerFactory resolves a specific handler using a multi-tier resolution chain. This allows most new metadata types to be handled automatically via the SDR (Salesforce Data Registry) without requiring custom code, while specialized types (like Flows or Bundles) use explicit overrides.

    Handler Resolution Tiers

    The system checks for handlers in the following order:

    1. Explicit override: Uses xmlName in the handlerMap (e.g., FlowFlowHandler).
    2. Folder-based: Uses inFolder: true (e.g., Document, EmailTemplate).
    3. Adapter-based: Uses adapter from SDR strategies (e.g., bundleInResourceHandler).
    4. Child heuristics: Uses xmlTag + key or folderPerType patterns (e.g., WorkflowAlert or ListView).
    5. InFile parent: Detects if the file has children with specific xmlTag+key patterns (e.g., Workflow).
    6. Fallback: Uses StandardHandler for general types (e.g., ApexClass).
    flowchart TD
        Line["Diff line<br/>'A force-app/main/.../MyClass.cls'"] --> TF["TypeHandlerFactory"]
        TF --> ME["MetadataBoundaryResolver<br/>creates MetadataElement"]
        TF --> T1{"xmlName in<br/>handlerMap?"}
        T1 -->|Yes| SH["Explicit Handler Override"]
        T1 -->|No| T2{"inFolder?"}
        T2 -->|Yes| IF["InFolderHandler"]
        T2 -->|No| T3{"adapter in<br/>adapterHandlerMap?"}
        T3 -->|Yes| AH["Adapter-Based Handler"]
        T3 -->|No| T4{"has parentXmlName?"}
        T4 -->|Yes| CH["Child Type Heuristics"]
        T4 -->|No| T5{"parent of<br/>InFile children?"}
        T5 -->|Yes| IFH["InFileHandler"]
        T5 -->|No| DH["StandardHandler"]
        CH --> C["handler.collect(sink)"]
        SH --> C
        IF --> C
        AH --> C
        IFH --> C
        DH --> C
        C --> HR["HandlerResult<br/>{changes: ChangeSet, copies, warnings}"]
  11. Configure and validate SGD settings

    main

    The ConfigValidator stage ensures that user inputs are valid before the pipeline starts. Fatal errors (ConfigError) at this stage will abort the entire process.

    Validation behaviors:

    • Git Refs: Resolves symbolic refs like HEAD or branch names to full SHAs.
    • SHA Existence: Verifies that both from and to SHAs exist in the repository.
    • API Version: Defaults to the version in sfdx-project.json or the latest SDR version. If the latest-version lookup (via AppExchange) fails due to network/firewall issues, the provided apiVersion is used as-is. Otherwise, a ConfigError is thrown.
    • Path Sanitization: Sanitizes output directories, source directories, and ignore files.