An open source starter kit for the Trailblazer Community that makes it easy to build flexible CI/CD pipelines for Salesforce projects with GitHub Actions.
The kit is built around a set of small, focused GitHub Actions that abstract away the complexity of the Salesforce CLI. Think of them as building blocks: each one does one job well, and you compose them into exactly the pipeline your project needs. On top of the building blocks, this repository provides ready-to-use reusable workflows and copy-paste examples so you can get from zero to a working pipeline in minutes.
The full documentation site is an auto-generated catalog of every building block and reusable workflow - each with its complete input/secret reference and copy-paste examples.
- GitHub-native - no external DevOps platform required, just GitHub and GitHub Actions.
- Composable - small actions with clear inputs and outputs that you wire together freely.
- Transparent - every action is a thin, readable wrapper around the official
sfCLI. - Flexible - use the turnkey reusable workflows, or build your own pipeline from the blocks.
Each building block lives in its own repository so it can be versioned and consumed independently.
| Action | Purpose |
|---|---|
| π΅π» get-node-version | Resolve and set up the Node.js version from package.json |
| βοΈ sfdx-cli-setup | Install the Salesforce CLI and related plugins |
| π sfdx-login | Authenticate to an org via SFDX Auth URL or JWT |
| π©οΈ sfdx-create-scratch-org | Create a scratch org (outputs username / org-id) |
| ποΈ sfdx-delete-scratch-org | Delete a scratch org (cleanup) |
| π sfdx-deploy | Deploy metadata, with delta and validation-only (dry-run) modes |
| β sfdx-run-tests | Run Apex, LWC (Jest) and Flow tests with coverage reporting |
| π sfdx-code-review | Static quality gate: Salesforce Code Analyzer, Prettier and ESLint |
| π¦ sfdx-package-installation | Install (managed/unlocked) packages on a target org |
| πΎ sfdx-data-import | Import records via Bulk API 2.0 (CSV) or sObject Tree (JSON) |
The building blocks share a common foundation (checkout β setup β login) and then branch into the flow you need:
flowchart TD
A[Checkout] --> B[get-node-version]
B --> C[sfdx-cli-setup]
C --> D[sfdx-login Β· JWT]
D --> E{Choose your flow}
E -->|PR Validation| P[code-review]
E -->|Create Scratch Org| F[create-scratch-org]
E -->|Scratch Org CI| F2[create-scratch-org]
E -->|Deployment| M[deploy Β· delta / dry-run]
subgraph prv [PR Validation]
direction TB
P --> Pd[deploy Β· delta Β· dry-run Β· tests]
end
subgraph cso [Create Scratch Org]
direction TB
F --> G[package-installation]
G --> H[deploy]
H --> I[data-import]
I --> PW[generate password]
end
subgraph sci [Scratch Org CI]
direction TB
F2 --> G2[package-installation]
G2 --> H2[deploy]
H2 --> J[run-tests]
J --> L[delete-scratch-org Β· always]
end
classDef setup fill:#e3f2fd,stroke:#1565c0,color:#0d2b45;
classDef gate fill:#fff3e0,stroke:#e65100,color:#3e2600;
class A,B,C,D setup;
class E gate;
- A Salesforce Dev Hub (for scratch org flows) and/or the target orgs you want to deploy to.
- A CI integration user authenticated with the JWT bearer flow - the recommended, production-ready option used throughout this kit.
- The credentials stored as GitHub Actions secrets:
SFDX_CONSUMER_KEY,SFDX_JWT_SECRET_KEYandSFDX_USERNAME.
See docs/authentication.md for a step-by-step JWT setup (and the simpler SFDX Auth URL alternative for local experiments).
name: CI
on:
pull_request:
branches: [main]
# Least-privilege token: this pipeline only reads the repository.
permissions:
contents: read
jobs:
validate:
name: Validate
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v7.0.1
with:
fetch-depth: 0 # full history so sfdx-deploy can derive delta/destructive changes
persist-credentials: false # don't leave the GITHUB_TOKEN in .git/config for later steps
- name: Install SF CLI
uses: svierk/sfdx-cli-setup@v1.1.2
- name: Salesforce Org Login
uses: svierk/sfdx-login@v1.4.2
with:
client-id: ${{ secrets.SFDX_CONSUMER_KEY }}
jwt-secret-key: ${{ secrets.SFDX_JWT_SECRET_KEY }}
username: ${{ secrets.SFDX_USERNAME }}
alias: ci
- name: Validate Deployment
uses: svierk/sfdx-deploy@v1.2.1
with:
source-dir: force-app
target-org: ci
test-level: RunLocalTests
dry-run: trueFor common scenarios you don't have to wire the blocks together yourself - call one of the maintained reusable workflows instead:
| Workflow | Purpose |
|---|---|
| pr-validation.yml | Validate a pull request: static analysis + check-only (delta) deployment with tests |
| create-scratch-org.yml | Self-service scratch org provisioning: create β install packages β deploy β import data β generate password |
| scratch-org-ci.yml | Full scratch org lifecycle for CI: create β deploy β test β delete |
| deployment.yml | Deploy metadata to a sandbox or production org |
Call one from your own repository like this:
# A reusable workflow can never get more permissions than the caller grants -
# pr-validation additionally needs security-events: write to upload its SARIF report.
permissions:
contents: read
security-events: write
jobs:
pr-validation:
uses: svierk/salesforce-devops-starter-kit/.github/workflows/pr-validation.yml@v1.0.0
with:
source-dir: force-app
secrets:
SFDX_CONSUMER_KEY: ${{ secrets.SFDX_CONSUMER_KEY }}
SFDX_JWT_SECRET_KEY: ${{ secrets.SFDX_JWT_SECRET_KEY }}
SFDX_USERNAME: ${{ secrets.SFDX_USERNAME }}The examples folder contains complete, copy-paste-ready caller workflows for each reusable workflow. Drop one into your project's .github/workflows/ directory and adjust the inputs.
- π Documentation site - the full building-block and workflow catalog, generated from this repository
- docs/getting-started.md - set up your first pipeline end to end
- docs/authentication.md - configure SFDX Auth URL and JWT authentication
Every uses: reference in this repository - in the reusable workflows, in the examples and in the snippets above - is pinned to an exact release version, e.g. svierk/sfdx-deploy@v1.2.1. Do the same in your own pipelines:
- Never reference a mutable ref such as
@mainor@v1. It runs whatever code sits on that branch/tag at run time - with access to your org credentials - so a compromised or rewritten ref would run unnoticed. - Good - pin to an exact release tag (
@v1.2.1). Readable, concrete, and bumped through reviewed pull requests. This is what the kit itself uses. - Strictest - pin to a full-length commit SHA (
@a1b2c3dβ¦) with the version as a trailing comment. A SHA can never be re-pointed by the publisher; the cost is readability. Worth it for actions from publishers you don't control. - Enable Dependabot for
github-actionsso those pins are bumped for you instead of silently ageing - see .github/dependabot.yml for the setup used here (one grouped pull request per ecosystem, merged automatically once the required checks pass).
This applies to all actions your workflows reference - the building blocks of this kit as well as actions/* and any other third-party action.
Beyond pinning, the workflows in this kit follow a few rules that are worth copying:
- Least-privilege
GITHUB_TOKEN- every workflow declares apermissions:block granting only what it needs (contents: readin most cases). Remember that a reusable workflow can never receive more than the caller grants, so the PR validation needssecurity-events: writein the calling workflow as well. persist-credentials: falseon checkout - the token is not written to.git/config, so later steps (SF CLI, third-party actions) cannot reuse it.- Secrets travel as secrets - pass them via the
secrets:block of a reusable workflow or directly into an action input, and reference them in shell steps as environment variables ("$TARGET_ORG"), never by interpolating${{ ... }}into the script itself - that would allow command injection and can leak values into the log. - Validate pull requests with
pull_request, neverpull_request_target- the latter runs with the base repository's secrets, which would let a fork execute its own code against your org. - Mask generated credentials - values created at run time (e.g. a scratch org password) are masked with
::add-mask::before they can reach the log. - Gate production behind a GitHub Environment - the deployment workflow takes an
environmentinput for required reviewers and environment-scoped secrets.
Contributions are welcome! See CONTRIBUTING.md for how to propose changes, report issues, or add new building blocks.
The scripts and documentation in this project are released under the MIT License.
