Newer Version Available

This content describes an older version of this product. View Latest

Replace Strings in Code Before Deploying

Automatically replace strings in your metadata source files with specific values right before you deploy the files to an org.
These sample use cases describe scenarios for using pre-deployment string replacement:
  • A NamedCredential contains an endpoint that you use for testing. But when you deploy the source to your production org, you want to specify a different endpoint.
  • An ExternalDataSource contains a password that you don’t want to store in your repository, but you’re required to deploy the password along with your metadata.
  • You deploy near-identical code to multiple orgs. You want to conditionally swap out some values depending on which org you’re deploying to.

String replacement actually occurs when source-formatted files are converted to a ZIP file right before being deployed to the org with the force:source:deploy and force:source:push commands. The changes that result from string replacement are never written to your project; they apply only to the deployed files.

Configure String Replacement

Configure string replacement by adding a replacements property to your sfdx-project.json file. The property accepts multiple entries that consist of keys that define the:
  • Source file or files that contain the string to be replaced.
  • The string to be replaced.
  • The replacement value.
Let’s look at an example to see how it works. This sample sfdx-project.json specifies that when the file force-app/main/default/classes/myClass.cls is deployed, all occurrences of the string replaceMe are replaced with the value of the THE_REPLACEMENT environment variable:
1{
2  "packageDirectories": [
3     {
4       "path": "force-app",
5       "default": true
6     }
7  ],
8  "name": "myproj",
9  "replacements": [
10    {
11      "filename": "force-app/main/default/classes/myClass.cls",
12      "stringToReplace": "replaceMe",
13      "replaceWithEnv": "THE_REPLACEMENT"  
14    }
15  ]
16}

You can specify these keys in the replacements property.

