Skip to content

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: true is set in the sfdx-hardis configuration;
  • checks that allowedPromotionSteps declares 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 .env file 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-promoted is 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 -x so 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-markers already 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 with origin/<target>, grouped with their Pull Requests like hardis:work:backpromote does (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 with git cherry-pick -x (-m 1 for merge commits).
  • The Pull Request is created through the git provider API (GitHub, GitLab, Azure DevOps, Bitbucket token), or with the gh CLI 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 under hardis-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-branch and --pull-requests are required; --target-branch defaults to the first merge target of the source branch. Outside agent mode, --pull-requests only 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-requests must 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 skip or --on-conflict commit-with-markers is 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 .env file 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

Comments