11

We've recently started adopting Salesforce DX and we're trying to figure out the best way to spin up Scratch orgs.

As of now, we have multiple managed packages from the AppExchange installed in production, including Marketing Cloud Connect and Cirrus Insight and we would like to make certain scratch orgs have them.

I know there is a way to install packages with the sfdx force:package:install command, but is there any way to do it via configuration so that we don't have to execute a certain set of commands everytime we create a scratch org?

Thanks!

6 Answers 6

9

This should be possible with Second Generation Packaging. You can specify the dependencies of a package in the sfdx-project.json file.

In case you prefer to install the package in a script, you could do it like this:

    #login to your DevHub
    sfdx force:auth:jwt:grant --clientid [consumerKey] --username [devHubUserName] --jwtkeyfile assets/server.key --setdefaultdevhubusername

    #create scratch
    sfdx force:org:create -v [devHubUserName] -s -f [config/project-scratch-def.json] -a [scratchOrgName]

    #push source to scratch
    sfdx force:source:push -u [scratchOrgName]

    #install package
    sfdx force:package:install -p [packageId] -w 30 -o [scratchOrgName]
1
  • 8
    Just because you can specify the dependencies in the sfdx-project.json doesn't mean that the SFDX CLI automatically installs those packages in your scratch org when you create it - I've tried this out.
    – Phil W
    Apr 30, 2020 at 7:10
13

As example with the Salesforce CPQ 216.10.1 package, you can do the following.

  1. Create the force-app/main/default/installedPackages directory
  2. Create a file named SBQQ.installedPackage-meta.xml inside the new directory

The file you created should have the XML content below.

<?xml version="1.0" encoding="UTF-8"?>
<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <activateRSS>false</activateRSS>
    <versionNumber>216.10.1</versionNumber>
</InstalledPackage>

You can take a look at this super simple repo as a reference.

13
  • I can confirm, that this also works for another tested Package: Cloud Coach Milestones. I added a CCMI.installedPackage-meta.xml. This answer is definitely underrated. Mar 11, 2019 at 9:32
  • Can you do this for an unmanaged package? eg, the packages used for some Trailhead Superbadge setups? Mar 16, 2019 at 0:59
  • Hi @ThomasTaylor you can definitely try and let us know! My initial guess would be "no", because there's no package prefix to put into the filename, but there may be some other workaround or special notation that makes magic happen.
    – Marty C.
    Mar 21, 2019 at 16:34
  • This worked for me as well.
    – BrBarr
    Jun 11, 2019 at 17:49
  • 1
    @PhilW you can install multiple packages in the same push or deployment with sfdx, and with the Metadata API as well. However, if there are dependencies, in my experience Salesforce won't know which package to install first. In those situations you'll have to manually script the correct order of deployments.
    – Marty C.
    Apr 29, 2020 at 19:58
6

There's two basic ways you can install packages, depending on if you have dependencies (which are unusual, but can happen).

MDAPI Deployment

If you do not have any dependencies, the most simple format is to use a package.xml mdapi style deployment.

sfdx force:mdapi:deploy -d packages/ -u alias -w 99

Where packages/ contains a package.xml:

<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
  <types>
    <members>*</members>
    <name>InstalledPackage</name>
  </types>
</Package>

And, for each package to install, a NAMESPACE.installedPackage:

<?xml version="1.0" encoding="UTF-8"?>
<InstalledPackage xmlns="http://soap.sforce.com/2006/04/metadata" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <activateRSS>false</activateRSS>
    <versionNumber>216.10.1</versionNumber>
    <password>ClearTextPassword</password>
</InstalledPackage>

<password> is required if the package install has a password, and must not be supplied if there is no package install password.

SFDX Install Script

The installing multiple packages can be written as a shell script.

For example:

sfdx force:package:install -w -99 -p package-1 -r -k password-1
sfdx force:package:install -w -99 -p package-2 -r -k password-2
sfdx force:package:install -w -99 -p package-3 -r

In this example, there's two packages that need a password, and a third that does not. Here, you have precise order over the order of execution, and you could also perform other tasks at the same time, such as importing custom settings or normal records (accounts, contacts, etc).

If all the packages don't require a password, you can also use this script from Salesforce:

#!/bin/bash
# The execution of this script stops if a command or pipeline has an error.
# For example, failure to install a dependent package will cause the script
# to stop execution.

set -e

# Specify a package version id (starts with 04t)
# If you know the package alias but not the id, use force:package:version:list to find it.
PACKAGE=04tB0000000NmnHIAS
# Specify the user name of the subscriber org.
[email protected]
# Specify the timeout in minutes for package installation.
WAIT_TIME=15
echo "Retrieving dependencies for package Id: "$PACKAGE
# Execute soql query to retrieve package dependencies in json format.
RESULT_JSON=`sfdx force:data:soql:query -u $USER_NAME -t -q "SELECT Dependencies FROM SubscriberPackageVersion WHERE Id='$PACKAGE'" --json`
# Parse the json string using python to test whether the result json contains a list of ids or not.
DEPENDENCIES=`echo $RESULT_JSON | python -c 'import sys, json; print json.load(sys.stdin)["result"]["records"][0]["Dependencies"]'`
# If the parsed dependencies is None, the package has no dependencies. Otherwise, parse the result into a list of ids.
# Then loop through the ids to install each of the dependent packages.
if [[ "$DEPENDENCIES" != 'None' ]]; then
    DEPENDENCIES=`echo $RESULT_JSON | python -c '
import sys, json
ids = json.load(sys.stdin)["result"]["records"][0]["Dependencies"]["ids"]
dependencies = []
for id in ids:
    dependencies.append(id["subscriberPackageVersionId"])
print " ".join(dependencies)
'` 
    echo "The package you are installing depends on these packages (in correct dependency order): "$DEPENDENCIES
    for id in $DEPENDENCIES
    do
        echo "Installing dependent package: "$id
        sfdx force:package:install --package $id -u $USER_NAME -w $WAIT_TIME --publishwait 10
    done
else
    echo "The package has no dependencies"
fi
# After processing the dependencies, proceed to install the specified package.
echo "Installing package: "$PACKAGE
sfdx force:package:install --package $PACKAGE -u $USER_NAME -w $WAIT_TIME --publishwait 10
exit 0;

This script queries a package from an org, figures out their dependencies in order, and then installs everything for you. Note that if there are passwords involve, this script will fail. You can also manually create a file with passwords in it, and write a shell script to read those.

set -e
while IFS=, read -r package password; do
  if [ -z "$password" ]
  then
    sfdx force:package:install -p "$package" -w 99
  else
    sfdx force:package:install -p "$package" -w 99 -k "$password"
  fi
done < packages-to-install.csv

Where the format is a list of package names or aliases, followed by an optional password:

package1,password1
package2,password2
package3

You could spruce this up with more elaborate features, but this should be fine for most simple install paths.

4

Not at present. Scripting the installations to happen after the scratch org has been created is the most appropriate way to do what you want today.

1
  • 5
    Any example scripts or guidelines would improve the answer
    – Eric
    Nov 8, 2017 at 7:17
3

If you add it to a git repo, CumulusCI is capable of installing dependencies (even multiple layers) based on the URL: http://cumulusci.readthedocs.io/en/latest/features.html#managing-dependencies

Additionally, the namespace and version or a zip file can be used. Examples from their documentation:

URL

project:
    dependencies:
        - github: https://github.com/SalesforceFoundation/HEDAP

ZIP

project:
    dependencies:
        - zip_url: https://github.com/SalesforceFoundation/CumulusReports/archive/master.zip
          subfolder: CumulusReports-master/src
          namespace_strip: npsp

Name & Version

project:
    dependencies:
        - namespace: npo02
          version: 3.8
          dependencies:
              - namespace: npe01
                version: 3.6
        - namespace: npe03
          version: 3.9
        - namespace: npe4
          version: 3.5
        - namespace: npe5
          version: 3.5

Note: CumulusCI is a free open source tool created by Salesforce.org. I do not work for or have any direct affiliation with this open source product besides being a user and perhaps one-day future contributor to the repo.

0
0

Best consistent solution I found

  • Update sfdx-project.json to specify dependant package in order of installation dependency
"packageDirectories": [
{
    "package": "MyApp",
    "path": "force-app",
    "versionNumber": "1.0.0.NEXT",
    "default": true,
    "dependencies": [
        {"package": "Package1"},
        {"package": "Package2DependsOnPackage1"}
     ]   
}
],
"packageAliases": {
    "Package1": "04t...",
    "Package2DependsOnPackage1": "04t..."
}
  • Install Tools

sfdx plugins:install @dx-cli-toolbox/sfdx-toolbox-package-utils

  • Install all dependednice into Scratch Org

sfdx toolbox:package:dependencies:install

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .