There's essentially a couple of steps involved to this process of doing a delta deployment.
The first is to identify the changed components. If you're working with Git and GitLab Pipelines, this is not difficult. Given a start and an end commit, it's just
git diff start-commit end-commit --name-only --diff-filter=ACM
to get a newline-separated list of path names that are changed. (Note: this does not include handling of deleted components. If you want to automatically handle component deletion, there'll be some additional logic involved, as those end up in a
The second step is, given that flat list of file names and your source tree, to build a Metadata API manifest (
package.xml) and deployment ZIP file to push only the changed metadata components.
When I've done this in the past (with GitHub Pipelines), I used ForceCLI as the deployment engine because it makes it easy to do this step by piping in the results of the above
git diff start-commit end-commit --name-only --diff-filter=ACM | force push -f -
force push -f takes that flat list of names and synthesizes a
package.xml and deployment ZIP, then deploys it to your authenticated target org.
If you're running this script on every commit,
start-commit can be
end-commit can be
HEAD. Otherwise, see below for Pipelines configuration.
Disclaimer: I haven't worked with this tooling in about a year.
When I was building out delta deployments I found that I needed to write some shell to manage deployments of components that are multi-file; that is, static resources and Lightning components. I did this with
sed by squashing all references to Lightning component files to the base component directory, and by zipping up static resources and likewise squashing references to the base .zip for that static resource.
I think the former has been fixed in ForceCLI; I don't know about the latter. I'd recommend doing some experiments.
For production deploys, it's common that you won't be deploying one commit at a time, and that your release manager will wish to specify a commit range for this deployment spanning a sprint's work. Likewise, it probably won't be fired automatically as with staging, but will be manually invoked by the release manager.
For that use case, I configured variables in GitLab Pipelines for the start and end commit (and altered the
git diff commands shown above to reference those variables). They defaulted to
HEAD. However, when the Pipeline for production is run manually by the release manager, they have the option to populate those commit SHAs to identify a specific range of changes to deploy:
No warranty, not under active development, etc.
The CI definition is mostly boilerplate and works the same as an SFDX CI setup, with JWT authentication as described in the docs.
Note that this is only set up for manual deployments, not automated staging pushes, but that could easily be added.
It's got two manual jobs in it, so once the pipeline is created, the
validate (check-only) job can be kicked off first, followed by the deploy step. This was for demonstration purposes and probably won't mirror what you'd want to do in production.
- curl https://force-cli.heroku.com/releases/v0.25.0/linux-amd64/force > force
- sudo mv force /usr/local/bin/force
- sudo chmod +x /usr/local/bin/force
- openssl aes-256-cbc -k $KEY -in assets/server.key.enc -out assets/server.key -d -md sha256
- force login --connected-app-client-id $CONSUMERKEY -u $USERNAME -key assets/server.key
- source assets/deploy.sh $START $END
- rm assets/server.key
This is the shim script I used to wrap ForceCLI and run the actual deployment. The manual handling of the static resources and Lightning components may not longer be necessary based on continued development of that tool.
# Use Force.com CLI and zip to compress static resources and perform delta deployment.
# David Reed, January 2019
# Compress unpacked static resources
for file in src/staticresources/* ; do
if [[ -d "$file" && ! -L "$file" ]]; then
zip -r ../`basename "$file"`.resource *
# Delta deploy items changed between our start and end commit
# We use `sed` to squash all references to Aura bundle components and static resources to their parent components
# `sort -u` ensures that we only have a single reference to each component.
git diff $1 $2 --name-only --diff-filter=ACM | grep "^src" | sed -E 's#^src/aura/([^/]+)/.+#src/aura/\1#' | sed -E 's#^src/staticresources/([^/]+)/.+#src/staticresources/\1\.resource#' | sort -u | force push $CHECK_ONLY -l RunLocalTests -r -f -