Repository
Repository manages a single GitHub repository — its description, visibility, topics, labels, features, merge strategy, branch protection rules, rulesets, secrets, variables, and Actions settings.
Metadata
Section titled “Metadata”metadata: owner: babarot # GitHub owner or organization name: my-project # Repository nameThe combination of owner and name identifies the target repository (babarot/my-project).
| Field | Description |
|---|---|
description |
Repository description |
homepage |
URL displayed on the repo page |
visibility |
public, private, or internal — see General Settings |
archived |
Archive (read-only) or unarchive — see General Settings |
topics |
GitHub topics for discoverability |
labels |
Repository issue/PR labels — see Labels |
label_sync |
Deprecated label sync mode: use reconcile.labels instead — see Labels |
features |
Toggle issues, projects, wiki, discussions, pull requests — see General Settings |
merge_strategy |
Merge commit, squash, rebase options — see General Settings |
branch_protection |
Classic branch protection rules — see Branch Protection |
rulesets |
Modern rulesets with enforcement modes and bypass actors — see Rulesets |
secrets |
GitHub Actions secrets (via ${ENV_*} references) — see Secrets & Variables |
variables |
Repository variables — see Secrets & Variables |
actions |
GitHub Actions permissions, SHA pinning, workflow defaults, and fork PR policy — see Actions |
All fields are optional — declare only what you want to manage. Fields not present in the YAML are left unchanged on GitHub.
The one exception is spec.actions.enabled: when managing any other Actions setting, GitHub requires enabled to be sent too. See Actions.
Reconcile
Section titled “Reconcile”By default, collection fields are managed additively: gh-infra creates or updates entries declared in YAML and leaves undeclared GitHub resources untouched.
Use top-level reconcile to make selected collections match YAML exactly:
reconcile: rulesets: authoritative labels: additive branch_protection: additive
spec: rulesets: - name: protect-main target: branch rules: non_fast_forward: true| Field | Modes | Description |
|---|---|---|
reconcile.labels |
additive, authoritative |
Controls how spec.labels is compared with existing GitHub labels |
reconcile.rulesets |
additive, authoritative |
Controls how spec.rulesets is compared with existing GitHub rulesets |
reconcile.branch_protection |
additive, authoritative |
Controls how spec.branch_protection is compared with existing classic branch protection rules |
additive is the default. authoritative deletes remote entries that are not declared in YAML, but only when the corresponding spec collection is present. If spec.rulesets, spec.branch_protection, or spec.labels is omitted, that collection is unmanaged even when reconcile has a policy for it.
To delete all labels, rulesets, or branch protection rules, use authoritative with an empty list:
reconcile: rulesets: authoritativespec: rulesets: []labels: null, labels:, rulesets: null, rulesets:, branch_protection: null, and branch_protection: are invalid. Use an empty list when you mean an empty managed collection.
When to Use
Section titled “When to Use”Use Repository when you want to manage one repo’s settings in a dedicated YAML file. This is the simplest resource kind and the starting point for most users.
It works best when:
- Each repo has its own distinct settings — different branch protection rules, different topics, different merge strategies.
- You want per-repo change tracking — each file maps to one repo, so
git blametells you exactly who changed what and when. - You’re managing a small number of repos — for 1–5 repos, separate files are easy to maintain.
If you find yourself copying the same settings across many files, consider RepositorySet instead.
Creating Repositories
Section titled “Creating Repositories”If a YAML manifest references a repository that doesn’t exist, plan shows it as a new resource, with what the same apply adds to it:
$ gh infra plan ./repos/
+ babarot/new-project will be created + repository babarot/new-project + ruleset "protect-main" + name protect-main + TOKEN (new) + labels + bug #ff0000 a label named like one of GitHub's default labels is updated instead of created branch protection for main is applied by a later plan, once the branch existsapply creates it with gh repo create, applying the settings, features, merge strategy, topics, security and Actions settings, and then the rulesets, secrets, variables, labels and milestones the plan shows, in the same run. archived: true is applied last.
A new repository is empty, so these are planned against nothing, with two exceptions:
- Default labels: GitHub creates default labels in a new repository (
bug,documentation, and others; an organization can change them). A label with the name of one of them is updated instead of created, so what the manifest leaves out of it, such as its description, stays as GitHub made it. Withreconcile.labels: authoritative, the default labels the manifest does not declare are deleted by the nextplanandapply. - Branch protection: a new repository has no branch to protect. The next
planafter the branch exists shows the protection, andapplyadds it. Rulesets do not need a branch and are applied at once.
The -o json and -o markdown plans list these notes as warnings for the repository. FileSets for the new repository are applied after it is created. The repository has no commits then, so a FileSet with via: pull_request is planned and applied as a push for it (see Empty repositories).
Deleting Repositories
Section titled “Deleting Repositories”gh-infra does not support repository deletion. Without a state file, there is no way to distinguish “removed from YAML” from “never managed” — and repository deletion is irreversible.
To delete a repository, use gh repo delete directly.