Skip to content

57. Repos management for per-repo installations

Date: 2026-06-17

Status

Accepted

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 upgrade-mintUpgrade token mint Cloud Function
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:

  • WIF provisioning and mint registration are serialized (read-modify-write on Cloud Run env vars). Install uses three phases: parallel discovery → sequential WIF → parallel scaffold.
  • 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, subcommand specifications, and implementation details are in the repos management plan and repos init plan.

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