Appendix A: Sample GitHub Actions Workflow YAML¶
This appendix provides annotated example GitHub Actions workflow files and composite actions implementing the pipeline architecture described throughout this whitepaper. They are examples to adapt, not files promised to run unchanged. The files follow a modular design: each quality gate lives in its own reusable workflow under .github/workflows/, and repeated step sequences (authentication, environment setup, PR commenting, result parsing) are extracted into composite actions under .github/actions/.
All workflow files follow the conventions established in this whitepaper: Conventional Commits-compatible job naming, structured PR comment output via $GITHUB_STEP_SUMMARY and dedicated PR comment composite actions, and jq for parsing JSON output.
With a release management tool
An organisation that deploys with a Salesforce release management tool, such as AutoRABIT ARM, Gearset, Copado, Flosum, or Salesforce DevOps Center, does not need most of the deployment workflows in this appendix. The tool moves each change from org to org itself, and most such tools also validate the change and run its tests. The gates that check the repository before a change is merged, such as Prettier, static analysis, and the secret scan, can still run as GitHub workflows beside the tool.
Action version note: every uses: reference on this page names the version that was current at the time of writing. Where a page references the organisation's own reusable workflows and composite actions by tag, the tag is an immutable release tag; third-party actions are shown at their floating major tag, a choice many organisations make for highly trusted actions such as actions/checkout (the trade-off is explained in Appendix G). Newer major versions may introduce breaking changes: review the release notes for an action before upgrading, and validate in a non-production pipeline before promoting to protected branches. Dependabot and Renovate (Section 7) open PRs when new versions are published, providing a structured upgrade path.
The sfdx-git-delta plugin (GitHub) is used throughout for delta package generation, scoping analysis and test execution to changed files rather than the full workspace. The delta package is the folder that sfdx-git-delta writes for a change: a package.xml that lists the changed metadata, and a copy of each changed file. It is not a Salesforce package. The delta package is generated once per PR run by a dedicated reusable workflow and uploaded as a GitHub Actions artifact, then downloaded by every downstream job that needs it: code analysis, Apex tests, Jest tests, and deployment validation. This avoids the cost and inconsistency of regenerating the delta independently in each job.
The Gates at a Glance¶
Each table lists the gates that one kind of trigger runs: the tool behind each gate, what the gate does, and what happens when it finds a violation. The tools a developer runs before any of this, on their own machine, are in Section 4.
On a Pull Request¶
The gates of the pull request callers (A.1.2 and A.1.3), in the order they run. The run has four stages. In stage 1 the seven quick gates run in parallel, each a checkout and a short script, or Prettier. Stage 2 builds the delta package, and it starts only when every stage 1 gate has passed, so a pull request that fails a quick gate ends in seconds, before any org is touched. Stage 3 works on the delta package, with its three gates in parallel. Stage 4 validates the delta package against the org. Stage 5 is the Quality Gates job, the one required check, which waits for every job before it. A Variables job, which is not a gate, runs beside stage 1 and passes the thresholds from the top of the file to the gates that take them. The diagram shows which job waits for which, with each job under the name it has in the callers.
---
config:
layout: dagre
---
flowchart TB
accTitle: The stages of a pull request run
accDescr: Stage 1 runs seven quick gates in parallel: formatting, the pull request title, secrets, prohibited metadata, destructive changes, the risk tier, and the changelog. Stage 2, the delta package, waits for all seven. Stage 3 runs code analysis, the Jest tests, and the Apex test selection in parallel, each on the delta package. Stage 4, the validation against the org, waits for code analysis and the Apex test selection. Stage 5, the Quality Gates job, is the one required check, and it waits for every job.
subgraph s1 ["Stage 1: the quick gates, in parallel"]
prettier["Formatting"]
title["PR Title"]
secrets["Secrets"]
tier0["Prohibited Metadata"]
destructive["Destructive Changes"]
risk["Risk Tier"]
changelog["Changelog"]
end
subgraph s2 ["Stage 2: the delta package, once every stage 1 gate has passed"]
delta["Delta Package"]
end
subgraph s3 ["Stage 3: the gates on the delta package, in parallel"]
sca["Code Analysis"]
jest["Jest Tests"]
apexsel["Apex Test Selection"]
end
subgraph s4 ["Stage 4: the validation against the org"]
validate["Validation"]
end
subgraph s5 ["Stage 5: the one required check"]
gates["Quality Gates"]
end
tier0 ~~~ delta
s1 --> delta
delta --> sca
delta --> jest
delta --> apexsel
sca --> validate
apexsel --> validate
jest --> gates
validate --> gates
| Stage | Gate | Tool | What It Does | Violations Outcome |
|---|---|---|---|---|
| 1 | Prettier Formatting Verification | Prettier | Checks that the source files are formatted to the repository's Prettier configuration | Blocks |
| 1 | Semantic PR Title Check | amannn/action-semantic-pull-request |
Checks that the pull request title follows Conventional Commits, because the title becomes the commit on the protected branch | Blocks |
| 1 | Secret Scanning | The scanner the organisation chooses, such as Gitleaks, TruffleHog, or GitHub secret scanning; the sample's scan step is a placeholder | Scans the change for committed credentials | Blocks, once a scanner is in place |
| 1 | Prohibited Metadata Check (Tier 0) | None: a script and the risk matrix | Looks for any Tier 0 path that the pull request adds or changes | Blocks |
| 1 | Destructive Change Detection | None: a script | Looks for destructiveChanges.xml in the diff |
Does not block. Labels the pull request destructive-change, so that branch protection holds it for a person's approval |
| 1 | Metadata Risk Tier | None: a script and the risk matrix | Labels the pull request with the highest metadata risk tier among its files | Never blocks. Disables auto-merge on Tier 1 |
| 1 | Changelog Check | None: a script | Looks for a change to CHANGELOG.md in any pull request but the release pull request of Release Please, which alone writes that file |
Blocks |
| 2 | Generate Delta Package | sfdx-git-delta |
Builds the delta package of what the pull request changes, which the gates below work on | None: it checks nothing. The gates below skip when the delta package is empty |
| 3 | Static Code Analysis | Salesforce Code Analyzer and its engines (Appendix E) | Scans the delta, uploads SARIF to GitHub code scanning, posts the violations as two tables, and stores the complete results as an artifact for 7 days | Blocks at severity 1 and 2. Severity 3 to 5 is reported |
| 3 | LWC Jest Tests | Jest, and Codecov | Runs the Jest tests when Lightning Web Components changed, comments the result on the pull request, and uploads coverage | Blocks on a failing test, and on a component that has a test and is below 80% line coverage; a component with no test is only named |
| 3 | Apex Test Selection | None: a script | Chooses the test level and the tests for the validation | None: it only chooses |
| 4 | Metadata Validation and Apex Tests | Salesforce CLI | Validates the delta against the target org without deploying it, runs the chosen Apex tests, and posts the results as tables | Blocks on a component that will not deploy, a failing test, or a changed class or trigger below 75% coverage |
| 5 | Quality Gates, the last job of each caller | None | Collects the result of every gate above. It is the one check the branch ruleset requires (Appendix C) | Blocks when any gate failed or was cancelled. A skipped gate counts as passing |
Two more workflows answer a pull request event. A.1.6 runs the Metadata Risk Tier gate again whenever auto-merge is enabled. A.1.8 gives a pull request from Dependabot a title that the Semantic PR Title Check accepts; it is not a gate.
On a Push¶
The gates of the push baseline (A.1.1), which runs on every push. On a branch whose name starts with feat, only the secret scan and the formatting check run. A push workflow cannot stop a commit that is already pushed, so a failed run flags the problem before a pull request is opened.
| Gate | Tool | What It Does | Violations Outcome |
|---|---|---|---|
| Prettier Formatting Verification | Prettier | Checks that the source files are formatted to the repository's Prettier configuration | Fails the run |
| Secret Scanning | The scanner the organisation chooses; the sample's scan step is a placeholder | Scans for committed credentials | Fails the run, once a scanner is in place |
| Static Code Analysis | Salesforce Code Analyzer and its engines | Scans only the files that the push changed, and writes violations to the job summary | Never fails: on a push it is advisory |
| LWC Jest Tests | Jest, and Codecov | Runs every Jest test, since a push has no pull request diff to narrow them | Fails the run for a failing test, and for a tested component below 80% line coverage |
On a push to main the same workflow also runs Release Please, which is not a gate.
On Demand¶
One workflow runs only when a person starts it, from the "Run workflow" button (A.1.7). It is a report, not a gate.
| Gate | Tool | What It Does | Violations Outcome |
|---|---|---|---|
| Full Repository Scan | Salesforce Code Analyzer and its engines | Scans the whole repository, and stores every violation as a CSV file, a JSON file, and a SARIF file in one artifact to download | Never fails. The job summary has the counts for each severity and a table of every blocking violation |
On a Schedule¶
The checks of the two scheduled callers (A.1.4 and A.1.5), which look at the org and at the whole repository, and not at a change.
| Gate | Tool | What It Does | Violations Outcome | Schedule |
|---|---|---|---|---|
| Full Code Analyzer Scan | Salesforce Code Analyzer and its engines | Scans the whole repository, as the on-demand scan does, so that a file that no change touches is still reported | Never fails. The job summary has the counts for each severity and a table of every blocking violation, and every violation is in an artifact | Weekly |
| Org Scan: Metadata Drift Detection | Salesforce CLI, and git diff |
Retrieves the org's metadata over the checked-out source and compares it with the last commit | Opens a GitHub issue; the run stays green, and Slack gets the report on every run, drift or none | Weekly |
| Apex Compilation Check | Salesforce CLI | Retrieves every Apex class and trigger from the org and checks that all of it compiles | Opens a GitHub issue, fails the run, and notifies Slack | Weekly in production, and nightly in a pipeline sandbox |
| Apex Tests (Full Org) | Salesforce CLI, and Codecov | Runs every local test with RunLocalTests and uploads coverage |
Opens a GitHub issue and fails the run; Slack gets the report on every run, pass or fail | Nightly |
| LWC Jest Tests | Jest, and Codecov | Runs every Jest test with coverage | Fails the run for a failing test, and for a tested component below 80% line coverage; Slack gets the report on every run, with the number of components with no test | Nightly |