generate

package
v0.8.0-rc.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 1, 2026 License: Apache-2.0 Imports: 12 Imported by: 0

Documentation

Index

Constants

View Source
const (
	SetupJobName    = "Setup"
	FinalizeJobName = "Finalize"
)

SetupJobName and FinalizeJobName are the GitHub Actions check-run names of the orchestrate workflow's two cascade-controlled steps jobs. GitHub records a job's check-run context under its name:, so these constants are the exact contexts a branch-protection rule can require with certainty. Both jobs are unconditional (the setup job has no if:, and finalize uses always()), so they always report on every run, which is what makes them safe to require. The branch-protection emitter references these same constants, so a rename here updates both the generated workflow and the emitted protection contexts in lockstep and never lets them drift apart.

View Source
const DefaultJobTimeoutMinutes = 30

DefaultJobTimeoutMinutes is the timeout-minutes applied to cascade-owned jobs (setup, finalize, retry shims, and passthrough artifact helper jobs) when config.job_timeout_minutes is not set. GitHub Actions defaults jobs to 360 minutes (6 hours); cascade's orchestration jobs are meant to be fast, so a hung git push, CLI download, or API call should not hold a runner for six hours. Override per manifest via config.job_timeout_minutes.

View Source
const GeneratedFileMarker = "# AUTO-GENERATED by cascade - DO NOT EDIT MANUALLY"

GeneratedFileMarker is the first line written at the top of every file the generators emit (workflows and the composite action). It marks a file as cascade-owned so tooling can distinguish generated output from hand-written files in the same directory. The verify command keys orphan detection off this exact string: a file carrying it that the manifest no longer plans is an orphan, while a file without it is treated as hand-written and never touched.

The string is load-bearing. Changing it is a breaking change for any repo whose committed workflows still carry the old marker, so treat it as stable.

Variables

This section is empty.

Functions

func GenerateLocalActions

func GenerateLocalActions(baseDir string, cfg *config.TrunkConfig) error

GenerateLocalActions creates the local action files in the user's repo. Uses cfg.GetActionFolder() for the folder name (default: "manage-release").

func NewCommand

func NewCommand() *cobra.Command

NewCommand creates the generate-workflow command

func ParseWorkflowInputs

func ParseWorkflowInputs(data []byte) ([]string, error)

ParseWorkflowInputs extracts input names from a workflow file

func ParseWorkflowOutputs

func ParseWorkflowOutputs(data []byte) ([]string, error)

ParseWorkflowOutputs extracts output names from a workflow file

func ParseWorkflowRequiredInputs

func ParseWorkflowRequiredInputs(data []byte) ([]string, error)

ParseWorkflowRequiredInputs extracts required input names from a workflow file An input is required if it has required: true and no default value

func ResolveBaseDir added in v0.3.0

func ResolveBaseDir(configPath string) string

ResolveBaseDir reports the repo root the generate command resolves workflow paths against for the given config path: the config's directory, promoted one level up when the config lives in .github/. Callers that compare planned files (which carry relative workflow paths) against bytes on disk use this to anchor those relative paths to the manifest's repo root instead of the process working directory.

Types

type CallbackInfo

