Back up the metadata of your Salesforce Service Cloud orgs automatically and track every change in a Git repository, for example one commit per hour for both preprod and production.
Using the sfdx CLI to retrieve the metadata listed in a package.xml manifest, and a cron job on a headless VM to commit and push the result.
Preparation
- Basic linux knowledge (check LINUX cheatsheet for help)
- Basic Salesforce knowledge (check Apex cheatsheet for help)
- SFSC instance
- SFSC User granted with permissions:
ModifyMetadataandAuthor Apex - Github.com account
- Basic git knowledge
Walkthrough
Setup from Personal PC
- From browser, create an empty Git repo βmyinstance-repoβ
- From ~/
# β οΈ sfdx force:* commands are deprecated, use the sf CLI equivalents
$ sfdx force:auth:web:login -r https://test.salesforce.com # authorize Org
Successfully authorized myinstance_username with org ID 00xxxxxxXXXxx
$ git clone git-username@myinstance-repo # pull git repo
$ sfdx force:project:create -n myinstance-repo # init sf project into repo
$ cd myinstance-repo && git status
$ mkdir manifest
$ vim manifest/package.xml # create package
get from https://myrosblog.com/salesforce/retrieve-code
$ git add . && git commit -m "sfdx project" && git push
$ sfdx force:org:display -u myinstance_username --verbose --json
{
"status": 0,
"result": {
"id": "00 [...]
}Setup for automatic backups from headless VM
From ~/
$ vim authFile.json
paste JSON data from Personal PC
$ export SFDX_USE_GENERIC_UNIX_KEYCHAIN=true # to bypass SFDX usage of gnome-keyring (errors: X11 $DISPLAY, secret-tool org.freedesktop.Secret.Error.IsLocked)
$ sfdx force:auth:sfdxurl:store -f authFile.json
Successfully authorized myinstance_username with org ID 00xxxXXXXXxxXXXX
$ rm authFile.json
$ git clone git-username@myinstance-repo
$ cd myinstance-repo
$ sfdx force:source:retrieve -u myinstance_username -x manifest/package.xml
$ git add . && git commit -m "package" && git pushMulti-instance setup
~/
.sfdx/
alias.json
instance1_preprod.json
instance1_prod.json
instance2_preprod.json
instance2_prod.json
instance1-preprod/
.git/ connected to repo1 @ preprod
force-app/
instance1-prod/
.git/ connected to repo1 @ main
force-app/
instance2-preprod/
.git/ connected to repo2 @ preprod
force-app/
instance2-prod/
.git/ connected to repo2 @ main
force-app/Make it recurrent with CRON jobs
Create cronjobs.bash:
#!/bin/bash
export SFDX_USE_GENERIC_UNIX_KEYCHAIN=true
githubToken="insert_your_github_token" # β οΈ hardcoded secret (12-factor): read it from the environment, and avoid tokens in push URLs
case $1 in
instance1-preprod)
cd ~/instance1-preprod && ~/sfdx/bin/sfdx force:source:retrieve -u user1@instance1-preprod -x manifest/package.xml && git add . && git commit -m "$(date +%Y-%m-%d_%H:%M:%S)" && git push https://$githubToken@github.com/MY_ORG/INSTANCE1
;;
instance1-prod)
cd ~/instance1-prod && ~/sfdx/bin/sfdx force:source:retrieve -u user1@instance1-prod -x manifest/package.xml && git add . && git commit -m "$(date +%Y-%m-%d_%H:%M:%S)" && git push https://$githubToken@github.com/MY_ORG/INSTANCE1
;;
# β οΈ truncated: the case block is never closed (esac) and the other instances are omittedMake it executable, test it, then schedule it:
$ chmod +x cronjobs.bash
$ ./cronjobs.bash instance1-preprod # test
$ crontab -e
20 * * * * ~/cronjobs.bash instance1-preprod
25 * * * * ~/cronjobs.bash instance1-prodValidation & troubleshooting
The cron jobs automatically generate commits like 2022-12-27_16:30:07 for both preprod & prod.
Common errors:
-
INSUFFICIENT_ACCESS: the user is missing theModifyMetadatapermission -
Entity type 'ApexClass' is not available in this organization: the user is missing theAuthor Apexpermission - X11
$DISPLAYorsecret-tool org.freedesktop.Secret.Error.IsLockedon the headless VM: setSFDX_USE_GENERIC_UNIX_KEYCHAIN=trueto bypasssfdxusage of gnome-keyring
Going further
-
Retrieve all source code via VS Code: the
package.xmlmanifest used here