Documentation
¶
Overview ¶
Package hotfix plans and validates per-environment hotfixes that apply a trunk commit onto an environment pinned to an older trunk base.
The plan verb computes and validates only; the cherry-pick, build, deploy, and state write happen in the generated workflow. A plan with --dry-run mutates nothing.
Index ¶
- Constants
- func HealOrphanEnvBranches(branches []string, state map[string]*config.EnvState, remote string, ...) ([]string, error)
- func HotfixTagsForBase(spec taggrammar.Spec, baseVersion string, tags []string) []string
- func NewCommand() *cobra.Command
- func OrphanEnvBranches(branches []string, state map[string]*config.EnvState) []string
- type EnvPlan
- type FinalizeOption
- func WithFinalizeDryRun(dryRun bool) FinalizeOption
- func WithReleaseManager(m releaseManager) FinalizeOption
- func WithStatePusher(p statePusher) FinalizeOption
- func WithTagLister(l tagLister) FinalizeOption
- func WithTipReader(r gitTipReader) FinalizeOption
- func WithTrunkStateReader(r trunkStateReader) FinalizeOption
- type Finalizer
- type FinalizerOptions
- type OpenPR
- type Option
- type PRChecker
- type PlanChainResult
- type PlanResult
- type Planner
- type PlannerOptions
Constants ¶
const EnvBranchPrefix = "env/"
EnvBranchPrefix is the prefix of the per-environment integration branches a hotfix creates (for example env/test). A branch carrying this prefix exists only while its environment is diverged; once the env rejoins trunk the branch is deleted.
Variables ¶
This section is empty.
Functions ¶
func HealOrphanEnvBranches ¶ added in v0.6.0
func HealOrphanEnvBranches(branches []string, state map[string]*config.EnvState, remote string, del func(remote, branch string) error) ([]string, error)
HealOrphanEnvBranches deletes every env/* branch that OrphanEnvBranches flags as having no matching divergence, calling del to remove each branch on remote. In production del is git.DeleteRemoteBranch, whose delete of an absent branch is a no-op success, so HealOrphanEnvBranches is idempotent: re-running it, or running it against an orphan that is already gone, deletes nothing further and never errors.
Only orphans are deleted. A branch backing a diverged environment is never touched, because the deletion set comes from OrphanEnvBranches, which excludes it by the same IsDiverged() predicate the hotfix preflight and status consistency classify on. The returned slice lists the branches deleted, in input order, and is nil when nothing was orphaned. On the first delete error the heal stops and returns that error with a nil healed slice.
func HotfixTagsForBase ¶
func HotfixTagsForBase(spec taggrammar.Spec, baseVersion string, tags []string) []string
HotfixTagsForBase returns the hotfix tags in tags that belong to the pre-release base of baseVersion under spec. A hotfix tag has the dotted shape <base>-<token><sep>N.hotfix.M (for example vX.Y.Z-rc.N.hotfix.M); it shares the pre-release base of the version the environment held while diverged. The pre-release-shaped cleanup in internal/release deliberately cannot see these tags, so divergence-end cleanup must collect them explicitly.
baseVersion may itself be a hotfix version; it is normalized to its pre-release base before matching. Parsing uses spec so a custom pre-release token still resolves. Tags that do not parse, are not hotfix tags, or belong to a different pre-release base are excluded. The result is nil when nothing matches.
func NewCommand ¶
NewCommand creates the `cascade hotfix` command and its subcommands.
func OrphanEnvBranches ¶
OrphanEnvBranches returns the env/* branches in branches that have no matching divergence in state. A branch env/<name> is healthy only while state[<name>] reports IsDiverged(); a branch with no diverged env behind it is an orphan left over from an interrupted hotfix or manual meddling and should be flagged.
Non env/* branches are ignored. The returned slice preserves the input order and is nil when nothing is orphaned, so callers can treat a nil result as "consistent".
Types ¶
type EnvPlan ¶ added in v0.3.0
type EnvPlan struct {
Env string `json:"env"`
Branch string `json:"branch"`
BaseSHA string `json:"base_sha"`
// Commits are the fix SHAs still to cherry-pick into this environment, in
// the caller's requested ref order, after skipping commits already present.
Commits []string `json:"commits"`
// NoOp is true when the whole requested set is already present in this env.
NoOp bool `json:"no_op"`
// BranchReset is true when the remote env/<env> branch existed at a stale tip
// but was force-reset back to BaseSHA by the orphan self-heal. It is set only
// when the env is not diverged and the single-flight gate ran with a real
// checker that found no open resolution PR.
BranchReset bool `json:"branch_reset"`
// ConflictExpected hints whether a cherry-pick is likely to conflict. The
// plan verb does not run the cherry-pick, so this is best-effort and false
// by default; the workflow is authoritative.
ConflictExpected bool `json:"conflict_expected"`
}
EnvPlan is the plan for one environment in the chain: the env branch, its recorded base SHA, and the ordered list of commits still to apply after per-(commit,env) idempotency skips. NoOp is true when every requested commit is already present in that environment.
type FinalizeOption ¶
type FinalizeOption func(*Finalizer)
FinalizeOption configures optional, additive Finalizer behavior.
func WithFinalizeDryRun ¶
func WithFinalizeDryRun(dryRun bool) FinalizeOption
WithFinalizeDryRun computes the finalize plan without writing state, tags, or release objects.
func WithReleaseManager ¶
func WithReleaseManager(m releaseManager) FinalizeOption
WithReleaseManager injects the release operations. When unset, finalize builds a *release.Manager from GITHUB_REPOSITORY and the release token at run time.
func WithStatePusher ¶
func WithStatePusher(p statePusher) FinalizeOption
WithStatePusher injects the manifest commit/push. The default reuses the shared rebase-retry helper.
func WithTagLister ¶
func WithTagLister(l tagLister) FinalizeOption
WithTagLister injects the existing-tag lookup used for version allocation.
func WithTipReader ¶ added in v0.5.0
func WithTipReader(r gitTipReader) FinalizeOption
WithTipReader injects the reader Finalize uses to cross-check the merge SHA against the resolution branch tip. The default reads the local env-branch tip via git. The what-if simulator injects a record-only reader so finalize can run without a git checkout.
func WithTrunkStateReader ¶
func WithTrunkStateReader(r trunkStateReader) FinalizeOption
WithTrunkStateReader injects the reader that returns the manifest as it exists on the trunk branch. Finalize uses it to read prior env state from trunk, the source of truth, rather than from the lagging env branch the hotfix merged into. The default reads via the GitHub Contents API on real GitHub and plain git under act.
type Finalizer ¶
type Finalizer struct {
// contains filtered or unexported fields
}
Finalizer writes the diverged state, tag, and release object for a completed hotfix. It mirrors the inputs of promote's Finalizer but targets one env on its integration branch rather than a trunk promotion.
func NewFinalizer ¶
func NewFinalizer(opts FinalizerOptions, options ...FinalizeOption) (*Finalizer, error)
NewFinalizer constructs a Finalizer over the manifest at opts.ConfigPath.
func (*Finalizer) Finalize ¶
Finalize writes the diverged state for a completed hotfix on env/<targetEnv>.
targetEnv is the environment being hotfixed; mergeSHA is the tip of env/<targetEnv> after the resolution PR merged; fixSHAs are the trunk commits the hotfix carries (every cherry-picked commit, in apply order); baseSHA is the trunk anchor the integration branch diverged from. It cross-checks the merge SHA against the env-branch tip, allocates the next free hotfix version, snapshots the prior state into the Previous ring, writes the divergence fields and substates, commits the manifest to trunk, and creates the hotfix tag and release object. Every commit in fixSHAs is appended to the env's recorded patch set so a multi-commit hotfix records all of its commits, not just the first.
Finalize is idempotent on identical inputs: a rerun after the state already records the merge SHA is a no-op that neither double-applies patches nor re-snapshots Previous.
func (*Finalizer) SetBuildResult ¶
SetBuildResult records the conclusion of a build job. Valid results mirror SetDeployResult; only successful builds update per-build state.
func (*Finalizer) SetDeployResult ¶
SetDeployResult records the result of a deploy job, mirroring promote.Finalizer.SetDeployResult. Valid results: "success", "failure", "skipped", "cancelled". Only successful deploys update per-deploy state.
type FinalizerOptions ¶
FinalizerOptions carries the required inputs for NewFinalizer.
type Option ¶
type Option func(*Planner)
Option configures optional, additive Planner behavior.
func WithDryRun ¶
WithDryRun controls whether the planner mutates anything. When true the env branch is computed but not created.
func WithPRChecker ¶
WithPRChecker injects the single-flight PR lookup. The default reports no open PRs.
func WithRemote ¶
WithRemote overrides the git remote env branches live on (default "origin").
type PRChecker ¶
type PRChecker interface {
// OpenHotfixPRs returns open PRs labeled cascade-hotfix whose base is baseBranch.
OpenHotfixPRs(baseBranch string) ([]OpenPR, error)
}
PRChecker reports open hotfix PRs targeting a given base branch. It is the single-flight gate: the plan verb refuses to proceed while a hotfix PR is already open against the target env branch. The default implementation is a no-op that reports no open PRs, so callers without GitHub context (and unit tests) are not forced to provide one.
type PlanChainResult ¶ added in v0.3.0
type PlanChainResult struct {
// Envs are the per-environment plans in bottom-up chain order (the first
// environment is excluded; the target environment is the last entry).
Envs []EnvPlan `json:"envs"`
}
PlanChainResult is the computed, validated plan for elevating a set of trunk commits across the bottom-up environment chain up to and including a target environment. It is the multi-commit, multi-env companion to PlanResult and is additive: the single-env Plan and PlanResult are unchanged.
type PlanResult ¶
type PlanResult struct {
TargetEnv string `json:"target_env"`
FixSHA string `json:"fix_sha"`
Branch string `json:"branch"`
BaseSHA string `json:"base_sha"`
// NoOp is true when the fix is already contained in the target state SHA.
NoOp bool `json:"no_op"`
// BranchCreated is true when env/<target> was (or, in dry-run, would be)
// created at BaseSHA. False when it already existed at the expected tip.
BranchCreated bool `json:"branch_created"`
// BranchReset is true when env/<target> existed at a stale tip but was (or, in
// dry-run, would be) force-reset back to BaseSHA by the orphan self-heal. It is
// set only when the env is not diverged and the single-flight gate ran with a
// real checker that found no open resolution PR.
BranchReset bool `json:"branch_reset"`
// HotfixVersionCandidate is the next free hotfix version over the target
// env's current version base (e.g. v1.0.0-rc.1 -> v1.0.0-rc.1.hotfix.1).
HotfixVersionCandidate string `json:"hotfix_version_candidate"`
// ConflictExpected hints whether the cherry-pick is likely to conflict.
// The plan verb does not run the cherry-pick, so this is best-effort and
// false by default; the workflow is authoritative.
ConflictExpected bool `json:"conflict_expected"`
// ProtectionSuggestions are ready-to-run gh/gh api commands an operator can
// paste to establish env/* branch protection. cascade never applies these.
ProtectionSuggestions []string `json:"protection_suggestions"`
DryRun bool `json:"dry_run"`
}
PlanResult is the computed, validated hotfix plan emitted as JSON and GHA outputs. It records only what the workflow needs; no mutation has happened beyond the optional env-branch creation.
type Planner ¶
type Planner struct {
// contains filtered or unexported fields
}
Planner validates and computes a hotfix plan for one environment.
func NewPlanner ¶
func NewPlanner(opts PlannerOptions, options ...Option) (*Planner, error)
NewPlanner constructs a Planner from the manifest at opts.ConfigPath.
func (*Planner) Plan ¶
func (p *Planner) Plan(fixRef, targetEnv string) (*PlanResult, error)
Plan validates the hotfix request and computes the env-branch plan.
fixRef is the trunk commit (or ref/short SHA) to apply; targetEnv is the environment to hotfix. It enforces, in order: trunk ancestry of the fix, target-env eligibility, no-op detection, the single-flight open-PR gate, and env-branch reconciliation. The single-flight gate runs before any branch mutation, so a blocked plan leaves no git state changes.
func (*Planner) PlanChain ¶ added in v0.3.0
func (p *Planner) PlanChain(refs []string, targetEnv string) (*PlanChainResult, error)
PlanChain validates and computes the per-environment plan for elevating a set of trunk commits across the bottom-up environment chain up to and including targetEnv. Commits are kept in the caller's ref order; environments run bottom-up. Each ref must resolve and be a trunk ancestor; each (commit, env) pair is skipped when the commit is already present in that environment (an ancestor of its state SHA or already in its patch list). An environment whose whole requested set is already present is reported as a no-op.
PlanChain is additive: it does not create branches or mutate state, and the single-env Plan and PlanResult are unchanged.
type PlannerOptions ¶
PlannerOptions carries the required inputs for NewPlanner.