Location of Files
One of the following properties is required:
  • filename: Single file that contains the string to be replaced.
  • glob: Collection of files that contain the string to be replaced. Example: **/classes/*.cls.
String to be Replaced
One of the following properties is required:
  • stringToReplace: The string to be replaced.
  • regexToReplace: Regular expression (regex) that specifies a string pattern to be replaced.
Replacement Value
One of the following properties is required:
  • replaceWithEnv: Specifies that the string is replaced with the value of the environment variable.
  • replaceWithFile: Specifies that the string is replaced with the contents of a file.
Conditional Processing
This property is optional:
  • replaceWhenEnv: Specifies that a string replacement occur only when a specific environment variable is set to a specific value. Use the property env to specify the environment variable and the property value to specify the value that triggers the string replacement.

Follow these syntax rules:

  • Always use forward slashes for directories (/), even on Windows.
  • Both JSON and regular expressions use the backslash (\) as an escape character. As a result, when you use a regular expression to match a dot, which requires escaping, you must use two backslashes for the regexToReplace value:
    1"regexToReplace" : "\\."
    Similarly, to match a single backslash, you must specify three of them.
    1"regexToReplace" : "\\\"

Examples

This example is similar to the previous example but shows how to configure string replacement for two files:
1"replacements": [
2  {
3    "filename": "force-app/main/default/classes/FirstApexClass.cls",
4    "stringToReplace": "replaceMe",
5    "replaceWithEnv": "THE_REPLACEMENT"
6  },
7  {
8    "filename": "force-app/main/default/classes/SecondApexClass.cls",
9    "stringToReplace": "replaceMe",
10    "replaceWithEnv": "THE_REPLACEMENT"
11  }
12]

This example shows how to specify that the string replacement occur only if an environment variable called DEPLOY_DESTINATION exists and it has a value of PROD.

1"replacements": [
2  {
3    "filename": "force-app/main/default/classes/myClass.cls",
4    "stringToReplace": "replaceMe",
5    "replaceWithEnv": "THE_REPLACEMENT",
6    "replaceWhenEnv": [{
7      "env": "DEPLOY_DESTINATION",
8      "value": "PROD"
9    }]  
10  }
11]

This example specifies that when the Apex class files in the force-app/main/default directory are deployed, all occurrences of the string replaceMe are replaced with the contents of the file replacementFiles/copyright.txt.

1"replacements": [
2  {
3    "glob": "force-app/main/default/classes/*.cls",
4    "stringToReplace": "replaceMe",
5    "replaceWithFile": "replacementFiles/copyright.txt"
6  }
7]
Use a regular expression to specify a search pattern for text rather than the literal text. For example, Apex class XML files always contain an <apiVersion> element that specifies the Salesforce API version, as shown in this snippet.
1<?xml version="1.0" encoding="UTF-8" ?>
2<ApexClass xmlns="http://soap.sforce.com/2006/04/metadata">
3    <apiVersion>55.0</apiVersion>
4    <status>Active</status>
5</ApexClass>
Let’s say you want to test your Apex classes on a more recent API version before you actually update all your classes. This example shows how to use a regular expression to search for the <apiVersion> element. At deploy, the element is then replaced with a specific string, such as <apiVersion>56.0</apiVersion>, which is contained in the replacementFiles/latest-api-version.txt file.
1"replacements": [
2  {
3    "glob": "force-app/main/default/classes/*.xml",
4    "regexToReplace": "<apiVersion>\\d+\\.0</apiVersion>",
5    "replaceWithFile": "replacementFiles/latest-api-version.txt"
6  }
7]

Test String Replacements

Follow these steps to test string replacement without actually deploying files to the org.

  1. Set the SF_APPLY_REPLACEMENTS_ON_CONVERT environment variable to true.
  2. Run the force:source:convert command, which converts the source files into metadata API format. For example:
    1sfdx force:source:convert --outputdir mdapiOut --sourcepath force-app
  3. Inspect the files in the output directory (mdapiOut in our example) for the string replacements and what exactly will be deployed to the org.

Be careful when writing passwords or secrets to the file system while testing. Also, be sure to reset any environment variables you set during testing so they aren’t accidentally applied later.

Warning

Tips and Tricks

  • (macOS or Linux only) When using the replaceWithEnv or replaceWhenEnv properties, you can specify that the environment variables apply to a single command by prepending the variables before the command execution. For example:
    1THE_REPLACEMENT="some text" DEPLOY_DESTINATION=PROD sfdx force:source:push

    Be careful when setting passwords or secrets this way, because they show up in your terminal history.

    Warning

  • If you’ve configured many string replacements, and are finding it difficult to manage, check out open-source tools that load the contents of one or more files to your environment, such as dotenv-cli. In this example, environment variables configured in two local .env files are loaded before the force:source:push command execution:
    1dotenv -e .env1 -e .env2 sfdx force:source:push

    Don’t commit passwords or secrets in .env files.

    Warning

  • If you specify --json for either force:source:deploy or force:source:push, the JSON output includes a replacements property that lists the affected files and the string that was replaced.

    The force:source:push human-readable output shows the string replacement information by default; use --quiet to remove it, which also removes it from the JSON output.

    To view string replacement information in the force:source:deploy human-readable output, specify --verbose.

Considerations and Limitations

  • If you configure multiple string replacements in multiple files, the performance of the deployment can degrade. Consider using the filename key when possible, to ensure that you open only one file. If you must use glob, try to limit the number of files that are opened by specifying a single directory or metadata type.

    For example, "glob": "force-app/main/default/classes/*.cls" targets Apex class files in a specific directory, which is better than "glob": "**/classes/**”, which searches for all Apex metadata files in all package directories.

  • Be careful using string replacement in static resources. When not doing string replacement, Salesforce CLI simply zips up all static resources when it first encounters their directory and deploys them as-is. If you configure string replacement for a large static resource directory, the CLI must inspect a lot more files than usual, which can degrade performance.
  • The force:mdapi:deploy command doesn’t support string replacement.
  • If your deployment times out, or you specify the --wait 0 flag of force:source:deploy, and then run force:source:deploy:report to see what happened, the deployed files contain string replacements as usual. However, the output of force:source:deploy:report doesn’t display the same string replacement information as force:source:deploy --verbose would have.