Foreman›Docs›Salesforce pack›foreman sf commands

foreman sf commands

Salesforce pack · page 9 of 9

skills, deletes, baseline, rollback and fixed.

Foreman 0.11.05 sections

Preview. The Salesforce pack ships in Foreman 0.11.0 as a preview: its release paths pass their own tests, but no story has yet shipped through them on a live org. Treat the Gearset path as experimental.

foreman sf holds the Salesforce pack’s own commands: five subcommands you run from your shell on a Salesforce project, or that Foreman runs for you when you answer a question on the dashboard. This page gives each one’s arguments, what it does, when to run it, and where the feature behind it is explained. Pack commands appear in foreman --help on every project, whatever its pack, so a pack’s set-up commands can run before its config is complete; on a project that does not use the Salesforce pack each one stops with “this project does not use the salesforce pack”. The Salesforce pack is a preview: its release paths pass their own tests, but no story has yet shipped through them on a live org, and the Gearset path has not run against a live Gearset pipeline.

Command What it does
foreman sf skills Installs the pinned Salesforce skills and writes skills-lock.json
foreman sf deletes allow|keep-out <story id> Records your answer to a story’s metadata deletes
foreman sf baseline [--refresh|--story] Prints, or refreshes, what each org already fails
foreman sf rollback <story id> Rolls stage 1 back from a story’s merge
foreman sf fixed <stage> Records a stage you fixed by hand as healthy

Every subcommand loads the resolved config first and exits 1 when it has errors. Exit codes are 0 for success, 1 for a failure, and 64 for a usage mistake, which prints the usage lines. Each command prints a line for people and then one JSON line, so a script can read the last line. Every sf call these commands make runs through the isolated sf home; see What the pack adds.

foreman sf skills

foreman sf skills

Installs the official Salesforce skills the pack’s agents load, from salesforce.skills.source (default forcedotcom/sf-skills) at salesforce.skills.tag (default 1.66.0), the way the npx skills CLI installs them, so either tool can read what the other wrote:

  • .agents/skills/<name>/ holds each skill’s files, copied from the source at the pinned tag
  • .claude/skills/<name> is a relative symlink to it
  • skills-lock.json records each skill’s source, ref, commit and a hash of its files, sorted and 2-space indented; commit is Foreman’s own field, the commit the tag pointed at

Foreman owns skills-lock.json: it replaces the skills the lock lists, removes the ones a new tag drops, and refuses to overwrite a skill folder it did not install, naming the clashing path (“.claude/skills/<name> is not from forcedotcom/sf-skills; move it or remove it”). A source holding a user name, a token or a query string is refused before any git call, and the reason does not repeat it.

It commits nothing. On success it says what to commit: skills-lock.json, .agents/skills and .claude/skills. When the tag now points at another commit than the lock recorded for the same source and tag, it warns on stderr that the tag was moved upstream, so check what changed before you commit; the JSON then carries movedFrom, the old commit.

Run it once when you turn the pack on, and again whenever you change salesforce.skills.tag. The Set up tab’s Skills step runs it for you, and foreman doctor’s salesforce skills lock and salesforce skills named checks tell you when the install is missing, changed or does not cover a skill an agent names. See What the pack adds and The salesforce block.

The JSON is { ok, source, tag, commit, installed, removed, movedFrom? }, or { ok: false, reason }.

foreman sf deletes

foreman sf deletes allow ACME-12
foreman sf deletes keep-out ACME-12

Records your answer to the metadata deletes a story’s release is waiting on. When a story’s change deletes Salesforce components and salesforce.destructive is ask (the default), the sf-delta ship step stops with exit code 10, records the question, and the story asks you on Waiting on you: “Allow deletes for ACME-12?”, with the options “Allow the deletes” and “Keep them out”. Answering there runs this command for you and puts the story back to todo; you only run it by hand when you answer from a shell.

allow holds for the exact destructiveChanges.xml the question was about: the record keeps its SHA-256, and on the next release the deletes go ahead only if the change still deletes exactly the same components. If they differ, you are asked again. keep-out sends the story back to be built without deleting anything, with a note for the architect, and a rebuilt change that still deletes something gets a fix round.

The command refuses when no deletes wait for the story ({ ok: false, reason: "no deletes wait for ACME-12" }, exit 1), and it refuses inside a story session against the run’s project, so only your shell or the supervisor’s answers path records an answer. The record is .foreman/salesforce/deletes/<story id>.json.

The JSON is { ok, story, answer, sha256, deletes }, where deletes is the count. The feature is explained under sf-delta and metadata deletes, and the dashboard side under Waiting on you.

foreman sf baseline

foreman sf baseline
foreman sf baseline --story
foreman sf baseline --refresh

