Documentation
¶
Overview ¶
Package plugin provides the compile-time plugin system for TerraCi.
Package layout ¶
The plugin system is organized into a small set of public packages:
- pkg/plugin — core interfaces, BasePlugin[C], AppContext, EnablePolicy, RuntimeProvider
- pkg/plugin/cliout — public command output helpers (Format, ParseFormat, WriteJSON)
- pkg/plugin/registry — factory catalog and per-command Registry capability resolution
- pkg/plugin/initwiz — init wizard types (StateMap, InitContributor, InitGroupSpec)
Plugin-author contract tests live in pkg/plugin/plugintest. Helpers shared between built-in CI provider plugins (gitlab, github, future Bitbucket/Jenkins/Azure DevOps) live in plugins/internal/ciplugin. Shared report rendering for plugin-owned ci.Report payloads lives in plugins/internal/reportrender. The plugins/internal packages are not part of the public API.
Plugin file convention ¶
Each command-oriented plugin with a typed runtime boundary (cost, policy, summary, tfupdate) keeps one file per capability so the file list reads as a capability index:
- plugin.go — registration shell + typed BasePlugin[C] config
- lifecycle.go — cheap Preflight checks only (no network, no FS scan)
- commands.go — CommandProvider with thin cobra/request parsing
- runtime.go — lazy immutable RuntimeProvider implementation
- usecases.go — typed Request/Result orchestration over runtime
- pipeline.go — PipelineContributor (pipeline DAG jobs; return builder errors)
- init_wizard.go — initwiz.InitContributor (TUI form fields)
- output.go — CLI rendering helpers
- report.go — render-ready CI report assembly via ci.NewRenderedReport
Smaller plugins (git, diskblob, inmemcache, localexec) only implement the capabilities they need — there is no minimum surface.
Lifecycle ¶
The framework drives every plugin through four lifecycle checkpoints per command run:
┌─────────────┐
│ Register │ init() → registry.RegisterFactory(factory)
│ │ Validator.Validate() runs here — misconfigured
│ │ plugins panic at startup, not at first use.
└──────┬──────┘
│
┌──────▼──────┐
│ Configure │ ConfigLoader.DecodeAndSet — extensions.<key>
│ │ YAML node decoded into BasePlugin[C]'s private config copy.
└──────┬──────┘
│
┌──────▼──────┐
│ Preflight │ Preflightable.Preflight(ctx, appCtx)
│ │ Cheap validation only. No network, no heavy state.
│ │ Skipped only through typed runflow.CommandPolicy.
└──────┬──────┘
│
┌──────▼──────┐
│ Execute │ RunE in command — RuntimeProvider builds heavy
│ │ state lazily; use-cases consume the typed runtime.
└─────────────┘
AppContext is constructed once per command run by the CLI runflow and attached to cmd.Context() so plugin RunE callbacks can retrieve it through CommandPlugin[T]. It is immutable — plugins receive a snapshot of Config / WorkDir / ServiceDir / narrow resolver accessors / pipeline contributions that do not change for the duration of the command.
Command boundary ¶
Command setup is framework-owned: cobra flags feed the CLI runflow, which loads config, decodes plugin config, runs preflight, collects contributions, and binds AppContext. Plugin command handlers should stay thin: resolve the command-scoped plugin with CommandPlugin[T], call RequireEnabled for ConfigLoader-backed plugins, parse cobra flags into a typed request, then hand the request to the plugin use-case. CommandPlugin and RequireEnabled return typed errors (CommandBindingError and DisabledPluginError) so tests can use errors.As. The canonical plugin command flow is:
cobra flags -> typed Request -> immutable Runtime -> use-case Result
-> artifact persistence -> output renderer
RuntimeProvider implementations should build immutable dependencies and normalized config only. Command-specific overrides belong in the request, so repeated command invocations cannot leak mutable runtime state.
Pipeline contribution boundary ¶
PipelineContributor implementations return (*pipeline.Contribution, error). Build jobs with pipeline.NewPluginCommandJob or pipeline.NewContributedJob, wrap them with pipeline.NewContribution, and return any builder error. A nil contribution with nil error is invalid; optional jobs belong behind PipelineContributionGate so the framework can distinguish "not enabled for this run" from "broken contribution".
Pipeline IR boundary ¶
Framework code plans projects through workflow.PlanProject and converts that result into a provider-agnostic immutable IR with pipeline.BuildProjectIR. CI providers and local execution are IR consumers only: they receive *IR values and read them through getters such as IR.Jobs, Job.Operation, and Operation.Terraform. Providers that need barrier groups use pipeline.Schedule, whose JobGroup values expose read-only Name, Jobs, and JobCount accessors. External plugin authors should not construct IR, Job, Operation, or TerraformOperation literals or depend on module job naming. Tests and advanced in-process tooling can use pkg/pipeline/pipelinetest for validated synthetic fixtures.
CI provider output boundary ¶
CI provider generators should treat pipeline.IR as the only provider input, then build provider-local immutable documents through their own builders. The canonical flow is:
workflow.PlanProject -> pipeline.BuildProjectIR -> provider document builder
-> immutable provider document -> ToYAML
ToYAML is the only place provider output should become raw YAML/maps. Tests should use semantic read helpers on provider documents, such as Job(name), JobNames, HasNeed, Steps, Needs, Env, and Variables, instead of indexing mutable job maps. Provider documents should not expose one-shot constructors or map-shaped Jobs accessors.
Init wizard boundary ¶
InitContributor implementations return typed config through initwiz.NewInitContribution. The canonical flow is:
registry -> initflow.New -> DefaultState/ApplyOverrides
-> StateMap -> typed config struct -> initwiz.NewInitContribution
-> config.ExtensionValue -> initflow.BuildConfig
Returning nil, nil is the only normal way to skip an optional init contribution. Do not build extension config with loose maps or construct InitContribution directly; config.NewExtensionValue owns YAML node encoding, key validation, and defensive copies. The terraci command package owns only cobra flags, TUI rendering, preview rendering, and file writes.
SDK contract tests ¶
External plugin authors should copy the contract-style tests from pkg/plugin/plugintest and pkg/ci/citest instead of writing ad-hoc tests for SDK behavior. The canonical helpers are:
- plugintest.AssertBaseConfigPlugin[C] — verifies Clone() C and BasePlugin defensive copies.
- plugintest.AssertCommandBinding[T] — verifies CommandPlugin[T] command lookup and stable CommandBindingError reasons.
- plugintest.AssertRequireEnabled — verifies DisabledPluginError behavior.
- plugintest.AssertRuntimeProvider[T] — verifies lazy RuntimeProvider construction and RuntimeAs[T].
- plugintest.AssertPipelineContributor — verifies deterministic generic contribution shape.
- plugintest.AssertPreflightable, AssertInitContributor, AssertVersionProvider, AssertKVCacheProvider, AssertBlobStoreProvider, AssertChangeDetector, and AssertCIProvider — verify the remaining capability contracts through focused, closure-based test fixtures.
- citest.AssertRenderedReportContract — verifies ci.NewRenderedReport output validates, decodes through ci.DecodeRenderSection, and renders.
- citest.AssertPublishArtifactsContract — verifies ci.PublishArtifacts replacement semantics with a recording ArtifactWriter.
Thread-safety contract ¶
AppContext fields are written exactly once at construction, so concurrent reads from any goroutine are safe without synchronization. Plugins should:
- Read project config through the immutable config.Snapshot returned by ctx.Config(). Snapshot accessors return defensive copies; production code should not call MutableCopy except in explicit compatibility adapters that need an isolated mutable configuration.
- Treat ctx.CIResolver(), ctx.ChangeDetectorResolver(), ctx.KVCacheResolver(), and ctx.BlobStoreResolver() as never-nil. They return NoopResolver{} behavior when no real resolver is bound and are safe to call from any goroutine.
- Implement Clone() C on plugin config types embedded in BasePlugin[C]. BasePlugin.Config(), NewConfig(), DecodeAndSet(), and SetTypedConfig() all use defensive copies; mutating Config() output never changes plugin state.
Plugin factories (the function passed to registry.RegisterFactory) MUST be pure: the catalog calls them once at startup for the prototype and once per command for the per-run plugin instance.
Capability discovery ¶
AppContext exposes narrow typed capability resolvers only. Plugins should call the resolver accessor for the capability they need instead of depending on the aggregate Resolver or looking up concrete plugin names. Framework code owns raw plugin enumeration inside pkg/plugin/registry and the CLI runflow / schemaflow / versionflow packages.
Cross-plugin communication ¶
Plugins must never import each other directly. The contract surfaces are:
- capability interfaces in pkg/plugin (CI provider, change detection, …) with plugin-agnostic domain contracts, such as workflow.ChangeDetector, owned by their core package.
- shared types in pkg/ci (Report, ReportSection, PlanResult, …)
- the ci.ReportStore on AppContext, which owns both file-backed artifacts ({producer}-report.json) and in-process report exchange
summary is the canonical consumer of report artifacts; cost/policy/ tfupdate are the canonical producers. Plan-aware producers should carry the scanned ci.PlanResultCollection into ci.NewArtifactRun, convert domain results into typed ci.RenderBlock/ci.RenderValue values, build reports with ci.NewRenderedReport, and persist raw results plus the report through ci.PublishArtifacts. That helper always preserves raw results and removes stale reports when report construction fails or intentionally returns nil. Non-plan producers may create an ArtifactRun without PlanResults; that is explicit degraded mode. Consumers should load through ci.ReportReader/ReportStore and call ci.SelectCurrentReports before rendering. ReportSection is a value object: external plugins should not construct section JSON, RenderBlock, RenderTable, or RenderValue payloads manually. Use constructors such as ci.NewTableBlock, ci.RenderStatus, ci.RenderMoney, and ci.RenderModulePath so ci.NewRenderedReport can publish the current versioned rendered payload schema. Consumers should use ci.DecodeRenderSection or plugins/internal/reportrender instead of importing producer-specific domain structs. Markdown/CLI presentation remains centralized in plugins/internal/reportrender. The contract test suite for blob backends lives at pkg/cache/blobcache/contracttest. Policy plugins keep raw OPA/JSON maps at the OPA adapter boundary: use-cases pass typed policyinput.Envelope values and consume typed policy results.
Pipeline contributions are value objects too. Producers should build jobs with pipeline.NewPluginCommandJob or pipeline.NewContributedJob and wrap them with pipeline.NewContribution. Consumers use Contribution.Jobs() and ContributedJob getters; direct struct literals are not part of the SDK.
Package plugin provides the compile-time plugin system for TerraCi. Plugins register themselves via init() and blank imports, following the same pattern as database/sql drivers and Caddy modules.
Core types (interfaces, BasePlugin, AppContext) live in this package. Plugin factories and per-command registries live in pkg/plugin/registry. Init wizard types live in pkg/plugin/initwiz.
Index ¶
- Variables
- func BuildRuntime[T any](ctx context.Context, p RuntimeProvider, appCtx *AppContext) (T, error)
- func RequireEnabled(p interface{ ... }, message string) error
- func RuntimeAs[T any](runtime any) (T, error)
- func WithContext(parent context.Context, appCtx *AppContext) context.Context
- type AppContext
- func (ctx *AppContext) BlobStoreResolver() BlobStoreResolver
- func (ctx *AppContext) CIResolver() CIResolver
- func (ctx *AppContext) ChangeDetectorResolver() ChangeDetectorResolver
- func (ctx *AppContext) Config() config.Snapshot
- func (ctx *AppContext) KVCacheResolver() KVCacheResolver
- func (ctx *AppContext) PipelineContributions() []*pipeline.Contribution
- func (ctx *AppContext) Reports() ci.ReportStore
- func (ctx *AppContext) ServiceDir() string
- func (ctx *AppContext) Version() string
- func (ctx *AppContext) WithPipelineContributions(contribs []*pipeline.Contribution) *AppContext
- func (ctx *AppContext) WorkDir() string
- type AppContextOptions
- type BasePlugin
- func (b *BasePlugin[C]) Config() C
- func (b *BasePlugin[C]) ConfigKey() string
- func (b *BasePlugin[C]) DecodeAndSet(decode func(target any) error) error
- func (b *BasePlugin[C]) Description() string
- func (b *BasePlugin[C]) IsConfigured() bool
- func (b *BasePlugin[C]) IsEnabled() bool
- func (b *BasePlugin[C]) Name() string
- func (b *BasePlugin[C]) NewConfig() any
- func (b *BasePlugin[C]) Reset()
- func (b *BasePlugin[C]) SetTypedConfig(cfg C)
- func (b *BasePlugin[C]) Validate() error
- type BlobStoreOptions
- type BlobStoreProvider
- type BlobStoreResolver
- type CIInfoProvider
- type CIResolver
- type ChangeDetectionProvider
- type ChangeDetectorResolver
- type CommandBindingError
- type CommandBindingReason
- type CommandLookup
- type CommandProvider
- type CommentServiceFactory
- type ConfigCloner
- type ConfigLoader
- type DisabledPluginError
- type EnablePolicy
- type EnvDetector
- type KVCache
- type KVCacheProvider
- type KVCacheResolver
- type NoopResolver
- func (NoopResolver) ResolveBlobStoreProvider(string, ...string) (BlobStoreProvider, error)
- func (NoopResolver) ResolveCIProvider() (*ResolvedCIProvider, error)
- func (NoopResolver) ResolveChangeDetector() (ChangeDetectionProvider, error)
- func (NoopResolver) ResolveKVCacheProvider(string, ...string) (KVCacheProvider, error)
- type PipelineContributionError
- type PipelineContributionGate
- type PipelineContributionPhase
- type PipelineContributor
- type PipelineGeneratorFactory
- type Plugin
- type Preflightable
- type ResolvedCIProvider
- func (c *ResolvedCIProvider) CommitSHA() string
- func (c *ResolvedCIProvider) Description() string
- func (c *ResolvedCIProvider) Name() string
- func (c *ResolvedCIProvider) NewCommentService(ctx *AppContext) (ci.CommentService, bool)
- func (c *ResolvedCIProvider) NewGenerator(ctx *AppContext, ir *pipeline.IR) pipeline.Generator
- func (c *ResolvedCIProvider) PipelineID() string
- func (c *ResolvedCIProvider) PipelineRequirements(ctx *AppContext) pipeline.BuildRequirements
- func (c *ResolvedCIProvider) Plugin() Plugin
- func (c *ResolvedCIProvider) ProviderName() string
- type Resolver
- type RuntimeProvider
- type Validator
- type VersionProvider
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNilPipelineContribution = errors.New("pipeline contribution is nil; use PipelineContributionGate to opt out")
ErrNilPipelineContribution is returned when a contributor opts out by returning nil instead of using PipelineContributionGate.
var ErrNoResolver = errors.New("plugin resolver is not configured")
ErrNoResolver is returned by NoopResolver capability lookups. Tests can match it via errors.Is to assert the no-resolver path.
Functions ¶
func BuildRuntime ¶ added in v0.9.0
func BuildRuntime[T any](ctx context.Context, p RuntimeProvider, appCtx *AppContext) (T, error)
BuildRuntime calls p.Runtime and type-asserts the result to T in one call. It combines the Runtime() call and RuntimeAs[T]() assertion: the recommended shorthand for plugin use-cases:
func (p *Plugin) runtime(ctx context.Context, appCtx *AppContext) (*myRuntime, error) {
return plugin.BuildRuntime[*myRuntime](ctx, p, appCtx)
}
func RequireEnabled ¶ added in v0.10.5
RequireEnabled returns message when p reports disabled.
func RuntimeAs ¶ added in v0.9.0
RuntimeAs converts a runtime returned by RuntimeProvider into the expected plugin-local type at the plugin boundary. The framework intentionally treats runtime values as opaque.
func WithContext ¶ added in v0.10.0
func WithContext(parent context.Context, appCtx *AppContext) context.Context
WithContext returns a child context.Context carrying appCtx. Used by the framework to attach the per-run AppContext to the cobra command context before RunE fires.
Types ¶
type AppContext ¶
type AppContext struct {
// contains filtered or unexported fields
}
AppContext is the public API available to plugins. It is immutable — constructed once per command run by the framework, then read-only.
ServiceDir is the resolved absolute path; use it for runtime file I/O. For pipeline artifact paths (CI templates), use Config().ServiceDir() which preserves the original relative value from .terraci.yaml.
Config returns an immutable snapshot. Production code should consume snapshot accessors; MutableCopy is reserved for tests or explicit compatibility adapters that need an isolated mutable configuration.
AppContext is safe for concurrent reads from any goroutine because all fields are written exactly once at construction.
func AppContextFromCommand ¶ added in v0.10.5
func AppContextFromCommand(cmd *cobra.Command) (*AppContext, error)
AppContextFromCommand returns the framework-bound AppContext for a cobra callback. Plugin command handlers usually call CommandPlugin instead.
func CommandPlugin ¶ added in v0.10.5
CommandPlugin resolves the command-scoped AppContext and plugin instance for a cobra callback. It is the preferred command boundary for built-in and external plugins.
func NewAppContext ¶ added in v0.9.0
func NewAppContext(opts AppContextOptions) *AppContext
NewAppContext creates a framework-managed plugin context.
func (*AppContext) BlobStoreResolver ¶ added in v0.10.5
func (ctx *AppContext) BlobStoreResolver() BlobStoreResolver
BlobStoreResolver returns the named blob store backend resolver. Always non-nil.
func (*AppContext) CIResolver ¶ added in v0.10.5
func (ctx *AppContext) CIResolver() CIResolver
CIResolver returns the active CI provider resolver. Always non-nil.
func (*AppContext) ChangeDetectorResolver ¶ added in v0.10.5
func (ctx *AppContext) ChangeDetectorResolver() ChangeDetectorResolver
ChangeDetectorResolver returns the active change detector resolver. Always non-nil.
func (*AppContext) Config ¶
func (ctx *AppContext) Config() config.Snapshot
Config returns the loaded TerraCi configuration snapshot.
func (*AppContext) KVCacheResolver ¶ added in v0.10.5
func (ctx *AppContext) KVCacheResolver() KVCacheResolver
KVCacheResolver returns the named KV cache backend resolver. Always non-nil.
func (*AppContext) PipelineContributions ¶ added in v0.10.1
func (ctx *AppContext) PipelineContributions() []*pipeline.Contribution
PipelineContributions returns the command-scoped pipeline contribution snapshot collected by the framework.
func (*AppContext) Reports ¶ added in v0.7.5
func (ctx *AppContext) Reports() ci.ReportStore
Reports returns the shared report store.
func (*AppContext) ServiceDir ¶ added in v0.7.3
func (ctx *AppContext) ServiceDir() string
ServiceDir returns the resolved absolute service directory path.
func (*AppContext) Version ¶
func (ctx *AppContext) Version() string
Version returns the current TerraCi version string.
func (*AppContext) WithPipelineContributions ¶ added in v0.10.1
func (ctx *AppContext) WithPipelineContributions(contribs []*pipeline.Contribution) *AppContext
WithPipelineContributions returns a copy of ctx bound to a contribution snapshot. The receiver is left untouched.
func (*AppContext) WorkDir ¶
func (ctx *AppContext) WorkDir() string
WorkDir returns the working directory for the current command.
type AppContextOptions ¶ added in v0.10.0
type AppContextOptions struct {
// Config is the loaded TerraCi configuration. Treated as read-only.
Config *config.Config
// WorkDir is the project working directory for the current command.
WorkDir string
// ServiceDir is the resolved absolute service directory path.
ServiceDir string
// Version is the current TerraCi version string.
Version string
// Reports is the shared report store. Defaults to a file-backed store when
// ServiceDir is set, otherwise to an in-process memory store.
Reports ci.ReportStore
// Resolver is the per-run plugin resolver. Defaults to NoopResolver{}
// when nil. Plugins should consume it through AppContext's narrow resolver
// accessors.
Resolver Resolver
// CommandLookup is the framework-side lookup used by CommandPlugin to
// bind cobra callbacks to command-scoped plugin instances.
CommandLookup CommandLookup
// PipelineContributions is a command-scoped snapshot of enabled pipeline
// contributions collected by the framework after config/preflight.
PipelineContributions []*pipeline.Contribution
}
AppContextOptions describes how to construct an AppContext.
type BasePlugin ¶ added in v0.7.5
type BasePlugin[C ConfigCloner[C]] struct { PluginName string PluginDesc string PluginKey string // config key; defaults to PluginName if empty EnableMode EnablePolicy // how the framework checks if this plugin is active DefaultCfg func() C // factory for default config // IsEnabledFn is an optional custom check for EnabledExplicitly and EnabledByDefault. // For EnabledExplicitly: called when configured, must return true to activate. // For EnabledByDefault: called when configured, return false to deactivate. IsEnabledFn func(C) bool // contains filtered or unexported fields }
BasePlugin provides shared implementation for all plugins that have configuration. C is the plugin's concrete config type. Embedding this gives you:
- Name(), Description()
- ConfigKey(), NewConfig(), DecodeAndSet(), IsConfigured(), IsEnabled()
- Config() (typed defensive-copy access to config)
- Reset() (resets config state; override to reset custom fields)
- Validate() (registration-time sanity check; see Validator)
func (*BasePlugin[C]) Config ¶ added in v0.7.5
func (b *BasePlugin[C]) Config() C
Config returns a defensive copy of the typed plugin configuration. Mutating the returned value never changes plugin state.
func (*BasePlugin[C]) ConfigKey ¶ added in v0.7.5
func (b *BasePlugin[C]) ConfigKey() string
ConfigKey returns the config section key under "extensions:" in .terraci.yaml.
func (*BasePlugin[C]) DecodeAndSet ¶ added in v0.7.5
func (b *BasePlugin[C]) DecodeAndSet(decode func(target any) error) error
DecodeAndSet decodes plugin config via the provided decoder and stores it.
func (*BasePlugin[C]) Description ¶ added in v0.7.5
func (b *BasePlugin[C]) Description() string
Description returns a human-readable description.
func (*BasePlugin[C]) IsConfigured ¶ added in v0.7.5
func (b *BasePlugin[C]) IsConfigured() bool
IsConfigured returns true if config was loaded for this plugin.
func (*BasePlugin[C]) IsEnabled ¶ added in v0.7.5
func (b *BasePlugin[C]) IsEnabled() bool
IsEnabled returns whether the plugin should be active, based on EnablePolicy.
func (*BasePlugin[C]) Name ¶ added in v0.7.5
func (b *BasePlugin[C]) Name() string
Name returns the plugin's unique identifier.
func (*BasePlugin[C]) NewConfig ¶ added in v0.7.5
func (b *BasePlugin[C]) NewConfig() any
NewConfig returns a new instance of the default config for schema generation.
func (*BasePlugin[C]) Reset ¶ added in v0.7.5
func (b *BasePlugin[C]) Reset()
Reset resets the config state. Override in your plugin to also reset custom fields.
func (*BasePlugin[C]) SetTypedConfig ¶ added in v0.7.5
func (b *BasePlugin[C]) SetTypedConfig(cfg C)
SetTypedConfig sets the typed config directly (used by tests and flag overrides).
func (*BasePlugin[C]) Validate ¶ added in v0.10.0
func (b *BasePlugin[C]) Validate() error
Validate performs registration-time sanity checks on the BasePlugin embedding. It is invoked by registry.RegisterFactory; a non-nil error panics there with a message identifying the misconfigured plugin.
Currently catches the most common silent-disable bug: a plugin that opts into EnabledExplicitly but forgets to set IsEnabledFn — IsEnabled() would always return false, so the plugin appears registered but never runs.
type BlobStoreOptions ¶ added in v0.9.4
type BlobStoreOptions struct {
RootDir string
}
BlobStoreOptions carries optional backend-specific initialization overrides. Pass the zero value to use the backend's defaults.
type BlobStoreProvider ¶ added in v0.9.4
type BlobStoreProvider interface {
Plugin
NewBlobStore(ctx context.Context, appCtx *AppContext, opts BlobStoreOptions) (blobcache.Store, error)
}
BlobStoreProvider creates a blob store backend for plugin consumers. Pass BlobStoreOptions{} to use the backend's defaults.
type BlobStoreResolver ¶ added in v0.10.5
type BlobStoreResolver interface {
ResolveBlobStoreProvider(name string, configPathHint ...string) (BlobStoreProvider, error)
}
BlobStoreResolver resolves named blob store backend providers. Pass an optional configPathHint to make ambiguous-backend errors point at the calling feature's exact config field.
type CIInfoProvider ¶ added in v0.9.4
type CIInfoProvider interface {
Plugin
ProviderName() string
PipelineID() string
CommitSHA() string
}
CIInfoProvider provides CI-specific metadata.
type CIResolver ¶ added in v0.10.5
type CIResolver interface {
ResolveCIProvider() (*ResolvedCIProvider, error)
}
CIResolver resolves the active CI provider in the current plugin set.
type ChangeDetectionProvider ¶
type ChangeDetectionProvider interface {
Plugin
workflow.ChangeDetector
}
ChangeDetectionProvider detects changed modules from git (or other VCS).
type ChangeDetectorResolver ¶ added in v0.10.5
type ChangeDetectorResolver interface {
ResolveChangeDetector() (ChangeDetectionProvider, error)
}
ChangeDetectorResolver resolves the active change-detection provider.
type CommandBindingError ¶ added in v0.10.5
type CommandBindingError struct {
Plugin string
Reason CommandBindingReason
ActualType string
ExpectedType string
}
CommandBindingError is returned when a cobra callback cannot be bound to the current command-scoped AppContext or plugin instance.
func (*CommandBindingError) Error ¶ added in v0.10.5
func (e *CommandBindingError) Error() string
type CommandBindingReason ¶ added in v0.10.5
type CommandBindingReason string
CommandBindingReason identifies a command binding failure class.
const ( CommandBindingNilCommand CommandBindingReason = "nil_command" CommandBindingMissingContext CommandBindingReason = "missing_context" CommandBindingMissingLookup CommandBindingReason = "missing_lookup" CommandBindingNotFound CommandBindingReason = "not_found" CommandBindingWrongType CommandBindingReason = "wrong_type" )
type CommandLookup ¶ added in v0.10.1
type CommandLookup interface {
// GetPlugin returns a plugin by name from the current command-scoped set.
GetPlugin(name string) (Plugin, bool)
}
CommandLookup is the framework-side lookup used only to bind a cobra command callback back to its command-scoped plugin instance.
type CommandProvider ¶
CommandProvider adds CLI subcommands to TerraCi. The framework calls Commands() once during root command registration. Inside RunE, plugins should use CommandPlugin[T] to resolve the per-run AppContext plus the command-scoped plugin instance in one typed call.
type CommentServiceFactory ¶ added in v0.9.4
type CommentServiceFactory interface {
Plugin
NewCommentService(ctx *AppContext) ci.CommentService
}
CommentServiceFactory creates PR/MR comment services.
type ConfigCloner ¶ added in v0.10.5
type ConfigCloner[C any] interface { Clone() C }
ConfigCloner is the config contract required by BasePlugin. Clone must return a deep copy of the concrete plugin config. Pointer config types should handle a nil receiver and return nil.
type ConfigLoader ¶ added in v0.7.5
type ConfigLoader interface {
Plugin
ConfigKey() string
NewConfig() any
DecodeAndSet(decode func(target any) error) error
IsConfigured() bool
IsEnabled() bool
}
ConfigLoader declares a config section under "extensions:" in .terraci.yaml. Implemented automatically by embedding BasePlugin[C] with a config type that implements Clone() C.
type DisabledPluginError ¶ added in v0.10.5
type DisabledPluginError struct {
Message string
}
DisabledPluginError is returned when a command targets a disabled plugin.
func (*DisabledPluginError) Error ¶ added in v0.10.5
func (e *DisabledPluginError) Error() string
type EnablePolicy ¶ added in v0.7.5
type EnablePolicy int
EnablePolicy controls how the framework determines if a plugin is active.
const ( // EnabledWhenConfigured means the plugin is active if its config section // exists in .terraci.yaml. EnabledWhenConfigured EnablePolicy = iota // EnabledExplicitly requires an explicit opt-in beyond having config. // When IsEnabledFn is set: called after config is loaded, must return true to activate. // When IsEnabledFn is nil: always returns false, even if configured. EnabledExplicitly // EnabledByDefault means the plugin is active unless enabled: false is set. EnabledByDefault // EnabledAlways means the plugin is always active regardless of config. EnabledAlways )
type EnvDetector ¶ added in v0.7.5
EnvDetector detects whether this plugin's CI environment is active.
type KVCache ¶ added in v0.9.4
type KVCache interface {
Get(ctx context.Context, namespace, key string) ([]byte, bool, error)
Set(ctx context.Context, namespace, key string, value []byte, ttl time.Duration) error
Delete(ctx context.Context, namespace, key string) error
DeleteNamespace(ctx context.Context, namespace string) error
}
KVCache is a pluggable key/value cache backend.
Values are stored as opaque bytes. Consumers own serialization, key layout, namespaces, and write-time TTL selection.
type KVCacheProvider ¶ added in v0.9.4
type KVCacheProvider interface {
Plugin
NewKVCache(ctx context.Context, appCtx *AppContext) (KVCache, error)
}
KVCacheProvider creates a KV cache backend for plugin consumers.
Providers are registered like any other TerraCi plugin and resolved by name through the global plugin registry.
type KVCacheResolver ¶ added in v0.10.5
type KVCacheResolver interface {
ResolveKVCacheProvider(name string, configPathHint ...string) (KVCacheProvider, error)
}
KVCacheResolver resolves named KV cache backend providers. Pass an optional configPathHint to make ambiguous-backend errors point at the calling feature's exact config field.
type NoopResolver ¶ added in v0.10.0
type NoopResolver struct{}
NoopResolver is the default-deny Resolver. It is bound to AppContext when no real resolver is supplied, so plugins may always call the narrow AppContext resolver accessors without nil-checks. Tests can embed it and override only the methods relevant to the case at hand.
func (NoopResolver) ResolveBlobStoreProvider ¶ added in v0.10.0
func (NoopResolver) ResolveBlobStoreProvider(string, ...string) (BlobStoreProvider, error)
ResolveBlobStoreProvider rejects with ErrNoResolver.
func (NoopResolver) ResolveCIProvider ¶ added in v0.10.0
func (NoopResolver) ResolveCIProvider() (*ResolvedCIProvider, error)
ResolveCIProvider rejects with ErrNoResolver.
func (NoopResolver) ResolveChangeDetector ¶ added in v0.10.0
func (NoopResolver) ResolveChangeDetector() (ChangeDetectionProvider, error)
ResolveChangeDetector rejects with ErrNoResolver.
func (NoopResolver) ResolveKVCacheProvider ¶ added in v0.10.0
func (NoopResolver) ResolveKVCacheProvider(string, ...string) (KVCacheProvider, error)
ResolveKVCacheProvider rejects with ErrNoResolver.
type PipelineContributionError ¶ added in v0.10.5
type PipelineContributionError struct {
Plugin string
Phase PipelineContributionPhase
Err error
}
PipelineContributionError wraps failures from a plugin's pipeline contribution hooks with stable plugin and phase metadata.
func (*PipelineContributionError) Error ¶ added in v0.10.5
func (e *PipelineContributionError) Error() string
func (*PipelineContributionError) Unwrap ¶ added in v0.10.5
func (e *PipelineContributionError) Unwrap() error
type PipelineContributionGate ¶ added in v0.10.3
type PipelineContributionGate interface {
Plugin
PipelineContributionEnabled(ctx *AppContext) (bool, error)
}
PipelineContributionGate optionally controls whether an enabled plugin should contribute to the current pipeline.
type PipelineContributionPhase ¶ added in v0.10.5
type PipelineContributionPhase string
PipelineContributionPhase identifies which contribution hook failed.
const ( PipelineContributionPhaseGate PipelineContributionPhase = "gate" PipelineContributionPhaseContribution PipelineContributionPhase = "contribution" )
type PipelineContributor ¶
type PipelineContributor interface {
Plugin
PipelineContribution(ctx *AppContext) (*pipeline.Contribution, error)
}
PipelineContributor plugins add provider-independent jobs to the pipeline DAG.
type PipelineGeneratorFactory ¶ added in v0.9.4
type PipelineGeneratorFactory interface {
Plugin
PipelineRequirements(ctx *AppContext) pipeline.BuildRequirements
NewGenerator(ctx *AppContext, ir *pipeline.IR) pipeline.Generator
}
PipelineGeneratorFactory declares provider IR requirements and creates pipeline generators bound to a pre-built IR. Core asks for requirements, builds the IR once via pipeline.BuildProjectIR, then passes it here, so providers do not need depGraph, target modules, or contributions — they only render the immutable IR through getters.
type Plugin ¶
type Plugin interface {
// Name returns a unique plugin identifier.
Name() string
// Description returns a human-readable description.
Description() string
}
Plugin is the core interface every plugin must implement.
type Preflightable ¶ added in v0.9.0
type Preflightable interface {
Plugin
Preflight(ctx context.Context, appCtx *AppContext) error
}
Preflightable plugins run cheap validation after config is loaded, before any command runs. Preflight should stay side-effect-light: do not cache mutable command state or perform heavy runtime setup that can be created lazily inside plugin use-cases.
type ResolvedCIProvider ¶ added in v0.9.4
type ResolvedCIProvider struct {
// contains filtered or unexported fields
}
ResolvedCIProvider is the resolved CI provider, assembled from the focused interfaces above. Returned by ResolveCIProvider(). CommentServiceFactory is optional — not all CI providers support PR/MR comments.
func NewResolvedCIProvider ¶ added in v0.9.4
func NewResolvedCIProvider(p Plugin, meta CIInfoProvider, gen PipelineGeneratorFactory, comment CommentServiceFactory) *ResolvedCIProvider
NewResolvedCIProvider constructs a ResolvedCIProvider. The comment parameter may be nil for CI providers that do not support PR/MR comments.
func (*ResolvedCIProvider) CommitSHA ¶ added in v0.9.4
func (c *ResolvedCIProvider) CommitSHA() string
func (*ResolvedCIProvider) Description ¶ added in v0.9.4
func (c *ResolvedCIProvider) Description() string
func (*ResolvedCIProvider) Name ¶ added in v0.9.4
func (c *ResolvedCIProvider) Name() string
func (*ResolvedCIProvider) NewCommentService ¶ added in v0.9.4
func (c *ResolvedCIProvider) NewCommentService(ctx *AppContext) (ci.CommentService, bool)
NewCommentService returns the comment service and true, or nil and false if the CI provider does not support PR/MR comments.
func (*ResolvedCIProvider) NewGenerator ¶ added in v0.9.4
func (c *ResolvedCIProvider) NewGenerator(ctx *AppContext, ir *pipeline.IR) pipeline.Generator
NewGenerator returns a pipeline generator bound to the supplied IR.
func (*ResolvedCIProvider) PipelineID ¶ added in v0.9.4
func (c *ResolvedCIProvider) PipelineID() string
func (*ResolvedCIProvider) PipelineRequirements ¶ added in v0.10.3
func (c *ResolvedCIProvider) PipelineRequirements(ctx *AppContext) pipeline.BuildRequirements
PipelineRequirements returns provider-specific IR build requirements.
func (*ResolvedCIProvider) Plugin ¶ added in v0.9.4
func (c *ResolvedCIProvider) Plugin() Plugin
Plugin returns the underlying plugin instance.
func (*ResolvedCIProvider) ProviderName ¶ added in v0.9.4
func (c *ResolvedCIProvider) ProviderName() string
type Resolver ¶ added in v0.10.0
type Resolver interface {
CIResolver
ChangeDetectorResolver
KVCacheResolver
BlobStoreResolver
}
Resolver is the framework resolver implementation contract. Plugin code should prefer AppContext's narrow resolver accessors instead of depending on this aggregate interface directly.
type RuntimeProvider ¶ added in v0.9.0
type RuntimeProvider interface {
Plugin
Runtime(ctx context.Context, appCtx *AppContext) (any, error)
}
RuntimeProvider is the preferred pattern for plugins with heavy command-time setup. Runtime creation is lazy and command-driven; the framework does not invoke it automatically during startup or preflight.
Use Preflightable for cheap validation and environment checks. Use RuntimeProvider for typed runtime construction inside plugin commands and use-cases.
Typical shape:
func (p *Plugin) Runtime(_ context.Context, appCtx *AppContext) (any, error) {
return newRuntime(appCtx, p.Config())
}
func (p *Plugin) runtime(ctx context.Context, appCtx *AppContext) (*myRuntime, error) {
return plugin.BuildRuntime[*myRuntime](ctx, p, appCtx)
}
Command flags belong in a typed use-case request, not in the runtime. The runtime should hold immutable dependencies and normalized config only.
Example ¶
package main
import (
"context"
"fmt"
"github.com/edelwud/terraci/pkg/plugin"
)
type exampleRuntime struct {
workDir string
}
type exampleRuntimePlugin struct {
plugin.BasePlugin[*exampleRuntimeConfig]
}
type exampleRuntimeConfig struct {
Enabled bool
}
func (c *exampleRuntimeConfig) Clone() *exampleRuntimeConfig {
if c == nil {
return nil
}
out := *c
return &out
}
func (p *exampleRuntimePlugin) Runtime(_ context.Context, appCtx *plugin.AppContext) (any, error) {
if p.Config() == nil || !p.Config().Enabled {
return nil, fmt.Errorf("example runtime is not enabled")
}
return &exampleRuntime{workDir: appCtx.WorkDir()}, nil
}
func main() {
p := &exampleRuntimePlugin{
BasePlugin: plugin.BasePlugin[*exampleRuntimeConfig]{
PluginName: "example",
PluginDesc: "example runtime plugin",
EnableMode: plugin.EnabledExplicitly,
DefaultCfg: func() *exampleRuntimeConfig { return &exampleRuntimeConfig{} },
IsEnabledFn: func(cfg *exampleRuntimeConfig) bool { return cfg != nil && cfg.Enabled },
},
}
p.SetTypedConfig(&exampleRuntimeConfig{Enabled: true})
appCtx := plugin.NewAppContext(plugin.AppContextOptions{
WorkDir: "/repo",
ServiceDir: "/repo/.terraci",
Version: "test",
})
rawRuntime, _ := p.Runtime(context.Background(), appCtx)
runtime, _ := plugin.RuntimeAs[*exampleRuntime](rawRuntime)
fmt.Println(runtime.workDir)
}
Output: /repo
type Validator ¶ added in v0.10.0
type Validator interface {
Validate() error
}
Validator is implemented by plugins that want the registry to perform a startup sanity check. The framework calls Validate() once after the plugin is constructed by its factory; a non-nil error panics in RegisterFactory with a clear message identifying the misconfigured plugin.
type VersionProvider ¶
VersionProvider plugins contribute version info to `terraci version`.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package cliout contains shared CLI output helpers for plugin commands.
|
Package cliout contains shared CLI output helpers for plugin commands. |
|
Package initwiz provides init wizard state management and types for TerraCi.
|
Package initwiz provides init wizard state management and types for TerraCi. |
|
Package registry provides TerraCi's plugin catalog and per-run plugin sets.
|
Package registry provides TerraCi's plugin catalog and per-run plugin sets. |