plugin

package
v0.10.6 Latest Latest
Warning

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

Go to latest
Published: May 29, 2026 License: MIT Imports: 10 Imported by: 1

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

Examples

Constants

This section is empty.

Variables

View Source
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.

View Source
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

func RequireEnabled(p interface{ IsEnabled() bool }, message string) error

RequireEnabled returns message when p reports disabled.

func RuntimeAs added in v0.9.0

func RuntimeAs[T any](runtime any) (T, error)

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

func CommandPlugin[T Plugin](cmd *cobra.Command, name string) (*AppContext, T, error)

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

type CommandProvider interface {
	Plugin
	Commands() []*cobra.Command
}

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

type EnvDetector interface {
	Plugin
	DetectEnv() bool
}

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

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

type VersionProvider interface {
	Plugin
	VersionInfo() map[string]string
}

VersionProvider plugins contribute version info to `terraci version`.

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.

Jump to

Keyboard shortcuts

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