Skip to content

CLI Reference

Complete reference for the cascade command-line tool.

Terminal window
# Latest stable
go install github.com/stablekernel/cascade/cmd/cascade@latest
# Latest build from master
go install github.com/stablekernel/cascade/cmd/cascade@master
# Specific version
go install github.com/stablekernel/cascade/cmd/cascade@v2.0.4
# Run without installing
go run github.com/stablekernel/cascade/cmd/cascade@latest version

In GitHub Actions, the generated workflows install the CLI via setup-cli. To invoke it manually:

- uses: stablekernel/cascade/.github/actions/setup-cli@master
with:
token: ${{ secrets.GITHUB_TOKEN }}
# version: latest # 'beta', or a specific version

The action downloads the GoReleaser archive (tar.gz), extracts it, and installs the cascade binary on PATH.

These flags are available on all commands:

FlagTypeDescription
--dry-runboolPreview mode: show what would happen without making changes
--traceboolEnable TRACE-level logging for detailed internals
--jsonboolOutput structured JSON for workflow consumption

Display version information.

Terminal window
cascade version

Output (with --json):

{
"version": "v2.0.4",
"commit": "abc123d",
"date": "2026-01-15T10:30:00Z"
}

Parse and validate the manifest file.

Terminal window
cascade parse-config
FlagTypeDefaultDescription
--configstringauto-detectPath to manifest file (auto-detects .github/manifest.yaml)
--manifest-keystringciTop-level key inside the manifest
--environmentstring-Filter deploys by environment

Determine which builds/deploys are triggered by file changes.

Terminal window
cascade detect-changes \
--base-sha abc123 \
--head-sha def456
FlagTypeRequiredDescription
--configstringNoPath to manifest file (default: auto-detect)
--base-shastringYesBase commit SHA
--head-shastringYesHead commit SHA
{
"triggered_builds": ["app"],
"triggered_deploys": ["cdk", "services"],
"has_changes": true,
"changed_files": [
"src/main.go",
"cdk/stack.ts"
]
}
  1. Get the changed file list between base and head
  2. For each build/deploy, check whether any changed file matches its triggers
  3. Build-linked deploys inherit triggers from referenced builds

Generate a markdown changelog from conventional commits.

Terminal window
cascade generate-changelog \
--base-sha abc123 \
--head-sha def456 \
--repo owner/repo
FlagTypeRequiredDescription
--base-shastringYesBase commit SHA
--head-shastringYesHead commit SHA
--repostringYesRepository (owner/repo)
--exclude-pathsstringNoComma-separated paths to exclude
--contributorsboolNoInclude contributors section
{
"changelog": "### Features\n\n- Add user authentication ...",
"has_breaking": false,
"has_features": true,
"has_fixes": true
}
TypeCategoryIncluded
featFeaturesYes
fixBug FixesYes
perfPerformanceYes
docs / chore / ci / test / style / refactorRoutineNo

Breaking changes detected via:

  • ! suffix: feat!: breaking change
  • Footer: BREAKING CHANGE: description (case-sensitive, line start)

Scaffold a starter manifest and matching callback workflow stubs, verified through the real generator before anything is written.

Terminal window
# Two-environment pipeline (dev, prod) in the current directory
cascade init --topology two-env
# Custom ordered environments; the last is the release stage
cascade init --envs staging,production --name my-service
# Preview without writing
cascade init --topology three-env --dry-run

init renders .github/manifest.yaml plus build (and, when environments are set, deploy) stubs under .github/workflows, runs the manifest through parse, validation, and generation, and only then writes the files. It also drops a starter .github/CODEOWNERS and an .github/aws-oidc-role.example.json IAM trust-policy example for GitHub Actions OIDC; both carry placeholder owners and account IDs to replace. The manifest carries a $schema directive for editor autocomplete and validation. After running it, fill in the stub callbacks, commit, and run cascade generate-workflow. The scaffold is a verifying starter: cascade init then cascade generate-workflow leaves cascade verify clean, so you can wire a drift gate from the first commit.

FlagTypeDefaultDescription
--topologystringtwo-envPreset shape: one of no-env, two-env, three-env, four-env
--envsstring-Comma-separated ordered environment names; the last is the release stage (overrides --topology)
--namestringdir base nameProject name woven into the stubs
--dirstring.Target directory to scaffold into
--cli-versionstringpinned releasecascade CLI version pinned in the manifest
--force, -fboolfalseOverwrite existing files
--dry-runboolfalsePrint what would be written without writing anything

Environment names are positional, not semantic: the last name is the release stage. An empty list (--topology no-env) produces a release-only project with no deploy stub. If any target file already exists and --force is not set, init aborts and lists the conflicts, writing nothing.

Generate the orchestrate and promote workflows from the manifest.

Terminal window
cascade generate-workflow
FlagTypeDefaultDescription
--config, -cstringauto-detectPath to manifest file
--manifest-keystringciTop-level key inside the manifest
--action-folderstringmanage-releaseFolder for the manage-release composite action
--output, -ostring.github/workflows/orchestrate.yamlOutput path for orchestrate workflow
--promote-outputstring.github/workflows/promote.yamlOutput path for promote workflow
--validate-onlyboolfalseOnly validate, don’t generate
--dry-runboolfalsePrint generated workflow to stdout
--force, -fboolfalseOverwrite output file without prompting
--commitboolfalseCommit generated files
--push, -pboolfalsePush (implies --commit)
--orchestrate-onlyboolfalseOnly generate orchestrate.yaml
--promote-onlyboolfalseOnly generate promote.yaml
  • Output discovery: parses callback workflows to discover declared outputs
  • Dependency ordering: topological sort honours depends_on
  • Output chaining: passes outputs from one callback to dependents
  • Per-callback policies: respects run_policy, on_failure, retries
  • Environment overrides: applies env_inputs per environment
  • Publish step: when publish: is configured, the promote workflow dispatches the callback once per build at the boundary where a prerelease becomes a release

Check that the committed workflow and action files match what the manifest would currently generate, without writing anything. verify is read-only: it never writes files, runs git, or modifies the repository.

Terminal window
cascade verify

verify reports drift when a file the manifest would generate is missing on disk, or when its committed bytes differ from the generated bytes. It covers the complete set of files generate-workflow emits (orchestrate, promote or release, external-update, validate-check, merge-queue, hotfix, rollback, pr-preview, drift-check, drift-comment, and the manage-release composite action), so adopters do not need to enumerate files by hand.

verify also reports orphans: cascade-owned workflow files left behind in the workflows output directory that the manifest no longer plans (for example, after removing an environment or build). Only files carrying the cascade-generated marker are considered, so hand-written workflows in the same directory are never flagged. An orphan is reported as drift and exits 1 like any other drift. Pass --allow-orphans to skip this check when stale generated files are expected. Orphan detection reads the workflows output directory only and never deletes anything.

FlagTypeDefaultDescription
--config, -cstringauto-detectPath to manifest file
--manifest-keystringciTop-level key inside the manifest
--action-folderstringmanage-releaseFolder for the manage-release composite action
--output, -ostring.github/workflows/orchestrate.yamlPath of the orchestrate workflow
--promote-outputstring.github/workflows/promote.yamlPath of the promote workflow
--quiet, -qboolfalseSuppress the per-file report body; only set the exit code
--allow-orphansboolfalseDo not report cascade-owned workflow files that are no longer in the plan as drift
ExitMeaning
0No drift: every generated file is present and byte-identical, and no orphaned generated files remain
1Drift detected: a generated file is missing, its committed bytes differ, or a cascade-owned file is orphaned (unless --allow-orphans is set)
2Error: the manifest is missing or invalid, or another operational failure prevented the check from running

verify replaces a hand-rolled “regenerate and git diff” drift check with a single step. A CI job can run cascade verify to fail the build whenever committed workflows fall out of sync with the manifest:

- run: cascade verify

Rather than wire this job by hand, set drift_check.enabled: true in the manifest and generate-workflow emits the drift-check workflow for you. See Drift-check workflow.

Preview, as a per-file unified diff, what generate-workflow would change in the committed workflow and action files, without writing anything. plan is read-only: it never writes files, runs git, or modifies the repository.

Terminal window
cascade plan

plan is the human-facing preview counterpart to verify. For every file the manifest would generate, it prints the diff between the committed bytes and the generated bytes: a new file appears as a whole-file add, a changed file as a unified hunk, and a file already in sync produces no diff. When nothing is pending it prints a single plan: N files, no pending changes line; otherwise it prints the diffs followed by a summary of how many files would change.

FlagTypeDefaultDescription
--config, -cstringauto-detectPath to manifest file
--manifest-keystringciTop-level key inside the manifest
--action-folderstringmanage-releaseFolder for the manage-release composite action
--output, -ostring.github/workflows/orchestrate.yamlPath of the orchestrate workflow
--promote-outputstring.github/workflows/promote.yamlPath of the promote workflow
ExitMeaning
0Success, whether or not any diff was printed. plan is informational, so a pending change does not change the exit code
non-zeroError: the manifest is missing or invalid, or another operational failure prevented the preview from running

plan and verify are separate commands with separate contracts. plan is the human preview you read before regenerating: it shows the actual diff and always exits 0 on success, so it never fails a build on its own. verify is the pass/fail gate you wire into CI: it prints a terse drift report and exits 1 on drift, 2 on an operational failure, so it fails the build when committed workflows fall out of sync. Reach for plan at the terminal to see what would change, and for verify in a CI job to enforce that nothing has.

Produce the branch-protection settings for a cascade-managed trunk. The command has two modes. By default it emits the JSON body for an operator to apply and makes no API call. Pass --apply and cascade PUTs the body to GitHub’s branch-protection API for you using a caller-supplied scoped token.

Terminal window
cascade branch-protection

The output is a wrapper with two top-level keys:

  • protection is the exact body to PUT to the branches protection API.
  • operator_todo is companion guidance and is NOT part of the PUT body.

Apply it by sending only the .protection object:

Terminal window
cascade branch-protection | jq .protection | \
gh api -X PUT repos/my-org/my-app/branches/main/protection --input -

Instead of piping the JSON to gh, pass --apply and cascade sends the PUT itself. It transmits only the .protection object; the operator_todo guidance is never part of the request. The default emit behavior is unchanged: without --apply cascade still only prints or writes the JSON.

Terminal window
cascade branch-protection --apply --repo my-org/my-app --branch main

With --apply, --branch is the real protection target rather than just a label on the guidance note. cascade resolves the target repository from --repo, falling back to the GITHUB_REPOSITORY environment variable, and the REST API base from --api-url, falling back to GITHUB_API_URL and then https://api.github.com. A missing token or repository is reported before any network call, and a non-2xx response from GitHub (for example a 403 from an under-scoped token) is surfaced with GitHub’s own rejection message.

Applying branch protection requires a token with repo-admin authority (the Administration: write permission). The workflow GITHUB_TOKEN does not carry that authority, so --apply needs a scoped personal access token supplied through --token or the GITHUB_TOKEN environment variable. Prefer the environment variable over the flag so the token stays out of process arguments and shell history.

Terminal window
GITHUB_TOKEN=ghp_your_admin_pat \
cascade branch-protection --apply --repo my-org/my-app --branch main

You can also pass --output alongside --apply to keep the emitted JSON on disk for your records; cascade writes the file first and then applies.

The required status checks contain only the cascade-controlled Setup and Finalize jobs. These are the orchestrate workflow’s two steps jobs; cascade knows their exact check-run names and both run on every pipeline run. Because of that, .protection applied as-is never creates a required check that can never report, so it never blocks a pull request on its own.

The reusable-workflow caller jobs (validate, build, deploy) are deliberately left out of the required contexts. cascade knows each caller’s display-name prefix (for example Build (my-app)) but not the inner job name that GitHub appends to form the real check-run context, which is <DisplayName> / <inner-job>. That inner job lives in your reusable workflow, which cascade does not author. Requiring a bare prefix would never match and would block every pull request, so cascade lists those prefixes under operator_todo.complete_these_contexts as <DisplayName> / <inner-job> placeholders instead. Replace <inner-job> with the job name inside each reusable workflow, then add the completed strings to required_status_checks.contexts when you want them required.

In the default emit mode the --branch flag only labels the guidance note (with --apply it is the real apply target, as described above). Either way the required contexts are the same across branches and environments because they are the orchestrate-workflow steps jobs, so --env would not change them and is not offered.

This command complements the hotfix branch-protection advisory (see Hotfix workflow): the advisory prints ready-to-run gh commands for env branches, while branch-protection emits the full PUT body for the trunk.

FlagTypeDefaultDescription
--config, -cstringauto-detectPath to manifest file
--manifest-keystringciTop-level key inside the manifest
--branchstringmainBranch the protection targets; with --apply this is the real apply target, otherwise it labels the guidance note only and does not change the required contexts
--output, -ostringstdoutWrite to this path instead of stdout (- also means stdout); honored alongside --apply to keep the JSON for your records
--applyboolfalsePUT the .protection body to GitHub instead of emitting JSON (requires a repo-admin token)
--tokenstringGITHUB_TOKENScoped repo-admin token used for --apply; falls back to the GITHUB_TOKEN environment variable
--repostringGITHUB_REPOSITORYowner/repo the apply targets; falls back to the GITHUB_REPOSITORY environment variable
--api-urlstringGITHUB_API_URLREST API base for --apply; falls back to GITHUB_API_URL, then https://api.github.com

Emit a per-environment configuration file an operator applies to GitHub’s Environments REST API. cascade emits the file; the operator applies it. cascade never calls the GitHub API.

Terminal window
cascade environments

The output is a wrapper. The top-level environments is an array with one entry per manifest environment, in the manifest’s environments order. Each entry has:

  • name is the cascade environment name.
  • gha_environment is the GitHub Environment to configure; it defaults to name.
  • environment is the exact body to PUT to the environments API.
  • operator_todo is companion guidance and is NOT part of the PUT body.

Apply it by sending only the .environment object per entry:

Terminal window
cascade environments | jq -c '.environments[] | {gha_environment, environment}' | while read -r row; do
env=$(jq -r .gha_environment <<<"$row")
jq .environment <<<"$row" | gh api -X PUT "repos/my-org/my-app/environments/$env" --input -
done

The per-environment settings come from the manifest under config.environment_config.<env>:

config:
environments: [dev, test, prod]
environment_config:
prod:
gha_environment: production
required_reviewers: [team/ops]
wait_timer: 10
branch_policy: protected
secrets: [MY_SECRET]
variables: [REGION]

What the body carries, and what is operator guidance

Section titled “What the body carries, and what is operator guidance”

The .environment body holds only the fields cascade can fully form from the manifest:

  • wait_timer in minutes (0..43200).
  • deployment_branch_policy, mapped from the manifest branch_policy: protected becomes {protected_branches: true, custom_branch_policies: false}; custom becomes {protected_branches: false, custom_branch_policies: true}; all or unset becomes null, meaning all branches.

Everything else cascade cannot fully form from the manifest is surfaced under operator_todo so the operator can finish it:

  • operator_todo.required_reviewers lists user and team slugs, NOT the body. The REST API requires a numeric reviewer id that the manifest does not carry, so the operator resolves each slug to an id and adds it to the body’s reviewers array.
  • operator_todo.secrets and operator_todo.variables list the expected env-scoped secret and variable names. cascade emits names only, never values; the operator creates them with values through the environment-secrets and environment-variables APIs.
  • branch_patterns and tag_patterns (custom policy only) are created through the deployment-branch-policies API and are surfaced under operator_todo.

The output is deterministic: the same manifest yields byte-identical output, and environments follow the manifest order.

This is the sibling of the branch-protection command, using the same emit-a-config-file pattern (operator applies; cascade never calls the API).

FlagTypeDefaultDescription
--config, -cstringauto-detectPath to manifest file
--manifest-keystringciTop-level key inside the manifest
--output, -ostringstdoutWrite to this path instead of stdout (- also means stdout)

Manage GitHub releases.

Terminal window
cascade manage-release \
--action create \
--repo owner/repo \
--tag v1.0.0 \
--changelog "Release notes here"
FlagTypeRequiredDescription
--actionstringYescreate, update, lock, prerelease, publish, delete
--repostringYesRepository (owner/repo)
--tagstringYesRelease tag
--environmentstringNoTarget environment
--shastringNoRelease commit SHA
--changelogstringNoRelease notes (markdown)
--changelog-filestringNoPath to file containing notes (overrides --changelog)
--tokenstringNoGitHub token (or use GITHUB_TOKEN)
--previous-tagstringNoPrevious tag for changelog comparison
--new-tagstringNoNew semver tag (for prerelease action)
--delete-tagstringNoTag to delete after publish (cleanup)
--create-tagboolNoCreate git tag on create
ActionDescription
createCreate a new release
updateUpdate an existing release
lockMark as pre-release
prereleaseRe-tag a draft RC as a non-draft pre-release
publishFinalize a release (drops the RC suffix)
deleteDelete a release

