Categories
salesforce sfdx

Testing Salesforce Managed Packages (2GP) using Bitbucket Pipelines & SFDX scratch orgs

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.

  1. Create a Private Key and Self-Signed Digital Certificate. You will end up with a server.crt and server.key file
  2. 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.
  3. 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, logout sfdx 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

  1. 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).

  1. 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).

  1. Go within your Bitbucket Repository, go to Settings and then Repository variables. Add those 4 variables
    1. DECRYPTION_KEY
      • Should be the key value you generated
    2. DECRYPTION_IV
      • Should be the iv value you generated
    3. HUB_CONSUMER_KEY
      • The consumer key from your Salesforce Connected App on your Dev Hub
    4. HUB_USER_NAME
      • The username you use to connect to the Dev Hub
  1. 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.
    1. Update the value of PACKAGENAME for the hardcoded ID value of your package. Which you can find within your sfdx-project.json or by typing sfdx force:package:list (this is the value that starts with 0Ho)
    2. Update the step Decrypt server key to point to the right location where you file server.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 step Authorize Dev Hub
  2. The last test run (Run unit tests on scratch org) step in the bitbucket-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.
  3. 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)
  4. After you commit all your files (bitbucket-pipelines.yml and server.key.enc), go to your repository, click settings, within Pipeline Settings click Enable Pipelines
  5. 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.