plugin

package
v0.9.3 Latest Latest
Warning

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

Go to latest
Published: Mar 31, 2026 License: MIT Imports: 10 Imported by: 1

Documentation

Overview

Package plugin provides the compile-time plugin system for TerraCi.

The plugin system is organized into three packages:

  • pkg/plugin: core interfaces, BasePlugin[C], AppContext, EnablePolicy, RuntimeProvider
  • pkg/plugin/registry: global plugin registration and resolution (Register, ByCapability, ResolveProvider)
  • pkg/plugin/initwiz: init wizard types (StateMap, InitContributor, InitGroupSpec)

Preferred plugin architecture for runtime-heavy built-in and external plugins:

  • plugin.go: registration shell and typed BasePlugin config
  • lifecycle.go: cheap Preflight checks only
  • runtime.go: lazy RuntimeProvider implementation
  • usecases.go: command orchestration over typed runtime
  • output.go / report.go: rendering and report assembly

Framework-owned lifecycle stops at configuration and preflight. Heavy clients, caches, and command-specific state should be created lazily from Runtime() and consumed by plugin commands or use-cases rather than cached during startup hooks.

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. Global registry functions live in pkg/plugin/registry. Init wizard types live in pkg/plugin/initwiz.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

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 step. 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 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.

Types

type AppContext

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

AppContext is the public API available to plugins.

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.

After initialization, call Freeze() to prevent the framework from refreshing this shared context from App state again during the current command run.

func NewAppContext added in v0.9.0

func NewAppContext(cfg *config.Config, workDir, serviceDir, version string, reports *ReportRegistry) *AppContext

NewAppContext creates a framework-managed plugin context.

func (*AppContext) Config

func (ctx *AppContext) Config() *config.Config

Config returns a defensive copy of the loaded TerraCi configuration. For repeated access within a single use-case, cache the result locally:

cfg := appCtx.Config()
// use cfg throughout the function

This avoids repeated deep copies while preserving immutability guarantees.

func (*AppContext) Freeze added in v0.7.5

func (ctx *AppContext) Freeze()

Freeze marks the context as final for framework-managed updates.

func (*AppContext) IsFrozen added in v0.7.5

func (ctx *AppContext) IsFrozen() bool

IsFrozen returns whether the context has been frozen.

func (*AppContext) Reports added in v0.7.5

func (ctx *AppContext) Reports() *ReportRegistry

Reports returns the shared in-process report registry.

func (*AppContext) ServiceDir added in v0.7.3

func (ctx *AppContext) ServiceDir() string

ServiceDir returns the resolved absolute service directory path.

func (*AppContext) Update added in v0.9.0

func (ctx *AppContext) Update(cfg *config.Config, workDir, serviceDir, version string)

Update refreshes the framework-managed view of app state until the context is frozen.

func (*AppContext) Version

func (ctx *AppContext) Version() string

Version returns the current TerraCi version string.

func (*AppContext) WorkDir

func (ctx *AppContext) WorkDir() string

WorkDir returns the working directory for the current command.

type BasePlugin added in v0.7.5

type BasePlugin[C any] 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 access to config)
  • Reset() (resets config state; override to reset custom fields)

func (*BasePlugin[C]) Config added in v0.7.5

func (b *BasePlugin[C]) Config() C

Config returns the typed plugin configuration.

func (*BasePlugin[C]) ConfigKey added in v0.7.5

func (b *BasePlugin[C]) ConfigKey() string

ConfigKey returns the config section key under "plugins:" 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).

type CIMetadata added in v0.7.5

type CIMetadata interface {
	Plugin
	ProviderName() string
	PipelineID() string
	CommitSHA() string
}

CIMetadata provides CI-specific metadata.

type CIProvider added in v0.7.5

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

CIProvider is the resolved CI provider, assembled from the focused interfaces above. Returned by ResolveProvider(). CommentFactory is optional — not all CI providers support PR/MR comments.

func NewCIProvider added in v0.7.5

func NewCIProvider(p Plugin, meta CIMetadata, gen GeneratorFactory, comment CommentFactory) *CIProvider

NewCIProvider constructs a CIProvider. The comment parameter may be nil for CI providers that do not support PR/MR comments.

func (*CIProvider) CommitSHA added in v0.7.5

func (c *CIProvider) CommitSHA() string

func (*CIProvider) Description added in v0.7.5

func (c *CIProvider) Description() string

func (*CIProvider) Name added in v0.7.5

func (c *CIProvider) Name() string

func (*CIProvider) NewCommentService added in v0.7.5

func (c *CIProvider) 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 (*CIProvider) NewGenerator added in v0.7.5

