Skip to content

Ship a change through the pipeline

The first two tutorials put the toolchain on our machine and a change into a scratch org. This one walks the road everyday work actually takes: branch → commit through the hooks → pull request → CI validation → a clean ending. On the way we will trip one gate on purpose, so the first rejection we meet in real life is one we have already read.

graph LR
    A["1 Branch"] --> B["2–3 Commit
    through the hooks"]
    B --> C["4 Push:
    Validate"]
    C --> D["4 Pull request:
    scratch + SIT check-only"]
    D --> E["5 Decline
    and clean up"]

Before we begin

  • The local setup is done and npm run setup has run in the repository.
  • We are on a clone of a repository that is wired into CI, and origin/develop exists.
  • Any branch rights suffice — the pull request is declined, not merged, at the end.

Step 1 — Create the branch

npm run branch:create

Answer the prompts: fork from develop, type feature, no Jira ticket, name it shipping-tutorial. The last prompt, Proceed?, defaults to No — type y. The command fetches first, validates the name against the convention and leaves us on feature/shipping-tutorial. (Running the tutorial a second time? Pick a fresh name — the command refuses a branch that already exists.)

Step 2 — Make a small, safe change

Open force-app/ and find any *.field-meta.xml file; improve its <description> — or add one where it is missing. A description edit deploys everywhere, breaks nothing, and the lint rules approve of it.

Step 3 — Commit, and fail on purpose

git add path/to/the/file.field-meta.xml
git commit -m "changed stuff"

Watch the hooks run: lint-staged formats the staged XML, npm run validate runs a build check — and then comes the rejection. Not for the change: for the message. The commit-msg hook enforces conventional commits, and changed stuff has no type:

✖   subject may not be empty [subject-empty]
✖   type may not be empty [type-empty]
…
husky - commit-msg script failed (code 1)

Nothing is lost — the file is still staged; only the message was refused. This is the shape of every rejection: the hooks stop the commit, say why, and leave the work intact. (When the change is what gets rejected, the output is longer — how to read a rejected commit is the map.)

Now commit properly — the hooks run again from the top, same wait:

git commit -m "chore: improve a field description"

This time the same checks pass and the commit lands.

Step 4 — Push and open the pull request

git push -u origin feature/shipping-tutorial

The push already starts a Validate run in CI: build, format:check, lint and the Jest tests, on a clean clone, no org involved — what the hooks checked per file, CI confirms for the whole tree. Now open a pull request against develop in Bitbucket. That starts the PR pipeline:

  • Scan Secrets checks every commit the pull request adds for credentials. Yours has none, so it passes in seconds.
  • Validate against Scratch claims a scratch org, deploys the change into it and runs the Apex tests. When the repository has not opted in, it skips, and a skip is a pass.
  • Validate against Org rehearses the change against the real SIT org as a check-only deploy — nothing is saved to the org.
  • An Audit Node Dependencies step appears additionally when the PR touches package.json, the lockfile or .npmrc.

Follow along under Pipelines; green checks appear on the pull request as the steps finish. Test results surface in Bitbucket's Tests tab only when something failed.

Step 5 — Close the loop and clean up

Decline the pull request in Bitbucket, then remove the branch:

git checkout develop
git branch -D feature/shipping-tutorial
git push origin --delete feature/shipping-tutorial

Nothing has reached an org anyone cares about — the scratch org was disposable and the SIT deploy was check-only.

What a real merge does

Merging a pull request into develop triggers the automatic stage deploy to SIT — delta or full, per the stage's opt-in; see delta deployments and quick deploys.

What we did

One change travelled the whole distance: a convention-checked branch, hooks that block locally with readable verdicts, a push-triggered validation, a pull request rehearsed against a scratch org and check-only against SIT, and a clean teardown. Everything else in this guide is detail on one of those stations — start with what happens when you commit.