[SalesForce] Script to dynamically generate XML file based on most Git commit contents (Salesforce CI)

I'm using DX for my Continuous Integration, but post-scratch org when I want to deploy into a traditional sandbox for staging, I'm struggling with an automated way to deploy only the new commits (as opposed to ALL of the metadata) in my Dev Branch. Is anyone using a script that archives this (dynamically creates the package.xml based on the most recent commit)?

The use case is that without this, I am deploying ALL the metadata each time, hitting the limit of 10,000 files.

Best Answer

I use the legacy/community maintained ForceCLI project to do delta deployments when not using Salesforce DX. It's pretty easy to get a basic workflow going using a shell script.

You can do something like this:

git diff [...] --name-only --diff-filter=ACM | force push -f -

What this does is get a list of changed files between two Git revisions (you'd replace [...] with either a reference to a start and end commit, or an expression like HEAD~1 for a delta of HEAD against the previous commit) and list their paths. It then pipes those paths into force, which will automatically construct a package.xml and run the delta deployment.

I've found a few issues with ForceCLI around delta-deploying unpacked Static Resources and Lightning Components (I believe the latter has already been fixed, but may not have been released yet), but those issues are more or less easily worked around with a little Bash. For static resources, I use a script that zips them up and then pipes the git diff output through sed to squash all references to static resource elements to a reference to the zip file, e.g.

sed -E 's#^src/staticresources/([^/]+)/.+#src/staticresources/\1\.resource#

Note that one thing this approach doesn't handle is deletions. I delete metadata so rarely that I have not invested the effort to script delta deletions. Since deletions can be run either pre- or post-deploy and sometimes need to be one or the other, I've felt the effort to introspect the desired delete operation off of a commit message was more than it was worth.