Check-only release and quick deploy
Validate a release candidate against an org with sfp release --checkonly, then commit that validation by id with --quickdeploy.
A release can be split into two runs of sfp release against the same org:
--checkonlydeploys the release candidate once in check-only mode and returns the validated deploy id. Nothing is committed to the org.--quickdeploycommits that validation by id with the Metadata APIdeployRecentValidation. Salesforce does not compile the payload again or run its tests again. The post-deployment steps and the release bookkeeping then run as they do for a plain release.
sfp release --releasecandidate core:Release-2.0.0 --targetorg production \
--repository myorg/myrepo --checkonly --json > validation.json
sfp release --releasecandidate core:Release-2.0.0 --targetorg production \
--repository myorg/myrepo \
--quickdeploy "$(jq -r '.releases[0].packages[0].deployId' validation.json)" \
--payload-fingerprint "$(jq -r '.releases[0].packages[0].payloadFingerprint' validation.json)" \
--jsonEligibility
Both flags apply the same rule, before any call to the org. The release must have:
- exactly one release definition
- exactly one package in that definition's deployment queue, after
skipDeployOnOrgsandignoreOnStageare applied - a
sourceordiffpackage
Unlocked packages install as package versions and data packages load through SFDMU; neither has a check-only mode. A release that breaks the rule exits with code 1 and one of the reason codes MULTIPLE_RELEASE_DEFINITIONS, MULTIPLE_PACKAGES or INELIGIBLE_PACKAGE_TYPE.
--checkonly and --quickdeploy cannot be combined with each other or with --dryrun.
Validating with --checkonly
The run fetches the artifact, compares the installed version (skipIfAlreadyInstalled), applies the component checksum skip, and then deploys once in check-only mode. Tests follow the plain release: production always runs them, and a sandbox runs them only when the environment's policy or --runtests asks.
Salesforce only quick deploys a validation that ran tests. On a sandbox, pass --runtests with --checkonly; a validation that ran no tests returns quickDeployEligible: false with NO_TESTS_RUN.
None of the following runs during a check-only release:
- installation of external package dependencies (a missing dependency fails the validation)
- promotion
- pre- and post-deployment scripts, permission set assignment and deployers
- the artifact commit to the org, deployment tracking and the environment baseline
- release metadata publishing and changelog generation (
--generatechangelogis ignored) release.*andpackage.installation.*webhooks
The top-level checkOnly block in the JSON output carries outcome, an optional reasonCode and a message:
| Outcome | Exit | Meaning |
|---|---|---|
validated | 0 | The check-only deployment succeeded and deployId is set |
nothing-to-validate | 0 | The package is already installed (ALREADY_INSTALLED) or has no components to deploy (NO_COMPONENTS_TO_DEPLOY) |
validation-failed | 1 | The deployment failed (DEPLOYMENT_FAILED) or a dependency is not installed (MISSING_PACKAGE_DEPENDENCIES) |
rejected | 1 | The release is not eligible |
The package entry in releases[].packages[] adds deployId, testLevel, testSummary, validatedAt, quickDeployEligible and payloadFingerprint. quickDeployEligible is true only for a successful validation that ran at least one test; otherwise quickDeployIneligibleReason is VALIDATION_FAILED, NO_DEPLOYMENT or NO_TESTS_RUN.
payloadFingerprint is a hash of the package name, version, source commit and the artifact's component checksums. It is computed without an org call, so the quick deploy can confirm that it is committing the validated artifact. sfp keeps no state between the two runs; pass the id and the fingerprint yourself.
Committing with --quickdeploy
--quickdeploy takes the deployId and requires --payload-fingerprint. The run fetches the artifact, then stops without an org call when the release is not eligible or when the fetched artifact's fingerprint differs from --payload-fingerprint (FINGERPRINT_MISMATCH). It then checks that external package dependencies are installed, without installing them (MISSING_PACKAGE_DEPENDENCIES).
Any deployment that reaches the org after the validation invalidates the id, so nothing that deploys runs before the commit:
- pre-deployment permission sets, the
preDeploymentscript and the pre-deployers are skipped, and each skip is logged - the payload is not rebuilt. Salesforce commits exactly what it validated, including reconciled profiles
After the commit, post-deployment permission sets, post-deployers, the postDeployment script and the artifact commit run in their normal order. Deployment tracking, the environment baseline, release metadata, the changelog and the webhooks follow, as for a plain release.
When the package is already installed at that version, the id is not used and the outcome is nothing-to-deploy.
When Salesforce rejects the id
Salesforce refuses a validated id (sf:INVALID_ID_FIELD) when:
- another deployment reached the org after the validation
- the id was already quick deployed
- the validation failed or ran no tests
- the id does not belong to a check-only deployment
- the validation is older than 10 days
The refusal comes before anything is committed.
- By default, the release fails with
QUICK_DEPLOY_REJECTEDand Salesforce's message. The org, the artifact record and the environment baseline are unchanged; run a plain release to deploy the candidate. - With
--quickdeploy-fallback, sfp deploys the same artifact in full instead, with its complete lifecycle including the pre-deployment steps. The outcome isfell-back, and the message carries Salesforce's reason.
An id that Salesforce does not know (INVALID_DEPLOY_ID), and a quick deploy that Salesforce accepted and that then failed (DEPLOYMENT_FAILED), fail the release. Neither falls back.
The top-level quickDeploy block in the JSON output carries outcome, an optional reasonCode, a message and the validatedDeployId:
| Outcome | Exit | Meaning |
|---|---|---|
quick-deployed | 0 | Salesforce committed the validated payload |
fell-back | 0 | The id was rejected and the package was deployed in full |
nothing-to-deploy | 0 | The package is already installed or has nothing to deploy |
rejected | 1 | The release is not eligible, the fingerprint differs, a dependency is missing, or Salesforce rejected the id without --quickdeploy-fallback |
failed | 1 | The id is unknown or the deployment failed |
The package entry in releases[].packages[], and the release history recorded on sfp server, add quickDeploy with the validatedDeployId, the deployId of the commit (or of the full deployment after a fallback), the outcome and, when Salesforce refused the id, its reason.