npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@bearingpointsalesforce/cicd-builder

v0.9.3

Published

Reusable Salesforce CI/CD step functions and pipeline runner.

Readme

CI/CD Builder

A shared Node.js library that provides reusable Salesforce CI/CD pipeline steps as plain async functions, driven by JSON configuration files. Replace complex bash scripts and other approaches that are not clear. Its dependency is node, which is required for SF CLI as well. If you use the loadData step, the SFDMU plugin must also be installed (sf plugins install sfdmu).

1. Quick Start

# Install dependencies
npm install

# Run a pipeline
node src/bin.js <pipeline.json> <scratchOrgAlias>

# Examples
node src/bin.js config/pipeline.validate.json myScratchOrg
node src/bin.js config/pipeline.scratch.json newDevOrg

The scratch org alias is required. If the org already exists, the createOrg step will be skipped. The dev hub is always taken from the sf CLI default configuration (sf config set target-dev-hub=yourHub).


2. Pipeline Files

A pipeline file defines an ordered sequence of steps to execute. It is a JSON file placed in the config/ directory alongside ciconfig.json.

2.1 Structure

{
    "pipeline": "pipelineName",
    "steps": [
        { "type": "stepType", "option": "value" },
        { "type": "anotherStep" }
    ]
}

| Property | Required | Description | |------------|----------|-------------------------------------------| | pipeline | Yes | Name of the pipeline (used in log output) | | steps | Yes | Ordered array of step objects to execute |

Each step must have a type property. Additional properties are step-specific options.

2.2 Available Step Types

| Step Type | Description | |-----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | createOrg | Creates a new scratch org using the default dev hub. If an org with the given alias already exists, creation is skipped. | | deleteOrg | Deletes the scratch org. | | printOrgUrl | Prints the org login URL without opening a browser. | | executeApex | Executes anonymous apex scripts for a named phase defined in ciconfig.json. | | runTests | Runs apex tests in the org. By default runs in parallel with automatic synchronous rerun of failures. | | installPackages | Installs packages defined in ciconfig.packageIds. | | deploySource | Deploys source metadata to the org. | | sortMetadata | Sorts metadata XML files (profiles, permission sets, muting permission sets, permission set groups) for consistent formatting. | | assignPermSets | Assigns permission sets from ciconfig.permSets to the default org user. Already assigned permission sets are skipped. | | assignPermSetGroups | Assigns permission set groups from ciconfig.permSetGroups to the default org user. Polls for PermissionSetGroup.Status = 'Updated' before assigning (up to 10 attempts, 7.5s interval). | | assignProfile | Assigns a profile from ciconfig.profile to the default org user. Uses a temporary System Administrator user as a workaround because Salesforce does not allow users to change their own profile. | | loadData | Loads data into the scratch org from CSV files using the SFDMU plugin. | | displayLimits | Displays the org's API limits. |

2.2.1 createOrg

Creates a new scratch org using the default dev hub. If an org with the given alias already exists, creation is skipped.

{
    "type": "createOrg",
    "definitionFile": "config/project-scratch-def.json",
    "ci": true
}

| Option | Type | Default | Description | |---------------------|---------|-----------------------------------|---------------------------------------------------------------------------------| | definitionFile | string | config/project-scratch-def.json | Path to the scratch org definition file | | durationDays | number | 30 | Number of days before the scratch org expires | | wait | number | 60 | Minutes to wait for org creation to complete | | ci | boolean | false | CI mode: merges features/settings into definition file, suppresses manual steps | | validateOrg | boolean | false | Run apex verification script after creation, retry if corrupted | | validationTimeout | number | 30 | Seconds to wait before running org validation |

2.2.2 deleteOrg

Deletes the scratch org.

{
    "type": "deleteOrg"
}

No options. Uses the scratch org alias from the CLI argument.

2.2.3 printOrgUrl

Prints the org login URL without opening a browser.

