feat(cutover): hard legacy cutover — drop projects/stacks/sites/deploys
Build / build (push) Successful in 10m39s
Build / build (push) Successful in 10m39s
The clean-break delete that closes the workload-first refactor arc.
Net diff: ~30 backend files deleted, ~20 modified, ~12k LOC removed
on the Go side; entire /projects /stacks /sites /deploy frontend
trees gone; ~6.7k LOC removed on the Svelte/TypeScript side.
Backend
- API handlers gone: internal/api/{projects,stages,stage_env,stacks,
static_sites,deploys,instances,volume_browser}.go
- Store CRUD + tests gone: internal/store/{projects,stages,stage_env,
stacks,static_sites,static_site_secrets,deploys,poll_state,volumes,
workload_sync}.go (+ _test.go siblings)
- Legacy deployer pipeline gone: internal/deployer/{bluegreen,promote,
rollback,subdomain,resolver_test}.go; deployer.go trimmed to just the
dispatch surface used by the plugin pipeline
- internal/staticsite/{manager,healthcheck}.go and
internal/stack/manager.go gone (the rest of those packages stay as
helpers imported by the static + compose plugins)
- internal/registry/poller.go gone (legacy registry poller)
- internal/volume.ResolvePath gone; ResolveWorkloadPath stays
- internal/webhook: handleWebhook (project) + handleSiteWebhook (site)
gone; only POST /api/webhook/triggers/{secret} remains
- workload-side webhook URL handlers (getWorkloadWebhook +
regenerateWorkloadWebhook + EnsureWorkloadWebhookSecret +
SetWorkloadWebhookSecret + GetWorkloadByWebhookSecret) gone — they
minted URLs that would 404 against the new trigger-only ingress
- cmd/server/main.go: dropped staticsite.Manager, stack.Manager,
staticsite.HealthChecker, registry poller, SetSiteSyncTriggerer,
SetStaticSiteManager, SetStackManager, wireStaticBackend
- store/store.go: idempotent DROP TABLE IF EXISTS for every legacy
table (projects, stages, stage_env, volumes, deploys, deploy_logs,
poll_states, stacks, stack_revisions, stack_deploys, static_sites,
static_site_secrets); FK order children-then-parents
- store/models.go: dropped Project, Stage, Deploy, DeployLog, StageEnv,
Volume, StaticSite, StaticSiteSecret, Stack, StackRevision,
StackDeploy types; kept WorkloadKind constants as documented strings
- internal/store/helpers.go (new): BoolToInt, rowScanner,
GenerateWebhookSecret extracted from deleted CRUD files
- internal/api/secrets.go (new): forwards to store.GenerateWebhookSecret
so api + store paths share one secret-generation impl (no
panic-vs-UUID-fallback divergence)
- internal/reconciler/reconciler.go: dropped legacy stack-by-compose
+ static-site label paths; only canonical tinyforge.workload.id
dispatch remains
- providers (gitea_content/github_provider/gitlab_provider) gained
path-traversal rejection on every tree entry
- internal/webhook ParsedImage / ParseImageRef demoted to package-
private (no external callers)
Frontend
- /projects /stacks /sites /deploy routes deleted (entire trees)
- ProjectCard / InstanceCard / StaleContainerCard components deleted
- api.ts: dropped every project/stage/stack/site/deploy/instance
helper + types (Project, Stage, Stack, StaticSite, Deploy,
Instance, Volume, etc.); kept Workload, Container, App, Settings,
Registry, EventTrigger, LogScanRule, webhook envelopes
- WorkloadWebhook type + getWorkloadWebhook/regenerateWorkloadWebhook
api functions gone (mirror of the backend deletion above)
- web/src/routes/+layout.svelte: dropped /projects /sites /stacks
/deploy nav entries, trimmed quick-nav keymap
- web/src/routes/+page.svelte: dashboard rewrite — reads
listWorkloads + listContainers only; 4-card stat grid
(workloads/running/failed/stale) + recent workloads strip
- navCounts.ts, SystemHealthCard.svelte, ContainerLogs.svelte,
ContainerStats.svelte, StatusBadge.svelte, TagCombobox.svelte,
proxies/+page.svelte, containers/+page.svelte all rewired to the
workload-first surface
- AbortController plumbing on dashboard, nav-counts, stale page,
SystemHealthCard so navigation doesn't leave dangling fetches
- i18n: dropped projects.*, projectDetail.*, envEditor.*,
volumeEditor.*, volumeBrowser.*, quickDeploy.*, sites.*, stacks.*,
instance.*, confirm.* namespaces; en/ru parity preserved (1042
keys each)
Hardening from go-reviewer + security-reviewer + typescript-reviewer
subagent passes (0 CRITICAL across all three; 1 HIGH + ~12 MEDIUM
addressed inline before commit):
- Sec H1: dead-end workload webhook URL handlers (would mint URLs
that 404 the new trigger-only ingress) deleted across backend +
frontend
- Go M1: IsTerminalDeployStatus dropped (no production callers)
- Go M2: ParsedImage/ParseImageRef lowercased (in-package only)
- Go M6: generateWebhookSecret unified — api shim forwards to
store.GenerateWebhookSecret
- Doc/comment freshness: stage_id (no longer FK), ProxyRoute legacy
field names, workloadIDRow rationale, webhook_deliveries.target_type
enum, WebhookDeliveryLog component header
Doc
- WORKLOAD_REFACTOR_TODO: cutover marked DONE; all three Priority 1
items are now shipped. Next focus is Priority 3 polish (apps.* i18n
+ codemap entries) and Priority 4 tests.
Behavioral notes for operators upgrading from a pre-cutover build
- Existing rows in the dropped tables disappear on first boot.
- Legacy webhook URLs at /api/webhook/{secret} and
/api/webhook/sites/{secret} return 404; CI configs must repoint to
/api/webhook/triggers/{secret} (the trigger-split boot backfill
lifted any embedded workload secret onto a Trigger row, so the
secret value itself carries over).
- Frontend routes /projects /stacks /sites /deploy are gone; nav
links replaced with /apps and /triggers.
This commit is contained in:
@@ -9,22 +9,26 @@ order.
|
||||
|
||||
> ## Current focus (read this first)
|
||||
>
|
||||
> **Triggers as first-class reusable entities — DONE** (2026-05-16) and
|
||||
> **Static source inline port — DONE** (2026-05-16). The phantom-row
|
||||
> adapter (`cmd/server/static_backend.go`) is gone; the static plugin
|
||||
> now operates directly on `plugin.Workload` + `containers` +
|
||||
> `workload_env`, with runtime state (`last_commit_sha`, `last_sync_at`,
|
||||
> `last_error`, `status`) carried in `containers.extra_json`. Provider
|
||||
> downloads enforce path-traversal rejection, error strings are
|
||||
> sanitized before persistence, and Docker resource names are suffixed
|
||||
> with the workload ID short prefix to dodge name collisions.
|
||||
> **Hard legacy cutover — DONE** (2026-05-16). All three Priority 1 items
|
||||
> are now shipped. The legacy `/api/{projects,stages,stacks,sites,
|
||||
> deploys,instances}/*` HTTP surface, every backing table (`projects`,
|
||||
> `stages`, `stage_env`, `volumes`, `deploys`, `deploy_logs`,
|
||||
> `poll_states`, `stacks`, `stack_revisions`, `stack_deploys`,
|
||||
> `static_sites`, `static_site_secrets`), the project-deploy pipeline
|
||||
> (`bluegreen.go`, `promote.go`, `rollback.go`, `subdomain.go` + most of
|
||||
> `deployer.go`), the legacy webhook routes (`/api/webhook/{secret}`,
|
||||
> `/api/webhook/sites/{secret}`, `/api/webhook/workloads/{secret}`), and
|
||||
> the legacy frontend (`/projects`, `/stacks`, `/sites`, `/deploy`) are
|
||||
> gone. The `internal/staticsite/{provider,gitea_content,
|
||||
> github_provider,gitlab_provider,markdown,deno}` and
|
||||
> `internal/stack/{compose,parse,validate}` files survive only as
|
||||
> helpers imported by the static + compose plugins.
|
||||
>
|
||||
> **Next on Priority 1** is the **hard legacy cutover** — drop
|
||||
> `/api/projects`, `/api/stacks`, `/api/sites`, `/api/stages` handlers,
|
||||
> drop their tables, delete `internal/stack/` + `internal/staticsite/`
|
||||
> packages, delete frontend `/projects` / `/stacks` / `/sites` routes.
|
||||
> The `internal/staticsite` package stays alive only for the legacy
|
||||
> `/api/sites/*` HTTP routes — once those drop, it dies with them.
|
||||
> **Next focus** is **Priority 3 polish** — the `apps.*` i18n namespace
|
||||
> still has ~60 hardcoded English strings on `/apps` and `/apps/new`,
|
||||
> and `docs/CODEMAPS/` lacks an entry for `internal/workload/plugin/`.
|
||||
> After that, **Priority 4 tests** — `/api/workloads/*` integration tests
|
||||
> and dispatcher coverage.
|
||||
|
||||
## Status at a glance
|
||||
|
||||
@@ -32,7 +36,7 @@ order.
|
||||
| ---- | -------- | ------ |
|
||||
| Triggers as first-class reusable entities | 1 | **DONE** (2026-05-16) |
|
||||
| Static source inline port | 1 | **DONE** (2026-05-16) |
|
||||
| Hard legacy cutover | 1 | **PENDING — current focus** |
|
||||
| Hard legacy cutover | 1 | **DONE** (2026-05-16) |
|
||||
| Generalized volume scopes | 2 | DONE |
|
||||
| Kind-aware editors (compose / image / static) | 2 | DONE |
|
||||
| Vendor-specific webhook parsing | 2 | DONE |
|
||||
@@ -241,19 +245,82 @@ addressed before merge):
|
||||
keep their old `dw-site-mysite` shape until they're redeployed
|
||||
through the plugin path.
|
||||
|
||||
### Hard legacy cutover
|
||||
### ~~Hard legacy cutover~~ — DONE (2026-05-16)
|
||||
|
||||
The static-source inline port (above) is now complete; the cutover is
|
||||
unblocked. Proceeding with the cutover means:
|
||||
The clean-break delete that closed the workload-first arc. Net diff:
|
||||
~30 files deleted, ~20 modified, ~12k LOC removed.
|
||||
|
||||
- Delete `/api/projects`, `/api/stacks`, `/api/sites`, `/api/stages` handlers.
|
||||
- Drop tables: `projects`, `stages`, `stacks`, `stack_revisions`,
|
||||
`stack_deploys`, `static_sites`, `static_site_secrets`, `deploys`,
|
||||
`poll_states`.
|
||||
- Delete `internal/stack/`, `internal/staticsite/` packages.
|
||||
- Delete frontend `/projects`, `/sites`, `/stacks` routes.
|
||||
- Delete legacy `volume.ResolvePath` + `internal/api/volume_browser.go`
|
||||
callers (the only remaining users).
|
||||
**Backend deletions:**
|
||||
|
||||
- API handlers: `internal/api/{projects,stages,stage_env,stacks,
|
||||
static_sites,deploys,instances,volume_browser}.go`.
|
||||
- Store CRUD + tests: `internal/store/{projects,stages,stage_env,
|
||||
stacks,static_sites,static_site_secrets,deploys,poll_state,volumes,
|
||||
workload_sync}.go` + their `_test.go`.
|
||||
- Deployer pipeline: `internal/deployer/{bluegreen,promote,rollback,
|
||||
subdomain,resolver_test}.go`; `deployer.go` trimmed to just the
|
||||
dispatch surface.
|
||||
- `internal/staticsite/{manager,healthcheck}.go` and
|
||||
`internal/stack/manager.go` (the rest of those packages are still
|
||||
imported by the static + compose plugins as helpers).
|
||||
- Webhook routes: `handleWebhook` (project) + `handleSiteWebhook`
|
||||
(site) handlers gone; `/api/webhook/triggers/{secret}` is the only
|
||||
inbound surface left. The workload-side webhook URL handlers
|
||||
(`getWorkloadWebhook` + `regenerateWorkloadWebhook`) were removed
|
||||
in the cutover-followup pass when a security review caught them
|
||||
minting URLs that 404'd.
|
||||
- `internal/registry/poller.go` (legacy registry poller).
|
||||
- `internal/volume/ResolvePath` (legacy resolver; the workload
|
||||
resolver `ResolveWorkloadPath` stays).
|
||||
- `cmd/server/main.go`: dropped `staticsite.Manager`,
|
||||
`stack.Manager`, `staticsite.HealthChecker`, registry poller,
|
||||
`SetSiteSyncTriggerer`, `SetStaticSiteManager`, `SetStackManager`.
|
||||
|
||||
**Schema migrations:** `internal/store/store.go` ends with
|
||||
idempotent `DROP TABLE IF EXISTS` for every legacy table
|
||||
(`projects`, `stages`, `stage_env`, `volumes`, `deploys`,
|
||||
`deploy_logs`, `poll_states`, `stacks`, `stack_revisions`,
|
||||
`stack_deploys`, `static_sites`, `static_site_secrets`). FK order is
|
||||
children-then-parents.
|
||||
|
||||
**Frontend deletions:** `web/src/routes/{projects,stacks,sites,
|
||||
deploy}/` (entire trees); legacy components
|
||||
(`ProjectCard.svelte`, `InstanceCard.svelte`,
|
||||
`StaleContainerCard.svelte`); `api.ts` legacy functions + types
|
||||
(`Project`, `Stage`, `Stack`, `StaticSite`, `Deploy`, `Instance`,
|
||||
plus their helpers); i18n namespaces (`projects.*`, `projectDetail.*`,
|
||||
`envEditor.*`, `volumeEditor.*`, `volumeBrowser.*`, `quickDeploy.*`,
|
||||
`sites.*`, `stacks.*`, `instance.*`, `confirm.*`); nav entries.
|
||||
Dashboard rewritten to read `listWorkloads()` + `listContainers()`
|
||||
only.
|
||||
|
||||
**Helper extractions** (to keep deletions atomic):
|
||||
`internal/store/helpers.go` (`BoolToInt`, `rowScanner`,
|
||||
`GenerateWebhookSecret`); `internal/api/secrets.go` (api shim that
|
||||
forwards to the store helper so the api + store paths share one
|
||||
secret-generation impl, no panic-vs-UUID-fallback divergence).
|
||||
|
||||
**Reviews shipped through go-reviewer + security-reviewer +
|
||||
typescript-reviewer subagents** — 0 CRITICAL across all three; 1
|
||||
HIGH (dead-end workload webhook surface) + ~12 MEDIUMs all
|
||||
addressed inline before commit.
|
||||
|
||||
**Behavioral notes for operators upgrading from a pre-cutover
|
||||
build:**
|
||||
|
||||
- Existing rows in `projects` / `stages` / `stacks` / `static_sites`
|
||||
/ `static_site_secrets` / `deploys` / `deploy_logs` / `volumes`
|
||||
/ `poll_states` / `stage_env` / `stack_revisions` / `stack_deploys`
|
||||
are dropped on first boot.
|
||||
- The legacy webhook URLs at `/api/webhook/{secret}` and
|
||||
`/api/webhook/sites/{secret}` return 404 — operators with old CI
|
||||
configs must repoint to `/api/webhook/triggers/{secret}` (the boot
|
||||
backfill from the trigger-split refactor lifted any embedded
|
||||
workload secret onto a Trigger row, so the secret value itself
|
||||
carries over).
|
||||
- Frontend routes `/projects`, `/stacks`, `/sites`, `/deploy` are
|
||||
gone. Nav links replaced with `/apps` (+ `/triggers` from the
|
||||
prior arc).
|
||||
|
||||
## Priority 2 — Behavior gaps
|
||||
|
||||
|
||||
Reference in New Issue
Block a user