If you’ve started using Salesforce DX, it’s likely you’ve already seen this picture of happy Trailhead characters moving metadata from the so-called “Happy soup” to separate projects:
In an older blog post, I wrote about our first steps with DX and Unlocked Packages. We started small with a new project that had no dependency with the existing org’s metadata.
At some point however, having more complex projects required to split metadata in separate projects and packages.
I’m not going to talk about the best strategy to split your org in separate projects, but if you’re interested (and I’m sure you are), you can have a look to the great blog post serie Working with Modular Development and Unlocked Packages on Salesforce Developer blog, or John Daniel & Dileep Burki’s talk at TDX18, Architecting Unlocked Packages in your Salesforce Org.
Sfdx plugin, what is a Package?
Before talking about dependencies, it’s important to understand what exactly is a package and what makes it unique.
A package has 2 parts:
- a Package Id (starting by 0Ho)
- a Package Version Id (starting by 04t)
You first create your Package, and then create versions of it that you will install and upgrade over time. It’s almost the same thing as what you get with an App Exchange package.
For instance you could create a package for your Service Cloud project, and then deploy several versions of it. So you’ll have:
- Your package “ServiceCloudProject”, Id 0Hoxxxxxxxxxxxx (would never change)
- Your package version ServiceCloudProject v1.0, Id 04tyyyyyyyyyyyy
- Your package version ServiceCloudProject v1.1, Id 04tzzzzzzzzzzzz
Additionally, depending on your branching model, you could have one version per branch, so for instance:
- a ServiceCloudProject v1.1 for the dev branch
- a ServiceCloudProject v1.1 for the release branch
Both would have a different Package Version (04t) id (but you can definitely build one version and deploy it everywhere without using the branch tag).
So to sum up, you can target a specific and unique package version with:
- Its Package Version Id (04t)
- Its Package Id (0ho) + its Version Number + its Branch
Packages Dependency
Let’s take a fictional project that would be separated into 4 Packages like this:
Let’s say you need to work on Project B, you’ll have to install the Base package first. To work on Project C, you’ll need to install both the Base package and Project B package. So how does it work ?
Specifying dependencies
Dependencies are listed in your project definition file (sfdx-project.json). For Project C, you’ll have something like this:
As you can see, dependencies are listed under…dependencies (unbelievable, right ?).
There are a few things to note here:
- You need to specify the whole dependency chain (Project C must add dependencies for both Project B and the Base package)
- Most importantly, and this is something that to me remains quite painful today, you need specify a dependency on one very specific Package Version.
A Package Version number looks like this: major.minor.patch.build.
You can use the LATEST keyword on the BUILD number of the package version when you specify a dependency, but that’s it! Every time you release a package version you’ll have to increment at least the minor number of the version, and thus update all your sfdx-project.json!
Where is dependency used?
So this seems pretty simple, but this dependency is actually only used for one thing: package creation.
When you’ll be ready to package your Project B and run sfdx force:package:version:create
, Salesforce will pick all the dependencies that you’ve listed in your sfdx-project.json, and validate your whole package.
So this makes sense, but what happens when you want to install your package in a whole new org ? Well, you’ll have to manually install all the dependencies of that package.
This means that in our example, you wouldn’t be able to install Package C before having installed Base Package and Package B first. Salesforce won’t install them for you. This can makes sense, if you don’t want to quickly find yourself into an npm-like dependency craziness.
So what’s the “issue” ?
Well the thing is that, you’ll likely have to use your dependencies in several places:
- in your sfdx-project.json
- in your CI, so that your package can be tested
- maybe in a script used to build a Scratch Org for your developers
- …
Looking back at our Package C, it has 2 dependencies:
- “Project B”: “0Ho58123456789BCAQ”
- “Base@1.3.0.0”: “04t58123456Kmn0AAC”
If you use these Ids in several scripts as said before, each time you build a new version of Project B or Base Package, you’ll have to update all these scripts. Does this makes sense ? Not really, as everything is already written down in our sfdx-project.json. Could we do a better job here ? Sure.
Introducing Texeï’s custom sfdx plugin
Last year, we wrote a custom sfdx plugin to automatically install dependencies. Just run the following command from your project’s folder, and that’s it:
sfdx texei:package:dependencies:install -u MyOrg
For instance, for Project C, both Base package and Package B would be installed. Next time you’ll have to update the package ids, you’ll just have to do it in the sfdx-project.json file and that’s it, as no id is hardcoded in the plugin command.
To know more about the available flags (to add installation key for instance), just run the help command:
sfdx texei:package:dependencies:install -h
You can install the plugin easily by running:
sfdx plugins:install texei-sfdx-plugin
Also, we’ve made the plugin fully open source, so you can have a look to the code on GitHub:
https://github.com/texei/texei-sfdx-plugin
I would like to thank John Daniel and Diéffrei Quadros, active members of the Salesforce community, that helped to make a better plugin with their Pull Requests. Thank you guys !
Would you have any question or comment, please feel free to reach out to me on Twitter.