repocfg

package
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jun 29, 2026 License: MIT Imports: 15 Imported by: 0

Documentation

Overview

Package repocfg models and applies a repository's GitHub configuration — general settings, security, CodeQL, Pages and the per-repo rulesets — from editable YAML templates under templates/.

The desired state for a repo is composed from three inputs:

  • a CLASS preset (classes/<class>.yaml) — the per-kind toggles,
  • an ORG profile (orgs/<org>.yaml) — bypass actors and signing,
  • semantically DISCOVERED checks (checks.yaml + the detect package),

then overridden by CLI flags / the interactive form. The templates are plain YAML so they can be edited without touching Go; an on-disk copy (under the stack checkout, or $REPO_CONFIG_DIR) wins over the embedded copy so edits don't need a rebuild.

Index

Constants

This section is empty.

Variables

AllClasses is the ordered set of known classes (for the UI picker and flag validation).

Functions

func ReadTemplate

func ReadTemplate(rel string) ([]byte, error)

ReadTemplate returns the bytes of templates/<rel>, preferring an on-disk copy so edits don't need a rebuild, falling back to the embedded copy. On-disk precedence: $REPO_CONFIG_DIR, else <stack>/cli/internal/repocfg/templates.

func RenderCodeQL

func RenderCodeQL(langs []string, querySuite, defaultBranch string) (string, error)

RenderCodeQL fills the CodeQL advanced-setup workflow template.

func SecurityBlock

func SecurityBlock(c *ClassPreset) map[string]any

SecurityBlock is the security_and_analysis sub-object.

func SettingsBody

func SettingsBody(c *ClassPreset) map[string]any

SettingsBody is the PATCH /repos body for general + merge + security.

Types

type APIBypassActor

type APIBypassActor struct {
	ActorID    *int64 `json:"actor_id"`
	ActorType  string `json:"actor_type"`
	BypassMode string `json:"bypass_mode"`
}

APIBypassActor / APIRule / APIRuleset mirror the GitHub rulesets API request body.

type APIRule

type APIRule struct {
	Type       string         `json:"type"`
	Parameters map[string]any `json:"parameters,omitempty"`
}

type APIRuleset

type APIRuleset struct {
	Name         string           `json:"name"`
	Target       string           `json:"target"`
	Enforcement  string           `json:"enforcement"`
	Conditions   map[string]any   `json:"conditions"`
	BypassActors []APIBypassActor `json:"bypass_actors"`
	Rules        []APIRule        `json:"rules"`
}

func BuildRuleset

func BuildRuleset(spec *RulesetSpec, org *OrgProfile, checks []CheckRef) (*APIRuleset, error)

BuildRuleset turns a ruleset template + org + discovered checks into the API body, resolving generic bypass entries and injecting the checks.

type CheckDef

type CheckDef struct {
	Context     string `yaml:"context"`
	Integration string `yaml:"integration"`
}

CheckDef is one required status check (its integration key resolves to an app id via ChecksConfig.Integrations).

type CheckRef

type CheckRef struct {
	Context       string
	IntegrationID int64
}

CheckRef is a required status check resolved to its posting app.

type ChecksConfig

type ChecksConfig struct {
	Integrations map[string]int64       `yaml:"integrations"`
	Always       []CheckDef             `yaml:"always"`
	Languages    map[string]LangRule    `yaml:"languages"`
	Features     map[string]FeatureRule `yaml:"features"`
	Docker       DockerRule             `yaml:"docker"`
	Codecov      CodecovRule            `yaml:"codecov"`
	// Retired lists check contexts the map once emitted but has since renamed
	// or removed. They stay part of the managed namespace (see
	// ChecksConfig.IsManaged) so `repo update` DROPS a stale live check by one
	// of these names instead of mistaking it for a manual (unmanaged) one.
	Retired []string `yaml:"retired"`
}

ChecksConfig is checks.yaml — the semantic discovery map.

func LoadChecks

func LoadChecks() (*ChecksConfig, error)

LoadChecks reads and parses checks.yaml.