func (c *CIProvider) NewGenerator(ctx *AppContext, depGraph *graph.DependencyGraph, modules []*discovery.Module) pipeline.Generator

func (*CIProvider) PipelineID added in v0.7.5

func (c *CIProvider) PipelineID() string

func (*CIProvider) Plugin added in v0.7.5

func (c *CIProvider) Plugin() Plugin

Plugin returns the underlying plugin instance.

func (*CIProvider) ProviderName added in v0.7.5

func (c *CIProvider) ProviderName() string

type ChangeDetectionProvider

type ChangeDetectionProvider interface {
	Plugin
	DetectChangedModules(ctx context.Context, appCtx *AppContext, baseRef string, moduleIndex *discovery.ModuleIndex) (changed []*discovery.Module, changedFiles []string, err error)
	DetectChangedLibraries(ctx context.Context, appCtx *AppContext, baseRef string, libraryPaths []string) ([]string, error)
}

ChangeDetectionProvider detects changed modules from git (or other VCS).

type CommandProvider

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

CommandProvider adds CLI subcommands to TerraCi.

type CommentFactory added in v0.7.5

type CommentFactory interface {
	Plugin
	NewCommentService(ctx *AppContext) ci.CommentService
}

CommentFactory creates PR/MR comment services.

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 "plugins:" in .terraci.yaml. Implemented automatically by embedding BasePlugin[C].

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 (e.g., gitlab, github).
	EnabledWhenConfigured EnablePolicy = iota

	// EnabledExplicitly requires an explicit opt-in beyond having config (e.g., cost, policy).
	// 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 (e.g., summary).
	EnabledByDefault

	// EnabledAlways means the plugin is always active regardless of config (e.g., git).
	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 FlagOverridable added in v0.7.5

type FlagOverridable interface {
	Plugin
	SetPlanOnly(bool)
	SetAutoApprove(bool)
}

FlagOverridable plugins support direct CLI flag overrides on their config.

type GeneratorFactory added in v0.7.5

type GeneratorFactory interface {
	Plugin
	NewGenerator(ctx *AppContext, depGraph *graph.DependencyGraph, modules []*discovery.Module) pipeline.Generator
}

GeneratorFactory creates pipeline generators.

type PipelineContributor

type PipelineContributor interface {
	Plugin
	PipelineContribution(ctx *AppContext) *pipeline.Contribution
}

PipelineContributor plugins add steps or jobs to the generated CI pipeline.

type Plugin

type Plugin interface {
	// Name returns a unique identifier (e.g., "gitlab", "cost", "slack").
	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 ReportRegistry added in v0.7.5

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

ReportRegistry allows plugins to publish and consume reports in-memory. In CI (multi-process), reports are still written to JSON files for artifacts. The registry provides an in-process fast path for single-process runs.

func NewReportRegistry added in v0.7.5

func NewReportRegistry() *ReportRegistry

NewReportRegistry creates a new empty ReportRegistry.

func (*ReportRegistry) All added in v0.7.5

func (r *ReportRegistry) All() []*ci.Report

All returns all published reports.

func (*ReportRegistry) Get added in v0.7.5

func (r *ReportRegistry) Get(pluginName string) (*ci.Report, bool)

Get retrieves a report by plugin name.

func (*ReportRegistry) Publish added in v0.7.5

func (r *ReportRegistry) Publish(report *ci.Report)

Publish stores a report in the registry, keyed by plugin name.

type Resettable added in v0.7.5

type Resettable interface {
	Plugin
	Reset()
}

Resettable plugins can reset their mutable state to zero values. Used by test infrastructure to isolate tests from shared plugin singletons.

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(), runtimeOptions{})
}

func (p *Plugin) runtime(ctx context.Context, appCtx *AppContext, opts runtimeOptions) (*myRuntime, error) {
	if opts == (runtimeOptions{}) {
		rawRuntime, err := p.Runtime(ctx, appCtx)
		if err != nil {
			return nil, err
		}
		return RuntimeAs[*myRuntime](rawRuntime)
	}
	return newRuntime(appCtx, p.Config(), opts)
}
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 (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(nil, "/repo", "/repo/.terraci", "test", nil)
	rawRuntime, _ := p.Runtime(context.Background(), appCtx)
	runtime, _ := plugin.RuntimeAs[*exampleRuntime](rawRuntime)

	fmt.Println(runtime.workDir)
}
Output:
/repo

type VersionProvider

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

VersionProvider plugins contribute version info to `terraci version`.

Directories

Path Synopsis
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 the global plugin registry for TerraCi.
Package registry provides the global plugin registry for TerraCi.

Jump to

Keyboard shortcuts

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