57. Repos management for per-repo installations
Date: 2026-06-17
Status
Accepted
Annotation (2026-08-18): The manifest format was simplified in #6346: the forge: wrapper and defaults.forge were replaced with top-level github: and gitlab: platform sections; per-repo entries use name: instead of repo: and always use object form; NullableString was replaced with a "none" sentinel.
Builds on ADR 0033 (per-repo installation mode) and ADR 0048 (automatic updates). Core commands work independently of ADR 0048; version-management commands require its --upstream-ref mechanism.
Context
Per-repo installation (ADR 0033) is fullsend's target model — each repo is self-contained with its own .fullsend/ directory, shim workflow, WIF provider, variables, and secrets. However, the per-repo install path (runPerRepoInstall() in admin.go) handles one repo at a time. Organizations managing tens or hundreds of repos across multiple GitHub orgs face three gaps:
- No bulk operations. Installing on 50 repos means 50 manual runs.
- No enrollment inventory. Discovering which repos have fullsend requires scanning for guard variables across all repos — an O(n) API operation with no centralized record.
- No drift detection. No reference state to compare against: "which repos have a stale mint URL?" or "which repos are still on v2.1.0?"
Per-org's enrollment system provides these operationally but at the cost of a dedicated config repo, org-level variables, enrollment PRs, reconciliation scripts, and three-level workflow nesting. A thin orchestration layer over existing per-repo machinery can provide the same value without that infrastructure.
Decision
Add a fullsend repos subcommand group that manages per-repo installations at scale via a declarative repos.yaml manifest.
Target persona: platform administrators (SRE/DevOps) managing fullsend across an organization. Individual repo owners continue using fullsend github for single-repo setup. The relationship is analogous to Terraform vs cloud provider CLIs.
Subcommands:
| Command | Purpose |
|---|---|
repos init | Discover existing installations, generate manifest |
repos status | Read-only comparison of manifest vs actual state |
repos install | Provision fullsend on uninstalled manifest repos |
repos sync / repos diff | Reconcile configuration drift |
repos upgrade | Upgrade scaffold shim ref across repos |
repos remove | Remove fullsend from specific repos |
Manifest: a YAML file declaring desired state — mint config, default field values, and a list of repos (strings for defaults, objects for overrides). Supports glob patterns (acme-corp/*) and multi-org repos in a single file. Lives wherever the operator chooses (not in a .fullsend config repo). All read commands accept --manifest as a local path or URL per ADR 0038.
Key design constraints:
- Install uses two phases: parallel discovery → parallel scaffold. GCP infrastructure (WIF, mint) is provisioned separately.
- Version changes (
repos upgrade) are separated from config reconciliation (repos sync) to prevent accidental upgrades. - Works alongside per-org installations during a migration period; serves as the migration path for ADR 0044 (pending) deprecation.
Manifest schema, field resolution semantics, and subcommand specifications are covered in the sections below.
Consequences
- Operational parity with per-org — bulk install, inventory, and drift detection without per-org's infrastructure complexity (config repo, org-level variables, enrollment workflows).
- Cross-org management — a single manifest manages repos across multiple GitHub orgs, unlike per-org's single-org model.
- Operator discipline required — the manifest must be maintained manually; adding repos to the org doesn't auto-add them (unless matched by a glob pattern).
- API rate scaling — discovery is O(n) API calls per repo; large deployments may require rate limiting and
--repofiltering. - Migration path — if per-org is deprecated, the repos tool provides the migration mechanism before the old model is removed.
References
- ADR 0033 — per-repo installation model
- ADR 0038 — URL-based resource references
- ADR 0044 (pending) — per-org deprecation; repos tool replaces its deferred Option C
- ADR 0045 — harness composition via
baseURLs - ADR 0048 —
--upstream-refversion pinning - PR #5713 — added forge URL fields (
forge.github.url,forge.gitlab.url) to the manifest schema and--forge-urlflag torepos init(nowrepos migrate);forge.gitlab.urlis required (breaking change)
Note: ADR 0074 consolidates the subcommands defined above into five commands (
migrate,install,status,uninstall,set-default).
Implementation status
Implemented subcommands:
repos migrate— PR #5816 (replacedrepos init; one-command migration from per-org to per-repo)repos install— PR #3033, consolidated into convergence operator in PR #5807repos status— PR #3031repos uninstall— PR #4081, expanded with--manifest-onlyand--uninstall-onlyin PR #5807
Consolidated in PR #5807 (absorbed into repos install and repos uninstall):
repos add(PR #4081) →repos installwith positional args and--forgerepos remove(PR #4081) →repos uninstallwith--manifest-onlyrepos diff(PR #4079) →repos install --dry-runrepos sync(PR #4079) →repos installconvergence phaserepos upgrade(PR #4080) →repos installconvergence phase
Removed:
repos init(PR #3033) → replaced byrepos migratein PR #5816
