Documentation
¶
Index ¶
- func AllocatePortOffset(envName string) int
- func RenderEnvMap(envMap map[string]string, ctx *TemplateContext) (map[string]string, error)
- func RenderTemplate(tmpl string, ctx *TemplateContext) (string, error)
- func ResolveEnvironmentFromCwd(cwd string, environments map[string]*EnvironmentEntry) (string, error)
- func TopologicalSort(services map[string]ServiceConfig) ([]string, error)
- func ValidateConfig(cfg *ProjectConfig) error
- func ValidateConfigWithFS(cfg *ProjectConfig, projectRoot string, fileExists func(string) bool) error
- type ComputePort
- type ComputeResources
- type CoreConfig
- type DatabaseConfig
- type DatabaseInfo
- type DatabasePort
- type DatabaseRef
- type EnvPort
- type EnvironmentDetail
- type EnvironmentEntry
- type EnvironmentMode
- type EnvironmentStatus
- type HookContext
- type HookDef
- type HookRunner
- type HooksConfig
- type InfraServiceConfig
- type LocalConfig
- type LocalMeta
- type Manager
- func (m *Manager) Destroy(ctx context.Context, envName string) error
- func (m *Manager) Init(ctx context.Context, envName string, branch string) (*EnvironmentEntry, error)
- func (m *Manager) List(ctx context.Context) ([]*EnvironmentEntry, error)
- func (m *Manager) ResetDatabase(ctx context.Context, envName string, dbName string) error
- func (m *Manager) SeedTemplate(ctx context.Context, dbName string, snapshotPath string) error
- func (m *Manager) Status(ctx context.Context, envName string) (*EnvironmentDetail, error)
- type ManagerDeps
- type NetworkingPort
- type NoopReporter
- type PortMap
- type ProgressReporter
- type ProjectConfig
- type ProvisionerPort
- type RemoteMeta
- type SeedConfig
- type ServiceConfig
- type SnapshotConfig
- type SnapshotState
- type SnapshotUpdate
- type State
- type StatePort
- type StepEvent
- type StepHooks
- type StepStatus
- type TemplateContext
- type VMInfo
- type VMSpec
- type ValidationError
- type WorktreeConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AllocatePortOffset ¶
AllocatePortOffset computes a deterministic port offset from an environment name using FNV-1a hashing. The offset is in the range [1, maxOffset].
func RenderEnvMap ¶
RenderEnvMap renders all template variables in a map of env vars.
func RenderTemplate ¶
func RenderTemplate(tmpl string, ctx *TemplateContext) (string, error)
RenderTemplate replaces {{var}} placeholders in a string with values from the context. Supported variable patterns:
- {{ports.<service>}} — allocated port for a service
- {{core.databases.<name>.connectionString}} — database connection string
- {{core.databases.<name>.host}} — database host
- {{core.databases.<name>.port}} — database port
- {{core.databases.<name>.user}} — database user
- {{core.databases.<name>.password}} — database password
- {{core.databases.<name>.database}} — database name
func ResolveEnvironmentFromCwd ¶
func ResolveEnvironmentFromCwd(cwd string, environments map[string]*EnvironmentEntry) (string, error)
ResolveEnvironmentFromCwd determines the environment name by checking if the current working directory is inside any known worktree path from state.
func TopologicalSort ¶
func TopologicalSort(services map[string]ServiceConfig) ([]string, error)
TopologicalSort performs Kahn's algorithm to produce a topological ordering of services based on their dependsOn relationships. Returns an error if a cycle is detected or a dependency references an unknown service.
func ValidateConfig ¶
func ValidateConfig(cfg *ProjectConfig) error
ValidateConfig performs deep validation of a ProjectConfig. It checks structural correctness, port collisions, dependency references, template variable references, and supported engine types. It does NOT check file system paths — use ValidateConfigWithFS for that.
func ValidateConfigWithFS ¶
func ValidateConfigWithFS(cfg *ProjectConfig, projectRoot string, fileExists func(string) bool) error
ValidateConfigWithFS performs all config validation plus file system checks. projectRoot is the directory containing previewctl.yaml. fileExists is a function that checks if a path exists (allows testing).
Types ¶
type ComputePort ¶
type ComputePort interface {
// Create sets up compute resources for an environment.
Create(ctx context.Context, envName string, branch string) (*ComputeResources, error)
// Start starts per-environment services (infra containers, etc).
Start(ctx context.Context, envName string, ports PortMap) error
// Stop stops services without destroying data or resources.
Stop(ctx context.Context, envName string) error
// Destroy tears down all compute resources.
Destroy(ctx context.Context, envName string) error
// IsRunning checks if environment compute resources are active.
IsRunning(ctx context.Context, envName string) (bool, error)
}
ComputePort manages the compute substrate for an environment. Local: git worktree + docker compose for per-env infrastructure. Preview: VM provisioning + full compose stack. Sandbox: isolated VM with network policies.
type ComputeResources ¶
type ComputeResources struct {
WorktreePath string `json:"worktreePath,omitempty"` // local mode
VMId string `json:"vmId,omitempty"` // preview/sandbox mode
ExternalIP string `json:"externalIp,omitempty"` // preview/sandbox mode
}
ComputeResources holds the result of creating compute resources for an environment.
type CoreConfig ¶
type CoreConfig struct {
Databases map[string]DatabaseConfig `yaml:"databases,omitempty"`
}
CoreConfig holds managed services requiring engine-specific lifecycle.
type DatabaseConfig ¶
type DatabaseConfig struct {
Engine string `yaml:"engine"`
Image string `yaml:"image"`
Port int `yaml:"port"`
User string `yaml:"user"`
Password string `yaml:"password"`
TemplateDb string `yaml:"templateDb"`
Seed *SeedConfig `yaml:"seed,omitempty"`
}
DatabaseConfig defines a core database.
type DatabaseInfo ¶
type DatabaseInfo struct {
Host string `json:"host"`
Port int `json:"port"`
User string `json:"user"`
Password string `json:"password"`
Database string `json:"database"`
ConnectionString string `json:"connectionString"`
}
DatabaseInfo holds connection details for a database instance.
type DatabasePort ¶
type DatabasePort interface {
// EnsureInfrastructure ensures the database server is running and reachable.
EnsureInfrastructure(ctx context.Context) error
// SeedTemplate populates the template database from a snapshot or migration.
// snapshotPath is optional; adapters may use config defaults.
SeedTemplate(ctx context.Context, snapshotPath string) error
// CreateDatabase creates an isolated database for the given environment.
CreateDatabase(ctx context.Context, envName string) (*DatabaseInfo, error)
// DestroyDatabase drops the environment's database.
DestroyDatabase(ctx context.Context, envName string) error
// ResetDatabase drops and re-creates the environment's database.
ResetDatabase(ctx context.Context, envName string) (*DatabaseInfo, error)
// DatabaseExists checks if the environment's database exists.
DatabaseExists(ctx context.Context, envName string) (bool, error)
}
DatabasePort manages per-environment database lifecycle. Local: template cloning on shared Postgres. Preview/Sandbox: Neon branching, RDS snapshot, or full pg_dump restore.
type DatabaseRef ¶
DatabaseRef is a lightweight reference to a database stored in state.
type EnvPort ¶
type EnvPort interface {
// Generate writes .env.local files for all services in the environment.
Generate(ctx context.Context, envName string, workdir string, ports PortMap, databases map[string]*DatabaseInfo) error
SymlinkSharedEnvFiles(ctx context.Context, workdir string) error
// Cleanup removes generated env files.
Cleanup(ctx context.Context, workdir string) error
}
EnvPort generates environment configuration files (.env.local, etc).
type EnvironmentDetail ¶
type EnvironmentDetail struct {
Entry *EnvironmentEntry `json:"entry"`
InfraRunning bool `json:"infraRunning"`
DatabaseExists map[string]bool `json:"databaseExists"`
SnapshotInfo map[string]*SnapshotState `json:"snapshotInfo"`
}
EnvironmentDetail is an enriched view with live infrastructure checks.
type EnvironmentEntry ¶
type EnvironmentEntry struct {
Name string `json:"name"`
Mode EnvironmentMode `json:"mode"`
Branch string `json:"branch"`
Status EnvironmentStatus `json:"status"`
CreatedAt time.Time `json:"createdAt"`
UpdatedAt time.Time `json:"updatedAt"`
Ports PortMap `json:"ports"`
Databases map[string]DatabaseRef `json:"databases"`
Local *LocalMeta `json:"local,omitempty"`
Remote *RemoteMeta `json:"remote,omitempty"`
}
EnvironmentEntry is a tracked environment persisted in state.
type EnvironmentMode ¶
type EnvironmentMode string
EnvironmentMode represents the deployment mode of an environment.
const ( ModeLocal EnvironmentMode = "local" ModePreview EnvironmentMode = "preview" ModeSandbox EnvironmentMode = "sandbox" )
type EnvironmentStatus ¶
type EnvironmentStatus string
EnvironmentStatus represents the lifecycle status of an environment.
const ( StatusCreating EnvironmentStatus = "creating" StatusRunning EnvironmentStatus = "running" StatusStopped EnvironmentStatus = "stopped" StatusError EnvironmentStatus = "error" )
type HookContext ¶
type HookContext struct {
EnvName string
Branch string
ProjectName string
ProjectRoot string
WorktreePath string
Ports PortMap
Databases map[string]*DatabaseInfo
Step string
Phase string // "before" or "after"
}
HookContext provides environment variables and working directory for hook execution.
type HookDef ¶
type HookDef struct {
Run string `yaml:"run"`
ContinueOnError bool `yaml:"continueOnError,omitempty"`
}
HookDef defines a single hook to execute.
type HookRunner ¶
type HookRunner struct {
// contains filtered or unexported fields
}
HookRunner executes hooks defined in the config.
func NewHookRunner ¶
func NewHookRunner(hooks HooksConfig, progress ProgressReporter) *HookRunner
NewHookRunner creates a new hook runner.
func (*HookRunner) RunAfter ¶
func (r *HookRunner) RunAfter(ctx context.Context, step string, hctx *HookContext) error
RunAfter executes all "after" hooks for the given step.
func (*HookRunner) RunBefore ¶
func (r *HookRunner) RunBefore(ctx context.Context, step string, hctx *HookContext) error
RunBefore executes all "before" hooks for the given step.
type HooksConfig ¶
HooksConfig maps step names to their before/after hooks. Step names match the lifecycle steps: allocate_ports, create_compute, ensure_database_<name>, clone_database_<name>, symlink_env, generate_env, start_infra, save_state, load_state, destroy_compute, destroy_database_<name>, cleanup_env, remove_state, reset_database, ensure_infra, seed_template.
Additionally, lifecycle-level hooks can be defined: create, delete, seed, reset — these run before/after the entire operation.
type InfraServiceConfig ¶
InfraServiceConfig defines a generic docker infrastructure service.
type LocalConfig ¶
type LocalConfig struct {
Worktree WorktreeConfig `yaml:"worktree"`
// ComposeFile is the path to the docker compose file for per-env infrastructure,
// relative to the project root. If empty, no per-env containers are started.
ComposeFile string `yaml:"composeFile,omitempty"`
}
LocalConfig holds local-mode specific configuration.
type LocalMeta ¶
type LocalMeta struct {
WorktreePath string `json:"worktreePath"`
ComposeProjectName string `json:"composeProjectName"`
}
LocalMeta holds local-mode specific metadata.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager orchestrates environment lifecycle by coordinating all ports. It is the single source of truth — all inbound adapters delegate to it.
func NewManager ¶
func NewManager(deps ManagerDeps) *Manager
NewManager creates a new Manager with the given dependencies.
func (*Manager) Init ¶
func (m *Manager) Init(ctx context.Context, envName string, branch string) (*EnvironmentEntry, error)
Init creates a new environment end-to-end.
func (*Manager) List ¶
func (m *Manager) List(ctx context.Context) ([]*EnvironmentEntry, error)
List returns all tracked environments.
func (*Manager) ResetDatabase ¶
ResetDatabase resets an environment's database from the template.
func (*Manager) SeedTemplate ¶
SeedTemplate seeds or refreshes a template database.
type ManagerDeps ¶
type ManagerDeps struct {
Databases map[string]DatabasePort
Compute ComputePort
Networking NetworkingPort
EnvGen EnvPort
State StatePort
Progress ProgressReporter
Config *ProjectConfig
ProjectRoot string
}
ManagerDeps holds the dependencies for creating a Manager.
type NetworkingPort ¶
type NetworkingPort interface {
// AllocatePorts returns deterministic port assignments for all services and infrastructure.
AllocatePorts(envName string) PortMap
// GetServiceURL returns the URL to reach a named service in the environment.
GetServiceURL(envName string, service string) (string, error)
}
NetworkingPort handles port allocation and service URL resolution. Local: deterministic offset from base ports. Preview: reverse proxy with subdomain routing (future).
type NoopReporter ¶
type NoopReporter struct{}
NoopReporter is a ProgressReporter that discards all events.
func (NoopReporter) OnStep ¶
func (NoopReporter) OnStep(StepEvent)
type ProgressReporter ¶
type ProgressReporter interface {
OnStep(event StepEvent)
}
ProgressReporter receives lifecycle step events from the manager. Inbound adapters implement this to render progress (CLI spinners, SSE, etc).
type ProjectConfig ¶
type ProjectConfig struct {
Version int `yaml:"version"`
Name string `yaml:"name"`
PackageManager string `yaml:"packageManager,omitempty"`
Core CoreConfig `yaml:"core"`
Infrastructure map[string]InfraServiceConfig `yaml:"infrastructure,omitempty"`
Services map[string]ServiceConfig `yaml:"services"`
Local *LocalConfig `yaml:"local,omitempty"`
Hooks HooksConfig `yaml:"hooks,omitempty"`
}
ProjectConfig is the top-level previewctl.yaml configuration.
func LoadConfig ¶
func LoadConfig(path string) (*ProjectConfig, error)
LoadConfig reads and parses a previewctl.yaml file.
func ParseConfig ¶
func ParseConfig(data []byte) (*ProjectConfig, error)
ParseConfig parses YAML bytes into a ProjectConfig.
func (*ProjectConfig) AllBasePorts ¶
func (c *ProjectConfig) AllBasePorts() map[string]int
AllBasePorts returns a map of all service and infrastructure names to their base ports.
type ProvisionerPort ¶
type ProvisionerPort interface {
Provision(ctx context.Context, envName string, spec VMSpec) (*VMInfo, error)
Deprovision(ctx context.Context, vmID string) error
Status(ctx context.Context, vmID string) (*VMInfo, error)
}
ProvisionerPort manages VM lifecycle for preview/sandbox modes. Not implemented in POC; exists to validate interface design.
type RemoteMeta ¶
RemoteMeta holds remote-mode specific metadata (future).
type SeedConfig ¶
type SeedConfig struct {
Strategy string `yaml:"strategy"`
Snapshot *SnapshotConfig `yaml:"snapshot,omitempty"`
Script string `yaml:"script,omitempty"`
}
SeedConfig defines how a database template is seeded.
type ServiceConfig ¶
type ServiceConfig struct {
Path string `yaml:"path"`
Port int `yaml:"port"`
Command string `yaml:"command,omitempty"`
DependsOn []string `yaml:"dependsOn,omitempty"`
Env map[string]string `yaml:"env,omitempty"`
}
ServiceConfig defines an application service.
type SnapshotConfig ¶
type SnapshotConfig struct {
Source string `yaml:"source"`
Bucket string `yaml:"bucket"`
Prefix string `yaml:"prefix"`
Region string `yaml:"region"`
}
SnapshotConfig defines S3 snapshot source.
type SnapshotState ¶
type SnapshotState struct {
LastSeeded *time.Time `json:"lastSeeded"`
TemplateReady bool `json:"templateReady"`
}
SnapshotState tracks the seeding status of a database template.
type SnapshotUpdate ¶
SnapshotUpdate holds fields to update on snapshot state.
type State ¶
type State struct {
Version int `json:"version"`
Snapshots map[string]*SnapshotState `json:"snapshots"`
Environments map[string]*EnvironmentEntry `json:"environments"`
}
State is the top-level persisted state.
type StatePort ¶
type StatePort interface {
// Load returns the full state.
Load(ctx context.Context) (*State, error)
// Save persists the full state.
Save(ctx context.Context, state *State) error
// GetEnvironment returns a single environment entry, or nil if not found.
GetEnvironment(ctx context.Context, name string) (*EnvironmentEntry, error)
// SetEnvironment creates or updates an environment entry.
SetEnvironment(ctx context.Context, name string, entry *EnvironmentEntry) error
// RemoveEnvironment deletes an environment entry.
RemoveEnvironment(ctx context.Context, name string) error
// UpdateSnapshot updates snapshot metadata for a named database.
UpdateSnapshot(ctx context.Context, dbName string, info *SnapshotUpdate) error
}
StatePort persists previewctl state. File-based for POC; interface accommodates Postgres/etcd later.
type StepEvent ¶
type StepEvent struct {
Step string // e.g. "allocate_ports", "create_worktree", "create_database"
Status StepStatus
Message string // human-readable detail
Error error // non-nil when Status == StepFailed
}
StepEvent is emitted by the manager at each lifecycle transition.
type StepHooks ¶
type StepHooks struct {
Before []HookDef `yaml:"before,omitempty"`
After []HookDef `yaml:"after,omitempty"`
}
StepHooks defines before and after hooks for a step.
type StepStatus ¶
type StepStatus string
StepStatus represents the status of a lifecycle step.
const ( StepStarted StepStatus = "started" StepCompleted StepStatus = "completed" StepFailed StepStatus = "failed" StepSkipped StepStatus = "skipped" )
type TemplateContext ¶
type TemplateContext struct {
Ports PortMap
Databases map[string]*DatabaseInfo
}
TemplateContext holds the values available for template substitution.
type ValidationError ¶
type ValidationError struct {
Errors []string
}
ValidationError collects multiple validation issues.
func (*ValidationError) Error ¶
func (e *ValidationError) Error() string
type WorktreeConfig ¶
type WorktreeConfig struct {
BasePath string `yaml:"basePath"`
// SymlinkPatterns are glob patterns for gitignored files to symlink from the
// main worktree into each new worktree (e.g. ".env" matches .env files recursively).
// These are typically secret/config files that exist in the main repo but aren't
// tracked by git. Each pattern is matched recursively across the entire repo.
SymlinkPatterns []string `yaml:"symlinkPatterns,omitempty"`
}
WorktreeConfig defines worktree settings.