The bin/*.sh stage script contract
Deploy pipeline stages explains that pre-destructive, deploy-unpackaged, post-destructive, and post-deploy each run a matching bin/*.sh script if one exists in the repo. This page is the contract those scripts are written against: the arguments they receive, the directory they run in, and what simply-cicd does to the repo before invoking them. None of this is written down anywhere else, so read it before writing your first script.
The scripts
Section titled “The scripts”| Stage | Script |
|---|---|
pre-destructive | bin/preDestructive.sh |
deploy-unpackaged | bin/unpackagedDeploy.sh |
post-destructive | bin/postDestructive.sh |
post-deploy | bin/postDeploy.sh |
Each is optional — a stage with no matching script for a given repo is a no-op for that repo. install-packaged has no script; it’s handled entirely by the CLI.
Before the script runs
Section titled “Before the script runs”For every deployment that has a matching script, simply-cicd:
- Installs dependencies. If
package.jsonexists at the repo root, it runsnpm install --omit=dev(withHUSKY_SKIP_INSTALL=1set) so the script can rely on localnode_modules. A missingpackage.json, or a failed install, is silently skipped either way — the script still runs. - Fixes permissions. It best-effort
chmods whichever oflogs/,data/,bin/, andscripts/exist in the repo (bin/gets+rx, the other three+rw), so a repo doesn’t need to pre-set permissions in git. A failedchmodonly logs a warning — on a Linux/macOS runner this can leave a script non-executable, so commitbin/*.shwith the executable bit set as a safety net rather than relying on this step alone.
Invocation
Section titled “Invocation”./bin/<script>.sh --target-org <alias> --test-level <level> [--tests <tests>]--target-org— the value passed to the stage command’s--aliasflag (an empty string if none was given).--test-level— the deployment’s owntestLevel(an optional per-entry override indeploy.json’sdeployments[]; see Project vs. Happy Soup), falling back to the stage command’s--test-levelflag, falling back toRunLocalTests.--tests— only appended when a value resolves, from the deployment’s owntestsor the stage command’s--testsflag; omitted entirely otherwise.
The script is invoked directly, not through a shell — its first line needs its own shebang (#!/usr/bin/env bash or equivalent), and it needs to be executable.
Working directory, output, and exit code
Section titled “Working directory, output, and exit code”- Working directory is the repository root: for a
projectpipeline’s implicitlocaldeployment, that’s the CI job’s own checkout; for ahappy-soupdeployment, that’s the fresh clonesimply-cicdjust made for thatdeployments[]entry. - stdout/stderr stream straight through to the CI job’s own log — nothing is captured, buffered, or filtered.
- Environment is inherited in full from the CI job’s process. Every
SIMPLY_CICD_*variable and CI-native variable (CI_COMMIT_SHA, etc.) visible to thesf simply cicdprocess is visible to the script too, with no extra plumbing required. - Exit code determines success or failure: a non-zero exit fails that deployment’s job for that stage, which is what
DEPLOY_PROGRESS.jsonthen records as the point to resume from on retry (see Deploy pipeline stages).