Documentation
¶
Index ¶
- func BuildComposeEnv(projectName, envName string, ports PortMap) []string
- func ComposeProjectName(projectName, envName string) string
- func ExecuteCoreHook(ctx context.Context, hookScript string, declaredOutputs []string, env []string, ...) (map[string]string, error)
- func GenerateComposeFile(cfg *ProjectConfig, manifest *Manifest) ([]byte, error)
- func GenerateErrorPages(envName string) map[string][]byte
- func GenerateNginxConfig(cfg *ProjectConfig, manifest *Manifest, domain string) ([]byte, error)
- func Hostname() string
- func ParseComposeFile(path string) (map[string]InfraService, error)
- func RenderEnvFileContent(env map[string]string) []byte
- 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 SanitizeName(name string) string
- func TopologicalSort(services map[string]ServiceConfig) ([]string, error)
- func ValidateConfig(cfg *ProjectConfig) error
- func ValidateConfigWithFS(cfg *ProjectConfig, projectRoot string, fileExists func(string) bool) error
- func WriteManifest(m *Manifest, w io.Writer) error
- type AuditEntry
- type ComposeConfig
- type ComputeAccess
- type ComputeAccessInfo
- type ComputeHooks
- type ComputePort
- type ComputeResources
- type DomainLocalComputeAccess
- func (l *DomainLocalComputeAccess) Exec(ctx context.Context, command string, env []string) (string, error)
- func (l *DomainLocalComputeAccess) ReadFile(_ context.Context, relPath string) ([]byte, error)
- func (l *DomainLocalComputeAccess) Root() string
- func (l *DomainLocalComputeAccess) SetStderr(w io.Writer)
- func (l *DomainLocalComputeAccess) VerboseExec(ctx context.Context, command string, env []string) (string, error)
- func (l *DomainLocalComputeAccess) WriteFile(_ context.Context, relPath string, data []byte, mode os.FileMode) error
- type DomainSSHComputeAccess
- func (s *DomainSSHComputeAccess) Exec(ctx context.Context, command string, env []string) (string, error)
- func (s *DomainSSHComputeAccess) Host() string
- func (s *DomainSSHComputeAccess) ProxyCommand() string
- func (s *DomainSSHComputeAccess) ReadFile(ctx context.Context, relPath string) ([]byte, error)
- func (s *DomainSSHComputeAccess) Root() string
- func (s *DomainSSHComputeAccess) SSHArgs() []string
- func (s *DomainSSHComputeAccess) SetStderr(w io.Writer)
- func (s *DomainSSHComputeAccess) User() string
- func (s *DomainSSHComputeAccess) VerboseExec(ctx context.Context, command string, env []string) (string, error)
- func (s *DomainSSHComputeAccess) WriteFile(ctx context.Context, relPath string, data []byte, _ os.FileMode) error
- type EnvironmentDetail
- type EnvironmentEntry
- func (e *EnvironmentEntry) AppendAudit(entry AuditEntry)
- func (e *EnvironmentEntry) DisableService(name string)
- func (e *EnvironmentEntry) EnableService(name string)
- func (e *EnvironmentEntry) GetEnv(key string) (string, bool)
- func (e *EnvironmentEntry) InvalidateStep(name, reason string) bool
- func (e *EnvironmentEntry) InvalidateStepsFrom(stepName string, orderedSteps []string)
- func (e *EnvironmentEntry) IsManagedWorktree() bool
- func (e *EnvironmentEntry) IsServiceEnabled(name string) bool
- func (e *EnvironmentEntry) SetEnv(key, value string)
- func (e *EnvironmentEntry) SetStepRecord(rec *StepRecord)
- func (e *EnvironmentEntry) StepCompleted(name string) bool
- func (e *EnvironmentEntry) StepOutputs(name string) map[string]any
- func (e *EnvironmentEntry) WorktreePath() string
- type EnvironmentMode
- type EnvironmentStatus
- type InfraService
- type InfrastructureConfig
- type Manager
- func (m *Manager) Attach(ctx context.Context, envName string, worktreePath string) (*EnvironmentEntry, error)
- func (m *Manager) BuildProvisionerStepOrder() []string
- func (m *Manager) BuildRunnerStepOrder() []string
- func (m *Manager) BuildSSHComputeAccess(entry *EnvironmentEntry) ComputeAccess
- func (m *Manager) CoreInit(ctx context.Context, svcName string) error
- func (m *Manager) CoreReset(ctx context.Context, svcName, envName string) error
- func (m *Manager) Destroy(ctx context.Context, envName string) error
- func (m *Manager) DryRunStep(ctx context.Context, envName, stepName string) (string, error)
- func (m *Manager) GetEnvironment(ctx context.Context, envName string) (*EnvironmentEntry, error)
- func (m *Manager) Init(ctx context.Context, envName, branch, baseBranch string) (*EnvironmentEntry, error)
- func (m *Manager) List(ctx context.Context) ([]*EnvironmentEntry, error)
- func (m *Manager) PrintStep(ctx context.Context, envName, stepName string) (string, error)
- func (m *Manager) Provision(ctx context.Context, envName, branch, baseBranch, fromStep string) (*EnvironmentEntry, error)
- func (m *Manager) ProvisionAttach(ctx context.Context, envName, worktreePath, fromStep string) (*EnvironmentEntry, error)
- func (m *Manager) Reconcile(ctx context.Context, envName string, dryRun ...bool) (*ReconcileReport, error)
- func (m *Manager) Run(ctx context.Context, manifestPath, fromStep string) error
- func (m *Manager) RunCoreHook(ctx context.Context, svcName, action, envName string) (map[string]string, error)
- func (m *Manager) RunStep(ctx context.Context, envName, stepName string) error
- func (m *Manager) RunSteps(ctx context.Context, envName string, stepNames []string) error
- func (m *Manager) SaveEnvironment(ctx context.Context, envName string, entry *EnvironmentEntry) error
- func (m *Manager) SetNoCache(v bool)
- func (m *Manager) SetStatus(ctx context.Context, envName string, status EnvironmentStatus) error
- func (m *Manager) Status(ctx context.Context, envName string) (*EnvironmentDetail, error)
- type ManagerDeps
- type Manifest
- type ManifestInfrastructure
- type ManifestService
- type NetworkingPort
- type NoopReporter
- type PortMap
- type ProgressReporter
- type ProjectConfig
- type ProvisionerConfig
- type ProvisionerServiceConfig
- type ProxyConfig
- type ReconcileReport
- type ReconcileResult
- type RunnerConfig
- type SSHComputeAccessOpts
- type SSHConfig
- type ServiceConfig
- type ServiceProxy
- type ServiceProxyTarget
- type State
- type StatePort
- type StepEvent
- type StepOpts
- type StepRecord
- type StepRecordStatus
- type StepStatus
- type TemplateContext
- type VMInfo
- type VMSpec
- type ValidationError
- type VerifyFunc
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func BuildComposeEnv ¶ added in v0.0.6
BuildComposeEnv builds the environment variables for running docker compose commands. Includes COMPOSE_PROJECT_NAME and port mappings (e.g., REDIS_PORT=6421). The returned slice is suitable for passing to ComputeAccess.Exec.
func ComposeProjectName ¶ added in v0.0.4
ComposeProjectName returns the compose project name for this environment. Sanitizes to match Docker Compose requirements: lowercase alphanumeric, hyphens, underscores.
func ExecuteCoreHook ¶ added in v0.0.2
func ExecuteCoreHook(ctx context.Context, hookScript string, declaredOutputs []string, env []string, workdir string, stderr io.Writer) (map[string]string, error)
ExecuteCoreHook runs a core service hook script and captures KEY=VALUE outputs. Both stdout and stderr stream to the terminal in real-time for visibility. KEY=VALUE lines on stdout are redacted (value replaced with ********) before display. stdout is also captured and parsed for KEY=VALUE pairs. Returns the captured outputs, validated against declaredOutputs.
func GenerateComposeFile ¶ added in v0.0.7
func GenerateComposeFile(cfg *ProjectConfig, manifest *Manifest) ([]byte, error)
GenerateComposeFile generates a Docker Compose YAML file from service definitions. Only services with a `start` command are included. nginx is always added implicitly. The compose file contains ALL startable services — autostart controls which are started on create, not which are generated.
func GenerateErrorPages ¶ added in v0.0.7
GenerateErrorPages returns a map of filename → HTML content for nginx error pages.
func GenerateNginxConfig ¶ added in v0.0.7
func GenerateNginxConfig(cfg *ProjectConfig, manifest *Manifest, domain string) ([]byte, error)
GenerateNginxConfig generates an nginx configuration file from the manifest and service definitions. Each service gets its own server block with explicit hostname matching.
Enabled services get full proxy blocks with a custom 502 error page. Disabled services (known but not started) get a friendly status page telling the user how to start the service. Unknown subdomains hit the default server with a 404 page.
func Hostname ¶ added in v0.0.4
func Hostname() string
Hostname returns the machine hostname for audit logging.
func ParseComposeFile ¶ added in v0.0.2
func ParseComposeFile(path string) (map[string]InfraService, error)
ParseComposeFile reads a docker compose YAML file and extracts infrastructure service definitions including service names, images, and port mappings.
func RenderEnvFileContent ¶ added in v0.0.4
RenderEnvFileContent renders a map of env vars to KEY=VALUE format content. Keys are sorted for deterministic output.
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:
- {{services.<name>.port}} — allocated port for an application service
- {{infrastructure.<name>.port}} — allocated port for an infrastructure service
- {{provisioner.<service>.<OUTPUT>}} — output value from a provisioner service
- {{env.name}} — the current environment name
- {{store.<key>}} — value from the persistent key-value store
- {{proxy.url.<service>}} — full URL for a service (https://{env}--{service}.{domain})
- {{proxy.domain}} — the proxy domain (e.g., "preview.airgoods.com")
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 SanitizeName ¶ added in v0.0.5
SanitizeName replaces characters not safe for use in database names, file paths, and Docker Compose project names. Lowercase alphanumeric, hyphens, and underscores only.
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, and template variable references. 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 AuditEntry ¶ added in v0.0.4
type AuditEntry struct {
Timestamp time.Time `json:"timestamp"`
Step string `json:"step"`
Action string `json:"action"` // "executed", "skipped", "verified", "verify_failed", "invalidated", "failed"
Machine string `json:"machine"`
DurationMs int64 `json:"durationMs,omitempty"`
Message string `json:"message,omitempty"`
Error string `json:"error,omitempty"`
}
AuditEntry is an append-only log entry recording what happened during lifecycle operations.
type ComposeConfig ¶ added in v0.0.7
type ComposeConfig struct {
Autostart []string `yaml:"autostart"` // services started on create (proxy is always implicit if enabled)
Image string `yaml:"image"` // base Docker image for app containers (e.g., "node:20")
Proxy *ProxyConfig `yaml:"proxy,omitempty"` // reverse proxy configuration
}
ComposeConfig defines how previewctl generates and manages Docker Compose for application services in remote mode.
type ComputeAccess ¶ added in v0.0.4
type ComputeAccess interface {
// WriteFile writes content to a path relative to the compute root.
WriteFile(ctx context.Context, relPath string, data []byte, mode os.FileMode) error
// ReadFile reads content from a path relative to the compute root.
ReadFile(ctx context.Context, relPath string) ([]byte, error)
// Exec runs a command in the compute root directory.
// Stderr streams to the configured writer. Stdout is captured and returned silently.
Exec(ctx context.Context, command string, env []string) (stdout string, err error)
// VerboseExec runs a command and tees stdout to stderr for real-time visibility.
// Use for user-defined hooks and build commands where output should always be visible.
VerboseExec(ctx context.Context, command string, env []string) (stdout string, err error)
// Root returns the compute root path (local path or remote working dir).
Root() string
}
ComputeAccess provides uniform access to a compute location. Local mode wraps filesystem operations; remote mode wraps SSH/SCP.
func NewDomainLocalComputeAccess ¶ added in v0.0.4
func NewDomainLocalComputeAccess(root string) ComputeAccess
func NewDomainSSHComputeAccess ¶ added in v0.0.4
func NewDomainSSHComputeAccess(host, user, root string) ComputeAccess
NewDomainSSHComputeAccess creates a ComputeAccess backed by SSH to a remote host. This is the direct-mode constructor (backward compatible).
func NewDomainSSHComputeAccessWithOpts ¶ added in v0.0.7
func NewDomainSSHComputeAccessWithOpts(opts SSHComputeAccessOpts) ComputeAccess
NewDomainSSHComputeAccessWithOpts creates a ComputeAccess with full SSH options.
type ComputeAccessInfo ¶ added in v0.0.4
type ComputeAccessInfo struct {
Type string `json:"type"` // "local" or "ssh"
Path string `json:"path,omitempty"` // local worktree path or remote root
Host string `json:"host,omitempty"` // VM hostname (ssh)
User string `json:"user,omitempty"` // SSH user
ManagedWorktree bool `json:"managedWorktree,omitempty"` // true = created by previewctl
Metadata map[string]string `json:"metadata,omitempty"` // hook-provided metadata (e.g., "vm_zone", "gcp_project")
}
ComputeAccessInfo stores how to reach the compute location so environments can be reconnected across CLI invocations.
type ComputeHooks ¶ added in v0.0.4
type ComputeHooks struct {
Create string `yaml:"create"`
Destroy string `yaml:"destroy"`
Outputs []string `yaml:"outputs,omitempty"`
SSH *SSHConfig `yaml:"ssh,omitempty"`
}
ComputeHooks defines lifecycle hooks for compute resources.
type ComputePort ¶
type ComputePort interface {
// Create sets up compute resources for an environment.
// branch is the target branch to use/create.
// baseBranch is the branch to create from (empty = use branch as-is).
Create(ctx context.Context, envName string, branch string, baseBranch 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)
// DetectBranch returns the current git branch for a worktree path.
DetectBranch(ctx context.Context, worktreePath string) (string, 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 DomainLocalComputeAccess ¶ added in v0.0.4
type DomainLocalComputeAccess struct {
// contains filtered or unexported fields
}
func (*DomainLocalComputeAccess) Root ¶ added in v0.0.4
func (l *DomainLocalComputeAccess) Root() string
func (*DomainLocalComputeAccess) SetStderr ¶ added in v0.0.7
func (l *DomainLocalComputeAccess) SetStderr(w io.Writer)
func (*DomainLocalComputeAccess) VerboseExec ¶ added in v0.0.16
type DomainSSHComputeAccess ¶ added in v0.0.4
type DomainSSHComputeAccess struct {
// contains filtered or unexported fields
}
DomainSSHComputeAccess implements ComputeAccess over SSH. It uses the system ssh binary. Lives in domain to avoid circular imports.
Two connection modes:
- ProxyCommand mode: uses -o ProxyCommand=... (cloud-agnostic, no local SSH config needed)
- Direct mode: uses user@host (requires SSH config or direct access)
func (*DomainSSHComputeAccess) Host ¶ added in v0.0.4
func (s *DomainSSHComputeAccess) Host() string
Host returns the SSH host.
func (*DomainSSHComputeAccess) ProxyCommand ¶ added in v0.0.7
func (s *DomainSSHComputeAccess) ProxyCommand() string
ProxyCommand returns the configured proxy command, if any.
func (*DomainSSHComputeAccess) Root ¶ added in v0.0.4
func (s *DomainSSHComputeAccess) Root() string
func (*DomainSSHComputeAccess) SSHArgs ¶ added in v0.0.7
func (s *DomainSSHComputeAccess) SSHArgs() []string
sshArgs returns the SSH arguments for building external SSH commands (e.g., for syscall.Exec).
func (*DomainSSHComputeAccess) SetStderr ¶ added in v0.0.7
func (s *DomainSSHComputeAccess) SetStderr(w io.Writer)
func (*DomainSSHComputeAccess) User ¶ added in v0.0.4
func (s *DomainSSHComputeAccess) User() string
User returns the SSH user.
func (*DomainSSHComputeAccess) VerboseExec ¶ added in v0.0.16
type EnvironmentDetail ¶
type EnvironmentDetail struct {
Entry *EnvironmentEntry `json:"entry"`
InfraRunning bool `json:"infraRunning"`
}
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"`
ProvisionerOutputs map[string]map[string]string `json:"provisionerOutputs"`
Compute *ComputeAccessInfo `json:"compute,omitempty"`
Env map[string]string `json:"env,omitempty"` // persistent key-value store for hooks
EnabledServices []string `json:"enabledServices,omitempty"` // services currently enabled (seeded from autostart, updated by service start/stop)
Steps map[string]*StepRecord `json:"steps,omitempty"`
AuditLog []AuditEntry `json:"auditLog,omitempty"`
}
EnvironmentEntry is a tracked environment persisted in state.
func (*EnvironmentEntry) AppendAudit ¶ added in v0.0.4
func (e *EnvironmentEntry) AppendAudit(entry AuditEntry)
AppendAudit adds an audit log entry.
func (*EnvironmentEntry) DisableService ¶ added in v0.0.7
func (e *EnvironmentEntry) DisableService(name string)
DisableService removes a service from the enabled set.
func (*EnvironmentEntry) EnableService ¶ added in v0.0.7
func (e *EnvironmentEntry) EnableService(name string)
EnableService adds a service to the enabled set (idempotent).
func (*EnvironmentEntry) GetEnv ¶ added in v0.0.7
func (e *EnvironmentEntry) GetEnv(key string) (string, bool)
GetEnv reads a value from the environment's persistent store.
func (*EnvironmentEntry) InvalidateStep ¶ added in v0.0.13
func (e *EnvironmentEntry) InvalidateStep(name, reason string) bool
InvalidateStep marks a completed step as invalidated so it will re-run. Returns true if the step was actually invalidated (was previously completed).
func (*EnvironmentEntry) InvalidateStepsFrom ¶ added in v0.0.4
func (e *EnvironmentEntry) InvalidateStepsFrom(stepName string, orderedSteps []string)
InvalidateStepsFrom removes checkpoint records for the named step and all steps that come after it in the given ordered step list.
func (*EnvironmentEntry) IsManagedWorktree ¶ added in v0.0.4
func (e *EnvironmentEntry) IsManagedWorktree() bool
IsManagedWorktree returns whether the worktree was created by previewctl.
func (*EnvironmentEntry) IsServiceEnabled ¶ added in v0.0.7
func (e *EnvironmentEntry) IsServiceEnabled(name string) bool
IsServiceEnabled returns true if the service is in the enabled set.
func (*EnvironmentEntry) SetEnv ¶ added in v0.0.7
func (e *EnvironmentEntry) SetEnv(key, value string)
SetEnv sets a key-value pair in the environment's persistent store.
func (*EnvironmentEntry) SetStepRecord ¶ added in v0.0.4
func (e *EnvironmentEntry) SetStepRecord(rec *StepRecord)
SetStepRecord records a step completion/failure.
func (*EnvironmentEntry) StepCompleted ¶ added in v0.0.4
func (e *EnvironmentEntry) StepCompleted(name string) bool
StepCompleted returns true if the named step has a "completed" record.
func (*EnvironmentEntry) StepOutputs ¶ added in v0.0.4
func (e *EnvironmentEntry) StepOutputs(name string) map[string]any
StepOutputs returns the outputs map for a completed step, or nil.
func (*EnvironmentEntry) WorktreePath ¶ added in v0.0.4
func (e *EnvironmentEntry) WorktreePath() string
WorktreePath returns the worktree path from ComputeAccessInfo, or empty string.
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" StatusProvisioned EnvironmentStatus = "provisioned" StatusRunning EnvironmentStatus = "running" StatusStopped EnvironmentStatus = "stopped" StatusError EnvironmentStatus = "error" )
type InfraService ¶ added in v0.0.2
type InfraService struct {
Name string
Image string
Port int
EnvVar string // e.g., "REDIS_PORT" (extracted from ${REDIS_PORT:-6379} patterns)
}
InfraService holds parsed infrastructure service information from a compose file.
type InfrastructureConfig ¶ added in v0.0.2
type InfrastructureConfig struct {
ComposeFile string `yaml:"compose_file"`
}
InfrastructureConfig holds infrastructure configuration.
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
Manager orchestrates environment lifecycle by coordinating all ports.
func NewManager ¶
func NewManager(deps ManagerDeps) *Manager
NewManager creates a new Manager with the given dependencies.
func (*Manager) Attach ¶ added in v0.0.4
func (m *Manager) Attach(ctx context.Context, envName string, worktreePath string) (*EnvironmentEntry, error)
Attach creates a preview environment using an existing worktree.
func (*Manager) BuildProvisionerStepOrder ¶ added in v0.0.4
BuildProvisionerStepOrder returns the canonical step order for the provisioner phase.
func (*Manager) BuildRunnerStepOrder ¶ added in v0.0.4
BuildRunnerStepOrder returns the canonical step order for the runner phase.
func (*Manager) BuildSSHComputeAccess ¶ added in v0.0.7
func (m *Manager) BuildSSHComputeAccess(entry *EnvironmentEntry) ComputeAccess
buildSSHComputeAccess constructs SSH compute access from an environment entry. It resolves the SSH config template from the project config using store values, or falls back to stored proxy_command in metadata, or direct host connection.
func (*Manager) DryRunStep ¶ added in v0.0.7
DryRunStep shows a diff of what a step would change. For generation steps, compares the current file on the VM with what would be generated. For other steps, describes what would happen.
func (*Manager) GetEnvironment ¶ added in v0.0.7
GetEnvironment returns a single environment entry from state, or nil if not found.
func (*Manager) Init ¶
func (m *Manager) Init(ctx context.Context, envName, branch, baseBranch string) (*EnvironmentEntry, error)
Init creates a new environment end-to-end: provisions then runs. branch is the target branch. baseBranch is the branch to create from (empty = use branch as-is).
func (*Manager) PrintStep ¶ added in v0.0.7
PrintStep generates and returns the full content for a step without executing it.
func (*Manager) Provision ¶ added in v0.0.4
func (m *Manager) Provision(ctx context.Context, envName, branch, baseBranch, fromStep string) (*EnvironmentEntry, error)
Provision runs the provisioner phase only. Does NOT run the runner. fromStep invalidates that step and all subsequent steps, forcing re-execution.
func (*Manager) ProvisionAttach ¶ added in v0.0.4
func (m *Manager) ProvisionAttach(ctx context.Context, envName, worktreePath, fromStep string) (*EnvironmentEntry, error)
ProvisionAttach runs the provisioner phase on an existing worktree.
func (*Manager) Reconcile ¶ added in v0.0.7
func (m *Manager) Reconcile(ctx context.Context, envName string, dryRun ...bool) (*ReconcileReport, error)
Reconcile verifies all runner steps and re-executes any whose side effects are missing. Hook-owned steps are skipped since previewctl can't verify user-defined hooks.
Progress is reported via the ProgressReporter for each step:
- StepStarted: "Verifying <step>..."
- StepCompleted: verification passed or heal succeeded
- StepFailed: heal failed
- StepSkipped: hook-owned or never completed
func (*Manager) Run ¶ added in v0.0.4
Run reads a manifest and executes the runner phase only. Stateless — does not persist state.
func (*Manager) RunCoreHook ¶ added in v0.0.2
func (*Manager) RunStep ¶ added in v0.0.7
RunStep executes a single runner-phase step in isolation. Loads the environment from state, reconstructs compute access, reads the manifest, and runs only the specified step (always executes, ignoring cache).
func (*Manager) RunSteps ¶ added in v0.0.7
RunSteps executes a sequence of runner steps in order, reusing a single SSH connection and step registry. All steps are forced (cache bypassed).
func (*Manager) SaveEnvironment ¶ added in v0.0.7
func (m *Manager) SaveEnvironment(ctx context.Context, envName string, entry *EnvironmentEntry) error
SaveEnvironment persists an environment entry to state.
func (*Manager) SetNoCache ¶ added in v0.0.4
SetNoCache sets whether checkpoint caching is disabled.
type ManagerDeps ¶
type ManagerDeps struct {
Compute ComputePort
Networking NetworkingPort
State StatePort
Progress ProgressReporter
Config *ProjectConfig
ProjectRoot string
NoCache bool
}
ManagerDeps holds the dependencies for creating a Manager.
type Manifest ¶ added in v0.0.4
type Manifest struct {
Version int `json:"version"`
EnvName string `json:"env_name"`
ProjectName string `json:"project_name"`
Branch string `json:"branch"`
Mode string `json:"mode"`
Ports PortMap `json:"ports"`
ProvisionerOutputs map[string]map[string]string `json:"provisioner_outputs,omitempty"`
Services map[string]ManifestService `json:"services,omitempty"`
Infrastructure *ManifestInfrastructure `json:"infrastructure,omitempty"`
EnabledServices []string `json:"enabled_services,omitempty"`
}
Manifest is the single source of truth written to the compute location. It contains fully resolved values — no template variables.
func BuildManifest ¶ added in v0.0.4
func BuildManifest( cfg *ProjectConfig, envName, branch, mode string, ports PortMap, provisionerOutputs map[string]map[string]string, store map[string]string, ) (*Manifest, error)
BuildManifest resolves all template variables and builds a complete manifest.
func ReadManifest ¶ added in v0.0.4
ReadManifest decodes a manifest from the given reader.
type ManifestInfrastructure ¶ added in v0.0.4
type ManifestInfrastructure struct {
ComposeFile string `json:"compose_file"`
}
ManifestInfrastructure describes infrastructure configuration.
type ManifestService ¶ added in v0.0.4
type ManifestService struct {
Path string `json:"path"`
EnvFile string `json:"env_file"`
Env map[string]string `json:"env,omitempty"`
}
ManifestService describes a service with fully resolved env vars.
type NetworkingPort ¶
type NetworkingPort interface {
// AllocatePorts returns deterministic port assignments for all services and infrastructure.
AllocatePorts(envName string) (PortMap, error)
// 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)
func (NoopReporter) StderrWriter ¶ added in v0.0.7
func (NoopReporter) StderrWriter() io.Writer
type PortMap ¶
PortMap maps service names to allocated ports.
func AllocatePortBlock ¶ added in v0.0.2
AllocatePortBlock selects a block of ports for an environment and assigns one port per service name. Ports are checked for availability. Returns a PortMap with service names mapped to allocated ports.
type ProgressReporter ¶
type ProgressReporter interface {
OnStep(event StepEvent)
// StderrWriter returns a writer for hook stderr output.
// The reporter may indent or buffer this output for display.
StderrWriter() io.Writer
}
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"`
Provisioner ProvisionerConfig `yaml:"provisioner"`
Infrastructure *InfrastructureConfig `yaml:"infrastructure,omitempty"`
Services map[string]ServiceConfig `yaml:"services"`
Runner *RunnerConfig `yaml:"runner,omitempty"`
// Mode is the deployment mode (e.g., "local"). Set at load time, not from YAML.
Mode string `yaml:"-"`
// InfraServices is populated by parsing the compose file referenced in
// Infrastructure.ComposeFile. It is not read from YAML directly.
InfraServices map[string]InfraService `yaml:"-"`
}
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 LoadConfigWithOverlay ¶ added in v0.0.2
func LoadConfigWithOverlay(basePath, mode string) (*ProjectConfig, error)
LoadConfigWithOverlay loads a base config and merges a mode-specific overlay if present.
func ParseConfig ¶
func ParseConfig(data []byte) (*ProjectConfig, error)
ParseConfig parses YAML bytes into a ProjectConfig.
func (*ProjectConfig) ServiceNames ¶ added in v0.0.2
func (c *ProjectConfig) ServiceNames() []string
ServiceNames returns a sorted list of all service and infrastructure names that need port assignments.
type ProvisionerConfig ¶ added in v0.0.4
type ProvisionerConfig struct {
Before string `yaml:"before,omitempty"`
After string `yaml:"after,omitempty"`
Compute *ComputeHooks `yaml:"compute,omitempty"`
Services map[string]ProvisionerServiceConfig `yaml:"services,omitempty"`
}
ProvisionerConfig holds managed provisioner services with hook-driven lifecycle.
type ProvisionerServiceConfig ¶ added in v0.0.4
type ProvisionerServiceConfig struct {
Outputs []string `yaml:"outputs,omitempty"`
Init string `yaml:"init,omitempty"`
Seed string `yaml:"seed,omitempty"`
Reset string `yaml:"reset,omitempty"`
Destroy string `yaml:"destroy,omitempty"`
}
ProvisionerServiceConfig defines a provisioner service managed by hooks.
type ProxyConfig ¶ added in v0.0.7
type ProxyConfig struct {
Enabled *bool `yaml:"enabled,omitempty"` // defaults to true if omitted
Domain string `yaml:"domain"` // e.g., "preview.airgoods.com"
Type string `yaml:"type,omitempty"` // "nginx" (default). Future: "traefik", "caddy"
}
ProxyConfig defines the reverse proxy that sits in front of preview services.
func (*ProxyConfig) IsEnabled ¶ added in v0.0.7
func (p *ProxyConfig) IsEnabled() bool
IsEnabled returns whether the proxy is enabled (defaults to true).
func (*ProxyConfig) ResolvedType ¶ added in v0.0.7
func (p *ProxyConfig) ResolvedType() string
ResolvedType returns the proxy type, defaulting to "nginx".
type ReconcileReport ¶ added in v0.0.7
type ReconcileReport struct {
Results []ReconcileResult
Healed int
Failed int
OK int
Skipped int
NotRun int
}
ReconcileReport is the full outcome of a reconcile run.
type ReconcileResult ¶ added in v0.0.7
type ReconcileResult struct {
Step string
Action string // "ok", "healed", "failed", "skipped", "not_run"
Message string
DurationMs int64
}
ReconcileResult describes the outcome of reconciling a single step.
type RunnerConfig ¶ added in v0.0.4
type RunnerConfig struct {
Before string `yaml:"before,omitempty"`
Deploy string `yaml:"deploy,omitempty"`
Destroy string `yaml:"destroy,omitempty"`
After string `yaml:"after,omitempty"`
Compose *ComposeConfig `yaml:"compose,omitempty"`
}
RunnerConfig holds runner lifecycle hooks.
type SSHComputeAccessOpts ¶ added in v0.0.7
type SSHComputeAccessOpts struct {
Host string // SSH host (direct mode) or logical hostname (proxy mode)
User string
Root string
ProxyCommand string // when set, uses -o ProxyCommand=... instead of relying on SSH config
IdentityFile string // path to SSH private key (optional)
}
SSHComputeAccessOpts configures SSH compute access creation.
type SSHConfig ¶ added in v0.0.7
type SSHConfig struct {
// ProxyCommand is the SSH ProxyCommand used to tunnel into the VM.
// Example: "gcloud compute start-iap-tunnel {{store.VM_NAME}} %p --listen-on-stdin --zone={{store.GCP_ZONE}} --project={{store.GCP_PROJECT}}"
ProxyCommand string `yaml:"proxy_command"`
// User is the SSH username. Example: "{{store.SSH_USER}}"
User string `yaml:"user"`
// UserCommand is a shell command that resolves the SSH username dynamically.
// Useful when different users (CI vs human) SSH into the same VM.
// Example: "gcloud compute os-login describe-profile --format='value(posixAccounts[0].username)'"
// Takes precedence over User when set.
UserCommand string `yaml:"user_command"`
// IdentityFile is the path to the SSH private key. Supports ~ expansion.
// Example: "~/.ssh/google_compute_engine"
IdentityFile string `yaml:"identity_file"`
// Root is the remote working directory. Defaults to "/app".
Root string `yaml:"root,omitempty"`
}
SSHConfig defines how previewctl connects to remote compute via SSH. All fields support {{store.KEY}} template resolution.
type ServiceConfig ¶
type ServiceConfig struct {
Path string `yaml:"path"`
Port int `yaml:"port,omitempty"` // fixed port — skips the port allocator when set
Command string `yaml:"command,omitempty"`
DependsOn []string `yaml:"depends_on,omitempty"`
Env map[string]string `yaml:"env,omitempty"`
EnvFile string `yaml:"env_file,omitempty"` // relative to path, defaults to ".env.local"
Build string `yaml:"build,omitempty"` // build command (run on host before container starts)
Start string `yaml:"start,omitempty"` // start command (run inside container). Required for compose generation.
Proxy []ServiceProxy `yaml:"proxy,omitempty"` // optional reverse proxy rules for nginx
}
ServiceConfig defines an application service.
func (ServiceConfig) ResolvedEnvFile ¶ added in v0.0.2
func (s ServiceConfig) ResolvedEnvFile() string
ResolvedEnvFile returns the env file path relative to the service path. Defaults to ".env.local" if not configured.
type ServiceProxy ¶ added in v0.0.7
type ServiceProxy struct {
Path string `yaml:"path"` // source path the browser sends, e.g., "/api" or "/iapi"
TargetPath string `yaml:"target_path,omitempty"` // path rewritten to on the target service. Defaults to Path if omitted.
To ServiceProxyTarget `yaml:"to"`
}
ServiceProxy defines a reverse proxy rule on a service's subdomain. When configured, nginx generates a location block that proxies the given path to the target service's port (same-origin for IAP cookie compatibility).
func (*ServiceProxy) ResolvedTargetPath ¶ added in v0.0.7
func (p *ServiceProxy) ResolvedTargetPath() string
ResolvedTargetPath returns the target path, defaulting to Path if not set.
type ServiceProxyTarget ¶ added in v0.0.7
type ServiceProxyTarget struct {
Service string `yaml:"service"` // target service name, resolved to its port at generation time
}
ServiceProxyTarget identifies the target service for a proxy rule.
type State ¶
type State struct {
Version int `json:"version"`
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
}
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 StepOpts ¶ added in v0.0.4
type StepOpts struct {
Name string
StartMsg string
CompleteMsg *string
Fn func() error
Verify VerifyFunc // nil = pure, skip on checkpoint alone
Outputs func() map[string]any // capture outputs after success
}
StepOpts configures step execution behavior.
type StepRecord ¶ added in v0.0.4
type StepRecord struct {
Name string `json:"name"`
Status StepRecordStatus `json:"status"`
StartedAt time.Time `json:"startedAt"`
FinishedAt time.Time `json:"finishedAt"`
DurationMs int64 `json:"durationMs"`
Machine string `json:"machine"`
Error string `json:"error,omitempty"`
Outputs map[string]any `json:"outputs,omitempty"`
}
StepRecord is the checkpoint persisted for a single step execution.
type StepRecordStatus ¶ added in v0.0.4
type StepRecordStatus string
StepRecordStatus represents the persisted outcome of a step.
const ( StepRecordCompleted StepRecordStatus = "completed" StepRecordFailed StepRecordStatus = "failed" StepRecordInvalidated StepRecordStatus = "invalidated" )
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" StepStreaming StepStatus = "streaming" // stop spinner, hook will stream its own output )
type TemplateContext ¶
type TemplateContext struct {
ServicePorts PortMap
InfraPorts PortMap
ProvisionerOutputs map[string]map[string]string
CurrentService string // set per-service during rendering, enables {{self.port}}
EnvName string // environment name, enables {{env.name}}
Store map[string]string // persistent key-value store, enables {{store.KEY}}
ProxyDomain string // proxy domain (e.g., "preview.airgoods.com"), enables {{proxy.*}}
}
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 VerifyFunc ¶ added in v0.0.4
VerifyFunc checks that a previously-completed step's side effects still hold.