type CallbackInfo struct {
	Name           string // Original name from config (e.g., "app")
	JobID          string // Prefixed job ID (e.g., "build-app")
	DisplayName    string // Display name (e.g., "Build (app)")
	Type           string // "build" or "deploy" or "validate"
	Workflow       string
	RunPolicy      string
	OnFailure      string
	Retries        int
	Matrix         *config.MatrixConfig // Build fan-out; nil for deploys and validate
	SupportsDryRun bool                 // When true, dry-run promotes invoke the callback with dry_run: true instead of skipping it

	// Per-callback job attributes carried from config. GHA forbids
	// runs-on/concurrency/timeout-minutes on a reusable-workflow uses: caller
	// job, and schema validation rejects runs_on/concurrency/timeout_minutes on
	// reusable callbacks, so those fields are populated from config but never
	// emitted as job-level keys. permissions: is different: GitHub allows it on a
	// uses: caller job, so Permissions IS rendered as a job-level permissions:
	// block scoped to exactly this callback (least privilege; see
	// writeCallbackPermissions). These scopes are not merged into the workflow's
	// top-level permissions block, keeping the OIDC blast radius confined to this
	// one caller job.
	TimeoutMinutes int                       // Per-callback timeout-minutes; validated-against, never emitted (belongs inside the called workflow)
	RunsOn         *config.RunsOn            // Per-callback runner selection (#12); validated-against, never emitted
	Permissions    map[string]string         // Per-callback job permissions, incl. id-token: write OIDC (#35, #15); emitted as job-level permissions:
	Concurrency    *config.ConcurrencyConfig // Per-callback concurrency override (#17); validated-against, never emitted

	// PassthroughArtifact declares GHA artifact upload/download steps to inject
	// around this job's callback invocation, enabling inter-job artifact passing
	// within a single orchestrate run (#16).
	PassthroughArtifact *config.PassthroughArtifact

	// Secrets holds the per-callback secrets passing config. Nil means no secrets
	// block is emitted at all (the default; secrets: inherit is opt-in). When
	// non-nil and Inherit is true, secrets: inherit is emitted. When non-nil with
	// an explicit Map, a secrets: block with per-entry ${{ secrets.CALLER_NAME }}
	// expressions is emitted instead.
	Secrets *config.SecretsConfig
}

CallbackInfo holds information about a callback

type DependencyGraph

type DependencyGraph struct {
	Nodes map[string]CallbackInfo // job ID -> info
	Edges map[string][]string     // job ID -> hard dependencies (as job IDs)

	// OptionalEdges holds optional_depends_on edges (job ID -> dependencies as
	// job IDs). Optional deps add to a job's needs: for ordering but do NOT
	// contribute a skip-gate to its if: condition. The job still runs when an
	// optional dep was skipped because its triggers didn't match (#18).
	OptionalEdges map[string][]string

	// Order lists every job ID in manifest declaration order (validate, then
	// builds, then deploys, each in the order they appear in config). It is the
	// deterministic seed for TopologicalSort: iterating Nodes (a map) directly
	// would randomize emitted job order, needs: lists, and if: conditions across
	// runs because Go randomizes map range order per process.
	Order []string
}

DependencyGraph represents the callback dependency graph Uses prefixed job IDs (build-app, deploy-app) to allow name reuse across sections

func BuildDependencyGraph

func BuildDependencyGraph(cfg *config.TrunkConfig) *DependencyGraph

BuildDependencyGraph creates a dependency graph from config Uses prefixed job IDs to support same names in builds and deploys

func (*DependencyGraph) GetAllDependencies

func (g *DependencyGraph) GetAllDependencies(node string) []string

GetAllDependencies returns all transitive dependencies for a node

func (*DependencyGraph) GetDirectDependencies

func (g *DependencyGraph) GetDirectDependencies(node string) []string

GetDirectDependencies returns only direct (hard) dependencies for a node

func (*DependencyGraph) GetOptionalDependencies added in v0.2.0

func (g *DependencyGraph) GetOptionalDependencies(node string) []string

GetOptionalDependencies returns the optional_depends_on dependencies for a node. These add to needs: for ordering only; they do not skip-gate the job.

func (*DependencyGraph) TopologicalSort

func (g *DependencyGraph) TopologicalSort() ([]string, error)

TopologicalSort returns nodes in dependency order

type DriftCheckGenerator added in v0.3.0

type DriftCheckGenerator struct {
	// contains filtered or unexported fields
}

