Simplifying Deploy and Destroy with the SF CLI

Personally, I never had any problems  with the SFDX CLI, which is why I was surprised they were replacing it with the SF CLI. What I now sometimes struggle with is translating all of my cheat sheet references from one to the other, and the sifting through the plethora of search results that still refer to the SFDX way of doing things. OK, I know I can minimize the second part by setting the date range in my Google searches, but when I’m being lazy (or in a hurry, both of which are more common than I prefer) I use Perplexity.ai rather than Google.

Most recently my conversion conundrum cropped up with removing components that I had deployed through the CLI. I found a great SFDX syntax in Perplexity literally one day before it was slated to be retired. Here is the relevant part of the session that got me to the solution:

Question: cli to delete objects from an org

Answer:
To delete objects from a Salesforce org using the CLI, you can use the destructive changes process. Here’s how to do it:

Step 1: Create Destructive Changes XML

Create a file named destructiveChanges.xml with the following structure:
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>ObjectName1__c</members>
        <members>ObjectName2__c</members>
        <name>CustomObject</name>
    </types>
</Package>
Replace ObjectName1__c and ObjectName2__c with the API names of the objects you want to delete.

Step 2: Create Empty Package XML

Create a file named package.xml with the following content:
xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <version>62.0</version>
</Package>

Step 3: Deploy Destructive Changes

Run the following Salesforce CLI command:
bash
sfdx force:mdapi:deploy -d . -w -1 -u YourOrgAlias
This command deploys the destructive changes to your org:
  • -d . specifies the current directory as the deployment source
  • -w -1 waits for the deployment to finish
  • -u YourOrgAlias specifies the target org alias

Additional Considerations

  • Ensure you have the necessary permissions to delete objects in your org.
  • Deleting objects will also delete all associated records and metadata (fields, validation rules, etc.).
  • Some objects may have dependencies that prevent deletion. You might need to remove these dependencies first.
  • Always test this process in a sandbox environment before applying it to production.
  • Consider backing up your metadata and data before performing destructive changes.
If you need to delete other types of metadata (like Apex classes, Visualforce pages, etc.), you can include them in the destructiveChanges.xml file using the appropriate metadata type names.
Remember, this process permanently deletes the specified objects and their data from your org, so use it with caution.

When I ran it, it worked perfectly, and output a warning that it would no longer work the next day (I did not preserve the message content, but I can tell you the date was 11/4/24 because UpNote metadata is useful). As I mentioned, I’m often in a hurry, so I often make mistakes, and what has kept me in technology since the original Wolfenstein was released is always making sure I have a way of undoing whatever I do. So I decided to go find the cool new sf CLI solution. Which was not terribly clear to me. I found the documentation a bit confusing. Going back to the old-fashioned Google search (trust me, it pains me more to write “old-fashioned Google search” than it does for you to read it), I found a Trailblazer Community post by Max Goldfarb that greatly clarified how to make the new sf call.

I have since refined the process clarified by Max once I read the documentation while in less than my usually rushed efforts. First, I keep that step 2 XML that Perplexity gave me stored in my repo as antiPackage.xml, the full path being manifest\destructiveArchive\antiPackage.xml. I could then generate the destructive xml with:

sf project generate manifest --source-dir force-app/main/default/[WHATEVER-I-WANT-TO-DESTROY] --type destroy

and then run my simplified killer command:

sf project deploy start --manifest manifest/destructiveArchive/antiPackage.xml --pre-destructive-changes manifest/destructiveArchive/destructiveChangesALL.xml -o [ALIAS]

and boom! stuff removed. I even keep a set of destructive xmls in a special folder (you may have noticed it in the command above). I continued to use this happily for a few weeks.

Today I was messing with a single component that was giving me problems (or, more accurately, I was giving it problems by building it incorrectly) and started by removing just it. Then I went about making my fixes and, feeling less hectic than usual, decided to have a package.xml just for it so the deployment would go faster (yes, I know I can do that with a right-click, but once I am comfortable with a CLI, I prefer it, and it lets me make sure that my eventual release script will have fewer issues). It was very small, which made the content leap out at me more, and, so I realized…There is no difference between the XML output with or without the “–type destroy” switch!

So now I just keep one XML for each context in manifest\deployArchive and reference it for both deploy and destroy.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson
Photo by Ruben Mishchuk from Unsplash

When Change Sets Fail in Salesforce

With the growing popularity of DX and the GA release of DevOps Center, I’ve seen fewer questions about Change Sets the last couple of years. I expect that organizations that have used change sets successfully for a long time are less motivated to change their processes and those that had issues have had sufficient time to move to the newer options.

What I have seen in the past around legacy processes that work well for small teams is that they are often passed to newer members experientially and at some point someone joins that is not mentored into to how things have been done and struggle on their own. Here are a few tips for those that run into this gap with change sets.

