branchprotection

package
v0.7.0-rc.0 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

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

func Marshal(p Payload) ([]byte, error)

Marshal renders the payload as indented JSON with exactly one trailing newline. The same manifest always produces byte-identical output.

func NewCommand

func NewCommand() *cobra.Command

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

func Run(o Options, stdout io.Writer) error

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.

Jump to

Keyboard shortcuts

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