Documentation
¶
Overview ¶
Package branchprotection emits the JSON body an operator applies to GitHub's branch-protection API for a cascade-managed trunk. cascade EMITS the file; the operator APPLIES it. cascade never calls the GitHub API.
The emitted artifact is a small wrapper with two top-level keys:
{
"protection": { ... }, // the EXACT body to PUT to the branches API
"operator_todo": { ... } // companion guidance, NOT part of the PUT body
}
An operator applies it with, for example:
cascade branch-protection | jq .protection | \ gh api -X PUT repos/OWNER/REPO/branches/main/protection --input -
The safety invariant is that the ".protection" object, applied verbatim, never creates a required status check that can never report. Only the two cascade controlled steps jobs (Setup and Finalize) are required, because cascade knows their exact check-run names and both run unconditionally on every run. The reusable-workflow caller jobs (validate, build, deploy) are deliberately left out of the required contexts: cascade knows their display-name prefix but not the inner job name that GitHub appends to form the real context, so requiring a bare prefix would block every pull request. Those prefixes are surfaced under operator_todo.complete_these_contexts as "<DisplayName> / <inner-job>" placeholders for the operator to complete once they know their reusable workflow's inner job names.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Marshal ¶
Marshal renders the payload as indented JSON with exactly one trailing newline. The same manifest always produces byte-identical output.
func NewCommand ¶
NewCommand creates the branch-protection command. It emits the JSON body an operator applies to GitHub's branch-protection API for a cascade-managed trunk. cascade emits the file; the operator applies it. cascade never calls the GitHub API.
func Run ¶
Run resolves the manifest and builds the payload, then either emits it or applies it. By default (Options.Apply false) the behavior is unchanged: when Options.Output is empty or "-" it writes the JSON to stdout, otherwise to that file path, and cascade makes no API call. When Options.Apply is set it PUTs the .protection body to GitHub; a non-empty Output still writes the JSON file first for the operator's records.
Types ¶
type OperatorTodo ¶
type OperatorTodo struct {
Note string `json:"note"`
CompleteTheseContexts []string `json:"complete_these_contexts"`
}
OperatorTodo is companion guidance emitted alongside the PUT body. It is NOT part of the body GitHub accepts, so it is carried as a sibling key the operator strips off (jq .protection) before applying.
type Options ¶
type Options struct {
// ConfigPath is the manifest path; empty means auto-detect.
ConfigPath string
// ManifestKey is the key in the manifest file holding the CI config.
ManifestKey string
// Branch labels the protection target. It affects only the operator_todo
// note; the PUT body itself is branch-agnostic. The required contexts (Setup
// and Finalize) are the orchestrate-workflow steps jobs, identical across
// branches and environments, so the branch never changes them.
Branch string
// Output is the destination path. Empty or "-" writes to stdout.
Output string
// Apply opts into calling GitHub: instead of emitting the JSON to stdout,
// cascade PUTs the .protection body to the branches protection API. The emit
// default (Apply false) is unchanged. When Output is also set, the JSON file
// is still written for the operator's records before the apply runs.
Apply bool
// Token is the credential used for the apply. It should be a scoped PAT the
// operator supplies that carries repo-admin; the workflow's GITHUB_TOKEN does
// not need admin. Empty falls back to the GITHUB_TOKEN environment variable.
Token string
// Repo is the owner/repo the apply targets. Empty falls back to the
// GITHUB_REPOSITORY environment variable.
Repo string
// APIURL is the REST API base for the apply. Empty falls back to GITHUB_API_URL
// and then https://api.github.com. It exists so the apply is testable against a
// mock server.
APIURL string
}
Options configures a branch-protection emit run.
type Payload ¶
type Payload struct {
Protection Protection `json:"protection"`
OperatorTodo OperatorTodo `json:"operator_todo"`
}
Payload is the full emitted artifact. The "protection" object is the exact PUT body; "operator_todo" is sibling guidance that must never be sent to GitHub.
func Build ¶
func Build(cfg *config.TrunkConfig, branch string) Payload
Build assembles the Payload from a resolved manifest. The required contexts are exactly the cascade-controlled steps jobs (Setup and Finalize), referenced from generate.SetupJobName and generate.FinalizeJobName so a rename in the generator updates both the workflow and these contexts together. Every reusable-workflow caller job (validate, build, deploy) is surfaced under operator_todo as a "<DisplayName> / <inner-job>" placeholder rather than required directly, because requiring a context cascade cannot fully name would block every pull request.
type Protection ¶
type Protection struct {
RequiredStatusChecks RequiredStatusChecks `json:"required_status_checks"`
EnforceAdmins bool `json:"enforce_admins"`
RequiredPullRequestReviews RequiredPullRequestReviews `json:"required_pull_request_reviews"`
Restrictions *Restrictions `json:"restrictions"`
RequiredLinearHistory bool `json:"required_linear_history"`
AllowForcePushes bool `json:"allow_force_pushes"`
AllowDeletions bool `json:"allow_deletions"`
RequiredConversationResolution bool `json:"required_conversation_resolution"`
}
Protection is the body an operator PUTs to /repos/{owner}/{repo}/branches/{branch}/protection. Every field is explicit so the marshaled output is byte-stable for a given manifest.
type RequiredPullRequestReviews ¶
type RequiredPullRequestReviews struct {
RequiredApprovingReviewCount int `json:"required_approving_review_count"`
DismissStaleReviews bool `json:"dismiss_stale_reviews"`
RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"`
}
RequiredPullRequestReviews mirrors the GitHub API review-protection block.
type RequiredStatusChecks ¶
type RequiredStatusChecks struct {
Strict bool `json:"strict"`
Contexts []string `json:"contexts"`
}
RequiredStatusChecks lists the checks GitHub requires before merge. contexts holds ONLY cascade-controlled steps-job names whose check-run context cascade knows exactly, kept sorted for deterministic output.
type Restrictions ¶
type Restrictions struct {
Users []string `json:"users"`
Teams []string `json:"teams"`
Apps []string `json:"apps"`
}
Restrictions models the push-restriction block. GitHub requires the key to be present; a nil *Restrictions marshals to null, meaning no push restriction.