hardis:project:promotion:create
Description
Command Behavior (Beta)
This feature is in Beta. Promotion branches are switched off by default; their behavior may still change from feedback. Please report any issue or feedback on the sfdx-hardis GitHub repository.
Assembles a promotion branch: a branch carrying only the approved User Stories of a major branch (ex: uat), so they reach the next major branch (ex: preprod) before the rest of the promotion window.
This is the only supported way to create a promotion branch (Beta). The command:
- checks that
enablePromotionBranches: trueis set in the sfdx-hardis configuration; - checks that
allowedPromotionStepsdeclares the steps promotions may run on (ex:- source: uat/target: preprod), and keeps to them: only those source and target branches are offered, and naming another one fails; - requires the git provider connection (GitHub, GitLab, Bitbucket or Azure DevOps) and refuses to run without it: the carried Pull Requests are read from the provider, so the command behaves the same on every platform and every carried Pull Request gets its real title and author. From VS Code, the extension passes its own connection; from a terminal, an agent or CI, set the provider token as an environment variable or in a
.envfile at the repository root; - lists the Pull Requests merged into the source branch and not yet promoted to the target branch, and lets you select the ones to carry (or takes them from
--pull-requests). A Pull Request another promotion branch already carries to the same target is left out, unless--include-already-promotedis passed; - creates the branch from the target branch, named
promotion/<source>/<target>/<YYYY-MM-DD>-<HHMM>(UTC, ex:promotion/uat/preprod/2026-09-06-1430), with-2,-3... added only when that name is already taken; - cherry-picks the merge commit of each selected Pull Request, oldest first, with
-xso each commit keeps a pointer to its origin; - pushes the branch and creates the Pull Request to the target branch, with a description declaring the carried Pull Requests (
promotionPullRequests), their titles, authors, source branches and tickets.
The deployment jobs then treat the declared Pull Requests as the scope of the promotion Pull Request: their deployment actions run, their Apex test classes are collected, their custom behaviors are inherited.
On a cherry-pick conflict, you choose (or --on-conflict decides) to:
- commit-with-markers, and all the following conflicts (recommended, the first answer offered): same as below, and the command stops asking for the rest of the promotion. A promotion window conflicts on the same files story after story, so the promotion is assembled in one go and every conflict is then solved in a single pass by a coding agent: copy the prompt from the Pull Request description (also saved in
hardis-report/) and paste it to Claude Code, Codex, Copilot... This is a prompt answer only,--on-conflict commit-with-markersalready applies to every conflict; - commit-with-markers: commit the story anyway with its git conflict markers, so the conflicts can be solved later on the branch, by hand or with a coding agent. The Pull Request description lists the files to fix and embeds a ready-to-paste prompt for a coding agent (Claude Code, Codex, Copilot...), also saved as a markdown report in
hardis-report/. The validation job fails until the markers are gone; - skip: leave the story out, it is listed as such in the Pull Request description;
- abort: stop, the branch is deleted and nothing is pushed.
Technical explanations
- Candidates are the first-parent commits of
origin/<source>since its merge base withorigin/<target>, grouped with their Pull Requests likehardis:work:backpromotedoes (Pull Request numbers read from the merge commit messages and completed by the git provider API, whose connection this command requires). - A first-parent commit that only moves other merges (a major-to-major sync like
integration -> uat, a promotion branch merged into its target) is opened up into the first-parent commits it brought in, so each User Story is a candidate of its own instead of the whole sync window being a single row. - The branch is created with
git checkout -b <name> origin/<target>, commits are applied withgit cherry-pick -x(-m 1for merge commits). - The Pull Request is created through the git provider API (GitHub, GitLab, Azure DevOps, Bitbucket token), or with the
ghCLI on GitHub. The creation is retried a few times: the branch is pushed a fraction of a second before, and a provider that has not indexed the new ref yet answers that the source branch does not exist. Without either, the branch is pushed and the description is saved underhardis-report/to create the Pull Request by hand, and the message names the reason the provider gave. - The date and the time are in UTC. The name is checked against every promotion branch of the step that exists or existed: local and remote branches, remote-tracking refs, the merge commits of the target branch and its Pull Requests merged in the last two days. A name already taken gets
-2,-3..., one more than the highest one found, and the branch creation stops on a name that still exists instead of resuming that branch.
Agent Mode
Supports non-interactive execution with --agent:
sf hardis:project:promotion:create --agent --source-branch uat --pull-requests 482,487,491
In agent mode:
--source-branchand--pull-requestsare required;--target-branchdefaults to the first merge target of the source branch. Outside agent mode,--pull-requestsonly preselects the stories in the prompt (this is how the VS Code extension passes the stories ticked in the DevOps Pipeline), and the user confirms the selection.- Every number of
--pull-requestsmust match a Pull Request merged into the source branch and not yet promoted, otherwise the command fails before touching git. - A cherry-pick conflict undoes the whole promotion (branch deleted, nothing pushed) and fails the command naming the conflicting Pull Request, unless
--on-conflict skipor--on-conflict commit-with-markersis passed. - To choose those numbers first, list what can be promoted with
sf hardis:project:promotion:list-candidates --agent --source-branch uat --json: same candidates, nothing created. - The git provider token must be available (ex:
GITHUB_TOKEN), from the environment or from a.envfile at the repository root: agent mode never prompts for the connection, it stops without it.
Learn by doing
The free Salesforce DevOps with sfdx-hardis course runs this command, click by click, on an org of your own:
Parameters
| Name | Type | Description | Default | Required | Options |
|---|---|---|---|---|---|
| agent | boolean | Run in non-interactive mode for agents and automation | |||
| debug -d |
boolean | Activate debug mode (more logs) | |||
| flags-dir | option | undefined | |||
| include-already-promoted | boolean | Also offer the Pull Requests another promotion branch already carries to the same target branch (left out by default). | |||
| json | boolean | Format output as json. | |||
| on-conflict | option | What to do when a cherry-pick conflicts: skip (leave the story out), commit-with-markers (commit it with its conflict markers, to solve later), abort (undo the whole promotion). Prompted if not provided, abort in agent mode. | skip commit-with-markers abort |
||
| pull-requests -p |
option | Comma-separated numbers of the Pull Requests to carry (ex: 482,487). Preselected in the prompt when provided, taken as is in agent mode where the flag is required. | |||
| skip-pull-request | boolean | Push the promotion branch without creating its Pull Request (the description is saved in hardis-report/). | |||
| skipauth | boolean | Skip authentication check when a default username is required | |||
| source-branch -s |
option | Major branch the approved User Stories are merged into (ex: uat). Prompted if not provided, required in agent mode. | |||
| target-branch -t |
option | Major branch the promotion goes to (ex: preprod). Defaults to the first merge target of the source branch. | |||
| websocket | option | Websocket host:port for VsCode SFDX Hardis UI integration |
Examples
$ sf hardis:project:promotion:create
$ sf hardis:project:promotion:create --source-branch uat
$ sf hardis:project:promotion:create --source-branch uat --target-branch preprod --pull-requests 482,487
$ sf hardis:project:promotion:create --agent --source-branch uat --pull-requests 482,487,491
$ sf hardis:project:promotion:create --agent --source-branch uat --pull-requests 482 --skip-pull-request
$ sf hardis:project:promotion:create --agent --source-branch uat --pull-requests 482,487 --on-conflict commit-with-markers