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:

File Emitted when Purpose
.github/workflows/orchestrate.yaml always CI/CD on trunk merges: build and deploy the first environment, cut the next rc.
.github/workflows/promote.yaml always Manual promotion between environments, including publish at the release boundary.
.github/workflows/cascade-hotfix.yaml 2 or more environments configured Roll a trunk fix onto a diverged environment.
.github/workflows/cascade-rollback.yaml 2 or more environments configured Re-deploy a prior version or SHA to a promoted environment.
.github/actions/manage-release/action.yaml always Composite 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, a required field with no default (the example below uses main):

on:
push:
branches: [main]

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

Output Description
deployed_sha Deployed commit SHA.
triggered_builds / triggered_deploys JSON arrays of what ran.
version Calculated rc version, for example v1.2.0-rc.0.
changelog Generated changelog markdown.
release_url URL to the GitHub release.
execution_plan JSON 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. Rollback re-runs the same deploy callbacks promote drives; the hotfix deploy jobs are placeholders (see the caution below). Full operational detail lives in the hotfix and rollback guides; this is the job-level anatomy.

Job Trigger Role
plan workflow_dispatch Fetch env branches and tags, run cascade hotfix plan, surface branch-protection suggestions.
apply workflow_dispatch (not dry-run) Cherry-pick onto each environment bottom-up; opens a resolution pull request.
check pull_request opened against env/* Validate the manifest while the hotfix pull request is open.
build merged hotfix pull request Build the merge SHA (a cherry-pick has no prebuilt artifact).
deploy merged hotfix pull request Placeholder: holds the target’s GitHub environment: gate and prints the configured deploy, but does not invoke it. Paired with an equally placeholder rollback job.
finalize deploy jobs succeed Run 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.

Job Role
preflight Read-only: resolves the target version or SHA (defaults to N-1).
deploy Re-runs the configured deploy callbacks keyed on the resolved SHA.
finalize Writes 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.

Input Purpose
action create, update, lock, prerelease, publish, or delete.
repo, sha, tag, environment Identify the release target.
changelog Release notes markdown, passed inline.
changelog_file Path to a file holding the release notes. Preferred over changelog; the generated workflows pass the built-in changelog this way.
previous_tag, new_tag, delete_tag, create_tag Used by specific actions (changelog comparison, retagging, cleanup).
token A 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.

The built-in changelog is passed by file reference, not inline. The Generate Changelog step writes the notes to a file under the runner temp dir and the Manage Release step reads that path via changelog_file. This keeps large content off the environment: a changelog placed on an action input becomes an environment variable, and execve caps a single environment variable near 128KB, so a big changelog on that path fails the step with E2BIG. A file path is small and fixed, so the content never transits an input regardless of size. The inline changelog input remains for callers that pass notes directly.

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

File Enabled by Purpose
cascade-pr-preview.yaml pr_preview.enabled: true Read-only PR plan preview; no deploys.
cascade-drift-check.yaml drift_check.enabled: true Fails a PR check when generated output has drifted from the manifest.
cascade-drift-comment.yaml drift_check.enabled: true and drift_check.comment: true Fork-safe companion that posts the drift result as a sticky PR comment.
cascade-reconcile-check.yaml reconcile.enabled: true Read-only detector for an external governed action-pin change.
cascade-reconcile-companion.yaml reconcile.enabled: true Adopts the detected pin change back into the manifest.
cascade-validate.yaml validate_check.enabled: true Runs manifest validation as its own PR check.
cascade-merge-queue.yaml merge_queue.enabled: true Adds a merge-queue validation lane.
external-update.yaml manifest declares external repos Accepts 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:

Workflow Default group Default cancel-in-progress Why
Orchestrate orchestrate-${{ github.ref }} true A newer push obsoletes an older in-flight build.
Promote ${{ github.workflow }} false Every run pushes the same manifest state and tags; queue rather than abandon a mid-flight write.
Hotfix per-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 }} false Same 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.