Scratch org with dependent packages
This post explores how to add a package to a scratch org. We will use the RFLIb package (see https://github.com/j-fischer/rflib) which is a logging framework.
Lets start a new scratch org with the code sfdx force:project:create –projectname TestScratch
Our sfdx-project.json file will be fairly blank:
{ "packageDirectories": [ { "path": "force-app", "default": true } ], "namespace": "", "sfdcLoginUrl": "https://login.salesforce.com", "sourceApiVersion": "50.0" }
In order to set up the project we need the dependencies in the sfdx-project.json file but we will also need to install the package to the scratch org after it is created. So create the scratch org:
sfdx force:org:create --setdefaultusername --setalias TestScratch --definitionfile config/project-scratch-def.json -d 5 -s
Now update the dependencies for the package:
{ "packageDirectories": [ { "path": "force-app", "default": true, "dependencies": [ { "subscriberPackageVersionId": "04t3h000004jpwzAAA" } ], "namespace": "", "sfdcLoginUrl": "https://login.salesforce.com", "sourceApiVersion": "50.0" }
and create the scratch org and then install the package from the id
sfdx force:org:create --setdefaultusername --setalias TestScratch --definitionfile config/project-scratch-def.json -d 5 -s
sfdx force:package:install -p 04t3h000004jpwzAAA -w 10 -s AllUsers
After opening the scratch org the package can be checked as installed:
Now we can create an Apex class to see the logger in action
public with sharing class loggerExample {
private static rflib_Logger LOGGER = rflib_LoggerUtil.getFactory().createLogger('TestLogger');
public static void runlogger() {
rflib_LogTimer logTimer = rflib_LoggerUtil.startLogTimer(LOGGER,300,'Handle_loggerExample');
LOGGER.info('sending info log message');
LOGGER.warn('sending WARN log message');
LOGGER.fatal('Sending Fatal log message');
logTimer.done();
}
}
From the developer console and the Execute Anonymous Window we can run the class
loggerExample.runLogger();
The results can then be shown in the
Now we can create a test class
@IsTest
public with sharing class loggerExample_Test {
@IsTest
static void runTest(){
Test.startTest();
loggerExample.runLogger();
Test.stopTest();
}
}
We need to get this as a package in our CI/CD process – va GitLab. So create a new project in GitLab
Below are the standard commands to link our local folder to the new project that is created:
git init
git remote add origin https://gitlab.com/andynix/testscratch.git
git add .
git commit -m "Initial commit"
git push -u origin master
Tasks in Gitlab are to create the .yml file for processing and the environment variables.
sfdx force:package:create --name LoggerTestPackage --packagetype Unlocked --path force-app --targetdevhubusername DevHub
This will return a new Package Id and update the sfdx-project.json
sfdx-project.json has been updated.
Successfully created a package. 0Ho4J000000TNkxSAG
=== Ids
NAME VALUE
────────── ──────────────────
Package Id 0Ho4J000000TNkxSAG
The updated sfdx-project.json is below:
{
"packageDirectories": [
{
"path": "force-app",
"default": true,
"dependencies": [
{
"subscriberPackageVersionId": "04t3h000004jpwzAAA"
}
],
"package": "LoggerTestPackage",
"versionName": "ver 0.1",
"versionNumber": "0.1.0.NEXT"
}
],
"namespace": "",
"sfdcLoginUrl": "https://login.salesforce.com",
"sourceApiVersion": "50.0",
"packageAliases": {
"LoggerTestPackage": "0Ho4J000000TNkxSAG"
}
}
There is a task to set up the Gitlab CD/CI process for Salesforce and this is described in a different post. However this does not actually install the dependent package(s) in the sfdx-project.json file which means that the scratch org built as part of CD/CI process will fail and so code push will fail and the rest of the process will fail. If you are doing the deployment steps manually then there is no need to proceed any further.
The out of the box gitlab script does not install the packages and the following function needs adding. This looks in our project file and then pulls out the packages in the dependencies section (note that we are looking for the subscriberPackageVersionId):
# install_dependencies
# Arguments
# $1 = scratch org username
function install_dependencies() {
local scratch_org_username=$1
echo "Installing package dependencies"
for package_version_id in $(jq -r '.packageDirectories[].dependencies[].subscriberPackageVersionId' < sfdx-project.json)
do
echo $package_version_id
if [ $package_version_id ]; then
# install the package
local cmd="sfdx force:package:install --targetusername $scratch_org_username --package $package_version_id --wait 10 --publishwait 10 --noprompt --json" && (echo $cmd >&2)
local output=$($cmd) && (echo $output | jq '.' >&2)
fi
done
}
also we need to add this function call in the following code. Here the packages are installed before the code is pushed to the new scratchorg.
function deploy_scratch_org() {
local devhub=$1
local orgname=$2
assert_within_limits $devhub DailyScratchOrgs
local scratch_org_username=$(create_scratch_org $devhub $orgname)
echo $scratch_org_username > SCRATCH_ORG_USERNAME.txt
get_org_auth_url $scratch_org_username > SCRATCH_ORG_AUTH_URL.txt
install_dependencies $scratch_org_username
push_to_scratch_org $scratch_org_username
populate_scratch_org_redirect_html $scratch_org_username
echo "Deployed to scratch org $username for $orgname"
}
This then allows the CD/CI process to follow.