Use this action to build a package.xml (or use your own) and deploy to a specified Salesforce environment. After deployment, it writes a job summary on the Actions run (including metrics, component changes—name, type, and create/change/delete/unchanged/failed—and optional failure details) and exposes outputs for downstream steps.
DRY_RUN: description: "Validate deploy and run Apex tests but don't save to the org." type: boolean TEST_LEVEL: description: "Deployment Apex testing level." type: choice default: "RunLocalTests" options: - NoTestRun - RunSpecifiedTests - RunLocalTests - RunAllTestsInOrg - RunRelevantTests WAIT: description: "Number of minutes to wait for command to complete and display results." type: number default: 30 SOURCE_DIRECTORY: description: "Path to the local source files to deploy (used only when MANIFEST_PATH is not set)." type: string default: "force-app" MANIFEST_PATH: description: "If set, deploy this manifest instead of generating one from SOURCE_DIRECTORY." type: string POST_DESTRUCTIVE_CHANGES: description: "If set, passed to sf project deploy start as --post-destructive-changes." type: string SFDX_AUTH_URL: description: "The auth url tied to your deployment environment" type: string required: true DEPLOYMENT_ID: description: "The deployment id from a validation/ dry run." type: string
| Output | Description |
|---|---|
deployment_id |
Salesforce deployment job id. |
deploy_status |
Status from the CLI (e.g. Succeeded, Failed). |
tests_run |
Apex tests run during deploy (empty if not in the CLI JSON). |
tests_failed |
Apex test failures (empty if not in the CLI JSON). |
apex_org_wide_coverage_percent |
Approximate org-wide line coverage from deploy codeCoverage rows, e.g. 87.42 (empty if no coverage data—e.g. NoTestRun or CLI shape differs). |
Coverage is derived from runTestResult.codeCoverage in the deploy result. It is a lines-hit / lines-total rollup across returned rows, not the same as every Salesforce UI metric.
The summary includes a Component changes table built from the deploy result’s componentSuccesses and componentFailures:
| Modification | Meaning |
|---|---|
created |
created on the DeployMessage (new in org). |
change |
changed (updated). |
deleted |
deleted. |
unchanged |
Deployed but same as org (created/changed/deleted all false). |
failed |
Row from componentFailures. |
The manifest artifact row package.xml is omitted. Large deploys list at most 500 rows, with a note if more exist.
This action does not run sfdx-git-delta. In your workflow, install the plugin, run sf sgd source delta, then pass the generated paths into this action.
- Use
MANIFEST_PATHfor the incrementalpackage.xml(for examplesgd-out/package/package.xml). - Set
POST_DESTRUCTIVE_CHANGESwhen you also want post-destructive deletes in the same deploy (for examplesgd-out/destructiveChanges/destructiveChanges.xml). Omit it when that file does not exist or you are not deleting metadata. - For comparisons against
origin/main(or similar), useactions/checkoutwith enough history (for examplefetch-depth: 0) so git can resolve both refs. - Avoid deploying an empty
package.xml; follow SGD’s guidance and branch in your workflow when there is nothing to deploy.
Example fragment (same job must have the Salesforce CLI available before SGD):
- name: Install Salesforce CLI run: npm install -g @salesforce/cli - name: Install SGD run: echo y | sf plugins install sfdx-git-delta - name: Delta manifest run: sf sgd source delta --from "origin/main" --to "HEAD" --output-dir sgd-out --source-dir force-app - name: Deploy uses: your-org/github-actions@main with: SFDX_AUTH_URL: ${{ secrets.SFDX_AUTH_URL }} MANIFEST_PATH: sgd-out/package/package.xml POST_DESTRUCTIVE_CHANGES: sgd-out/destructiveChanges/destructiveChanges.xml
(Adjust the action reference and wire POST_DESTRUCTIVE_CHANGES only when that file exists, for example with a conditional step or if: in your workflow.)
To get the SFDX_AUTH_URL, enter the following command in your terminal:
sf org display --verbose --json -o <MY_TARGET_ORG_ALIAS>
Copy down the value of sfdxAuthUrl for later.
You can see how these work by checking my write-up or YouTube video.