Skip to Content
CLIGitHub Action

GitHub Action

The sfdt repo ships a composite GitHub Action: run any sfdt command as a single uses: step. The action sets up Node, installs the Salesforce CLI and a pinned @sfdt/cli, and optionally authenticates the org.

- uses: scoobydrew83/sfdt@v0 with: args-json: '["deploy","--smart","--org","ci","--dry-run"]'

Inputs

InputDefaultDescription
args-jsonPreferred. The sfdt arguments as a JSON array of strings, executed with no shell — values can never be interpreted as shell syntax. Exactly one of args-json / command is required
commandDeprecated — prefer args-json. The sfdt command line as one string. Without allow-shell-command it accepts only shell-neutral characters (letters, digits, space, _@%+=:,./-) and runs without a shell; quotes and metacharacters are rejected
allow-shell-commandfalseSet 'true' to run command through a shell (legacy eval) when it needs quoting or expansion. Only for fully trusted static strings — never workflow-interpolated user input. Removed in v1
cli-versionautoauto installs the CLI version shipped at the pinned action ref — pinning the action tag pins the CLI. latest or an exact semver also accepted
auth-methodnonenone (job already authenticated) | sfdx-url | jwt
sfdx-auth-urlSfdx auth URL for sfdx-url (pass a secret)
consumer-keyConnected-app consumer key (client id) for jwt (pass a secret)
jwt-secret-keyContents of the JWT signing private key for jwt (pass a secret), not a file path
usernameUsername to authenticate as for jwt
instance-urlhttps://login.salesforce.comLogin URL for jwt
org-aliasci-orgAlias assigned to the authenticated org (also set as default org)
node-version22Node to set up — @sfdt/cli requires Node ≥ 22.15; the action fails fast with a clear ::error:: on older versions

Example — PR validation (sfdx auth URL)

- uses: actions/checkout@v4 with: fetch-depth: 0 - name: Smart delta validation uses: scoobydrew83/sfdt@v0 with: args-json: '["deploy","--smart","--org","ci","--delta-base","origin/${{ github.event.pull_request.base.ref }}","--dry-run"]' auth-method: sfdx-url sfdx-auth-url: ${{ secrets.SFDX_AUTH_URL }} org-alias: ci

Example — JWT release deploy

- name: Smart delta deploy uses: scoobydrew83/sfdt@v0 with: args-json: '["deploy","--smart","--org","prod","--delta-base","v1.4.0","--notify"]' auth-method: jwt consumer-key: ${{ secrets.SFDX_CONSUMER_KEY }} jwt-secret-key: ${{ secrets.SFDX_JWT_SECRET_KEY }} username: ${{ secrets.SFDX_USERNAME }} org-alias: prod

Migrating from command

command still works for shell-neutral strings (it is now word-split without a shell), but anything needing quotes or expansion must either move to args-json (recommended — each argument becomes one array element, no quoting needed) or explicitly opt in with allow-shell-command: 'true':

# before (ran through a shell — quoting-sensitive, injection-prone): # command: deploy --smart --delta-base "origin/main" --dry-run # after (no shell involved): args-json: '["deploy","--smart","--delta-base","origin/main","--dry-run"]'

Version pinning

The floating v0 tag tracks the newest stable release automatically (the release pipeline force-moves it; beta releases never move it). Pin an exact tag (@v0.16.2) or a commit SHA for immutable references — with cli-version: auto that also pins the CLI version.

Security

  • Pass secrets only as ${{ secrets.X }} expressions — never as literals in the workflow file.
  • Prefer args-json: its values are never interpreted by a shell. Never feed untrusted input (branch names, PR titles, issue text) to command with allow-shell-command: 'true' — that mode runs the string through a shell.
  • The JWT key is written to a temp file and always deleted (trap on exit).

sfdt ci init --provider github --runner action generates complete workflows built on this action — see CI/CD Integration.

Last updated on