func (*ChecksConfig) IsManaged added in v1.1.1

func (cc *ChecksConfig) IsManaged(ctx string) bool

IsManaged reports whether ctx is a status-check context the discovery map (checks.yaml) owns by name: the catalog of contexts it can emit (always + every language + every feature + codecov) plus the `retired` names it once emitted. A context that is NOT managed is "unmanaged" — a manual ruleset addition, or a name-quirk derivation the map cannot reproduce — which `update` preserves rather than reconciling away.

Docker build-<target> contexts are deliberately NOT classified managed by pattern. Their CI job names are hand-authored and drift from the Dockerfile name (init.Dockerfile yields the context "build-job-init", not "build-init"), so matching them by a "build-" prefix would wrongly drop a live context that discovery cannot reproduce. A docker check discovery DOES produce rides along in the discovered set; a stale one is preserved until a `--full` reset — or an explicit `retired` entry — removes it.

type Class

type Class string

Class is a repository kind; each maps to one classes/<class>.yaml preset.

const (
	ClassService   Class = "service"
	ClassLibrary   Class = "library"
	ClassWorkflows Class = "workflows"
	ClassMeta      Class = "meta"
)

func DetectClass added in v1.2.0

func DetectClass(repo string) Class

DetectClass infers a repo's class from its name, used when neither a repos/<org>_<repo>.yaml override nor the --class flag is given. The strong-semantic classes match exact names (workflows, .github); the service-* family is the service class; everything else falls to the freeform library default. platform-* frontends are not modelled yet — they fall to library or need an explicit override until platform conventions exist.

type ClassPreset

type ClassPreset struct {
	Class       Class           `yaml:"class"`
	Features    Features        `yaml:"features"`
	Merge       Merge           `yaml:"merge"`
	Security    SecurityToggles `yaml:"security"`
	CodeQL      CodeQLPreset    `yaml:"codeql"`
	Pages       bool            `yaml:"pages"`
	Codecov     CodecovMode     `yaml:"codecov"`
	CodeQuality bool            `yaml:"code_quality"`
	Rulesets    ClassRulesets   `yaml:"rulesets"`
}

ClassPreset is one classes/<class>.yaml file.

func LoadClass

func LoadClass(c Class) (*ClassPreset, error)

LoadClass reads and parses classes/<class>.yaml.

func LoadRepoOverride

func LoadRepoOverride(org, repo string) (*ClassPreset, bool, error)

LoadRepoOverride reads repos/<org>_<repo>.yaml if it exists. The bool is false (nil error) when the repo has no override and should use its class.

type ClassRulesets

type ClassRulesets struct {
	Master          bool `yaml:"master"`
	RequireApproval bool `yaml:"require_approval"`
	Tags            bool `yaml:"tags"`
}

ClassRulesets says which named rulesets the class applies. codecov is driven separately by ClassPreset.Codecov.

type CodeQLPreset

type CodeQLPreset struct {
	Enabled    bool   `yaml:"enabled"`
	QuerySuite string `yaml:"query_suite"`
}

CodeQLPreset configures CodeQL advanced setup. Languages are discovered, not declared here.

type CodeQualityParams

type CodeQualityParams struct {
	Severity string `yaml:"severity"`
}

CodeQualityParams is the code_quality rule's parameters.

type CodecovMode

type CodecovMode string

CodecovMode is auto | enabled | disabled (auto = on when the repo has tests). Kept a string, not a bool, to avoid the yaml off/on pitfall.

const (
	CodecovAuto     CodecovMode = "auto"
	CodecovEnabled  CodecovMode = "enabled"
	CodecovDisabled CodecovMode = "disabled"
)

type CodecovRule

type CodecovRule struct {
	EnableWhenTests bool       `yaml:"enable_when_tests"`
	Checks          []CheckDef `yaml:"checks"`
}

CodecovRule controls the codecov ruleset's auto-enable + its checks.

type Discovered

type Discovered struct {
	Checks      []CheckRef
	CodeQLLangs []string
	HasTests    bool
}

