Streamlining Salesforce Package Creation, Promotion, and Installation with Bash

In the world of Salesforce development, managing packages can sometimes be a challenging task. However, with the right tools and scripts, we can automate and streamline these processes. Today, I’m going to share a bash script that simplifies package creation, promotion, and installation in Salesforce. Join us to learn about Streamlining Salesforce Package Creation, Promotion, and Installation with Bash.

Understanding the Script

This script is designed to automate the process of creating a package version in Salesforce. It uses the Salesforce CLI (sfdx) to interact with your Salesforce org and perform various tasks.

Script

#!/usr/bin/env bash




# Exit script on any error
set -e


#npm install -g @salesforce/cli


log() {
  echo -e "$1" >&2
  echo ""
}


log_file() {
  file_name=$1
  file_ext=${file_name: -5}
  # if file is json then prettyprint with jq
  # else just output the file contents
  if [ "$file_ext" == ".json" ]; then
    cat $file_name | jq '.' >&2
  else
    cat $file_name >&2
  fi
  log ""
}


error() {
  echo -e "\033[0;31mERROR $@ \033[0m" >&2
  exit 1
}


branch_name="${branch_name:-develop}"




sf org login jwt --client-id 3MVG9I9urWXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXSlZ3kloVSXnGPb4YYYYYYYYYYYY_2ut9 --jwt-key-file ./server.key --username [email protected] --alias test


sf config set defaultdevhubusername='test'




# Determine the default devhub username
cmd="sf config get defaultdevhubusername --json" && (log "${cmd}")
output=$($cmd) && (echo $output | jq '.' >&2)


devhub_username=$(jq -r '.result[0]? | .value // ""' <<< $output)


if [ -z "$devhub_username" ]; then
  error "No default devhub username set. Run 'sf config set defaultdevhubusername= 'test' then try again."
else
  log "devhub_username=$devhub_username"
fi


# Check for default package directory in sfdx-project.json
if [ -z "$package_name" ]; then
  log "No package_name environment variable set, will use default package directory in sfdx-project.json."
  log_file "sfdx-project.json"
  package_name=$(cat sfdx-project.json | jq -r '.packageDirectories[]? | select(.default==true) | .package // ""')
fi
# Check for any package directory in sfdx-project.json
if [ -z "$package_name" ]; then
  log "No default package directory found, will use first package directory in sfdx-project.json."
  log_file "../../sfdx-project.json"
  package_name=$(cat ../../sfdx-project.json | jq -r '.packageDirectories? | .[0] | .package // ""')
fi


if [ -z "$package_name" ]; then
  package_name="${package_name:-Test Package}"
  echo $package_name
else
  log "package_name=$package_name"
fi


# Giving up
if [ -z "$package_name" ]; then
  error "Package name not specified. Set the package_name environment variable or specify a default package directory in sfdx-project.json."
else
  log "package_name=$package_name"
fi




# Retrieve package id for package name
cmd="sf package list --target-dev-hub $devhub_username --json" && (log "${cmd}")
output=$($cmd) && (echo $output | jq '.' >&2)


package_id=$(jq --arg package_name "$package_name" -r '.result[] | select(.Name == $package_name) | .Id // ""' <<< $output)




if [ ! $package_id ]; then
  error "We could not find a package with name '$package_name' owned by '$devhub_username' dev hub org."
else
  log "package_id=$package_id"
fi
# Calculate next version number.
# If the latest package version is released then
# we need to increment the major or minor version numbers.
cmd="sf package version list -v $devhub_username --packages $package_id --concise --released --json" && (log "${cmd}")
output=$($cmd) && (echo $output | jq '.' >&2)


last_package_version=$(jq '.result | sort_by(-.MajorVersion, -.MinorVersion, -.PatchVersion, -.BuildNumber) | .[0] // ""' <<< $output)
last_package_version_id=$(jq -r '.Id?' <<< $last_package_version)
is_released=$(jq -r '.IsReleased?' <<< $last_package_version)
major_version=$(jq -r '.MajorVersion?' <<< $last_package_version)
minor_version=$(jq -r '.MinorVersion?' <<< $last_package_version)
patch_version=$(jq -r '.PatchVersion?' <<< $last_package_version)
build_version=$(jq -r '.BuildVersion?' <<< $last_package_version)




if [ "$branch_name" == "master" ]; then
  major_version=$(($major_version+1));
  minor_version=0
  patch_version=0
  build_version=1
else
  if [ ! $major_version ]; then major_version=1; fi;
  if [ ! $minor_version ]; then minor_version=0; fi;
  if [ ! $patch_version ]; then patch_version=0; fi;
  if [ "$is_released" == "true" ]; then
    minor_version=$(($minor_version+1));
    build_version=1
  else
    build_version=$(($build_version+1));
  fi;
fi;


version_number=$major_version.$minor_version.$patch_version.$build_version
log "version_number=$version_number"
log "last_package_version_id=$last_package_version_id"