{
    "type": "printOrgUrl"
}

No options.

2.2.4 executeApex

Executes anonymous apex scripts for a named phase defined in ciconfig.json.

{
    "type": "executeApex",
    "phase": "setup"
}

| Option | Type | Required | Description | |---------|--------|----------|---------------------------------------------------------------------| | phase | string | Yes | Key in ciconfig.apexPhases (e.g. setup, postDeploy, verify) |

2.2.5 runTests

Runs apex tests in the org. By default, tests run in parallel. Any failed test classes are automatically rerun synchronously to catch flaky failures caused by parallel execution. Set parallel to false to run all tests synchronously in a single run.

{
    "type": "runTests",
   "testLevel": "RunLocalTests",
   "parallel": true
}

| Option | Type | Default | Description | |-------------|---------|-----------------|----------------------------------------------------------------------------------------------------------| | testLevel | string | RunLocalTests | RunLocalTests or RunAllTestsInOrg | | parallel | boolean | true | Run tests in parallel with automatic synchronous rerun of failures. Set to false for synchronous only. |

2.2.6 installPackages

Installs packages defined in ciconfig.packageIds.

{
    "type": "installPackages"
}

| Option | Type | Default | Description | |----------------|--------|--------------|----------------------------| | securityType | string | AdminsOnly | AdminsOnly or AllUsers |

2.2.7 deploySource

Deploys source metadata to the org.

{
    "type": "deploySource",
    "sourceDir": "force-app",
    "ci": true
}

| Option | Type | Default | Description | |-------------|---------|---------|-----------------------------------------------------------------------| | sourceDir | string | — | Source directory to deploy (e.g. force-app) | | checkOnly | boolean | false | Validate only, do not deploy | | ci | boolean | false | Append ciconfig.ciForceignore paths to .forceignore before deploy |

2.2.8 sortMetadata

Sorts metadata XML files (profiles, permission sets, muting permission sets, permission set groups) for consistent formatting.

{
    "type": "sortMetadata",
    "directory": "force-app/main/default"
}

| Option | Type | Default | Description | |-------------|--------|--------------------------|-------------------------| | directory | string | force-app/main/default | Root metadata directory |

2.2.9 assignPermSets

Assigns permission sets from ciconfig.permSets to the default org user. Already assigned permission sets are skipped.

{
    "type": "assignPermSets"
}

No options. Reads from ciconfig.permSets.

2.2.10 assignPermSetGroups

Assigns permission set groups from ciconfig.permSetGroups to the default org user. Polls for PermissionSetGroup.Status = 'Updated' before assigning (up to 10 attempts, 7.5s interval).

{
    "type": "assignPermSetGroups"
}

No options. Reads from ciconfig.permSetGroups.

2.2.11 assignProfile

Assigns a profile from ciconfig.profile to the default org user. Uses a temporary System Administrator user as a workaround because Salesforce does not allow users to change their own profile.

{
    "type": "assignProfile"
}

No options. Reads from ciconfig.profile.

2.2.12 loadData

Loads data into the scratch org from CSV files using the SFDMU SF CLI plugin. The data directory must contain an export.json configuration file and CSV files named after sObject API names (e.g. Account.csv, Contact.csv). SFDMU handles relationship resolution, lookup matching, and bulk operations automatically.

Requires the SFDMU plugin: sf plugins install sfdmu

{
   "type": "loadData",
   "path": "data"
}

| Option | Type | Default | Description | |--------|--------|---------|-------------------------------------------------------| | path | string | data | Directory containing export.json and CSV data files |

2.2.13 displayLimits

Displays the org's API limits.

{
    "type": "displayLimits"
}

No options.

2.3 Example Pipelines

2.3.1 Validation pipeline — create scratch org, deploy, test, delete:

{
    "pipeline": "validate",
    "steps": [
        { "type": "createOrg", "definitionFile": "config/project-scratch-def.json", "durationDays": 1, "ci": true },
        { "type": "installPackages" },
        { "type": "deploySource", "sourceDir": "force-app", "ci": true },
        { "type": "runTests", "testLevel": "RunLocalTests" },
        { "type": "deleteOrg" }
    ]
}

2.3.2 Scratch org setup pipeline — full setup for development:

{
    "pipeline": "createScratch",
    "steps": [
        {
            "type": "createOrg",
            "definitionFile": "config/project-scratch-def.json",
            "ci": true,
            "validateOrg": true
        },
        {
            "type": "installPackages"
        },
        {
            "type": "deploySource",
            "sourceDir": "force-app"
        },
        {
            "type": "assignPermSets"
        },
        {
            "type": "assignPermSetGroups"
        },
        {
            "type": "assignProfile"
        },
       {
          "type": "loadData"
        }
    ]
}

3. Configuration File (ciconfig.json)

The ciconfig.json file lives in the config/ directory alongside the pipeline files. It holds project-specific settings consumed by the pipeline steps.

3.1 Full Example

{
    "projectName": "my-project",
    "packageIds": [
        { "id": "04t5p000000Gey6AAC", "name": "DLRS", "versionNumber": "x.y" },
        { "id": "04t1n000002NZ6gAAG" }
    ],
    "apexPhases": {
        "setup": ["scripts/apex/enableMarketingUser.apex"],
        "postDeploy": ["scripts/apex/insertCustomSettings.apex"],
        "verify": ["scripts/apex/verifyOrgFeatures.apex"]
    },
    "profile": "Custom_Sales",
    "permSetGroups": ["Integration_Managers", "Standard_User_Access"],
    "permSets": ["Custom_Edit_Permissions"],
    "defaultUserFields": {
        "Country": "Czechia",
        "City": "Prague",
        "TimeZoneSidKey": "Europe/Prague"
    },
    "ciForceignore": [
        "Admin.profile",
        "src/main/default/entitlementProcesses/basic_sla.entitlementProcess-meta.xml"
    ],
    "postCreateManualSteps": {
        "apexOrgVerification": "scripts/apex/verifyOrgFeatures.apex",
        "continueInstruction": "Please continue by running 'npm run sfci:init:postcreate'.",
        "features": [
            {
                "name": "ContactsToMultipleAccounts",
                "manualStepDescription": "Enable 'Allow users to relate a contact to multiple accounts' in Setup > Account Settings."
            }
        ],
        "settings": {
            "accountSettings": {
                "enableRelateContactToMultipleAccounts": true
            }
        }
    }
}

3.2 Properties Overview

| Property | Type | Required | Description | |-------------------------|------------------|---------------------------------|--------------------------------------------------------------------------------------------| | projectName | string | Yes | A human-readable name for your Salesforce project, used in log output during org creation. | | packageIds | array of objects | Yes (for installPackages) | List of managed/unlocked packages to install into the scratch org. | | apexPhases | object | Yes (for executeApex) | Map of phase name to array of apex script file paths, executed by executeApex steps. | | profile | string | Yes (for assignProfile) | Salesforce profile name to assign to the default scratch org user. | | permSetGroups | array of strings | Yes (for assignPermSetGroups) | Permission set group developer names to assign to the default org user. | | permSets | array of strings | Yes (for assignPermSets) | Permission set API names to assign to the default org user. | | defaultUserFields | object | No | User field values to set on the scratch org admin user after creation. | | ciForceignore | array of strings | No | Additional paths to append to .forceignore before deployment in CI mode. | | postCreateManualSteps | object | No | Configuration for post-creation behaviour: CI auto-merge and local manual step display. |

3.3 Properties Reference


3.3.1 projectName

| | | |--------------|-------------| | Type | string | | Required | Yes | | Used by | createOrg |

A human-readable name for your Salesforce project. This is used to identify the project during org creation and will appear in log output. The pipeline will fail early if this is missing, so make sure to set it before running any pipeline.


3.3.2 packageIds

| | | |--------------|----------------------------------| | Type | array of objects | | Required | Yes (for installPackages step) | | Used by | installPackages |

The list of managed or unlocked packages that need to be installed into the scratch org before you can work with it. Think of this as your project's package dependencies — things like DLRS, CPQ, or any custom packages your org relies on.

Each entry must have an id (the package version ID starting with 04t). You can optionally add name and versionNumber for clearer log output, but they don't affect installation.

| Attribute | Description | Example | |-----------------|------------------------------------------------------------------|------------------------| | id | Package version ID (required). Must start with 04t. | "04t5p000000Gey6AAC" | | name | Human-readable package name (optional). Used in log output only. | "DLRS" | | versionNumber | Package version number (optional). Used in log output only. | "x.y" |

{
   "packageIds": [
      {
         "id": "04tXXXXXXXXXXXXXXX",
         "name": "Package Name",
         "versionNumber": "1.0"
      },
      {
         "id": "04tYYYYYYYYYYYYYYY"
      }
   ]
}

3.3.3 apexPhases

| | | |--------------|---------------------------------------------------| | Type | object (map of phase name to array of file paths) | | Required | Yes (for executeApex step) | | Used by | executeApex |

Defines groups of anonymous apex scripts that should run at different points during the pipeline. Each phase is a named collection of scripts — you choose when they run by placing executeApex steps in your pipeline and referencing the phase name.

Common phases include:

  • setup — scripts that need to run before anything is deployed (e.g. enabling features, setting up users)
  • postDeploy — scripts that depend on your deployed metadata (e.g. inserting custom settings, test data)
  • verify — scripts that validate the org is configured correctly after everything is done

You can define as many phases as you need with any names you want.

| Attribute | Description | Example | |---------------|--------------------------------------------------------------------------------|------------------------------------------------| | <phaseName> | Array of file paths to anonymous apex scripts. The key is any name you choose. | "setup": ["scripts/apex/enableFeature.apex"] |

{
   "apexPhases": {
      "setup": [
         "scripts/apex/enableFeature.apex"
      ],
      "postDeploy": [
         "scripts/apex/insertData.apex",
         "scripts/apex/configureOrg.apex"
      ],
      "verify": [
         "scripts/apex/verifyFeatures.apex"
      ]
   }
}

3.3.4 profile

| | | |--------------|--------------------------------| | Type | string | | Required | Yes (for assignProfile step) | | Used by | assignProfile |

The name of the Salesforce profile to assign to the default scratch org user. By default, scratch org users get the System Administrator profile. If your project requires developers to work under a different profile (e.g. a custom sales profile), specify it here.

Note: Salesforce doesn't allow users to change their own profile, so the tool creates a temporary admin user behind the scenes to perform the switch.


3.3.5 permSetGroups

| | | |--------------|--------------------------------------| | Type | array of strings | | Required | Yes (for assignPermSetGroups step) | | Used by | assignPermSetGroups |

List of permission set group developer names to assign to the default scratch org user. These are the API names you see in Setup, not the labels.

The tool automatically waits for each permission set group to reach Status = 'Updated' before assigning it, since Salesforce needs time to calculate the combined permissions after a deployment.


3.3.6 permSets

| | | |--------------|---------------------------------| | Type | array of strings | | Required | Yes (for assignPermSets step) | | Used by | assignPermSets |

List of permission set names to assign to the default scratch org user. These are the API names of the permission sets.

The tool checks which permission sets are already assigned and skips them, so it's safe to re-run the pipeline without causing duplicate assignment errors.


3.3.7 defaultUserFields

| | | |--------------|-------------| | Type | object | | Required | No | | Used by | createOrg |

Salesforce user field values to set on the scratch org admin user right after creation. This prevents common errors like FIELD_INTEGRITY_EXCEPTION on the Country field, which happens when the default user has no country set and certain features expect one.

