Documentation
¶
Overview ¶
Package repos provides reusable per-repo installation logic for fullsend. It decouples the core install flow (guard check, WIF provisioning, scaffold commit, variable/secret writes) from CLI concerns (prompts, spinners, flag parsing) so that both the interactive CLI and future bulk-install commands can share the same logic.
Package repos implements parsing, validation, and resolution of the repos.yaml declarative manifest that drives multi-repo management (ADR 0057).
Index ¶
- Variables
- func AddToManifest(ctx context.Context, cfg ManifestEditConfig, entries []RepoEntry, ...) (*ManifestAddResult, *Manifest, error)
- func BuildScaffoldFiles(cfg InstallConfig) ([]forge.TreeFile, error)
- func FormatDiffTable(result *DiffResult) string
- func IsValidRef(ref string) bool
- func MarshalWithHeader(m *Manifest) ([]byte, error)
- func MatchManifestRepos(manifest *Manifest, patterns []string) ([]string, error)
- func RemoveFromManifest(cfg ManifestEditConfig, repos []string, progress ProgressFunc) (*ManifestRemoveResult, *Manifest, error)
- func UpgradeMint(ctx context.Context, manifest *Manifest, provisioner WIFProvisioner, ...) error
- type BatchInstallConfig
- type BatchInstallResult
- type Change
- type DefaultsConfig
- type DiffResult
- type DiscoveredRepo
- type Drift
- type InitConfig
- type InitResult
- type InstallConfig
- type InstallResult
- type Manifest
- func (m *Manifest) ExpandGlobs(ctx context.Context, client forge.Client) ([]ResolvedRepo, error)
- func (m *Manifest) Marshal() ([]byte, error)
- func (m *Manifest) ResolveConfig(owner, repo string) (ResolvedConfig, bool)
- func (m *Manifest) ResolveConfigForEntry(owner, repo string, entry RepoEntry) ResolvedConfig
- func (m *Manifest) Validate() error
- type ManifestAddResult
- type ManifestEditConfig
- type ManifestRemoveResult
- type MintConfig
- type MintDiscovery
- type NullableString
- type ProgressFunc
- type ProvisionerFactory
- type RepoCandidate
- type RepoEntry
- type RepoSelectFunc
- type RepoState
- type RepoStatus
- type ResolvedConfig
- type ResolvedRepo
- type ScaffoldCommitFunc
- type StatusResult
- type StatusSummary
- type SyncResult
- type UninstallConfig
- type UninstallResult
- type UpgradeConfig
- type UpgradeResult
- type WIFProvisioner
Constants ¶
This section is empty.
Variables ¶
var ErrMintNotFound = errors.New("mint function not found")
ErrMintNotFound indicates the mint function does not exist.
var WIFProviderPattern = regexp.MustCompile(
`^projects/\d+/locations/global/workloadIdentityPools/[a-z][a-z0-9-]{2,30}[a-z0-9]/providers/[a-z][a-z0-9-]{2,30}[a-z0-9]$`,
)
WIFProviderPattern validates the full WIF provider resource name format required by google-github-actions/auth@v3. GCP pool/provider IDs: 4-32 chars, [a-z0-9-], start with letter, no trailing hyphen.
Functions ¶
func AddToManifest ¶ added in v0.32.0
func AddToManifest(ctx context.Context, cfg ManifestEditConfig, entries []RepoEntry, client forge.Client, progress ProgressFunc) (*ManifestAddResult, *Manifest, error)
AddToManifest appends repo entries to the manifest, skipping duplicates. When client is non-nil, each non-glob repo is probed for existing installation state and per-repo overrides are populated where the discovered values differ from manifest defaults. Returns the result and the modified manifest. The manifest is written to disk only when ManifestPath is set and DryRun is false.
func BuildScaffoldFiles ¶
func BuildScaffoldFiles(cfg InstallConfig) ([]forge.TreeFile, error)
BuildScaffoldFiles generates the scaffold tree files for a per-repo install. Exported so the CLI dry-run path can display the file list without running the full install.
func FormatDiffTable ¶ added in v0.32.0
func FormatDiffTable(result *DiffResult) string
FormatDiffTable renders a DiffResult as a human-readable table.
func IsValidRef ¶ added in v0.32.0
IsValidRef reports whether ref contains only characters safe for use in GitHub Actions workflow uses: lines.
func MarshalWithHeader ¶ added in v0.31.0
MarshalWithHeader serializes the manifest with a descriptive header comment.
func MatchManifestRepos ¶ added in v0.32.0
MatchManifestRepos returns the list of repo names from the manifest that match any of the given patterns. Used by CLI commands to resolve positional args (which may contain globs) against manifest entries.
func RemoveFromManifest ¶ added in v0.32.0
func RemoveFromManifest(cfg ManifestEditConfig, repos []string, progress ProgressFunc) (*ManifestRemoveResult, *Manifest, error)
RemoveFromManifest removes matching repo entries from the manifest. Patterns containing glob characters (*, ?, [) are matched against manifest entries using filepath.Match. Returns the result and the modified manifest.
func UpgradeMint ¶ added in v0.32.0
func UpgradeMint(ctx context.Context, manifest *Manifest, provisioner WIFProvisioner, progress ProgressFunc) error
UpgradeMint verifies the token mint deployment matches the manifest configuration.
Types ¶
type BatchInstallConfig ¶ added in v0.31.0
type BatchInstallConfig struct {
Manifest *Manifest
DryRun bool
RepoFilter []string
MaxConcurrency int
SkipMintCheck bool
// Roles is the list of agent roles to install (e.g., "triage", "coder").
Roles []string
// UpstreamRef is the git ref (SHA) used to pin scaffold workflow refs.
UpstreamRef string
// UpstreamTag is the version tag corresponding to UpstreamRef.
UpstreamTag string
// Direct controls scaffold delivery: true pushes directly to the default
// branch; false creates a PR.
Direct bool
}
BatchInstallConfig holds all inputs for a multi-repo install operation.
type BatchInstallResult ¶ added in v0.31.0
type BatchInstallResult struct {
Installed []InstallResult
Skipped []InstallResult
Failed []InstallResult
}
BatchInstallResult holds the outcome of a multi-repo install operation.
func BatchInstall ¶ added in v0.31.0
func BatchInstall(ctx context.Context, cfg BatchInstallConfig, client forge.Client, provisionerFactory ProvisionerFactory, commitScaffold ScaffoldCommitFunc, progress ProgressFunc) (*BatchInstallResult, error)
BatchInstall provisions fullsend on multiple repos from a manifest.
It runs in three phases:
- Parallel discovery: check guard variables to partition repos into toInstall and alreadyInstalled.
- Sequential WIF: EnsureOrgInMint per unique org, then ProvisionWIF and RegisterPerRepoWIF per repo. These operations modify shared GCP state and are not concurrent-safe.
- Parallel scaffold: commit scaffold files and write variables/secrets for each repo where Phase 2 succeeded.
Errors on individual repos do not abort the batch.
type Change ¶ added in v0.32.0
type Change struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
Field string `json:"field"`
Type string `json:"type"` // "variable" or "secret"
Action string `json:"action"` // "create" or "update"
OldValue string `json:"old_value,omitempty"` // empty for secrets (not readable)
NewValue string `json:"new_value,omitempty"` // empty for secrets
}
Change describes a single field that needs to be updated to reconcile a repo's actual state with the manifest's desired state.
type DefaultsConfig ¶
type DefaultsConfig struct {
InferenceProject string `yaml:"inference_project"`
InferenceRegion string `yaml:"inference_region"`
FullsendRef string `yaml:"fullsend_ref"`
BaseHarness string `yaml:"base_harness"`
AllowedRemoteResources []string `yaml:"allowed_remote_resources"`
}
DefaultsConfig holds default field values applied to every repo unless overridden at the per-repo level.
type DiffResult ¶ added in v0.32.0
type DiffResult struct {
Changes []Change `json:"changes"`
Warnings []string `json:"warnings,omitempty"`
}
DiffResult holds the output of a diff operation.
func Diff ¶ added in v0.32.0
func Diff(ctx context.Context, manifest *Manifest, client forge.Client, maxConcurrency int, repoFilter []string) (*DiffResult, error)
Diff compares the manifest's desired state against the actual forge state and returns a list of changes needed to reconcile. Only examines repos that are already installed (guard variable is "true"); uninstalled repos are reported as warnings.
For secrets, Diff only reports missing secrets (action "create") because secret values cannot be read back for comparison.
type DiscoveredRepo ¶ added in v0.31.0
type DiscoveredRepo struct {
Owner string
Repo string
Source string // "per-repo", "per-org", or "new"
MintURL string
InferenceRegion string
FullsendRef string
}
DiscoveredRepo holds the result of discovering a single repo's fullsend installation status.
type Drift ¶ added in v0.31.0
type Drift struct {
Field string `json:"field"`
Expected string `json:"expected"`
Actual string `json:"actual"`
}
Drift describes a single field that differs between the manifest's desired state and the repo's actual state.
type InitConfig ¶ added in v0.31.0
type InitConfig struct {
Target string
Repos []string
All bool
MintProject string
MintRegion string
InferenceProject string
MaxConcurrency int
CLIVersion string
}
InitConfig holds configuration for the repos init command.
type InitResult ¶ added in v0.31.0
type InitResult struct {
Manifest *Manifest
PerRepoCount int
PerOrgCount int
NewCount int
TODOs []string
Errors []string
}
InitResult holds the output of Init.
func Init ¶ added in v0.31.0
func Init(ctx context.Context, cfg InitConfig, client forge.Client, selectRepos RepoSelectFunc, progress ProgressFunc) (*InitResult, error)
Init discovers existing fullsend installations and generates a repos.yaml manifest. It supports both greenfield onboarding and migration from existing per-repo or per-org installations.
type InstallConfig ¶
type InstallConfig struct {
Owner string
Repo string
// Roles is the list of agent roles to install (e.g., "triage", "code").
Roles []string
MintURL string
InferenceProject string
InferenceRegion string
// UpstreamRef is the git ref (SHA) used to pin scaffold workflow refs.
// Empty for dev builds (falls back to config.DefaultUpstreamRef).
UpstreamRef string
// UpstreamTag is the version tag corresponding to UpstreamRef (e.g., "v0.42.0").
UpstreamTag string
// Skip flags control which install steps are executed. Set by callers
// that handle specific steps externally (e.g., admin.go handles guard
// checks and mint setup before calling Install).
SkipAppSetup bool
SkipMintCheck bool
SkipWIF bool
SkipGuardCheck bool
// SkipScaffoldAndConfig skips scaffold file delivery and variable/secret
// writes. The caller handles these steps externally (e.g., vendor mode
// commits scaffold and vendor files together atomically).
SkipScaffoldAndConfig bool
// WIFProvider is a pre-provisioned WIF provider resource name. When set
// and SkipWIF is true, the install skips WIF provisioning and uses this
// value directly.
WIFProvider string
VendorBinary bool
// Direct controls scaffold delivery: true pushes directly to the default
// branch; false creates a PR.
Direct bool
}
InstallConfig is a pure data struct holding all inputs needed for a per-repo installation. CLI flags, environment variables, and interactive prompts are resolved by the caller before constructing this struct.
type InstallResult ¶
type InstallResult struct {
Owner string
Repo string
Success bool
Error error
AlreadyInstalled bool
// WIFProvider is the WIF provider resource name, either pre-existing
// (from InstallConfig) or newly provisioned.
WIFProvider string
}
InstallResult holds the outcome of a per-repo installation.
func Install ¶
func Install(ctx context.Context, cfg InstallConfig, client forge.Client, provisioner WIFProvisioner, commitScaffold ScaffoldCommitFunc, progress ProgressFunc) (*InstallResult, error)
Install performs a per-repo fullsend installation. It checks for an existing installation, optionally discovers mint infrastructure and provisions WIF, generates and commits scaffold files, and writes repository variables and secrets.
The commitScaffold callback handles scaffold file delivery. The CLI layer provides an implementation with retry and fallback semantics; tests provide a simple fake.
CLI concerns (prompts, spinners, token resolution, scope checks, dry-run) are handled by the caller. This function contains only the pure install logic.
type Manifest ¶
type Manifest struct {
Version int `yaml:"version"`
Mint MintConfig `yaml:"mint"`
Defaults DefaultsConfig `yaml:"defaults"`
Repos []RepoEntry `yaml:"repos"`
}
Manifest is the top-level structure of a repos.yaml file.
func LoadManifest ¶
LoadManifest reads and parses a repos.yaml manifest from a local file path or an HTTPS URL. Remote fetches enforce a 30-second timeout and a 1 MB response size limit.
func (*Manifest) ExpandGlobs ¶
ExpandGlobs resolves wildcard repo entries by listing org repos via the forge API (requires network access). Explicit entries always win over glob-matched entries. The returned list is deduplicated and sorted.
ListOrgRepos excludes private, archived, and forked repositories. Private repos must be listed as explicit entries in the manifest until the forge interface is extended (see implementation plan).
func (*Manifest) ResolveConfig ¶
func (m *Manifest) ResolveConfig(owner, repo string) (ResolvedConfig, bool)
ResolveConfig computes the fully merged configuration for the given owner/repo by looking up the entry in the manifest's repo list. The resolution order is:
- Per-repo override (from RepoEntry)
- Manifest defaults (from DefaultsConfig)
- Built-in defaults (empty strings)
An explicit null at any level stops the fallback chain, returning "". The second return value indicates whether the repo was found in the manifest's repo list. When false, the returned config uses only manifest defaults and built-in values.
For repos matched via glob expansion, use ResolveConfigForEntry instead — this method only finds exact matches in the manifest's repo list and will not match glob patterns.
func (*Manifest) ResolveConfigForEntry ¶
func (m *Manifest) ResolveConfigForEntry(owner, repo string, entry RepoEntry) ResolvedConfig
ResolveConfigForEntry computes the fully merged configuration for the given owner/repo using the provided RepoEntry. Use this with entries returned by ExpandGlobs, which carry per-glob overrides that ResolveConfig cannot find by exact match.
func (*Manifest) Validate ¶
Validate checks the manifest for structural correctness:
- version must be 1
- mint.url must be a valid HTTPS URL
- mint.project and mint.region must be non-empty
- each repo entry must have a valid owner/repo or owner/glob format
- glob characters are only allowed in the repo name, not the owner
- no duplicate repo entries (before glob expansion)
- glob patterns must be valid filepath.Match patterns
type ManifestAddResult ¶ added in v0.32.0
ManifestAddResult holds the outcome of adding repos to a manifest.
type ManifestEditConfig ¶ added in v0.32.0
ManifestEditConfig holds inputs for manifest add/remove operations.
type ManifestRemoveResult ¶ added in v0.32.0
ManifestRemoveResult holds the outcome of removing repos from a manifest.
type MintConfig ¶
type MintConfig struct {
URL string `yaml:"url"`
Project string `yaml:"project"`
Region string `yaml:"region"`
}
MintConfig holds the mint service connection parameters.
type MintDiscovery ¶
MintDiscovery holds the results of a mint infrastructure discovery call.
type NullableString ¶
NullableString distinguishes three YAML states: omitted (zero value), explicit null (Set=true, Null=true), and an explicit string value (Set=true, Null=false, Value holds the string). This three-state design lets per-repo overrides explicitly clear a default with "field: null" rather than inheriting it.
A fourth state — Set=true, Value="" (explicit empty string in YAML) — is treated as unset by resolveField and falls through to defaults. This matches the ADR spec: "Empty-string and zero-value overrides are treated as unset and fall through to defaults."
func (NullableString) IsZero ¶
func (n NullableString) IsZero() bool
IsZero reports whether n was never set, used by the YAML encoder to honor the omitempty tag.
func (NullableString) MarshalYAML ¶
func (n NullableString) MarshalYAML() (interface{}, error)
MarshalYAML serializes a NullableString back to YAML, preserving the null vs omitted distinction.
func (*NullableString) UnmarshalYAML ¶
func (n *NullableString) UnmarshalYAML(node *yaml.Node) error
UnmarshalYAML decodes a YAML scalar into a NullableString, treating the !!null tag as an explicit null.
type ProgressFunc ¶
type ProgressFunc func(repo, phase, message string)
ProgressFunc is a callback for reporting installation progress. The caller maps this to spinner output, structured logs, or whatever UI is appropriate.
Parameters:
- repo: the "owner/repo" being installed
- phase: a machine-readable phase name (e.g., "guard", "wif", "scaffold", "vars")
- message: a human-readable status message
type ProvisionerFactory ¶ added in v0.31.0
type ProvisionerFactory func(cfg ResolvedConfig) WIFProvisioner
ProvisionerFactory creates a WIFProvisioner scoped to a specific repo's infrastructure config (GCP project, region, org).
type RepoCandidate ¶ added in v0.31.0
type RepoCandidate struct {
Owner string
Repo string
Status string // "per-repo", "per-org", "new"
Ref string
}
RepoCandidate is presented to the interactive selection callback.
type RepoEntry ¶
type RepoEntry struct {
Repo string `yaml:"repo"`
InferenceProject NullableString `yaml:"inference_project,omitempty"`
InferenceRegion NullableString `yaml:"inference_region,omitempty"`
FullsendRef NullableString `yaml:"fullsend_ref,omitempty"`
BaseHarness NullableString `yaml:"base_harness,omitempty"`
}
RepoEntry represents a single repo or glob pattern in the manifest. It supports two YAML forms: a plain string ("acme/repo") or an object with optional per-repo overrides.
func (*RepoEntry) UnmarshalYAML ¶
UnmarshalYAML handles both string and mapping YAML forms. It manually walks the mapping node to correctly detect !!null values on NullableString fields, since yaml.v3's struct decoder skips calling UnmarshalYAML for null-tagged scalars.
type RepoSelectFunc ¶ added in v0.31.0
type RepoSelectFunc func(candidates []RepoCandidate) ([]string, error)
RepoSelectFunc is a callback the CLI layer provides to handle interactive repo selection. It receives candidates and returns the full names (owner/repo) the operator selected.
type RepoState ¶ added in v0.32.0
RepoState holds the installation state of a single repo as read from GitHub variables and workflow files.
type RepoStatus ¶ added in v0.31.0
type RepoStatus struct {
Owner string `json:"owner"`
Repo string `json:"repo"`
Installed bool `json:"installed"`
CurrentRef string `json:"current_ref,omitempty"`
ExpectedRef string `json:"expected_ref,omitempty"`
MintURL string `json:"mint_url,omitempty"`
ExpectedMintURL string `json:"expected_mint_url,omitempty"`
Region string `json:"region,omitempty"`
ExpectedRegion string `json:"expected_region,omitempty"`
Drifts []Drift `json:"drifts,omitempty"`
Error string `json:"error,omitempty"`
}
RepoStatus holds the status of a single repo as compared against the manifest's desired state.
type ResolvedConfig ¶
type ResolvedConfig struct {
Owner string
Repo string
MintURL string
MintProject string
MintRegion string
InferenceProject string
InferenceRegion string
FullsendRef string
BaseHarness string
AllowedRemoteResources []string
}
ResolvedConfig is the fully resolved configuration for a single repository after merging per-repo overrides, manifest defaults, and built-in defaults.
type ResolvedRepo ¶
ResolvedRepo pairs an owner/repo with the manifest entry that matched it (either an explicit entry or a glob-generated one).
type ScaffoldCommitFunc ¶
type ScaffoldCommitFunc func(ctx context.Context, owner, repo string, files []forge.TreeFile, direct bool) error
ScaffoldCommitFunc delivers scaffold files to a repository and returns any error encountered.
The CLI layer provides an implementation wrapping layers.CommitScaffoldFiles, which adds retry on non-fast-forward errors, branch-protection fallback to PR delivery, and fork-based PR support for non-owner users.
type StatusResult ¶ added in v0.31.0
type StatusResult struct {
Repos []RepoStatus `json:"repos"`
Summary StatusSummary `json:"summary"`
}
StatusResult holds the full output of a status check.
func Status ¶ added in v0.31.0
func Status(ctx context.Context, manifest *Manifest, client forge.Client, maxConcurrency int, repoFilter []string) (*StatusResult, error)
Status compares the manifest's desired state against the actual forge state for each repo. It returns a StatusResult with per-repo status and aggregate counts. API calls are parallelised up to maxConcurrency.
type StatusSummary ¶ added in v0.31.0
type StatusSummary struct {
Total int `json:"total"`
Installed int `json:"installed"`
NotInstalled int `json:"not_installed"`
Drifted int `json:"drifted"`
Errored int `json:"errored"`
}
StatusSummary provides aggregate counts across all repos. Counts are not mutually exclusive: a repo can be both Installed and Errored (e.g. guard variable set but workflow read fails), so Installed + NotInstalled + Errored may exceed Total.
type SyncResult ¶ added in v0.32.0
type SyncResult struct {
Applied []Change `json:"applied"`
Failed int `json:"failed,omitempty"`
Warnings []string `json:"warnings,omitempty"`
}
SyncResult holds the output of a sync operation.
func Sync ¶ added in v0.32.0
func Sync(ctx context.Context, manifest *Manifest, client forge.Client, maxConcurrency int, repoFilter []string, progress ProgressFunc) (*SyncResult, error)
Sync reconciles configuration drift for installed repos by applying variable and secret changes to match the manifest's desired state. Variables are only written when drift is detected; secrets are always written for convergence since their values cannot be read back.
Sync does NOT touch scaffold shim version (@ref) or harness files. Version changes are managed by `repos upgrade`.
type UninstallConfig ¶ added in v0.32.0
type UninstallConfig struct {
Manifest *Manifest
Repos []string
DryRun bool
SkipWIFCleanup bool
MaxConcurrency int
}
UninstallConfig holds all inputs for a multi-repo uninstall operation.
type UninstallResult ¶ added in v0.32.0
type UninstallResult struct {
Owner string
Repo string
Success bool
Error error
WorkflowDeleted bool
VarsDeleted int
SecretsDeleted int
WIFDeregistered bool
}
UninstallResult holds the outcome of uninstalling fullsend from a single repo.
func Uninstall ¶ added in v0.32.0
func Uninstall(ctx context.Context, cfg UninstallConfig, client forge.Client, provisionerFactory ProvisionerFactory, progress ProgressFunc) ([]UninstallResult, error)
Uninstall tears down fullsend from the specified repos.
It runs in two phases:
- Parallel per-repo cleanup (bounded by MaxConcurrency): delete workflow file, then delete variables and secrets. If workflow deletion fails, variables and secrets are left intact.
- Sequential WIF cleanup (only for Phase 1 successes): deregister from mint's PER_REPO_WIF_REPOS and delete WIF provider. Sequential because mint env var updates are read-modify-write operations.
Does NOT modify repos.yaml — use RemoveFromManifest for that.
type UpgradeConfig ¶ added in v0.32.0
type UpgradeConfig struct {
Manifest *Manifest
RefOverride string
RepoFilter []string
DryRun bool
Force bool
Direct bool
MaxConcurrency int
}
UpgradeConfig holds configuration for a batch upgrade operation.
type UpgradeResult ¶ added in v0.32.0
type UpgradeResult struct {
Owner string
Repo string
OldRef string
NewRef string
Upgraded bool
Skipped bool
SkipReason string
Error error
}
UpgradeResult holds the outcome of upgrading a single repo.
func Upgrade ¶ added in v0.32.0
func Upgrade(ctx context.Context, cfg UpgradeConfig, client forge.Client, commitFn ScaffoldCommitFunc, progress ProgressFunc) ([]UpgradeResult, error)
Upgrade upgrades the scaffold shim ref across repos in the manifest. It reads each repo's current workflow file, determines whether an upgrade is needed, and commits the updated workflow with the new ref.
type WIFProvisioner ¶
type WIFProvisioner interface {
// DiscoverMint fetches mint infrastructure info (URL, role-to-app-ID
// mappings, per-repo WIF repos). Returns an error wrapping
// ErrMintNotFound if the mint function does not exist.
DiscoverMint(ctx context.Context) (*MintDiscovery, error)
// ProvisionWIF creates WIF infrastructure (service account, pool,
// provider, principal binding) and returns the full WIF provider
// resource name.
ProvisionWIF(ctx context.Context) (string, error)
// RegisterPerRepoWIF adds a repo to the mint's PER_REPO_WIF_REPOS
// env var so the mint routes OIDC tokens for that repo to a dedicated
// WIF provider.
RegisterPerRepoWIF(ctx context.Context, repo string) error
// EnsureOrgInMint validates that a mint function exists at expectedURL
// and that the given org is registered in ALLOWED_ORGS.
EnsureOrgInMint(ctx context.Context, expectedURL string, org string) error
// DeletePerRepoWIF removes a repo from per-repo WIF registration.
DeletePerRepoWIF(ctx context.Context, repo string) error
// DeleteWIFProvider deletes the WIF provider for a repo from the
// GCP project (used during uninstall to clean up IAM resources).
DeleteWIFProvider(ctx context.Context, repo string) error
}
WIFProvisioner abstracts Workload Identity Federation and mint discovery operations, decoupling the install logic from the concrete GCF provisioner.
All methods return wrapped errors suitable for errors.Is checks. DiscoverMint wraps ErrMintNotFound when the mint function does not exist. Other methods return provider-specific errors (e.g., IAM permission denied).