# Update the 'ancestorId' property of the default package directory
# in sfdx-project.json because as of Winter '20 it's not able to
# be specified via the force:package:version:create CLI command.
project_json=$(jq --arg last_package_version_id "$last_package_version_id" --arg package_name "$package_name" --arg version_number "$version_number" '(.packageDirectories[] | select(.package == $package_name) | .versionNumber) |= $version_number | (.packageDirectories[] | select(.package == $package_name) | .ancestorId) |= $last_package_version_id' < ../../sfdx-project.json) && (echo $project_json > ../../sfdx-project.json)
log_file "sfdx-project.json"


# Create a new package version
cmd="sf package version create --target-dev-hub $devhub_username --package  $package_id --version-number $version_number --installation-key-bypass --code-coverage --branch $branch_name  --wait 10 --json"
package_definition_file="../../config/project-scratch-def.json"
if [ -f "$package_definition_file" ]; then
  # The org definition file when package version is created
  log "Found org definition file for package version creation: $package_definition_file"
  log_file "$package_definition_file"
  cmd="$cmd --definitionfile $package_definition_file"
else
  log "Didn't find an org definition file at $package_definition_file, will not use --definitionfile flag."
fi;
log "${cmd}"




output=$($cmd) && (echo $output | jq '.' >&2)


echo "command output : $output"


subscriber_package_version_id=$(jq -r '.result.SubscriberPackageVersionId?' <<< $output)


if [ ! $subscriber_package_version_id ]; then
  error "Unable to determine the generated subscriber package version id."
else
  log "subscriber_package_version_id=$subscriber_package_version_id"
fi


echo $subscriber_package_version_id




# Authenticating to other Sandbox so new Package can be installed.
sf org login jwt --client-id 3MVG9od6vNolXXCXXXXXXFSFSFSFFGGSGSGSSSSSSSSS6yzn0ez9qHP4oU15GSGSGSGSGGS--jwt-key-file ./server2.key --username [email protected] --alias test-org


sf config set [email protected]


# Promote package version so that we can
cmd="sf package version promote --target-dev-hub $devhub_username --p $subscriber_package_version_id --no-prompt --json" && (log "${cmd}")
output=$($cmd) && (echo $output | jq '.' >&2)


#For report
cmd="sf package version report --target-dev-hub $devhub_username --p $subscriber_package_version_id --verbose --json" && (log "${cmd}")
output=$($cmd) && (echo $output | jq '.' >&2)


echo $subscriber_package_version_id

Let’s break down the script command by command

  • set -e: This command tells the shell to exit immediately if any command exits with a non-zero status, indicating failure.
  • log(), log_file(), and error(): These are custom functions defined to print messages to the console in different formats. log() prints a message, log_file() prints the contents of a file (with optional pretty-printing using jq if it’s a JSON file), and error() prints an error message and exits the script.
  • branch_name=”${branch_name:-develop}”: This line sets the variable branch_name to the value of an environment variable named branch_name, defaulting to “develop” if the environment variable is not set.

Salesforce CLI commands

  • sf org login jwt …: Authenticates to a Salesforce org using JWT authentication.
  • sf config set defaultdevhubusername=’test’: Sets the default Dev Hub username to ‘test’.
  • sf config get defaultdevhubusername –json: Retrieves the default Dev Hub username.
  • sf package list …: Lists packages in the Dev Hub org.
  • sf package version list …: Lists package versions in the Dev Hub org.
  • sf package version create …: Creates a new package version.
  • sf package version promote …: Promotes a package version.
  • sf package install …: Installs a package in a Salesforce org.

Learn more about Salesforce DX Useful CLI Commands.

Conditional statements (if): These statements check various conditions and execute commands based on the results.

jq commands: jq is a command-line JSON processor used to parse and manipulate JSON data. It is used in this script to parse the JSON output of Salesforce CLI commands.

Output redirection (>&2): This redirects the output of commands to the standard error stream (stderr) instead of the standard output (stdout).

echo commands: These commands print messages to the console.

Variable assignments: Variables like package_name, package_id, version_number, etc., are assigned values based on the output of Salesforce CLI commands or other calculations.

File operations: The script reads and modifies JSON files (sfdx-project.json and project-scratch-def.json) to update package information.

Key Features

  • Error Handling: The script is designed to exit when any command fails, ensuring that errors are caught and handled immediately.
  • Logging: The script includes functions for logging messages and file contents to the console, which can be useful for debugging and tracking the script’s progress.
  • Package Creation: The script retrieves the package name from the sfdx-project.json file or an environment variable, then uses sfdx to create a new package version.

Conclusion

This script is a powerful tool for Salesforce developers, allowing them to automate and streamline the process of creating, promoting, and installing packages. By integrating this script into your development workflow, you can save time, reduce errors, and ensure that your packages are always built from the latest code in your repository.

References : https://gist.github.com/douglascayers/e3a9525729a1b512618adf720e3fbe94

Nishchal vashisht
Nishchal vashisht

Nishchal vashisht
5x certified, Developer at Yoti

Articles: 9

Leave a Reply

Your email address will not be published. Required fields are marked *