DriftCheckGenerator emits the opt-in workflow drift-check lane (#229).

It mirrors cascade's own dogfood: a pull_request job builds nothing dangerous, runs cascade verify against the manifest, captures the result as an artifact, and re-exits on drift to keep the PR gate red. The pull_request job is strictly read-only (contents: read, no secrets, posts no comment) so a fork PR cannot abuse it.

When Comment is enabled it also emits a fork-safe workflow_run companion that runs in the base-repo context with a scoped write token, downloads the artifact (data only), and posts a sticky comment. The companion derives the target PR number ONLY from trusted workflow_run run metadata (the source run's pull_requests array, or a head-SHA lookup for fork PRs), never from the fork-controlled artifact, so a fork cannot redirect the comment at another PR.

func NewDriftCheckGenerator added in v0.3.0

func NewDriftCheckGenerator(cfg *config.TrunkConfig, baseDir string) *DriftCheckGenerator

NewDriftCheckGenerator creates a new drift-check workflow generator.

func (*DriftCheckGenerator) Enabled added in v0.3.0

func (g *DriftCheckGenerator) Enabled() bool

Enabled reports whether the drift-check lane should be emitted.

func (*DriftCheckGenerator) Generate added in v0.3.0

func (g *DriftCheckGenerator) Generate() (string, error)

Generate creates the pull_request drift-check workflow content.

func (*DriftCheckGenerator) GenerateComment added in v0.3.0

func (g *DriftCheckGenerator) GenerateComment() (string, error)

GenerateComment creates the fork-safe workflow_run comment companion content. Callers must check commentEnabled (via the command/plan wiring) before emitting this file.

type ExternalUpdateGenerator

type ExternalUpdateGenerator struct {
	// contains filtered or unexported fields
}

ExternalUpdateGenerator handles external-update workflow generation

func NewExternalUpdateGenerator

func NewExternalUpdateGenerator(cfg *config.TrunkConfig, baseDir string) *ExternalUpdateGenerator

NewExternalUpdateGenerator creates a new external-update workflow generator

func (*ExternalUpdateGenerator) Generate

func (g *ExternalUpdateGenerator) Generate() (string, error)

Generate creates the external-update workflow content

type Generator

type Generator struct {
	// contains filtered or unexported fields
}

Generator handles workflow file generation

func NewGenerator

func NewGenerator(cfg *config.TrunkConfig, baseDir string) *Generator

NewGenerator creates a new workflow generator

func (*Generator) Generate

func (g *Generator) Generate() (string, error)

Generate creates the orchestration workflow content

func (*Generator) SetState added in v0.2.0

func (g *Generator) SetState(state map[string]*config.EnvState)

SetState threads the manifest state block into the generator so ${{ state.<env>.<field> }} input references resolve at generation time.

func (*Generator) Validate

func (g *Generator) Validate() []string

Validate checks for potential issues and returns warnings

type HotfixGenerator added in v0.2.0

type HotfixGenerator struct {
	// contains filtered or unexported fields
}

HotfixGenerator emits the cascade-hotfix workflow. It cherry-picks a trunk fix onto a diverged intermediate environment by replaying the commit on an env/<env> integration branch, opening a resolution pull request, and then building, deploying, and finalizing the hotfix once that pull request merges.

The workflow carries two triggers in one file: a workflow_dispatch entry that plans and applies the cherry-pick, and a pull_request (closed) entry that runs the build, deploy, and finalize stages when the resolution pull request merges. The clean-path merge runs as the configured state token so the merge is trigger capable and the pull_request (closed) chain actually fires; a merge authored by the default GITHUB_TOKEN would not emit that event.

This generator is gated on the configured environment count: it emits only when two or more environments are declared, because a single-environment pipeline has no intermediate target to hotfix onto.

func NewHotfixGenerator added in v0.2.0

func NewHotfixGenerator(cfg *config.TrunkConfig, baseDir string) *HotfixGenerator

NewHotfixGenerator creates a hotfix-workflow generator bound to the given trunk config and repository base directory.

func (*HotfixGenerator) Enabled added in v0.2.0

func (g *HotfixGenerator) Enabled() bool

Enabled reports whether the hotfix workflow should be emitted. The workflow is emitted only when the manifest declares two or more environments, since the first environment is the build target and at least one further environment is required as a hotfix target.

func (*HotfixGenerator) Generate added in v0.2.0

func (g *HotfixGenerator) Generate() (string, error)

Generate renders the cascade-hotfix workflow.

type MergeQueueGenerator added in v0.2.0

type MergeQueueGenerator struct {
	// contains filtered or unexported fields
}

MergeQueueGenerator emits the opt-in merge-queue validation lane. When config.merge_queue.enabled is set, cascade generates a merge_group-triggered workflow that validates the prospective trunk commit with cascade's own logic: it runs `cascade parse-config` as a validity gate and a dry-run `cascade orchestrate setup` to preview the build/deploy decisions against the merge-group candidate ref. The lane is read-only (no state writes, no releases, no deploys) and reports a status the merge queue can require.

This generator owns the LANE behavior. The raw merge_group trigger itself is expressible separately under extra_triggers.merge_group; the two are intentionally distinct.

func NewMergeQueueGenerator added in v0.2.0

func NewMergeQueueGenerator(cfg *config.TrunkConfig, baseDir string) *MergeQueueGenerator

NewMergeQueueGenerator creates a merge-queue validation-lane generator.

func (*MergeQueueGenerator) Enabled added in v0.2.0

func (g *MergeQueueGenerator) Enabled() bool

Enabled reports whether the manifest opts in to the merge-queue lane.

func (*MergeQueueGenerator) Generate added in v0.2.0

func (g *MergeQueueGenerator) Generate() (string, error)

Generate renders the merge-queue validation-lane workflow.

type PRPreviewGenerator added in v0.2.0

type PRPreviewGenerator struct {
	// contains filtered or unexported fields
}

PRPreviewGenerator emits the opt-in read-only PR plan-preview workflow (#40).

The workflow runs on pull_request and is strictly read-only: it validates the manifest, detects which builds/deploys this merge would trigger against the PR diff, and dry-runs the applicable deploy callbacks, then writes a human-readable plan to $GITHUB_STEP_SUMMARY. When Comment is set it also posts (or updates) that plan as a PR comment via actions/github-script.

dry_run is enforced (--dry-run is always passed, never sourced from an input) so the preview can never create a release, write state, or trigger a deploy.

func NewPRPreviewGenerator added in v0.2.0

func NewPRPreviewGenerator(cfg *config.TrunkConfig, baseDir string) *PRPreviewGenerator

NewPRPreviewGenerator creates a new PR plan-preview workflow generator.

func (*PRPreviewGenerator) Generate added in v0.2.0

func (g *PRPreviewGenerator) Generate() (string, error)

Generate creates the PR plan-preview workflow content.

type PlanOptions added in v0.3.0

type PlanOptions struct {
	ConfigPath        string
	ManifestKey       string
	ActionFolder      string
	OutputPath        string
	PromoteOutputPath string
}

PlanOptions configures a Plan run. The fields mirror the generate-workflow flags that affect which files are emitted and where, so a Plan reproduces the generate command's full-set output without touching the filesystem.

type PlannedFile added in v0.3.0

type PlannedFile struct {
	Path    string
	Content string
}

PlannedFile is a single workflow or action file the manifest would generate, paired with its rendered content. Path mirrors the exact location the generate command writes to: relative paths for the workflow files (resolved against the current working directory) and an absolute path under baseDir for the composite action.

func Plan added in v0.3.0

func Plan(opts PlanOptions) ([]PlannedFile, error)

Plan resolves the manifest and returns the complete set of files the generate command would write for it, each paired with its rendered content. The result is sorted by Path and is deterministic across calls. Plan never touches the filesystem beyond reading the manifest and the reusable-workflow stubs the generators inspect; it performs no writes, no directory creation, and no git invocation.

A parse failure, a missing manifest, or a config validation failure returns a non-nil error. These are operational failures distinct from any drift a caller may compute by comparing the returned content to bytes on disk.

func RenderLocalActions added in v0.3.0

func RenderLocalActions(baseDir string, cfg *config.TrunkConfig) (PlannedFile, error)

RenderLocalActions returns the composite action file the manifest would generate, paired with its rendered content, without writing anything to disk. The path is baseDir/.github/actions/<folder>/action.yaml where <folder> is cfg.GetActionFolder() (default: "manage-release").

type PromoteGenerator

type PromoteGenerator struct {
	// contains filtered or unexported fields
}

PromoteGenerator handles promote workflow generation

func NewPromoteGenerator

func NewPromoteGenerator(cfg *config.TrunkConfig, baseDir string) *PromoteGenerator

NewPromoteGenerator creates a new promote workflow generator

func (*PromoteGenerator) Generate

func (g *PromoteGenerator) Generate() (string, error)

Generate creates the promote workflow content

func (*PromoteGenerator) SetState added in v0.2.0

func (g *PromoteGenerator) SetState(state map[string]*config.EnvState)

SetState threads the manifest state block into the promote generator so ${{ state.<env>.<field> }} input references resolve at generation time.

type ReleaseGenerator

type ReleaseGenerator struct {
	// contains filtered or unexported fields
}

ReleaseGenerator handles release workflow generation for single-environment projects

func NewReleaseGenerator

func NewReleaseGenerator(cfg *config.TrunkConfig, baseDir string) *ReleaseGenerator

NewReleaseGenerator creates a new release workflow generator

func (*ReleaseGenerator) Generate

func (g *ReleaseGenerator) Generate() (string, error)

Generate creates the release workflow content for single-environment projects

type RollbackGenerator added in v0.2.0

type RollbackGenerator struct {
	// contains filtered or unexported fields
}

RollbackGenerator emits the cascade-rollback workflow. The workflow re-deploys a prior version or SHA to an environment: a read-only preflight job resolves the target, one deploy job per configured deployable re-runs the deploy keyed on the resolved SHA, and a finalize job applies the state write back to trunk (marking the environment diverged until a forward promotion rejoins it).

The deploy stage reuses the same deploy callbacks (reusable-workflow, or matrix) the promote workflow drives; there is no separate rollback deploy path. The generator is gated on the configured environment count: it emits only when at least one environment is declared.

func NewRollbackGenerator added in v0.2.0

func NewRollbackGenerator(cfg *config.TrunkConfig, baseDir string) *RollbackGenerator

NewRollbackGenerator creates a rollback-workflow generator bound to the given trunk config and repository base directory.

func (*RollbackGenerator) Enabled added in v0.2.0

func (g *RollbackGenerator) Enabled() bool

Enabled reports whether the rollback workflow should be emitted. It requires at least two environments: a rollback re-points a promoted environment at a prior deployment, and the first environment tracks trunk (it is never promoted into, so it has no rollback history and reverts via a merge to trunk instead). A single-environment project therefore has no rollbackable environment, so the workflow is not emitted, mirroring the hotfix generator.

func (*RollbackGenerator) Generate added in v0.2.0

func (g *RollbackGenerator) Generate() (string, error)

Generate renders the cascade-rollback workflow.

type ValidateCheckGenerator added in v0.2.0

type ValidateCheckGenerator struct {
	// contains filtered or unexported fields
}

ValidateCheckGenerator emits the opt-in manifest-validation PR check. When config.validate_check.enabled is set, cascade generates a lightweight pull_request workflow that runs `cascade parse-config` against the manifest and fails when the manifest is invalid, so a malformed configuration cannot merge to trunk. The check validates cascade's own configuration only: it does not run the consumer's build/test CI, requests contents: read alone, and has no dry-run or comment side effects.

func NewValidateCheckGenerator added in v0.2.0

func NewValidateCheckGenerator(cfg *config.TrunkConfig, baseDir string) *ValidateCheckGenerator

NewValidateCheckGenerator creates a manifest-validation PR-check generator.

func (*ValidateCheckGenerator) Enabled added in v0.2.0

func (g *ValidateCheckGenerator) Enabled() bool

Enabled reports whether the manifest opts in to the validation check.

func (*ValidateCheckGenerator) Generate added in v0.2.0

func (g *ValidateCheckGenerator) Generate() (string, error)

Generate renders the manifest-validation PR-check workflow.

type WorkflowCallConfig

type WorkflowCallConfig struct {
	Inputs  map[string]WorkflowInput  `yaml:"inputs"`
	Outputs map[string]WorkflowOutput `yaml:"outputs"`
}

WorkflowCallConfig represents the workflow_call trigger configuration

type WorkflowFile

type WorkflowFile struct {
	Name string                 `yaml:"name"`
	On   map[string]interface{} `yaml:"on"`
}

WorkflowFile represents a GitHub Actions workflow file structure

type WorkflowInput

type WorkflowInput struct {
	Type        string      `yaml:"type"`
	Description string      `yaml:"description"`
	Required    bool        `yaml:"required"`
	Default     interface{} `yaml:"default"`
}

WorkflowInput represents an input parameter

type WorkflowOutput

type WorkflowOutput struct {
	Description string `yaml:"description"`
	Value       string `yaml:"value"`
}

WorkflowOutput represents an output value

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL