layers

package
v0.32.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 20, 2026 License: Apache-2.0 Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// VendoredBinaryPath is the upload path inside the per-org .fullsend config repo.
	VendoredBinaryPath = "bin/fullsend"
	// VendoredBinaryPathPerRepo is the upload path inside a per-repo target repo.
	VendoredBinaryPathPerRepo = ".fullsend/bin/fullsend"
)

Variables

This section is empty.

Functions

func CommitFilesViaPR added in v0.24.0

func CommitFilesViaPR(ctx context.Context, client forge.Client, printer *ui.Printer,
	owner, repo, defaultBranch, branch, commitMsg, prTitle, prBody string,
	files []forge.TreeFile) (bool, error)

CommitFilesViaPR delivers files via a pull request on the given branch. Uses a fixed branch name so re-runs update the same PR.

func CommitScaffoldFiles added in v0.18.0

func CommitScaffoldFiles(ctx context.Context, client forge.Client, printer *ui.Printer,
	owner, repo, defaultBranch, commitMsg, prTitle, prBody string,
	files []forge.TreeFile, direct bool, in io.Reader) (bool, error)

CommitScaffoldFiles delivers scaffold files to a repository. When direct is false (the default), files are committed to a feature branch and delivered via PR. When direct is true, files are pushed directly to the default branch, falling back to a PR if branch protection blocks the push.

The in parameter enables interactive prompts (e.g., fork-vs-upstream choice). Pass os.Stdin for interactive CLI callers; pass nil for non-interactive callers (sync-scaffold), which default to forking without prompting.

The returned bool is true when files were committed directly to the default branch (false for PR-based delivery, idempotent no-ops, or unchanged content).

func DeleteVendoredPaths added in v0.19.0

func DeleteVendoredPaths(ctx context.Context, client forge.Client, owner, repo string, paths []string) (int, error)

DeleteVendoredPaths removes stale vendored paths in a single commit when possible.

func RemoveStaleBinaryCommitMessage added in v0.17.0

func RemoveStaleBinaryCommitMessage(destPath string) string

RemoveStaleBinaryCommitMessage returns title + body for stale binary deletion.

func RemoveStaleContentCommitMessage added in v0.19.0

func RemoveStaleContentCommitMessage(path string) string

RemoveStaleContentCommitMessage returns title + body for stale content deletion.

func RemoveStaleVendoredAssets added in v0.19.0

func RemoveStaleVendoredAssets(ctx context.Context, client forge.Client, printer *ui.Printer, owner, repo, workflowPrefix, binaryPath string) error

RemoveStaleVendoredAssets deletes vendored assets when --vendor is not set. It skips work when neither the vendor manifest nor vendored binary exists.

func RemoveStaleVendoredAssetsCommitMessage added in v0.19.0

func RemoveStaleVendoredAssetsCommitMessage(paths []string) string

RemoveStaleVendoredAssetsCommitMessage returns title + body for batch stale deletion.

func VendorBinary added in v0.0.4

func VendorBinary(ctx context.Context, client forge.Client, owner, repo, destPath, binaryPath, commitMsg string) error

VendorBinary uploads a pre-built fullsend binary to the given destPath. CI workflows detect this file and use it instead of downloading from GitHub releases, enabling development iteration without cutting a release.

func VendorCommitMessage added in v0.17.0

func VendorCommitMessage(source binary.Source, version, destPath string, sizeBytes int64) string

VendorCommitMessage returns a GitHub commit message (title + body) for upload.

func VendorContentCommitMessage added in v0.19.0

func VendorContentCommitMessage(version, pathPrefix string, fileCount int) string

VendorContentCommitMessage returns a commit message for vendored content upload.

Types

type AgentCredentials

type AgentCredentials struct {
	Role     string
	Name     string
	Slug     string
	PEM      string
	ClientID string
	AppID    int
}

AgentCredentials holds agent identity (role, name, slug) and app credentials for layer operations.

type ConfigRepoLayer

type ConfigRepoLayer struct {
	// contains filtered or unexported fields
}

ConfigRepoLayer manages the .fullsend configuration repository. This is the foundational layer — it must be installed before any other layers that depend on the config repo existing.

func NewConfigRepoLayer

func NewConfigRepoLayer(org string, client forge.Client, cfg *config.OrgConfig, printer *ui.Printer, hasPrivate bool) *ConfigRepoLayer

NewConfigRepoLayer creates a new ConfigRepoLayer. Set hasPrivate to true if the org's GitHub plan (team/enterprise) supports workflow_call from private repos. GitHub free tier cannot use workflow_call with private repos, so the config repo must be public on free orgs.

func (*ConfigRepoLayer) Analyze

func (l *ConfigRepoLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze checks whether the .fullsend repo and config.yaml exist and are valid.

func (*ConfigRepoLayer) Install

func (l *ConfigRepoLayer) Install(ctx context.Context) error

Install creates the .fullsend config repo (if it doesn't exist) and writes config.yaml into it.

Timing note: after CreateRepo with auto_init, the default branch may not be fully materialized yet. The Contents API call to write config.yaml can get transient 404s. The GitHub client's retry-with-backoff in do() handles this, but callers should be aware that the first file write to a newly created repo may take several seconds to succeed.

func (*ConfigRepoLayer) Name

func (l *ConfigRepoLayer) Name() string

func (*ConfigRepoLayer) RequiredScopes

func (l *ConfigRepoLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation.

func (*ConfigRepoLayer) Uninstall

func (l *ConfigRepoLayer) Uninstall(ctx context.Context) error

Uninstall deletes the .fullsend config repo. Idempotent: if the repo is already gone, this is a no-op.

type DispatchTokenLayer

type DispatchTokenLayer struct {
	// contains filtered or unexported fields
}

DispatchTokenLayer manages the org-level dispatch mechanism that enrolled repos use to trigger agent workflows on the .fullsend repo via workflow_call.

The mint URL is stored as an org-level variable (FULLSEND_MINT_URL). A repo-level copy is also set on the config repo (.fullsend) because GitHub org variables with "selected" visibility are not accessible to repos whose names start with a dot.

func NewOIDCDispatchLayer added in v0.8.0

func NewOIDCDispatchLayer(org string, client forge.Client, enrolledRepoIDs []int64, dispatcher dispatch.Dispatcher, printer *ui.Printer) *DispatchTokenLayer

NewOIDCDispatchLayer creates a new DispatchTokenLayer in OIDC mode. It uses a dispatch.Dispatcher to provision cloud infrastructure and store the mint URL as an org-level variable.

func (*DispatchTokenLayer) Analyze

func (l *DispatchTokenLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze checks whether the dispatch mechanism is configured.

func (*DispatchTokenLayer) Install

func (l *DispatchTokenLayer) Install(ctx context.Context) error

Install creates or updates the dispatch mechanism.

func (*DispatchTokenLayer) Name

func (l *DispatchTokenLayer) Name() string

Name returns the layer name.

func (*DispatchTokenLayer) RequiredScopes

func (l *DispatchTokenLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation.

func (*DispatchTokenLayer) Uninstall

func (l *DispatchTokenLayer) Uninstall(ctx context.Context) error

Uninstall removes the dispatch mechanism.

type EnrollmentLayer

type EnrollmentLayer struct {
	// contains filtered or unexported fields
}

EnrollmentLayer monitors workflow-driven enrollment of target repos. Enrollment is performed by the repo-maintenance workflow in .fullsend, which creates PRs with shim workflows in response to config.yaml changes. This layer dispatches that workflow and reports the results.

func NewEnrollmentLayer

func NewEnrollmentLayer(org string, client forge.Client, enabledRepos, disabledRepos []string, printer *ui.Printer) *EnrollmentLayer

NewEnrollmentLayer creates a new EnrollmentLayer.

func (*EnrollmentLayer) Analyze

func (l *EnrollmentLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze checks which enabled repos have the shim workflow installed and which disabled repos still have it.

func (*EnrollmentLayer) Install

func (l *EnrollmentLayer) Install(ctx context.Context) error

Install dispatches the repo-maintenance workflow to handle enrollment, waits for it to complete, and reports any enrollment PRs created.

func (*EnrollmentLayer) Name

func (l *EnrollmentLayer) Name() string

func (*EnrollmentLayer) RequiredScopes

func (l *EnrollmentLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation.

func (*EnrollmentLayer) Uninstall

func (l *EnrollmentLayer) Uninstall(ctx context.Context) error

Uninstall updates config.yaml to mark all repos as disabled and dispatches the repo-maintenance workflow to create unenrollment PRs that remove shim workflows from enrolled repos. This runs before ConfigRepoLayer deletes the .fullsend repo (layers uninstall in reverse order), so the workflow is still available to dispatch.

Errors during unenrollment are non-fatal — the user is informed but the uninstall continues. Repos that cannot be unenrolled automatically will need manual removal of .github/workflows/fullsend.yaml.

func (*EnrollmentLayer) WithScaffoldPending added in v0.20.0

func (l *EnrollmentLayer) WithScaffoldPending() *EnrollmentLayer

WithScaffoldPending marks that scaffold files were delivered via PR and have not yet been merged to the default branch. Enrollment is deferred until the scaffold PR is merged and repo-maintenance triggers on push.

type HarnessWrappersLayer added in v0.18.0

type HarnessWrappersLayer struct {
	// contains filtered or unexported fields
}

HarnessWrappersLayer generates thin harness wrapper files in the .fullsend config repo. Each wrapper references an upstream scaffold harness via a base: URL and sets role/slug locally, enabling orgs to customize by adding override fields. Config-driven agents (registered via the agents list in config.yaml) are skipped — they are resolved at runtime by fullsend run.

func NewHarnessWrappersLayer added in v0.18.0

func NewHarnessWrappersLayer(org string, client forge.Client, printer *ui.Printer, agents []AgentCredentials, commitSHA string, configAgentNames []string) *HarnessWrappersLayer

NewHarnessWrappersLayer creates a new HarnessWrappersLayer. configAgentNames lists agent names registered in config; wrappers are not generated for these because fullsend run resolves them at runtime from config sources.

func (*HarnessWrappersLayer) Analyze added in v0.18.0

func (*HarnessWrappersLayer) Install added in v0.18.0

func (l *HarnessWrappersLayer) Install(ctx context.Context) error

func (*HarnessWrappersLayer) Name added in v0.18.0

func (l *HarnessWrappersLayer) Name() string

func (*HarnessWrappersLayer) RequiredScopes added in v0.18.0

func (l *HarnessWrappersLayer) RequiredScopes(op Operation) []string

func (*HarnessWrappersLayer) Uninstall added in v0.18.0

func (l *HarnessWrappersLayer) Uninstall(_ context.Context) error

type InferenceLayer added in v0.0.3

type InferenceLayer struct {
	// contains filtered or unexported fields
}

InferenceLayer manages inference provider credentials in the .fullsend repo.

func NewInferenceLayer added in v0.0.3

func NewInferenceLayer(org string, client forge.Client, provider inference.Provider, printer *ui.Printer) *InferenceLayer

NewInferenceLayer creates a new InferenceLayer.

func (*InferenceLayer) Analyze added in v0.0.3

func (l *InferenceLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze checks whether inference credentials exist in the .fullsend repo.

func (*InferenceLayer) Install added in v0.0.3

func (l *InferenceLayer) Install(ctx context.Context) error

Install provisions inference credentials and stores them as repo secrets. Secrets are written unconditionally because the GitHub Secrets API does not expose values, so we cannot detect stale entries. The API PUT is an upsert, making repeated writes safe.

func (*InferenceLayer) Name added in v0.0.3

func (l *InferenceLayer) Name() string

Name returns the layer name.

func (*InferenceLayer) RequiredScopes added in v0.0.3

func (l *InferenceLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation.

func (*InferenceLayer) Uninstall added in v0.0.3

func (l *InferenceLayer) Uninstall(_ context.Context) error

Uninstall is a no-op. Secrets are removed when the .fullsend repo is deleted.

type Layer

type Layer interface {
	// Name returns a human-readable name for this layer.
	Name() string

	// RequiredScopes returns the OAuth scopes this layer needs for the
	// given operation. Scopes are GitHub-flavored strings like "repo",
	// "delete_repo", "workflow". Used by Preflight to fail early when
	// the token is missing required scopes.
	RequiredScopes(op Operation) []string

	// Install creates or configures this layer's concern.
	Install(ctx context.Context) error

	// Uninstall tears down this layer's concern.
	Uninstall(ctx context.Context) error

	// Analyze assesses the current state and reports what would change.
	Analyze(ctx context.Context) (*LayerReport, error)
}

Layer is the interface each installation concern implements. Layers are processed in order for install, reverse order for uninstall.

func NewBothModesDispatchLayer added in v0.8.0

func NewBothModesDispatchLayer(org string, client forge.Client, dispatcher dispatch.Dispatcher, printer *ui.Printer) Layer

NewBothModesDispatchLayer creates a layer that cleans both dispatch modes. The PAT-mode layer is constructed inline for stale secret cleanup only.

type LayerReport

type LayerReport struct {
	Name         string
	Status       LayerStatus
	Details      []string // human-readable detail lines
	WouldInstall []string // what install would create
	WouldFix     []string // what install would fix (for degraded state)
}

LayerReport is the result of analyzing a single layer.

type LayerStatus

type LayerStatus int

LayerStatus represents the current state of a layer.

const (
	StatusNotInstalled LayerStatus = iota
	StatusInstalled
	StatusDegraded // partially installed or misconfigured
	StatusUnknown  // cannot determine
)

func (LayerStatus) String

func (s LayerStatus) String() string

String returns a human-readable description of the status.

type Operation

type Operation int

Operation identifies which action is being performed on a layer.

const (
	OpInstall Operation = iota
	OpUninstall
	OpAnalyze
)

func (Operation) String

func (o Operation) String() string

type PreflightResult

type PreflightResult struct {
	// Required is the set of scopes the operation needs.
	Required []string
	// Granted is the set of scopes the token actually has.
	Granted []string
	// Missing is the set of scopes needed but not granted.
	Missing []string
	// Skipped is true when scope introspection was unavailable
	// (e.g., fine-grained tokens that don't report scopes).
	Skipped bool
	// SkippedReason indicates why the preflight was skipped.
	SkippedReason SkipReason
}

PreflightResult describes what a preflight check found.

func (*PreflightResult) Error

func (r *PreflightResult) Error() string

Error returns a human-readable error describing missing scopes and how to fix the problem.

func (*PreflightResult) OK

func (r *PreflightResult) OK() bool

OK returns true if no scopes are missing.

func (*PreflightResult) SkipGuidance added in v0.30.0

func (r *PreflightResult) SkipGuidance() string

SkipGuidance returns a human-readable message listing the scopes that could not be verified and their fine-grained equivalents.

type SecretsLayer

type SecretsLayer struct {
	// contains filtered or unexported fields
}

SecretsLayer manages agent app secrets and variables in the .fullsend repo. When oidcMode is true, Install is a no-op because agent workflows use the OIDC token mint instead of create-github-app-token, so repo secrets (FULLSEND_{ROLE}_APP_PRIVATE_KEY) and variables (FULLSEND_{ROLE}_CLIENT_ID) are not needed.

func NewSecretsLayer

func NewSecretsLayer(org string, client forge.Client, agents []AgentCredentials, printer *ui.Printer) *SecretsLayer

NewSecretsLayer creates a new SecretsLayer.

func (*SecretsLayer) Analyze

func (s *SecretsLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze checks whether all expected agent secrets and variables exist in the .fullsend repo. In OIDC mode, repo secrets are not required — any present are flagged as stale.

func (*SecretsLayer) Install

func (s *SecretsLayer) Install(ctx context.Context) error

Install stores agent app private keys as repo secrets and client IDs as repo variables in the .fullsend config repo. In OIDC mode this is a no-op because PEMs are stored in GCP Secret Manager and workflows use the mint.

func (*SecretsLayer) Name

func (s *SecretsLayer) Name() string

Name returns the layer name.

func (*SecretsLayer) RequiredScopes

func (s *SecretsLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation. In OIDC mode, Install needs no scopes (it's a no-op) but Analyze still needs "repo" to check for stale secrets via RepoSecretExists.

func (*SecretsLayer) Uninstall

func (s *SecretsLayer) Uninstall(_ context.Context) error

Uninstall is a no-op. Secrets are removed when the .fullsend repo is deleted.

func (*SecretsLayer) WithOIDCMode added in v0.8.0

func (s *SecretsLayer) WithOIDCMode() *SecretsLayer

WithOIDCMode enables OIDC mode and returns the receiver for chaining. In OIDC mode, Install is a no-op because PEMs are stored in GCP Secret Manager and workflows use the OIDC token mint.

type SkipReason added in v0.30.0

type SkipReason string

SkipReason describes why a preflight check was skipped.

const (
	// SkipNone means the preflight was not skipped.
	SkipNone SkipReason = ""
	// SkipInstallationToken means the token is a GitHub App installation token.
	SkipInstallationToken SkipReason = "installation"
	// SkipFineGrained means the token is a fine-grained PAT whose
	// permissions cannot be introspected.
	SkipFineGrained SkipReason = "fine-grained"
)

type Stack

type Stack struct {
	// contains filtered or unexported fields
}

Stack is an ordered collection of layers.

func NewStack

func NewStack(layers ...Layer) *Stack

NewStack creates a new Stack with the given layers in order.

func (*Stack) AnalyzeAll

func (s *Stack) AnalyzeAll(ctx context.Context) ([]*LayerReport, error)

AnalyzeAll runs Analyze on each layer and returns reports.

func (*Stack) CollectRequiredScopes

func (s *Stack) CollectRequiredScopes(op Operation) []string

CollectRequiredScopes returns the deduplicated set of scopes needed by all layers for the given operation.

func (*Stack) InstallAll

func (s *Stack) InstallAll(ctx context.Context) error

InstallAll runs Install on each layer in order. Stops on first error, returning the error and the name of the failed layer.

func (*Stack) Layers

func (s *Stack) Layers() []Layer

Layers returns the layers in order.

func (*Stack) Preflight

func (s *Stack) Preflight(ctx context.Context, op Operation, client forge.Client) (*PreflightResult, error)

Preflight checks that the forge client's token has all the scopes required by the stack's layers for the given operation. It returns a PreflightResult describing what was found.

If the token is a GitHub App installation token, or scope introspection is unavailable (e.g., fine-grained PATs), Preflight returns a result with OK() == true and Skipped set. OAuth scope preflight does not apply to installation tokens; for tokens we cannot introspect we let the operation proceed and fail at the point of use if permissions are actually missing.

func (*Stack) UninstallAll

func (s *Stack) UninstallAll(ctx context.Context) []error

UninstallAll runs Uninstall on each layer in reverse order. Collects all errors rather than stopping on first.

type VendorBinaryLayer added in v0.1.0

type VendorBinaryLayer struct {
	// contains filtered or unexported fields
}

VendorBinaryLayer manages vendored binary and content assets. The type name retains "Binary" from when the layer only uploaded the CLI binary; it now vendors the full stack (workflows, actions, agent content).

When enabled (--vendor), it calls VendorFunc to upload binary and content. When disabled, it removes stale vendored assets from prior installs.

func NewVendorBinaryLayer added in v0.1.0

func NewVendorBinaryLayer(org, repo string, client forge.Client, printer *ui.Printer, enabled bool, vendorFn VendorFunc) *VendorBinaryLayer

NewVendorBinaryLayer creates a new VendorBinaryLayer.

func (*VendorBinaryLayer) Analyze added in v0.1.0

func (l *VendorBinaryLayer) Analyze(ctx context.Context) (*LayerReport, error)

Analyze reports vendored asset presence, manifest alignment, and optional source-tree alignment (via SetAnalyzeOptions).

func (*VendorBinaryLayer) Install added in v0.1.0

func (l *VendorBinaryLayer) Install(ctx context.Context) error

Install either vendors assets (when enabled) or removes stale ones.

func (*VendorBinaryLayer) Name added in v0.1.0

func (l *VendorBinaryLayer) Name() string

func (*VendorBinaryLayer) RequiredScopes added in v0.1.0

func (l *VendorBinaryLayer) RequiredScopes(op Operation) []string

RequiredScopes returns the scopes needed for the given operation.

func (*VendorBinaryLayer) SetAnalyzeOptions added in v0.19.0

func (l *VendorBinaryLayer) SetAnalyzeOptions(fullsendSource, cliVersion string)

SetAnalyzeOptions configures optional source-tree alignment during Analyze.

func (*VendorBinaryLayer) SetCombinedWithScaffold added in v0.19.0

func (l *VendorBinaryLayer) SetCombinedWithScaffold(combined bool)

SetCombinedWithScaffold marks vendored assets as already committed by WorkflowsLayer.

func (*VendorBinaryLayer) Uninstall added in v0.1.0

func (l *VendorBinaryLayer) Uninstall(_ context.Context) error

Uninstall is a no-op. Vendored assets are removed when the config repo is deleted by ConfigRepoLayer, or when install runs without --vendor.

type VendorCollectFunc added in v0.19.0

type VendorCollectFunc func(ctx context.Context, printer *ui.Printer, owner, repo string) ([]forge.TreeFile, int, error)

VendorCollectFunc gathers vendored tree files without committing. Used to combine scaffold and vendor assets in a single CommitFiles call.

type VendorFunc added in v0.1.0

type VendorFunc func(ctx context.Context, client forge.Client, printer *ui.Printer, owner, repo string) error

VendorFunc uploads vendored binary and content when --vendor is set.

type WorkflowsLayer

type WorkflowsLayer struct {
	// contains filtered or unexported fields
}

WorkflowsLayer manages workflow files and CODEOWNERS in the .fullsend config repo.

func NewWorkflowsLayer

func NewWorkflowsLayer(org string, client forge.Client, printer *ui.Printer, user, version string, vendored bool) *WorkflowsLayer

NewWorkflowsLayer creates a new WorkflowsLayer.

func (*WorkflowsLayer) Analyze

func (l *WorkflowsLayer) Analyze(ctx context.Context) (*LayerReport, error)

func (*WorkflowsLayer) Install

func (l *WorkflowsLayer) Install(ctx context.Context) error

func (*WorkflowsLayer) Name

func (l *WorkflowsLayer) Name() string

func (*WorkflowsLayer) RequiredScopes

func (l *WorkflowsLayer) RequiredScopes(op Operation) []string

func (*WorkflowsLayer) Uninstall

func (l *WorkflowsLayer) Uninstall(_ context.Context) error

func (*WorkflowsLayer) WithDirect added in v0.19.0

func (l *WorkflowsLayer) WithDirect(direct bool) *WorkflowsLayer

WithDirect configures direct-commit mode (push to default branch, fall back to PR on branch protection). The default is PR-based delivery.

func (*WorkflowsLayer) WithSignOff added in v0.19.0

func (l *WorkflowsLayer) WithSignOff(name, email string) *WorkflowsLayer

WithSignOff configures a Signed-off-by trailer to append to commit messages. This is used for human-driven CLI operations where DCO sign-off is required. Pass an empty string to disable.

func (*WorkflowsLayer) WithUpstreamRef added in v0.21.0

func (l *WorkflowsLayer) WithUpstreamRef(ref, tag string) *WorkflowsLayer

WithUpstreamRef configures SHA pinning for scaffolded workflow refs.

func (*WorkflowsLayer) WithVendorCollect added in v0.19.0

func (l *WorkflowsLayer) WithVendorCollect(fn VendorCollectFunc) *WorkflowsLayer

WithVendorCollect configures combined scaffold+vendor commits for --vendor installs.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL