This post assumes you have access to a Salesforce Dev Hub
I’ve been working on a Second-Generation Managed Package (2GP) and one frequent issue is that the package needs to support both Person Accounts and the standard business accounts and contacts model. On top of that, the package also needs to support a Multiple Currencies environment and a standard one. That means that the same APEX code might work in an environment but not the other.
One great benefit of SFDX is that you can easily spun out a new scratch org that supports Person Accounts, deploy your code and run the tests, and ensure there are no errors thrown. However it is easy to work on your code for a few hours (or days!) in your standard business accounts and contacts model environment before you notice that your package is now broken with Person Accounts environments. That’s where a good Continuous Integration (CI) solution is a good way to remedy to those problems. You might want to spend some time on Trailhead for Continuous Integration Using Salesforce DX if you are unfamiliar with the concept.
I am using Bitbucket for source control so i’ll break down the necessary steps to setup Bitbucket Pipelines with Salesforce DX scratch orgs using the JWT authorization flow.
- Create a Private Key and Self-Signed Digital Certificate. You will end up with a
server.crt
andserver.key
file - Create a Connected App within your Salesforce Dev Hub (the same you use to create scratch orgs for your development). You will assign your new
server.crt
digital certificate file to this connected app. Salesforce will provide you with a new Consumer Key. - From your own environment, authenticate to your Dev Hub using the JWT-based flow using your Consumer Key,
server.key
file and the login username used with your Dev Hub. Important, logoutsfdx force:auth:logout -u DevHub
before you try to connect again to your Dev Hub with the JWT flow. The JWT to authenticate should look like this
sfdx force:auth:jwt:grant --clientid CONSUMERKEY --jwtkeyfile PATH/TO/server.key --username DEVHUBUSERNAMEEMAIL --setdefaultdevhubusername --setalias DevHub
Now that you have tested that you can connect to your Dev Hub using the Connected App, it’s time to get ready to set up your Bitbucket pipeline. Very conveniently, Salesforce is providing a pre-made test Salesforce Package you can use on Bitbucket to test the Bitbucket Pipelines before you configure it within your own existing package.
https://github.com/forcedotcom/sfdx-bitbucket-package
- Because you don’t want to store your key
server.key
that allows a direct access to your Dev Hub within source control (you could if you want, but it would be safer to avoid it), you want instead to encrypt it and provide the keys to decrypt it at runtime using Bitbucket Repository variables. You first want to generate a key and initializtion vector (iv), which will be needed for encrypting your key (and decrypting it later)
openssl enc -aes-256-cbc -k <passphrase here> -P -md sha1 -nosalt
That commande will provide you with a key and a iv value (keep note of them in a safe space).
- Encrypt your
server.key
file using the key and iv value
openssl enc -nosalt -aes-256-cbc -in server.key -out server.key.enc -base64 -K <key from above> -iv <iv from above>
You now have a server.key.enc
file, which is what you will commit and store in your repository (remember not to store the server.key file in source control).
- Go within your Bitbucket Repository, go to Settings and then Repository variables. Add those 4 variables
- DECRYPTION_KEY
- Should be the key value you generated
- DECRYPTION_IV
- Should be the iv value you generated
- HUB_CONSUMER_KEY
- The consumer key from your Salesforce Connected App on your Dev Hub
- HUB_USER_NAME
- The username you use to connect to the Dev Hub
- DECRYPTION_KEY
- Copy the bitbucket-pipelines.yml file to the root directory of your Salesforce managed package project and customize it. This is the configuration file for your Bitbucket Pipeline where all the scratch orgs are created, tests are run, package version are created, etc.
- Update the value of
PACKAGENAME
for the hardcoded ID value of your package. Which you can find within yoursfdx-project.json
or by typingsfdx force:package:list
(this is the value that starts with 0Ho) - Update the step
Decrypt server key
to point to the right location where you fileserver.key.enc
is located in your repository (and make sure the--out
argument output (where the decrypted certificate will be stored) is the same as the input for the stepAuthorize Dev Hub
- Update the value of
- The last test run (
Run unit tests on scratch org
) step in thebitbucket-pipelines.yml
file runs all the tests located on the scratch org that installed the package. However, because i’m working on a managed package in a Namespace environment, I had to change this line to manually define all the tests I want to run manually using the--tests
argument otherwise the tests will not run. - The file
bitbucket-pipelines.yml
is configured to execute every time you commit anything on any branch, of course you might want to change that. Read the doc on how to Configure your pipeline (I’ve changed it to only execute on schedule every night) - After you commit all your files (
bitbucket-pipelines.yml
andserver.key.enc
), go to your repository, click settings, within Pipeline Settings click Enable Pipelines - Your next commit should execute the pipeline! In order to confirm the execution was completed successfully, go within the Pipelines section of your repository.
If you encounter an issue with the pipeline running in your project and can’t figure out the problem, I’d recommend to create a new repository using the SFDX bitbucket package, create a sample unlocked package and practice on this one first.