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.

Leave a Reply

Your email address will not be published.