refactor(workload): plugin architecture wave + apps UI + volume scopes
Completes the workload-first refactor's plugin layer:
- internal/workload/plugin/ — Source/Trigger plugin contract,
registry, types (Workload, DeploymentIntent, InboundEvent,
PublicFace). Self-registering init() pattern + blank-import
in cmd/server/main.go.
- Source plugins: image (blue-green with multi-face proxy routing),
compose, static. Trigger plugins: registry, git, manual.
- internal/deployer/dispatch.go — DispatchPlugin/Teardown/Reconcile
seam routing the legacy deployer through plugins.
- internal/api/workload_*.go — REST surface: workloads, env,
volumes, chain (parent/children), promote-from. hooks.go
serves /api/hooks/kinds/{kind}/schema for the wizard.
- internal/store: workload_env (encrypt-at-rest secrets) and
workload_volumes tables, keyed on workload_id.
- cmd/server/static_backend.go — phantom-row adapter delegating
the static source plugin to the legacy staticsite.Manager
(deleted at hard cutover once the static inline port lands).
- web/src/routes/apps/ — /apps list + /apps/new wizard +
/apps/[id] detail with kind-aware compose / image / static
forms (Advanced JSON toggle), env panel, volumes panel,
webhook panel, chain panel, manual deploy.
Volume scope generalization (v2 resolver):
- internal/volume.ResolveWorkloadPath (workload-keyed, sits
next to legacy ResolvePath). Honors all VolumeScope values:
absolute, ephemeral, instance, stage, project, project_named,
named. internal/workload/plugin/source/image/image.go
computeMounts wires settings + imageTag through. Coverage in
internal/volume/resolver_test.go (portable Linux/Windows via
t.TempDir).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,79 @@
|
||||
// Package plugin defines the Source and Trigger contracts that decouple
|
||||
// Tinyforge's deployer pipeline from any single deployable shape (image,
|
||||
// compose, static, ...) or any single redeploy trigger (registry push,
|
||||
// git push, manual, ...).
|
||||
//
|
||||
// A Workload is the unifying user-facing entity. It carries an opaque
|
||||
// SourceConfig (interpreted by the matching Source) and an opaque
|
||||
// TriggerConfig (interpreted by the matching Trigger). Both kinds are
|
||||
// strings; lookup happens through the registries below.
|
||||
//
|
||||
// New deployable shapes or trigger types are added by:
|
||||
// 1. Implementing Source or Trigger in a sub-package.
|
||||
// 2. Calling Register (Source/Trigger) from that package's init().
|
||||
// 3. Blank-importing the sub-package from cmd/ to pull the registration in.
|
||||
//
|
||||
// No code in this package or in the deployer/api layers needs to change
|
||||
// when a new kind appears — the registry is the only seam.
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/alexei/tinyforge/internal/dns"
|
||||
"github.com/alexei/tinyforge/internal/docker"
|
||||
"github.com/alexei/tinyforge/internal/events"
|
||||
"github.com/alexei/tinyforge/internal/health"
|
||||
"github.com/alexei/tinyforge/internal/notify"
|
||||
"github.com/alexei/tinyforge/internal/proxy"
|
||||
"github.com/alexei/tinyforge/internal/store"
|
||||
)
|
||||
|
||||
// Deps is the bundle of services every Source or Trigger may need. Passed
|
||||
// per-call so plugin implementations stay stateless and testable.
|
||||
type Deps struct {
|
||||
Store *store.Store
|
||||
Docker *docker.Client
|
||||
Proxy proxy.Provider
|
||||
DNS dns.Provider // nil when wildcard DNS is active
|
||||
Health *health.Checker
|
||||
Notifier *notify.Notifier
|
||||
Events EventPublisher
|
||||
EncKey [32]byte // pass-through to crypto.Encrypt/Decrypt for config secrets
|
||||
}
|
||||
|
||||
// EventPublisher matches the deployer's existing event-bus surface. Kept as
|
||||
// a local interface so plugin/ does not pull events transitively into every
|
||||
// caller.
|
||||
type EventPublisher interface {
|
||||
Publish(evt events.Event)
|
||||
}
|
||||
|
||||
// Workload is the value-shape every plugin consumes. It is constructed by
|
||||
// the store layer from the workloads row plus its decoded JSON blobs; the
|
||||
// physical schema can evolve independently of this struct.
|
||||
type Workload struct {
|
||||
ID string
|
||||
Name string
|
||||
GroupID string // formerly app_id; "" = ungrouped
|
||||
ParentWorkloadID string // for stage chains; "" = root
|
||||
|
||||
SourceKind string // "image" | "compose" | "static" | ...
|
||||
SourceConfig json.RawMessage // shape determined by SourceKind
|
||||
|
||||
TriggerKind string // "registry" | "git" | "manual" | "cron" | ...
|
||||
TriggerConfig json.RawMessage // shape determined by TriggerKind
|
||||
|
||||
PublicFaces []PublicFace // zero or more public routes
|
||||
|
||||
// Notification + webhook security live on the Workload itself rather
|
||||
// than on per-kind tables so the rules are consistent across shapes.
|
||||
NotificationURL string
|
||||
NotificationSecret string
|
||||
WebhookSecret string
|
||||
WebhookSigningSecret string
|
||||
WebhookRequireSignature bool
|
||||
|
||||
CreatedAt string
|
||||
UpdatedAt string
|
||||
}
|
||||
Reference in New Issue
Block a user