If this property is absent, no user fields are updated. Add any standard User field name as a key with the desired value.

| Attribute | Description | Example | |------------------|-------------------------------------------------------------------------|------------------------| | <fieldName> | Any standard Salesforce User field name as key, with the desired value. | "Country": "Czechia" | | Country | Country of the scratch org user. | "Czechia" | | City | City of the scratch org user. | "Prague" | | TimeZoneSidKey | Timezone identifier for the scratch org user. | "Europe/Prague" |

{
   "defaultUserFields": {
      "Country": "Czechia",
      "City": "Prague",
      "TimeZoneSidKey": "Europe/Prague"
   }
}

3.3.8 ciForceignore

| | | |--------------|----------------------------------| | Type | array of strings | | Required | No | | Used by | deploySource (when ci: true) |

Additional paths to append to .forceignore before deployment when running in CI mode. This is useful for metadata that exists in your repository but causes deployment failures in scratch orgs — for example, certain profiles or org-specific configurations.

These paths are only added when the deploySource step has "ci": true. When developers run locally without the ci flag, their .forceignore stays untouched.


3.3.9 postCreateManualSteps

| | | |--------------|-------------| | Type | object | | Required | No | | Used by | createOrg |

Configuration for what happens after a scratch org is created. This serves two purposes:

  1. In CI mode (ci: true): The features names and settings are automatically merged into the scratch org definition file before creation, so the org gets the right configuration without manual intervention.

  2. For local development (no ci flag): The tool prints a coloured box with manual steps the developer needs to perform in the org's Setup UI, since some features can't be enabled through the definition file alone.

| Attribute | Description | Example | |-----------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------| | apexOrgVerification | Path to an apex script that checks whether all required features were enabled correctly. Used when validateOrg: true — if the script detects errors, the org is deleted and re-created automatically. | "scripts/apex/verifyOrgFeatures.apex" | | continueInstruction | A message shown to developers after org creation, typically telling them what command to run next. Only shown outside CI mode. | "Please continue by running 'npm run sfci:init:postcreate'." | | features | List of features that need special handling. Each entry has a name (merged into the definition file in CI) and a manualStepDescription (shown to developers locally). | See sub-attributes below. | | settings | Salesforce settings to merge into the scratch org definition file in CI mode. Uses the same structure as settings in the definition file itself. | {"accountSettings": {"enableRelateContactToMultipleAccounts": true}} |

Each entry in features has:

| Attribute | Description | Example | |-------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------| | name | Salesforce feature name — automatically added to the scratch org definition file when running in CI mode. | "ContactsToMultipleAccounts" | | manualStepDescription | Human-readable instructions for the developer on how to enable this feature manually in the org's Setup UI. Only shown when running locally (non-CI). | "Enable 'Allow users to relate a contact to multiple accounts' in Setup > Account Settings." |


4. CI Integration

Install the package in your project, then call the CLI in your pipeline. CI stays thin — one command per pipeline:

# GitHub Actions
-   run: npm install @bearingpointsalesforce/cicd-builder
-   run: npx sf-cicd-builder config/pipeline.validate.json ci-${{ github.run_id }}
# GitLab CI
script:
   - npm install @bearingpointsalesforce/cicd-builder
   - npx sf-cicd-builder config/pipeline.validate.json ci-$CI_PIPELINE_ID
# Azure DevOps
-   script: npm install @bearingpointsalesforce/cicd-builder
-   script: npx sf-cicd-builder config/pipeline.validate.json ci-$(Build.BuildId)

5. Consuming as a Dependency

Add the package to your project:

npm install @bearingpointsalesforce/cicd-builder

Then run pipelines from the command line:

# Run a full pipeline
npx sf-cicd-builder config/pipeline.validate.json myScratchOrg

# Different pipeline for scratch org setup
npx sf-cicd-builder config/pipeline.scratch.json newDevOrg