repocfg

package
v1.4.1 Latest Latest
Warning

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

Go to latest
Published: Jul 3, 2026 License: MIT Imports: 16 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 CODEOWNERS added in v1.3.3

func CODEOWNERS() (string, error)

CODEOWNERS returns the uniform CODEOWNERS file provisioned to every repo. It is static today — a single owner (`* @kushuh`) — so there is nothing to render; lift it to an org/class config field if/when teams appear.

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"`
	Exclude      ExcludeRules     `yaml:"exclude"`
	CodeQL       CodeQLRule       `yaml:"codeql"`
	Codecov      CodecovRule      `yaml:"codecov"`
}

ChecksConfig is checks.yaml. Required checks are the jobs declared in a repo's .github/workflows/main.yaml (minus Exclude), plus the Always set — there is no per-class or per-file derivation. CodeQL languages (for the codeql.yml workflow) and the Codecov ruleset's checks are the only other pieces here.

func LoadChecks

func LoadChecks() (*ChecksConfig, error)

LoadChecks reads and parses checks.yaml.

func (*ChecksConfig) ResolveBotIntegrations added in v1.3.5

func (c *ChecksConfig) ResolveBotIntegrations(org *OrgProfile)

ResolveBotIntegrations fills the integration ids that belong to a per-org bot rather than a global app. checks.yaml can't hardcode these: each org runs its own [Agent] App with a distinct id (a-novel and a-novel-kit differ), and the same checks.yaml is loaded for both. Call this with the target repo's org profile before discovery so a check posted by the [Agent] App — the merge-gate — is required against that org's App and can actually be satisfied.

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 CodeQLLangRule added in v1.3.2

type CodeQLLangRule struct {
	Detect []string `yaml:"detect"`
	Lang   string   `yaml:"lang"`
}

CodeQLLangRule maps a detection signal to a CodeQL language.

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 CodeQLRule added in v1.3.2

type CodeQLRule struct {
	Always    []string         `yaml:"always"`
	Languages []CodeQLLangRule `yaml:"languages"`
}

CodeQLRule is the only file-based detection left: the CodeQL workflow's languages. Always-on languages need no signal (e.g. actions); the rest are detected by a canonical file (go.mod → go, package.json → javascript).

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. Codecov posts its own statuses (codecov/patch, codecov/project), so they are not main.yaml jobs — they live in this separate, bot-bypassing ruleset.

type Discovered

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

Discovered is what probing a repo tells us: the required checks (for the master ruleset) and the CodeQL languages (for the codeql.yml workflow).

func Discover

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

Discover computes a repo's required checks and CodeQL languages.

Required checks are the `always` set plus every job declared in the repo's .github/workflows/main.yaml, minus cc.Exclude — the job id is the check context, so there is no class- or file-based derivation to drift. A missing main.yaml (e.g. a docs/meta repo) simply contributes no jobs.

CodeQL languages are the one remaining file-based signal, used only to render the codeql.yml workflow.

type ExcludeRules added in v1.3.2

type ExcludeRules struct {
	Prefixes   []string `yaml:"prefixes"`
	IfContains []string `yaml:"if_contains"`
}

ExcludeRules narrows the main.yaml jobs that become required checks. A job is excluded when its id starts with any Prefix (reporting / post-merge jobs) or its `if:` contains any IfContains string (e.g. master-only jobs, which never run on a PR and so cannot gate one).

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 LabelDef added in v1.3.4

type LabelDef struct {
	Name        string `yaml:"name"`
	Color       string `yaml:"color"`
	Description string `yaml:"description"`
}

LabelDef is one entry in the canonical label set — name, hex colour (no leading '#', lower-case, as GitHub stores it) and description.

type LabelsConfig added in v1.3.4

type LabelsConfig struct {
	Ensure []LabelDef `yaml:"ensure"`
	Retire []string   `yaml:"retire"`
}

LabelsConfig is governance/labels.yaml: the uniform label vocabulary every repo carries, provisioned like CODEOWNERS. Ensure labels are upserted (created when absent, recoloured / re-described when they drift); Retire labels are deleted. A label in neither list is left untouched — apply never removes a label it was not told about.

func LoadLabels added in v1.3.4

func LoadLabels() (*LabelsConfig, error)

LoadLabels reads and parses governance/labels.yaml.

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

	// 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.

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"`
}

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) 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