Discovered is what probing a repo tells us: the required checks, the CodeQL languages, and whether the repo has tests (informational; the codecov ruleset is gated on actual Codecov reporting, not this).

func Discover

func Discover(repoPath string, cc *ChecksConfig) (*Discovered, error)

Discover walks repoPath and applies the checks.yaml map to produce the required-check set + CodeQL languages. A language/feature signal is matched by detect.ExistsUnder — a bounded, gitignore-aware walk — so a module that lives in a sub-directory (e.g. stack's Go module under cli/) is detected, not just one at the repo root. Docker build targets come from the detect package.

type DockerRule

type DockerRule struct {
	ContextFormat string `yaml:"context_format"`
	Integration   string `yaml:"integration"`
}

DockerRule formats a build-<target> check per detected docker target.

type FeatureRule

type FeatureRule struct {
	Detect []string   `yaml:"detect"`
	Checks []CheckDef `yaml:"checks"`
}

FeatureRule maps a sub-feature (e.g. pkg/js) to extra checks.

type Features

type Features struct {
	Issues      bool `yaml:"issues"`
	Wiki        bool `yaml:"wiki"`
	Projects    bool `yaml:"projects"`
	Discussions bool `yaml:"discussions"`
}

Features mirrors the repo "Features" toggles.

type LangRule

type LangRule struct {
	Detect []string   `yaml:"detect"`
	Checks []CheckDef `yaml:"checks"`
	CodeQL []string   `yaml:"codeql"`
}

LangRule maps a detected language to its checks + CodeQL languages.

type Merge

type Merge struct {
	Squash              bool `yaml:"squash"`
	MergeCommit         bool `yaml:"merge_commit"`
	Rebase              bool `yaml:"rebase"`
	AutoMerge           bool `yaml:"auto_merge"`
	DeleteBranchOnMerge bool `yaml:"delete_branch_on_merge"`
	AllowUpdateBranch   bool `yaml:"allow_update_branch"`
	SignoffRequired     bool `yaml:"signoff_required"`
}

Merge mirrors the merge-button + PR settings.

type Op

type Op struct {
	Method      string `json:"method,omitempty"`
	Path        string `json:"path,omitempty"`
	Body        any    `json:"body,omitempty"`
	Content     string `json:"content,omitempty"`
	RulesetName string `json:"ruleset,omitempty"`
}

Op is one API operation the plan would perform.

  • settings/pages: Method + Path + Body.
  • contents (codeql/dependabot): Method PUT + Path + Content (file text).
  • ruleset: RulesetName set; reconciled by name at apply time (POST when absent, PUT .../{id} when present), Body is the ruleset payload.

func (Op) Title

func (o Op) Title() string

Title is a one-line human label for the op.

type OrgProfile

type OrgProfile struct {
	Org             string           `yaml:"org"`
	SigningRequired bool             `yaml:"signing_required"`
	Bots            map[string]int64 `yaml:"bots"`
}

OrgProfile is orgs/<org>.yaml — per-org bot ids and signing policy. A ruleset's generic bot name (bots key) resolves to the app id here.

func LoadOrg

func LoadOrg(org string) (*OrgProfile, error)

LoadOrg reads and parses orgs/<org>.yaml.

type Plan

type Plan struct {
	Ops []Op
}

Plan is the ordered set of operations to bring a repo to desired state.

func BuildPlan

func BuildPlan(t *RepoTarget) (*Plan, error)

BuildPlan assembles the desired-state operations for a repo.

func (*Plan) Render

func (p *Plan) Render(w io.Writer) error

Render writes the plan as labelled raw JSON (and verbatim file content for content writes) — close to what GitHub receives, no extra formatting.

func (*Plan) RenderJSON

func (p *Plan) RenderJSON(w io.Writer) error

RenderJSON writes the plan as a JSON array of ops — machine-readable, so the exact operations can be inspected or applied verbatim.

type RSCParams

type RSCParams struct {
	Strict               bool `yaml:"strict"`
	DoNotEnforceOnCreate bool `yaml:"do_not_enforce_on_create"`
}

RSCParams is the static part of a required_status_checks rule; the checks list is injected from discovery.

type RepoTarget

type RepoTarget struct {
	Org, Repo, DefaultBranch string
	Class                    *ClassPreset
	OrgProfile               *OrgProfile
	Checks                   *ChecksConfig
	Discovered               *Discovered

	// LiveMasterChecks is the master ruleset's current required-check set,
	// read from GitHub on `update` so reconciliation can preserve the checks
	// the discovery map does not own. Nil (e.g. on `create`, or under a
	// `--full` reset) means there is nothing to preserve and the result is the
	// discovered set alone. See RepoTarget.ResolveMasterChecks.
	LiveMasterChecks []CheckRef
	// ForceChecks ignores LiveMasterChecks entirely and resets the master
	// ruleset's required checks to a plain rediscovery — dropping even the
	// unmanaged (manual) live checks. It backs `repo update --full`, so a bad
	// manual edit can be wiped.
	ForceChecks bool
	// CodecovReports is whether Codecov posts a status on this repo. It
	// gates `codecov: auto` — a coverage ruleset is only enforced where
	// Codecov actually reports, so it never blocks PRs on a repo that has
	// no coverage upload.
	CodecovReports bool
}

RepoTarget is everything needed to compute a repo's desired config.

func (*RepoTarget) ResolveMasterChecks added in v1.1.1

func (t *RepoTarget) ResolveMasterChecks() []CheckRef

ResolveMasterChecks computes the required status checks the master ruleset will carry for this target. On `create` (no live set) or under a `--full` reset (ForceChecks), it is exactly the discovered set — a plain rediscovery. Otherwise it reconciles discovery with the live ruleset; see reconcileMasterChecks. The CLI summary and the API plan both call this, so what the human confirms is exactly what gets applied.

type RulesetConditions

type RulesetConditions struct {
	RefName struct {
		Include []string `yaml:"include"`
		Exclude []string `yaml:"exclude"`
	} `yaml:"ref_name"`
}

RulesetConditions is the ref-name match for a branch ruleset.

type RulesetRules

type RulesetRules struct {
	Creation             bool               `yaml:"creation"`
	Update               bool               `yaml:"update"`
	Deletion             bool               `yaml:"deletion"`
	NonFastForward       bool               `yaml:"non_fast_forward"`
	RequiredSignatures   bool               `yaml:"required_signatures"`
	RequiredStatusChecks *RSCParams         `yaml:"required_status_checks"`
	MergeQueue           map[string]any     `yaml:"merge_queue"`
	CodeQuality          *CodeQualityParams `yaml:"code_quality"`
	PullRequest          map[string]any     `yaml:"pull_request"`
	CopilotCodeReview    map[string]any     `yaml:"copilot_code_review"`
}

RulesetRules holds every rule a template may set; absent ones stay nil/ false. Param blobs that pass straight through to the API (merge_queue, pull_request, copilot_code_review) are kept as maps so the template owns their shape.

type RulesetSpec

type RulesetSpec struct {
	Name        string            `yaml:"name"`
	Target      string            `yaml:"target"`
	Enforcement string            `yaml:"enforcement"`
	Conditions  RulesetConditions `yaml:"conditions"`
	Bypass      []string          `yaml:"bypass"`
	Rules       RulesetRules      `yaml:"rules"`
}

RulesetSpec is one rulesets/<name>.yaml base. The required-status-checks list and concrete bypass actors are injected by the builder, not stored here.

func LoadRuleset

func LoadRuleset(name string) (*RulesetSpec, error)

LoadRuleset reads rulesets/<name>.yaml.

type SecurityToggles

type SecurityToggles struct {
	SecretScanning bool `yaml:"secret_scanning"`
	PushProtection bool `yaml:"push_protection"`
	Dependabot     bool `yaml:"dependabot"`
}

SecurityToggles mirrors the security_and_analysis block we manage.

Jump to

Keyboard shortcuts

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