Skip to content

Generated workflows

cascade generate-workflow compiles your manifest into GitHub Actions workflows and one composite action. This page is the structural reference: the exact files, their triggers, jobs, and outputs. For “run this, watch that” operator recipes, see the promote, hotfix, and rollback guides.

Every run of generate-workflow against an environment pipeline emits these files unconditionally:

FileEmitted whenPurpose
.github/workflows/orchestrate.yamlalwaysCI/CD on trunk merges: build and deploy the first environment, cut the next rc.
.github/workflows/promote.yamlalwaysManual promotion between environments, including publish at the release boundary.
.github/workflows/cascade-hotfix.yaml2 or more environments configuredRoll a trunk fix onto a diverged environment.
.github/workflows/cascade-rollback.yaml2 or more environments configuredRe-deploy a prior version or SHA to a promoted environment.
.github/actions/manage-release/action.yamlalwaysComposite action wrapping GitHub release create, update, lock, prerelease, publish, and delete.

orchestrate.yaml and promote.yaml are two separate files by design, generated together in one run; --orchestrate-only or --promote-only limits a run to one of them. Their output paths are configurable (--output, --promote-output); cascade-hotfix.yaml and cascade-rollback.yaml are always written to those fixed paths. The action folder name defaults to manage-release and is configurable via --action-folder.

A single-environment project never gets cascade-hotfix.yaml or cascade-rollback.yaml: the first environment tracks trunk directly, so there is nothing to hotfix or roll back onto.

Any claim that generate-workflow emits only an orchestrate and a promote workflow is out of date. Two more workflows and a composite action are unconditional, and the opt-in companions below add more on top depending on your manifest.

Orchestrate fires on every merge to the trunk branch and runs the first environment’s full pipeline.

flowchart TD
    M["Merge to trunk"] --> S["Setup"] --> V["Validate"] --> B["Build"] --> D["Deploy"] --> F["Finalize"]
    S -.-> sn["Parse manifest, detect changes,<br/>compute version"]
    V -.-> vn["Optional pre-build validation"]
    B -.-> bn["Matrix: triggered builds only"]
    D -.-> dn["Matrix: triggered deploys,<br/>dependency-ordered"]
    F -.-> fn["Update state, generate changelog,<br/>draft pre-release"]

    classDef note fill:none,stroke:none,color:#8A929C;
    class sn,vn,bn,dn,fn note;

The trigger is written directly from config.trunk_branch (default main):

on:
push:
branches: [main]

Orchestrate takes no manual inputs; it runs automatically on push. Its outputs:

OutputDescription
deployed_shaDeployed commit SHA.
triggered_builds / triggered_deploysJSON arrays of what ran.
versionCalculated rc version, for example v1.2.0-rc.0.
changelogGenerated changelog markdown.
release_urlURL to the GitHub release.
execution_planJSON execution plan with dependency-ordered waves.

The setup job reads the manifest’s recorded SHA, diffs it against the current head, matches changed files against each callback’s triggers, and builds an execution plan that respects depends_on. Version is computed from conventional commits since the last release: feat!:/BREAKING CHANGE: bumps major, feat: bumps minor, fix:/perf: bumps patch. The first environment always gets a pre-release suffix (v1.2.0-rc.0 by default; configurable via tag_grammar); each further orchestrate run increments the pre-release counter.

Promote is a manual (workflow_dispatch) workflow that walks the environment chain.

flowchart TD
    M["Default mode<br/>(one step at a time)"] --> P["Preflight"] --> D["Deploy"] --> Pub["Publish"] --> F["Finalize"]
    P -.-> pn["Validate source/target, check ancestry,<br/>gate breaking changes"]
    D -.-> dn["Matrix: per-deploy with change detection"]
    Pub -.-> pubn["Only at prerelease-to-release boundary,<br/>if publish: configured"]
    F -.-> fn["Update state, publish release,<br/>dispatch Release workflow"]

    classDef note fill:none,stroke:none,color:#8A929C;
    class pn,dn,pubn,fn note;

Inputs: mode (default or a cascade target such as dev-to-prod), force, allow_breaking_changes, dry_run, deploys, rollback_on_failure. See the promote guide for what each one does operationally.

Each deploy-<name> job carries a strategy: block. promote.go:writeDeployStrategyOptions writes it from the deploy’s rollout config:

jobs:
deploy-app:
strategy:
fail-fast: false # rollout.fail_fast (default false when unset)
max-parallel: 3 # rollout.max_parallel, only when > 0
matrix:
environment: ${{ fromJson(needs.preflight.outputs.deploy_app_matrix) }}

Only fail_fast and max_parallel reach this block. rollout.type, rollout.canary, and rollout.blue_green parse and validate but are reserved: they carry zero generator consumption today. See Progressive rollout for the full reserved-field list.

When the manifest has a publish: callback, promote adds a publish step that runs once per build at the prerelease-to-release boundary. It reads artifact_id from the source environment’s build state and dispatches the publish workflow with build_name, old_version, new_version, sha, and artifact_id; the callback performs the registry operation (retag, copy, sign).

Both carry two triggers in one file and both mirror the promote deploy shape. Full operational detail lives in the hotfix and rollback guides; this is the job-level anatomy.

JobTriggerRole
planworkflow_dispatchFetch env branches and tags, run cascade hotfix plan, surface branch-protection suggestions.
applyworkflow_dispatch (not dry-run)Cherry-pick onto each environment bottom-up; opens a resolution pull request.
checkpull_request opened against env/*Validate the manifest while the hotfix pull request is open.
buildmerged hotfix pull requestBuild the merge SHA (a cherry-pick has no prebuilt artifact).
deploymerged hotfix pull requestDeploy to the target environment, paired with a rollback job mirroring promote.
finalizeall deploys succeedRun cascade hotfix finalize: write the diverged state, tag, and release.

Dispatch inputs: commit (one or more trunk fix SHAs), target_env (every configured environment except the first), pr_number (optional, to replay an existing resolution pull request), dry_run. The second trigger is pull_request on types: [closed] against branches: ['env/*'], gated on the pull request having merged with the cascade-hotfix label.

JobRole
preflightRead-only: resolves the target version or SHA (defaults to N-1).
deployRe-runs the configured deploy callbacks keyed on the resolved SHA.
finalizeWrites the rolled-back state to trunk, marking the environment diverged.

Baseline trigger is workflow_dispatch only, with inputs environment, target, deployable, dry_run. Setting rollback.repository_dispatch in the manifest adds a repository_dispatch trigger alongside it; every parameter read then coalesces github.event.inputs.* with github.event.client_payload.* so both paths resolve the same target. See the rollback guide and the manifest reference for the block’s fields.

.github/actions/manage-release/action.yaml wraps GitHub release operations behind one composite action so orchestrate, promote, hotfix, and rollback all call the same code path.

InputPurpose
actioncreate, update, lock, prerelease, publish, or delete.
repo, sha, tag, environmentIdentify the release target.
changelogRelease notes markdown.
previous_tag, new_tag, delete_tag, create_tagUsed by specific actions (changelog comparison, retagging, cleanup).
tokenA GitHub token with repo permissions.

Outputs: release_id, release_url, html_url. The action shells out to the same cascade binary already installed by setup-cli, so its behavior matches the CLI exactly.

These emit only when their manifest block is present and enabled; an unconfigured manifest is unaffected.

FileEnabled byPurpose
cascade-pr-preview.yamlpr_preview.enabled: trueRead-only PR plan preview; no deploys.
cascade-drift-check.yamldrift_check.enabled: trueFails a PR check when generated output has drifted from the manifest.
cascade-drift-comment.yamldrift_check.enabled: true and drift_check.comment: trueFork-safe companion that posts the drift result as a sticky PR comment.
cascade-reconcile-check.yamlreconcile.enabled: trueRead-only detector for an external governed action-pin change.
cascade-reconcile-companion.yamlreconcile.enabled: trueAdopts the detected pin change back into the manifest.
cascade-validate.yamlvalidate_check.enabled: trueRuns manifest validation as its own PR check.
cascade-merge-queue.yamlmerge_queue.enabled: trueAdds a merge-queue validation lane.
external-update.yamlmanifest declares external reposAccepts satellite deploy notifications into the primary’s state; see the multi-repo guide.

How concurrency, timeouts, and permissions appear in output

Section titled “How concurrency, timeouts, and permissions appear in output”

Concurrency. Every cascade-owned workflow gets a top-level concurrency: block, but the default cancel-in-progress value differs by workflow because the risk differs:

WorkflowDefault groupDefault cancel-in-progressWhy
Orchestrateorchestrate-${{ github.ref }}trueA newer push obsoletes an older in-flight build.
Promote${{ github.workflow }}falseEvery run pushes the same manifest state and tags; queue rather than abandon a mid-flight write.
Hotfixper-environment (dispatch) or per-repo (finalize)false (fixed, not configurable)Concurrent finalize runs on different environments must not race the same trunk push.
Rollback${{ github.workflow }}falseSame reasoning as promote.

Set concurrency.group and concurrency.cancel_in_progress in the manifest to override the orchestrate, promote, and rollback defaults; hotfix’s grouping is fixed.

Timeouts. Cascade-owned jobs (setup, finalize, preflight, and similar) get timeout-minutes: 30 unless job_timeout_minutes is set in the manifest. This never applies to your own callback jobs, since GitHub forbids timeout-minutes on a job that calls a reusable workflow with uses:; set your own timeout inside the called workflow.

Permissions. Each callback job gets a job-level permissions: block scoped to only what that callback declared, including id-token: write when the callback needs OIDC:

jobs:
deploy-app:
permissions:
contents: read
id-token: write
uses: ./.github/workflows/deploy-app.yaml

This is least-privilege by construction: the top-level workflow permissions stay read-only, and write scopes (contents: write for state pushes, actions: write to dispatch Release) live only on the jobs that need them.

Prerequisite: How Cascade works for the mental model these workflows implement.

Next: Manifest reference for every field that shapes this output.