Main CI/CD orchestration command with subcommands.

Prepare the execution plan.

Terminal window
cascade orchestrate setup \
--environment dev \
--sha def456
FlagTypeRequiredDescription
--configstringNoPath to manifest file (default: auto-detect)
--manifest-keystringNoTop-level key (default: ci)
--environmentstringYesTarget environment (empty for no-env setup)
--shastringNoHead SHA (default: current HEAD)
--gha-outputboolNoWrite outputs to $GITHUB_OUTPUT
{
"triggered_builds": ["app"],
"triggered_deploys": ["cdk", "services"],
"version": "v1.2.0-rc.0",
"execution_plan": {
"waves": [
{"callbacks": ["app", "cdk"]},
{"callbacks": ["services"]}
]
}
}

Update state and the release after deployments.

Terminal window
cascade orchestrate finalize \
--environment dev \
--sha abc123 \
--version v1.2.0-rc.0 \
--build-results "app:success" \
--deploy-results "cdk:success,services:success"
FlagTypeRequiredDescription
--environmentstringYesTarget environment
--shastringYesHead SHA
--versionstringYesCalculated version
--build-resultsstringNoComma-separated name:status pairs
--deploy-resultsstringNoComma-separated name:status pairs

Promotion command with subcommands.

FlagTypeDefaultDescription
--configstringauto-detectPath to manifest file
--dry-runboolfalsePreview without modifying state
--jsonboolfalseOutput result as JSON
--gha-outputboolfalseWrite to $GITHUB_OUTPUT
--actorstring$GITHUB_ACTORActor performing the action

Validate and plan a promotion.

Terminal window
cascade promote preflight \
--mode dev-to-test \
--deploys "app,infra" \
--rollback-on-failure
FlagTypeDefaultDescription
--modestringdefaultdefault or cascade target (e.g., dev-to-prod)
--forceboolfalseContinue on failure (default mode only)
--allow-breakingboolfalseAllow breaking changes past the prerelease boundary
--deploysstringallDeploys to promote (comma-separated names or all)
--rollback-on-failurebooltrueRevert successful deploys if any fails
--allow-downgradeboolfalsePermit promoting an older version onto an env (a downgrade). Blocked by default; prod always requires this flag

A promotion that would place a strictly older semver version onto an env than the version it currently holds is a downgrade. Preflight blocks it by default, naming both versions and the env. Pass --allow-downgrade to permit it. The terminal (prod) env always requires the flag, even when a lower env in the same cascade already permitted the same downgrade. Equal versions are an idempotent re-promote and are never treated as a downgrade. When either version is not parseable as semver the gate fails open with a warning rather than blocking, so non-semver pipelines keep working.

{
"mode": "cascade",
"target": "dev-to-test",
"source_env": "dev",
"target_env": "test",
"source_sha": "abc123",
"rollback_sha": "def456",
"rollback_on_failure": true,
"deploys_to_run": ["cdk", "services"],
"external_deploys_to_run": ["satellite-cdk"],
"can_proceed": true,
"version": "v1.2.0-rc.0"
}

Update state after promotion deploys complete.

Terminal window
cascade promote finalize \
--promotion-result "$RESULT_JSON" \
--repo owner/repo \
--run-id "$GITHUB_RUN_ID" \
--commit-push
FlagTypeRequiredDescription
--promotion-resultstringYesJSON from preflight output
--repostringNoRepository (owner/name) for job query
--run-idstringNoWorkflow run ID for job query
--commit-pushboolNoCommit and push state changes

Apply one or more trunk commits onto an environment pinned to an older base. A hotfix elevates the commit set bottom-up across the environment chain, up to and including the target environment, on each environment’s env/<env> integration branch. The fixes must already be on trunk; cascade refuses to apply any commit that is not an ancestor of trunk tip. The subcommands compute and validate the hotfix and write its final state; the cherry-pick, build, and deploy run in the generated cascade-hotfix.yaml workflow. See the Hotfix section of Workflows for the full flow.

