0

I'm working on a pipeline to package and deploy sfdc code. I'm attaching the script.

# CI process for SFDX
image: radon57/sfdx-cli #image with sfdx installed

definitions:
  steps:
    - step: &package_install
        name: Package and Install
        script:
          - apt-get install -y jq  
    
          - export PACKAGE_NAME=$( cat sfdx-project.json | jq -r '.packageDirectories[0].package') 
 
          - sfdx force:package:create --name "$PACKAGE_NAME" --description "My Package" --packagetype Unlocked --path force-app --nonamespace --targetdevhubusername $USERNAME || true
          # Create a new package version.
          #- sfdx force:package:version:create:list --targetusername $USERNAME
          - sfdx force:package:version:create -c -v $USERNAME --package "$PACKAGE_NAME" --versionnumber=$VERSION_NUMBER --installationkey $PASSWORD --wait 30 --json > package-output.json || true
          - cat package-output.json
          - version_data=$(cat package-output.json)
          - PKG_VERSION=$( jq -r '.result.SubscriberPackageVersionId' <<< "${version_data}" )
    
          # need to parse out the version from the previous command.
          - sfdx force:package:install --package "$PKG_VERSION" --targetusername $USERNAME --wait 10 --publishwait 10 --installationkey $PASSWORD --json -r

pipelines:
  default:
    - step: *package_install

This script works fine for the first time. When I update the code and execute the pipeline, it fails because of the unique package name constraint. As a solution for that, I bypassed the package creation and just updated the package version by calculating the version number. This method works locally. But since this pipeline runs on a container, the sfdx-package.json is not getting updated with the package name and versions. So I've to create a new package name for every source update, which is inefficient. Is there any workaround for this? The pipeline should package the code and install it in the org, if there is any update in the code, it should be promoted as a new version, not as a new package.

2
  • What you are trying to achieve is increment the package version number automatically, every time the pipeline runs. Is that correct? Jan 15 at 10:13
  • Yes, just to update the package version, and bypass the unique package name error while creating the package
    – Anjana
    Jan 15 at 15:31
2

I think you should consider re-writing your pipeline a little bit. You do not need to create a new Package2 every time the pipeline runs. Instead, only create Package2Versions. The package itself is a one-time setup that is literally required once in the whole lifecycle of a second generation package. Absolutely no need to add this to your pipeline.

I also strongly recommend to not promote the package version without at least one manual approval step, a deployment on staging, and a full regression test run. This will enable you to iterate faster and safer. Your current pipeline may easily deploy broken code into production (assuming the last step uses a production username).

I recently wrote and open-sourced a CircleCI pipeline that does the following:

  • Full scratch org setup including regression test (dependencies), LWC unit tests and test data import
  • Deployment on a staging (after manual approval), including unvalidated package build and full regression test of all unit tests. This job also performs an automatic rollback, if one step fails
  • Deployment on production (after manual approval), using the validated package build, full regression test, package version promotion and automatic rollback

Feel free to use these snippets as a starting point for your pipeline.

I like your idea of automatically incrementing the package version. However, this essentially prevents you from iterating multiple commits on the same package version. Therefore, we opted to manually setup a package version and only automate from there (constantly making new builds, but manually controlling the package version).

Here's a link to the demo repository including the pipeline. The full file's to big to put it here: https://github.com/j-schreiber/1-1-1-setup-template

Here is an excerpt that builds the validated package version asynchronously (huge performance improvement!)

  build_validated_package:
    executor:
      name: default
    steps:
      - checkout
      - sfdx/install
      - run:
          name: Setup SFDX CLI
          command: |
            echo $SFDX_JWT_KEY | base64 --decode --ignore-garbage > ./server.key
            sfdx auth:jwt:grant --clientid $SFDX_CONSUMER_KEY --jwtkeyfile server.key --username $USERNAME_PRODUCTION
            mkdir -p force-app
      - run:
          name: Build Production Package
          command: |
            sfdx force:package:version:create -p $PACKAGE_ID -v $USERNAME_PRODUCTION -w 60 -k $INSTALLATION_KEY -c