Practice makes it home by dinner time

Even the simplest change can result in big problems if something critical is missed. Always test the change set by pushing from one sandbox to another that has been refreshed since the last production update. If there is a possibility of changes being done directly in production, this means to create or refresh the test target sandbox immediately before the deployment test.

Go small or stay home

Incremental changes are best. I prefer to do a deployment anytime there is a completed task that has tested in development. The push could just be to a fresh sandbox with the final production release being a series of pushes. Another approach is to use dark deployments, pushing out tested work products incrementally even though not all pieces are ready for users. The dark deployments may require some custom metadata types or permissions to keep them from being used before they are ready, something it is really useful for large projects to avoid big-bang pushes.

The incremental pushes to sandboxes should result in a series for change sets. As they progress, dependencies may be discovered and indicate a need to revise the change sets so that dependencies are pushed first. Again, the dark deployments are useful here.

The mighty DX

Among many good reasons for switching to DX, verbose error messages for deployments helps speed up debugging. Using Workbench, it is possible to turn change sets into packages for use in DX, which is handy if you are just starting to migrate to DX. Again, test deployments against fresh sandboxes are critical to avoiding critical situations.

The only constant…

Is change, not change sets. Eventually change sets will probably go away, so if you are reading this post you are either bored and avoiding more productive tasks or you are struggling with a change set today.  Now is a good time to start looking into alternatives.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson
Daily Limit Reached

Solution for sfdx package create error “couldn’t retrieve the design time component information for component”

(Formerly titled SOLVED! sfdx force:package:version:create -p error: We couldn’t retrieve the design time component information for component)

Now and then I find myself looking for what I expect to be a simple set of steps for what seems to be a common need and instead wind up on a Google journey that is frustrating as all heck. I hit a double-header in this area over the weekend (ironically, following the steps from my own post Get Hands-on with VS Code, Salesforce DX and Packages), and while the second annoyance is still annoying me the first part was resolved and I’m sharing that here. While I won’t make you repeat all the steps I went through to complete a 5 minute task in as many hours, I will share the path before the conclusion (tldr;).

Problem: running force:package:version:create -p to create an unlocked package for an app that has a dependency on a managed package results in an error “We couldn’t retrieve the design time component information for component [foo:bar]”.

Ok, simple enough. Add the managed package as a dependency. GiMF, so off I go and find https://salesforce.stackexchange.com/questions/236881/how-do-you-create-a-package-version-with-dependencies-on-both-2gp-and-1st-gen-ma which usefully points out I need to add the package as dependancy and some examples of what it looks like. Hmmm. I note a key requirement is the package ID. There examples clearly show the ID but no where in the thread does it mention how to get the ID. Down the search engine rabbit hole again to find https://developer.salesforce.com/docs/atlas.en-us.222.0.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_config_file.htm, which tells me exactly how to find it for my own packages, but not a hint of how to do this for a package from AppExchange. Continuing on… I find how to find the info using SOQL at https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_unlocked_pkg_extract_dependency_info.htm, but, alas, still only good for my own packages.

Eventually I found this command (and, in all honesty, I was so frustrated by then that I lost the link where I found it):

sfdx force:package:installed:list -u [alias to org that has managed packaged installed]

Which rendered:

ID Package ID Package Name Namespace Package Version ID Version Name Version
------------------ ------------------ -------------------------- ------------ ------------------ ----------------- --------
0A375000000GsstCAC 0332E000000U76DQAS Launch Flow Modal sf_flowmodal 04t2E000003VsuMQAS Launch Flow Modal 1.14.0.1
0A375000000Gss2CAC 03330000000wDAbAAM Salesforce Connected Apps sf_com_apps 04t30000001DUvrAAG Winter '16 1.7.0.1
0A375000000GssoCAC 0333X0000006XwlQAE Enhanced Approval Requests ear 04t3X000002xUYTQA2 Summer 2021 1.10.0.1

And yay, I had what I needed to create the entry depencies entry for managed packages used by an unlocked package:

"dependencies": [
    {
        "subscriberPackageVersionId": "04t2E000003VsuMQAS"
    },
    {
        "subscriberPackageVersionId": "04t3X000002xUYTQA2"
    }
]

Basically. I actually would have preferred to find the 0Ho ID so I could also just use LATEST instead of the last digit of the version, but alas, time to move on to the remaining issue(s):

Picklist value: ...not found
Defined value not found

Which I am stopping on today because:

ERROR running force:package:version:create: The package2 version create request failed because this organization has reached its daily limit
Author has (also) reached its daily limit

In summary, to add a managed package as a dependency for an unlocked package:

With an org where the managed package is installed run the following:

sfdx force:package:installed:list -u [alias to org that has managed packaged installed]

Then add this to your sfdx-project.json under packageDirectories that contains your package:

"dependencies": [ { "subscriberPackageVersionId": "04tPACKAGEID" } ]

Where 04tPACKAGEID is the value returned from the CLI command.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson
sfdx cli versions.map-is-not-a-function

FIXED! ERROR running force:mdapi:retrieve: versions.map is not a function

Ran into this fun error message today when attempting to pull from a Salesforce sandbox from the CLI:

ERROR running force:mdapi:retrieve: versions.map is not a function

Most of what I found on the web was FUD and also about trying to push rather than pull. The comments all seem to agree it was related to the recent update for Enhanced Domains on Winter23-enabled sandboxes.

Fortunately for me, I had just added another org to my CLI. I was surprised at no approval prompt and it got me to thinking that I had probably authorized it before. So it occured to me that the fix may just be to run the authorization again, i.e.,

sfdx force:auth:web:login --setalias [ALIAS] --instanceurl [MY.DOMAIN.URL]

And that did the trick for me. Hope this helps others.

Facebooktwitterredditlinkedinmail
© Scott S. Nelson
Package with Zipper

A simple Salesforce Package cheat sheet

As an IT consultant, I frequently change technologies and project roles. The frequent shift of focus is great for staying interested, engaged, and marketable. The downside is that many mental-muscle-memory tasks fade or never take root with long gaps between repetitions. One example is XSLT, which I have had to re-learn three times because it is not difficult to learn but only find I need it every five to seven years. Another example is various Linux commands, which I will post on my blog so that I can find them quickly when needed.

An additional technique I use, when available, is little cheats where the broad strokes are easy to remember and help prompt my memory for the details. The example of this I want to share today is creating Salesforce Unlocked Packages.

Before I start, it is important to understand that for a true package-management and -deployment strategy there is a lot more involved than just the ability to create a package. A package strategy requires thinking about how the org is used, who the stakeholders are, how many teams contribute to code and metadata within the org and how different their focuses are, and clear roadmap for an enterprise architecture with agreements on the direction and commitment to sustainable governance model. This post is just about being able to take something built in one org and being able to deploy it into another org as either a consulting starter, a demo set, or a shared feature.

In this example, I built a useful little Flow demo that guides CSRs in first searching for a contact before creating them to reduce duplication while following standard procedures for customer contact.

Flow module in action

It is a good component to keep handy as a starting point for clients that need a similar feature, so I want to be able to easily manage it in source control and distribute it to other orgs. Or I may want to share it with other developers for enhancements. In either case, I don’t work hands-on with packages to easily remember how to construct them. No fear! I do remember how to create unmanaged packages, which is a good start. However, it has been a while since I built this component, so looking at the list of flows does not make it clear to me what is what:

Call in contact flow package definition

I could guess, based on the names, but it is safer to be sure, so I go to the flow itself and look at the Properties to find the API Name:

Flow version properties

Components can have dependencies. Sometimes it is necessary to track them down and can require some test deployments to be sure all have been captured. In this case, the implementation is straight-forward, and Salesforce still finds a component that I had forgotten was involved:

Package component dependencies

Now that there is a package definition in the Salesforce org, I want to be able to move it to Github where I can work with it. The next step is to go to https://workbench.developerforce.com and navigate to Migration\Retrieve and select to extract the package by name:

Using Workbench to extract package

This will produce a zip file of the package components. To make these useful, set up a Visual Studio Code project for Salesforce (see if How to set up a Salesforce development environment you need help with this part). Now unzip the package into a folder name “unpackaged” at the same level as [PROJEC_PATH] and run the following command:

sf project convert mdapi --root-dir ../unpackaged --output-dir force-app

If the above paths were laid out correctly, you will see an output showing the components added to your project. Otherwise, check your folder layout and naming conventions and try again. At this point, it is useful to replace the default /manifest/package.xml with the one in the root of the unzipped package from Workbench (the following step assumes this). Finally, test that you can push the package contents to another org with:

sf project deploy start --target-org [USERNAME] --manifest ../[PROJECT_PATH]/manifest/package.xml --test-level RunLocalTests --wait 100

Once all components are deployed, you should see them in your org (they may not be enabled):

sf cli deployment
Package deployed

In this case, the flows require activation before they can be used. Post deployment steps will depend on your components.

Finally, add your project to source control. The project used in this article can be found at https://github.com/ssnsolutionist/Call-in-Contact-Flow-main.

Some of the steps above included commands from a cheat-sheet I maintain at https://github.com/ssnsolutionist/trailhead1/blob/master/sfdx-cli-common-commands.md

09-08-24 Revision Notes:
  1. The cheat sheet linked above is out of date. I’m working on it, mostly by having Perplexity.ai re-write them for me as I need them.
  2. Originally published as a Logic20/20 Insight article, the editors there later stripped out the screen shots, so publishing the full version here.
Facebooktwitterredditlinkedinmail
© Scott S. Nelson