Validate a hotfix request and compute the integration-branch plan. It enforces, in order: trunk ancestry of every fix, target-environment eligibility (a configured environment that is not the first; prod is allowed), no-op detection when a fix is already present, the single-flight open-pull-request gate, and env/<env> branch reconciliation. With --dry-run nothing is mutated (the env branches are planned but not created).

Supply the fixes with one of two mutually exclusive flags, exactly one of which is required:

  • --commit <sha> applies a single commit to the target environment.
  • --commits <sha,sha,...> takes a comma-delimited set and elevates it bottom-up across the chain, from the environment above the first up to and including --target-env. On this path each (commit, environment) pair is skipped when the commit is already an ancestor of that environment’s state SHA or already in its recorded patches; an environment whose whole set is already present is a no-op and the chain moves on.
Terminal window
cascade hotfix plan \
--commit abc1234 \
--target-env test \
--gha-output

To carry a set of commits and elevate them across the chain:

Terminal window
cascade hotfix plan \
--commits abc1234,def5678 \
--target-env staging \
--gha-output
FlagTypeRequiredDescription
--config, -cstringNoPath to manifest file (default: .github/manifest.yaml)
--keystringNoTop-level manifest key (default: ci)
--commitstringOne of commit/commitsSingle trunk commit (SHA or ref) carrying the fix; single-env path
--commitsstringOne of commit/commitsComma-delimited trunk commits to elevate across the env chain up to --target-env
--target-envstringYesEnvironment to hotfix
--actorstringNoActor recorded on the plan (default: $GITHUB_ACTOR)
--remotestringNoGit remote env branches live on (default: origin)
--repostringNoowner/repo for single-flight pull-request lookup via gh (default: skip the check)
--dry-runboolNoCompute the plan without mutating anything
--jsonboolNoOutput the plan as JSON
--gha-outputboolNoWrite outputs to $GITHUB_OUTPUT for workflow consumption

With --json:

{
"target_env": "test",
"fix_sha": "abc1234...",
"branch": "env/test",
"base_sha": "def5678...",
"no_op": false,
"branch_created": true,
"hotfix_version_candidate": "v1.4.0-rc.2.hotfix.1",
"conflict_expected": false,
"protection_suggestions": ["gh api -X PUT repos/{owner}/{repo}/branches/env/test/protection ..."],
"dry_run": false
}

The GHA output writes target_env, fix_sha, branch, base_sha, no_op, branch_created, hotfix_version_candidate, conflict_expected, dry_run, and the protection_suggestions commands (as JSON and as multiline text). On the --commits path the plan also writes env_sequence (the environments to walk bottom-up) and a commits_<env> list per environment that the apply job replays in order.

Write the diverged state, tag, and release for a merged hotfix. Run after the resolution pull request merges and the build and deploy succeed. It cross-checks the merge SHA against the env/<target> branch tip, allocates the next free hotfix version, snapshots the prior state into the rollback ring, writes the divergence fields and substates, commits the manifest to trunk with the rebase-retry push, and creates the hotfix tag and release object. The verb is idempotent on identical inputs: a rerun after the state already records the merge SHA is a no-op.

Terminal window
cascade hotfix finalize \
--target-env test \
--merge-sha 1234abc \
--fix-sha abc1234 \
--base-sha def5678 \
--build-result app=success \
--deploy-result app=success
FlagTypeRequiredDescription
--config, -cstringNoPath to manifest file (default: .github/manifest.yaml)
--keystringNoTop-level manifest key (default: ci)
--target-envstringYesEnvironment to finalize
--merge-shastringYesTip of env/<target> after the resolution pull request merged
--fix-shastringYesTrunk commit(s) the hotfix carries; comma-delimited for a multi-commit set. Every commit applied to the environment is appended to its recorded patches (commits already present in that environment are skipped)
--base-shastringYesTrunk anchor the integration branch diverged from
--actorstringNoActor recorded on the state (default: $GITHUB_ACTOR)
--dry-runboolNoValidate and compute without writing state, tags, or releases
--build-resultstringNoBuild result as name=result (repeatable)
--deploy-resultstringNoDeploy result as name=result (repeatable)

Only successful build and deploy results update the per-build and per-deploy substates. For a prerelease-environment target the hotfix release is promoted to a GitHub prerelease, superseding that environment’s current prerelease object; for other environments it stays a draft.

Calculate the next semantic version.

Terminal window
cascade next-version \
--environment prod \
--base-sha abc123 \
--head-sha def456
FlagTypeRequiredDescription
--config, -cstringNoPath to manifest file
--environment, -estringYesTarget environment
--base-shastringNoBase SHA (defaults to next env’s SHA)
--head-shastringNoHead SHA (defaults to HEAD)
--jsonboolNoOutput as JSON

Bump rules:

  • Breaking change (feat!, BREAKING CHANGE:) triggers a major bump
  • Feature (feat) triggers a minor bump
  • Fix (fix) triggers a patch bump
  • Pre-release environments append an RC suffix (e.g., v1.3.0-rc.0)

Commands for multi-repo orchestration.

Update primary repo state when a satellite repo deploys.

Terminal window
cascade external update \
--source-repo org/cdk-infra \
--deploy-name cdk \
--environment dev \
--sha abc123 \
--version v1.2.0 \
--artifacts '{"image_tag": "cdk-abc123"}'
FlagTypeRequiredDescription
--configstringNoPath to manifest file
--source-repostringYesSource repository (e.g., org/cdk-infra)
--deploy-namestringYesDeploy name
--environmentstringYesTarget environment
--shastringYesCommit SHA from source repo
--versionstringNoVersion from source repo
--artifactsstringNoArtifacts JSON
--dry-runboolNoPreview mode

This is typically called by the satellite’s external-update.yaml workflow after deploying to dev.

Reset releases and state for testing.

Terminal window
cascade reset --state --push
FlagTypeDefaultDescription
--stateboolfalseReset the state section in the manifest
--dry-runboolfalsePreview without executing
--pushboolfalsePush state changes (requires --state)
--repostringcwdPath to repository
--configstringauto-detectPath to manifest file
--manifest-keystringciTop-level key

Deletes all GitHub releases and tags. With --state, also clears the state section.

Print the manifest JSON Schema. Point your editor at it for autocomplete, type checking, and hover docs while authoring .github/manifest.yaml. See Editor support for registration.

Terminal window
# Print the schema to stdout
cascade schema
# Write the schema to a file
cascade schema --output manifest.schema.json
FlagTypeDefaultDescription
--output, -ostringstdoutWrite the schema to a file instead of stdout

The same schema is published at https://stablekernel.github.io/cascade/manifest.schema.json. parse-config remains the authority for semantic and cross-field rules; the schema covers structure, types, enums, and hover docs.

Preview a hypothetical action against a clone of your manifest and print what would happen, without changing anything. The engine replays the real orchestration logic in record-only mode: it touches no GitHub, starts no container, runs no git command, and leaves the manifest untouched. It validates orchestration, the state transitions and run/skip/gate decisions, not your build and deploy scripts.

Terminal window
cascade simulate promote
cascade simulate release
cascade simulate rollback --env prod
cascade simulate hotfix --env uat --fix <sha>

Each run prints a before/after state diff and an ordered effect sequence. The four subcommands are promote, release, rollback, and hotfix. See Local Simulation for the full walkthrough, example output, and the deploy-stub model.

The following flags are shared by every subcommand.

FlagTypeDefaultDescription
--configstringauto-detectPath to manifest file
--actorstring(none)Actor performing the hypothetical action
--deploy-resultstring(none)Simulated outcome for a build or deploy callback, name=success|failure|skipped (repeatable)
--jsonboolfalseOutput result as JSON

Subcommand-specific flags: promote takes --mode (default or cascade) and --target; rollback takes --env (required), --to, and --deployable; hotfix takes --env (required), --fix, and --merge-sha; release takes only the shared flags.

VariableDescription
GITHUB_TOKENGitHub API token for releases
GITHUB_ACTORDefault actor for commits/promotions
GITHUB_RUN_IDWorkflow run ID (used by promote finalize)
LOG_LEVELLogging verbosity (info, debug, trace)
NO_COLORDisable colored output
CodeMeaning
0Success
1General error

Pass --json (or use --gha-output inside Actions) to get machine-readable output:

- name: Detect changes
id: detect
run: |
cascade detect-changes \
--base-sha "${{ github.event.before }}" \
--head-sha "${{ github.sha }}" \
--gha-output
Terminal window
cascade --trace parse-config

Trace logs include:

  • File matching details
  • Dependency resolution steps
  • API request/response info