And here's an excerpt that deploys to production (after approval), with automatic rollback:

  install_production:
    executor:
      name: default
    steps:
      - checkout
      - sfdx/install
      - run:
          name: Setup SFDX CLI
          command: |
            echo $SFDX_JWT_KEY | base64 --decode --ignore-garbage > ./server.key
            sfdx auth:jwt:grant --clientid $SFDX_CONSUMER_KEY --jwtkeyfile server.key --username $USERNAME_PRODUCTION
            sfdx auth:jwt:grant --clientid $SFDX_CONSUMER_KEY_STAGING --jwtkeyfile server.key --username $USERNAME_STAGING --instanceurl https://test.salesforce.com
            mkdir -p force-app
      - run:
          name: Retrieve Originally Installed Package Version For Rollback
          command: |
            output=$(sfdx force:data:soql:query -t -q "SELECT SubscriberPackageId FROM Package2 WHERE Id = '$PACKAGE_ID'" -u $USERNAME_PRODUCTION)
            SUBSCRIBER_PACKAGE_ID=$(echo $output | grep -o '033[a-zA-Z0-9]*')
            output=$(sfdx force:data:soql:query -t -q "SELECT SubscriberPackageVersionId FROM InstalledSubscriberPackage WHERE SubscriberPackageId = '$SUBSCRIBER_PACKAGE_ID'" -u $USERNAME_PRODUCTION)
            SUBSCRIBER_PACKAGE_VERSION_ID=$(echo $output | grep -o '04t[a-zA-Z0-9]*')
            if [ -z $SUBSCRIBER_PACKAGE_VERSION_ID ]; then
                echo "No installed version found. Rollback disabled."
            else
                echo "Storing originally installed package version for rollback: $SUBSCRIBER_PACKAGE_VERSION_ID ..."
                echo $SUBSCRIBER_PACKAGE_VERSION_ID >> rollback_package_version_id.txt
            fi
      - run:
          name: Install Latest Validated Package (Staging)
          command: |
            query=$(sfdx force:data:soql:query -t -q "SELECT SubscriberpackageVersionId FROM Package2Version WHERE Package2Id = '$PACKAGE_ID' AND ValidationSkipped = false ORDER BY CreatedDate DESC LIMIT 1" -u $USERNAME_PRODUCTION)
            SUBSCRIBER_PACKAGE_VERSION_ID=$(echo $query | grep -o '04t[a-zA-Z0-9]*')
            echo "Installing latest package version: $SUBSCRIBER_PACKAGE_VERSION_ID ..."
            sfdx force:package:install -w 10 -b 10 -u $USERNAME_STAGING -p $SUBSCRIBER_PACKAGE_VERSION_ID -k $INSTALLATION_KEY -r
      - run:
          name: Deploy Source (Staging)
          command: |
            sfdx force:source:deploy -p src/deploy -u $USERNAME_STAGING -w 10 -l RunLocalTests
      - run:
          name: Promote Latest Build
          command: |
            query=$(sfdx force:data:soql:query -t -q "SELECT SubscriberpackageVersionId FROM Package2Version WHERE Package2Id = '$PACKAGE_ID' AND ValidationSkipped = false ORDER BY CreatedDate DESC LIMIT 1" -u $USERNAME_PRODUCTION)
            SUBSCRIBER_PACKAGE_VERSION_ID=$(echo $query | grep -o '04t[a-zA-Z0-9]*')
            echo "Promoting latest package version: $SUBSCRIBER_PACKAGE_VERSION_ID ..."
            sfdx force:package:version:promote -v $USERNAME_PRODUCTION -p $SUBSCRIBER_PACKAGE_VERSION_ID -n
      - run:
          name: Install Latest Validated Package (Production)
          command: |
            query=$(sfdx force:data:soql:query -t -q "SELECT SubscriberpackageVersionId FROM Package2Version WHERE Package2Id = '$PACKAGE_ID' AND ValidationSkipped = false ORDER BY CreatedDate DESC LIMIT 1" -u $USERNAME_PRODUCTION)
            SUBSCRIBER_PACKAGE_VERSION_ID=$(echo $query | grep -o '04t[a-zA-Z0-9]*')
            echo "Installing latest package version: $SUBSCRIBER_PACKAGE_VERSION_ID ..."
            sfdx force:package:install -w 10 -b 10 -u $USERNAME_PRODUCTION -p $SUBSCRIBER_PACKAGE_VERSION_ID -k $INSTALLATION_KEY -r
      - run:
          name: Deploy Source (Production)
          command: |
            sfdx force:source:deploy -p src/deploy -u $USERNAME_PRODUCTION -w 10 -l RunLocalTests
      - run:
          name: Run Apex Tests (Production)
          command: |
            mkdir -p test-results/apex
            set +e
            sfdx force:apex:test:run -w 10 -u $USERNAME_PRODUCTION -r junit -d test-results/apex
            exitCode=$?
            rm -f test-results/apex/test-result.xml
            exit $exitCode
      - run:
          name: Rollback To Previous Package Version
          command: |
            ROLLBACK_PACKAGE_VERSION=rollback_package_version_id.txt
            if [ -f "$ROLLBACK_PACKAGE_VERSION" ]; then
                read -r original_package_version < $ROLLBACK_PACKAGE_VERSION
                echo "Rolling back to original package version ... $original_package_version"
                sfdx force:package:install -w 10 -b 10 -u $USERNAME_PRODUCTION -p $original_package_version -k $INSTALLATION_KEY -r
            else
                echo "No package version was installed. Rollback disabled."
            fi
          when: on_fail
      - store_test_results:
          path: test-results

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.