Skip to content

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:

  1. No bulk operations. Installing on 50 repos means 50 manual runs.
  2. 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.
  3. 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:

CommandPurpose
repos initDiscover existing installations, generate manifest
repos statusRead-only comparison of manifest vs actual state
repos installProvision fullsend on uninstalled manifest repos
repos sync / repos diffReconcile configuration drift
repos upgradeUpgrade scaffold shim ref across repos
repos removeRemove 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 --repo filtering.
  • 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 base URLs
  • ADR 0048--upstream-ref version pinning
  • PR #5713 — added forge URL fields (forge.github.url, forge.gitlab.url) to the manifest schema and --forge-url flag to repos init (now repos migrate); forge.gitlab.url is 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 (replaced repos init; one-command migration from per-org to per-repo)
  • repos install — PR #3033, consolidated into convergence operator in PR #5807
  • repos status — PR #3031
  • repos uninstall — PR #4081, expanded with --manifest-only and --uninstall-only in PR #5807

Consolidated in PR #5807 (absorbed into repos install and repos uninstall):

  • repos add (PR #4081) → repos install with positional args and --forge
  • repos remove (PR #4081) → repos uninstall with --manifest-only
  • repos diff (PR #4079) → repos install --dry-run
  • repos sync (PR #4079) → repos install convergence phase
  • repos upgrade (PR #4080) → repos install convergence phase

Removed:

  • repos init (PR #3033) → replaced by repos migrate in PR #5816
Content