The baseline is what each org already fails before any story touches it: its failing local Apex tests and its org-wide coverage, which foreman doctor records so that an inherited failure is never blamed on a story. The command works on each org in the layout once, the build orgs first and then each stage’s org, with its roles.

Without a flag it prints what the doctor saved for each org and runs no sf: “acme-qa (stage qa): already fails AcmeServiceTest.sumsTheOrder, 71% Apex coverage”, or “no failing tests”, “no Apex”, “no baseline yet”, running or aborted. The JSON is { ok: true, testOrg, orgs: [{ alias, roles, state, failing, coverage, finishedAt }] }, where testOrg is the org the tester’s dry run uses: the test stage’s org when salesforce.test names one, else this slot’s build org.

--story prints the same, and trusts an org’s failing tests only when the baseline finished, and its file was written, before this checkout’s branch was last created or reset (its newest “Created from” or “Reset to” reflog entry). A baseline saved during the story never counts, so a test the story itself broke cannot be called inherited. Each entry gets trusted, and the JSON gets branchStart, an ISO time, or null when the branch start cannot be read, in which case nothing is trusted. The tester runs this form. It also runs no sf and exits 0.

--refresh drops each saved baseline and starts the org’s local tests again (sf apex run test --test-level RunLocalTests --code-coverage, without waiting) in the isolated sf home, sandboxes only: each org is checked on the server first to be a sandbox with the org ID salesforce.orgs gives it. It prints the test run ID per org and tells you foreman doctor records the result once the run finishes. The JSON is { ok, started: [{ alias, testRunId }], failed: [{ alias, reason }] }, with ok false and exit 1 when any org could not start.

Run --refresh after you fix the failing tests in an org, or after a sandbox refresh, so the doctor stops warning that RunLocalTests cannot pass there. See The baseline and the salesforce baseline and salesforce test level checks on Doctor checks.

foreman sf rollback

foreman sf rollback ACME-12

Rolls stage 1 back from a story’s merge: reverts the merge on the branch and deploys the reverse delta to stage 1’s org, then puts back any agent the story published there. On the sf release path it is ship.rollback.command, which Foreman runs itself when a step fails after the stage 1 deploy succeeded; the release stays halted either way, and the halt reason says whether the rollback worked. Run it by hand when you want a shipped story out of stage 1 again, for instance after a problem the release’s checks did not catch. It keeps the slot’s heartbeat itself while it waits on the deploy, which can take up to 30 minutes.

It refuses, before any git or sf call, a release path other than sf, a story with no stage 1 deploy recorded, a story that reached a later stage (“Foreman never rolls back a later stage, because people share those orgs. Releases stay halted: roll uat back by hand”), a story already rolled back, a stage 1 org that changed since the deploy, an unknown merge commit, a rebase merge, and a branch that is the production branch. A refusal is { ok: false, refused: true, reason } and exit 1; a failure part way is { ok: false, reason } with what to do by hand.

On success it prints “rolled ACME-12 back on acme-qa (qa): reverted 1a2b3c4 with 5d6e7f8, deploy 0Af…, 1 manual step(s) left”, and the JSON is { ok: true, story, stage, org, merge, revert, jobId, components, deletes, manualSteps, agents }: components is what the reverse delta put back, deletes what it removed, manualSteps the Flows and RecordTypes you delete by hand, and agents the agents restored with their versions. jobId is null when the reverse delta was empty and nothing was deployed.

The full order of work and every failure case are under Rollback on stage 1.

foreman sf fixed

foreman sf fixed qa

After you fixed a halted stage by hand, records that stage’s last deploy as the branch head and the org’s newest real deploy, so foreman health passes and “I’ve fixed it” can clear the halt. Run it when a release left a stage in a state Foreman cannot confirm: a deploy that outlived the 30-minute wait and which you finished or repeated yourself, a ci path deploy you re-ran, or a later stage you rolled back by hand. The health check’s own failure message names this command when it can help.

The argument is a stage name from salesforce.stages. The command fetches the stage’s branch from the remote and takes its head as the commit; checks the stage’s org on the server, a sandbox with the org ID salesforce.orgs gives it (production is never a target); reads the org’s newest succeeded deploy that is not check-only; and writes .foreman/salesforce/deployed/<stage>.json with that commit and job ID. It records nothing on a failure: an unknown stage, a branch it cannot read, an org it cannot check or that is not the configured sandbox, or an org with no deploy to record.

On success it prints “recorded acme-qa (qa) as fixed at 1a2b3c4 with deploy 0Af…; “I’ve fixed it” can clear the halt now”, and the JSON is { ok: true, stage, org, commit, jobId }. Then clear the halt on the dashboard or with foreman fixed, which runs foreman health and lets the held stories ship only when every check passes. See Release safety for halts and sf-deploy and sf-verify for the health check this command satisfies.