Simple Salesforce CICD for Developers
Hello Salesforce Developers!
Are you ready to automate your life away with a few lines of YAML? Today, I will walk you through setting up a CI/CD pipeline that's so simple, that even your pet could probably do it.
Overview
The pipeline we will set up today will automatically deploy your code to a Salesforce scratch org, run tests, and handle clean-up tasks whenever you push changes to your main branch or create a pull request.
What's CI/CD? It automates all the boring stuff like unit testing and deployment so you can focus on what developers should do.
The pipeline we will set up today will:
- Deploy your code to a Salesforce scratch org
- Run unit tests
- Upload coverage reports
- Delete Scratch Org
Prerequisites
Before we start, you need:
- GitHub Account: We will create a repository where your Salesforce project will be stored.
- Salesforce CLI: Locally installed for initial setup.
- Salesforce Dev Hub: Access to a Salesforce Production Org where you can enable Dev Hub (available in Developer, Enterprise, Performance, and Unlimited Editions - more info)
If you don't have any private Salesforce Org you can create one for free - Developer Edition
Step 1: Create Github Repository
-
Go to GitHub and create a new repository - https://github.com/new
You can set it as Public or Private, it is up to you. -
Clone the newly created repo on your local PC.
git clone https://github.com/0ptaq0/simple-cicd.git
Step 2: Generate SFDX Project
- Open the newly cloned GIT repository and type the below commands in the bash terminal:
cd ..
sf project generate --name simple-cicd
cd simple-cicd
Step 3: Prepare Your Salesforce Dev Hub
-
Enable Dev Hub in your Salesforce Production Org:
- Navigate to Setup, enter
Dev Hub
in the Quick Find box, then select Dev Hub. - Turn on the Enable Dev Hub feature (Be aware it cannot be undone, wise you must be my young Padawan)
- Navigate to Setup, enter
Step 4: Generate Dev Hub Auth URL
- Open your command line interface.
- Log in to your Dev Hub org via Salesforce CLI by running:
sfdx auth:web:login --setdefaultdevhubusername --setalias myDevHub
- Retrieve an SFDX Auth URL which is used in the CI/CD pipeline for authentication:
sfdx force:org:display --target-org myDevHub --verbose --json
OR
sf force org display --target-org myDevHub --verbose --json
- Securely store the
sfdxAuthUrl
value
This will be used as a secret in GitHub.
In my example, it will be likeforce://PlatformCLI::5Aep..
- Navigate to your GitHub repository.
- Go to Settings > Secrets > Actions.
- Click on New repository secret.
- Add the following secrets:
SFDX_AUTH_URL_DEVHUB
: Paste the SFDX Auth URL you generated from your Dev Hub.
Step 5: Create GitHub Pipeline
- Configure GitHub Actions Workflow
- In your repository, create a new file under
.github/workflows/
namedsalesforce-ci.yml
.
mkdir -p .github/workflows && touch .github/workflows/salesforce-ci.yml
- Copy and paste the below CI configuration into this file:
(be careful about indentations, they do matter in YAML files)
- In your repository, create a new file under
name: Salesforce CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
deploy-to-scratch-org-and-run-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Salesforce CLI with NPM
run: |
npm install @salesforce/cli --global
- name: Authorize with the dev hub
run: echo "${{ secrets.SFDX_AUTH_URL_DEVHUB }}" | sf org login sfdx-url --alias DevHub --set-default-dev-hub --sfdx-url-stdin
- name: Create scratch org
run: sf org create scratch --definition-file config/project-scratch-def.json --alias ScratchOrg --wait 30 --duration-days 1 --set-default --json
- name: Push source
run: sf project deploy start --target-org ScratchOrg --wait 30
- name: Run tests
run: sf apex run test --target-org ScratchOrg --code-coverage --result-format human --output-dir ./tests/apex --test-level RunLocalTests --wait 30
# - name: Upload coverage reports to Codecov
# uses: codecov/codecov-action@v4.0.1
# with:
# token: ${{ secrets.CODECOV_TOKEN }}
# flags: Apex
- name: Delete scratch org
if: always()
run: sf org delete scratch --target-org ScratchOrg --no-prompt
-
Understanding Your GitHub Actions Workflow
- on: Specifies the GitHub events that trigger the workflow.
- jobs: Defines the jobs that the workflow will execute. Our job,
deploy-to-scratch-org-and-run-tests
, performs several steps: - Checkout: Checks out your repository content into the GitHub Actions runner.
- Install Node.js: Salesforce CLI requires Node.js, installed here using a GitHub Action.
- Install Salesforce CLI with NPM: Installs Salesforce CLI globally on the runner.
- Authorize with Dev Hub: Uses the stored SFDX Auth URL to authenticate.
- Create Scratch Org: Generates a temporary org for testing.
- Push Source and Run Tests: Deploys all your Salesforce Metadata to the scratch org and runs unit tests.
- Upload Coverage Reports: Optional step to upload test coverage to Codecov - I will cover that later.
- Delete Scratch Org: Cleans up by deleting the scratch org after tests are complete.
-
Now you should understand how the pipeline is going to work. Before we see it in action, let's add at least one Salesforce Metadata to be deployed to our Scratch Org. We'll create a simple Apex Class and Unit Test to see if our CICD works.
- In Visual Studio Code press
Ctrl/Cmd + Shift + P
and add Apex Class and then Apex Unit Test Class
- SimpleCalculator Apex Class
- In Visual Studio Code press
public class SimpleCalculator {
public static Integer addNumbers(Integer num1, Integer num2) {
return num1 + num2;
}
}
- SimpleCalculatorTest - Apex Unit Test Class
@isTest
public class SimpleCalculatorTest {
@isTest
static void testAddNumbers() {
Integer a = 5;
Integer b = 3;
Integer result = SimpleCalculator.addNumbers(a, b);
System.assertEquals(8, result, 'The addNumbers method did not return the expected result.');
}
}
- Nice, we have all we need, now time for testing!
Step 7: Test Your CI/CD Pipeline
-
Commit and Push your changes to the main branch
-
Go to the Actions tab in GitHub to see your workflow in action.
Step 8: Optional - Set Up CodeCov integration
If you want to see Unit Test Coverage information in Pull Requests add Codecov integration.
-
Login into CodeCov app
-
Find your repository and click the
Configure
button
(if you cannot find your repository - make sure you have installed CodeCov application in your GitHub account) -
Add
CODECOV_TOKEN
repository secret -
Uncomment below code in your Pipeline
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: Apex
-
Open Pull Request to the
main
branch and see beautiful comments with coverage information
Step 9: Optional - Add Static Code Analysis
A quick way to check your code against the most common best practices is to run a static code analysis.
There are multiple tools on the market for that, but here we will use a dedicated tool from Salesforce - Salesforce Code Analyzer - which you can run locally and in CICD!
It comes with built-in scanning engines like:
- Salesforce Graph Engine
- PMD
- ESLint
- RetireJS
Also, you can customize it by writing your own rules, but come on - this article is about simple CICD, so let's use the default options.
We can implement in 3 steps:
- First we need to install it using this command
sf plugins install @salesforce/sfdx-scanner@latest
-
Then execute scan
sf scanner run --target="force-app" --outfile sf-code-scanner-results.html
You can use flag
--severity-threshold
i.e.--severity-threshold 3
to throw error if there are rule violations - At last archive the HTML report and that's it!
The Final code assuming CodeCov integration will look like this:
name: Salesforce CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
workflow_dispatch:
jobs:
deploy-to-scratch-org-and-run-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
- name: Install Salesforce CLI with NPM
run: |
npm install @salesforce/cli --global
sf plugins install @salesforce/sfdx-scanner@latest
- name: Run SF Code Scanner
run: |
sf scanner run --target="force-app" --outfile sf-code-scanner-results.html
- name: Upload SF Code Scanner Analysis
uses: actions/upload-artifact@v4
with:
name: sf-code-scanner-results
path: |
sf-code-scanner-results.html
- name: Authorize with the dev hub
run: echo "${{ secrets.SFDX_AUTH_URL_DEVHUB }}" | sf org login sfdx-url --alias DevHub --set-default-dev-hub --sfdx-url-stdin
- name: Create scratch org
run: sf org create scratch --definition-file config/project-scratch-def.json --alias ScratchOrg --wait 30 --duration-days 1 --set-default --json
- name: Push source
run: sf project deploy start --target-org ScratchOrg --wait 30
- name: Run tests
run: sf apex run test --target-org ScratchOrg --code-coverage --result-format human --output-dir ./tests/apex --test-level RunLocalTests --wait 30
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
flags: Apex
- name: Delete scratch org
if: always()
run: sf org delete scratch --target-org ScratchOrg --no-prompt
Conclusion
With this setup, you've created a robust CI/CD pipeline that requires minimal maintenance and helps you ensure the quality and reliability of your Salesforce applications. This pipeline not only automates your deployments but also reinforces best practices by running unit tests automatically and performing static code analysis.
Feel free to adjust the workflow to fit more complex development processes, or keep it as is for a simple, effective CI/CD solution.
Happy coding!