Implement native fast iterative deploy workflow
This commit is contained in:
parent
687a217014
commit
d589858c03
17 changed files with 873 additions and 110 deletions
|
|
@ -1,4 +1,4 @@
|
|||
{"_type":"issue","id":"islandflow-jbi","title":"Hydrate alert evidence details from ClickHouse","description":"Alert detail drawers need to fetch persisted alert context from ClickHouse by trace id, including linked flow packets, option prints, preserved execution context, and explicit missing refs for UI diagnostics.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-17T14:55:43Z","created_by":"dirtydishes","updated_at":"2026-05-17T15:01:58Z","started_at":"2026-05-17T14:55:53Z","closed_at":"2026-05-17T15:01:58Z","close_reason":"Implemented ClickHouse-backed alert context hydration across storage, API, terminal drawer, tests, and turn documentation.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-9rc","title":"Implement native fast iterative deploy plan","description":"Implement the checked-in plan at plans/2026-05-18-native-fast-iterative-deploy-plan.md. Cover deploy-phase timing instrumentation, native deployment operational assets, deploy guardrails, validation/cutover documentation, and any required live VPS remediation that is safely actionable from this session. Track follow-up items separately if anything cannot be completed in-repo or on the live host.","status":"in_progress","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-18T07:15:19Z","created_by":"dirtydishes","updated_at":"2026-05-18T07:15:25Z","started_at":"2026-05-18T07:15:25Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-8kj","title":"Configure persistent beads Dolt remote on deltaisland server","description":"Install the beads and Dolt CLIs on the server, configure a persistent Dolt sync remote backed by the server-hosted Forgejo repository, verify refs/dolt/data publication, and document Nginx Proxy Manager / firewall considerations.","status":"closed","priority":1,"issue_type":"task","assignee":"delta","created_at":"2026-05-17T10:31:31Z","created_by":"delta","updated_at":"2026-05-17T10:37:47Z","started_at":"2026-05-17T10:32:16Z","closed_at":"2026-05-17T10:37:47Z","close_reason":"Installed bd and dolt on the server, configured the Forgejo-backed Dolt remote, published refs/dolt/data, and documented the setup.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-200","title":"Implement durable options tape history","description":"Implement the plan from docs/plans/2026-05-16-1711-durable-options-tape-history.html: durable ClickHouse-backed options history, signal/all prints view selection, preserved execution context, stale semantics limited to live health, reset runbook, tests, and turn documentation.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T21:21:30Z","created_by":"dirtydishes","updated_at":"2026-05-16T21:26:51Z","started_at":"2026-05-16T21:21:33Z","closed_at":"2026-05-16T21:26:51Z","close_reason":"Implemented durable options tape history, signal/raw view selection, reset runbook, tests, and turn documentation.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-k4f","title":"Gate deploy script on docker workspace snapshot sync","description":"Prevent frozen-lockfile build failures during deploy by adding a local preflight in scripts/deploy.ts that runs bun run check:docker-workspace and aborts with a clear sync+commit remediation message when stale.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-15T23:01:44Z","created_by":"dirtydishes","updated_at":"2026-05-15T23:04:11Z","started_at":"2026-05-15T23:01:48Z","closed_at":"2026-05-15T23:04:11Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
@ -13,14 +13,11 @@
|
|||
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-1ei","title":"Make deploy helper remote-aware for Forgejo","description":"Why: scripts/deploy.ts hardcodes git remote name origin for fetch/pull/push and branch verification, but this repository now uses forgejo/github remotes and may not have an origin remote. What: update deploy.ts to resolve the deploy git remote robustly (Forgejo-aware), use it across local prechecks, branch publish, and remote rollout git operations, and keep behavior explicit in output.","status":"closed","priority":2,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-18T03:20:12Z","created_by":"dirtydishes","updated_at":"2026-05-18T03:22:39Z","started_at":"2026-05-18T03:20:16Z","closed_at":"2026-05-18T03:22:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-xod","title":"Add --fast mode to deploy helper","description":"Why: full main deploys rebuild all images and run full verification, which is slow for routine rollouts. What: add a --fast flag to scripts/deploy.ts with explicit behavior that short-circuits slow steps while preserving basic safety checks; update help text/docs for discoverability.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-18T02:50:47Z","created_by":"dirtydishes","updated_at":"2026-05-18T02:53:41Z","started_at":"2026-05-18T02:50:50Z","closed_at":"2026-05-18T02:53:41Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-cif","title":"hydrate alert evidence context from clickhouse","description":"Implement alert detail hydration from ClickHouse with a new context endpoint and frontend drawer evidence resolution. Includes storage lookup by alert trace_id/evidence refs, unresolved refs diagnostics, API route GET /flow/alerts/:trace_id/context, terminal evidence hydration + loading states/copy updates, and tests across storage/api/web.","status":"closed","priority":2,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-18T00:15:55Z","created_by":"dirtydishes","updated_at":"2026-05-18T00:17:38Z","started_at":"2026-05-18T00:16:00Z","closed_at":"2026-05-18T00:17:38Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-4e9","title":"Polish terminal view","description":"Improve the Islandflow web terminal view with a focused UI polish pass aligned to the product design system.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-17T15:18:18Z","created_by":"dirtydishes","updated_at":"2026-05-17T15:25:02Z","started_at":"2026-05-17T15:18:21Z","closed_at":"2026-05-17T15:25:02Z","close_reason":"Polished terminal shell styling, responsive Tape actions, and documented the turn.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-lyt","title":"Summarize 2026-05-16 git activity for standup","description":"Create a grounded standup summary for yesterday's git activity, anchored to commits, changed files, and any linked PR context if present. Produce the required HTML document in docs/general and complete the beads + git handoff workflow.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-17T14:02:57Z","created_by":"dirtydishes","updated_at":"2026-05-17T14:05:37Z","started_at":"2026-05-17T14:03:09Z","closed_at":"2026-05-17T14:05:37Z","close_reason":"Created docs/general standup summary for 2026-05-16 git activity, grounded to commits and changed files, and prepared the repo handoff workflow.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-sz8","title":"Fix public /replay/options proxy regression","description":"## Summary\nThe new deploy-time public route checker added in commit 1424a27 (\"fix durable options history routing\") currently fails against https://flow.deltaisland.io because GET /replay/options returns HTML instead of JSON.\n\n## Evidence\n- `bun run scripts/check-public-api-routes.ts https://flow.deltaisland.io` fails on `/replay/options?view=signal\u0026after_ts=0\u0026after_seq=0\u0026limit=1` with `returned non-JSON content (text/html; charset=UTF-8)`\n- `services/api/src/index.ts` implements `GET /replay/options`, so the HTML response indicates the request is landing on the web app instead of the API service\n- `deployment/docker/README.md` documents that same-origin proxy mode must include `/replay/*` in the API route matcher\n\n## Minimal Fix\nUpdate the live reverse proxy / edge route matcher for flow.deltaisland.io so `/replay/*` is forwarded to the API host, then rerun `bun run check:public-api-routes`.\n\n## Notes\nThis looks like a production proxy configuration regression rather than an in-repo application bug.","status":"open","priority":2,"issue_type":"bug","owner":"dishes@dpdrm.com","created_at":"2026-05-17T13:06:11Z","created_by":"dirtydishes","updated_at":"2026-05-17T13:06:11Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-vvw","title":"Stage native public-edge cutover after worker soak","description":"Why this issue exists and what needs to be done:\\n- The native deploy path is now provisioned for worker-first iteration, with checked-in user units, rollback helpers, and edge guardrails\\n- Remaining work is to enable and soak native worker units, validate duplicate-processing behavior, then deliberately cut over the public web/api edge if warranted\\n- Final acceptance should include deciding whether Docker or native becomes the default runtime after operational evidence","status":"open","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-18T07:32:35Z","created_by":"dirtydishes","updated_at":"2026-05-18T07:32:35Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-bsg","title":"Fix public /replay/options proxy regression","description":"Restore correct public routing for GET /replay/options on flow.deltaisland.io. The app currently serves HTML for that API path, which indicates edge/proxy routing drift. Update the live proxy topology or deployment assets as needed, then validate with bun run scripts/check-public-api-routes.ts.","status":"closed","priority":2,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-18T07:15:19Z","created_by":"dirtydishes","updated_at":"2026-05-18T07:32:51Z","started_at":"2026-05-18T07:15:24Z","closed_at":"2026-05-18T07:32:51Z","close_reason":"Audited the live VPS and reverse proxy on 2026-05-18: public /replay/options now returns JSON, bun run scripts/check-public-api-routes.ts passes, and the active Nginx Proxy Manager config includes /replay in the API route matcher. No in-repo app code change was required.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-9j5","title":"Prepare PR for deploy allowlist cleanup","description":"Why this issue exists and what needs to be done:\\n- Package current deploy allowlist cleanup into a reviewable PR with multiple commits\\n- Add required turn documentation in docs/turns\\n- Run validation and push all artifacts","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-17T15:44:12Z","created_by":"dirtydishes","updated_at":"2026-05-17T15:53:55Z","started_at":"2026-05-17T15:44:22Z","closed_at":"2026-05-17T15:53:55Z","close_reason":"Packaged deploy allowlist cleanup into multi-commit PR branch with required turn documentation and push workflow.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-0sa","title":"Fix live tape auto-hold, history seam, and remove manual pause control","description":"The live tape should automatically hold when the user scrolls away from the top, resume when they return to the top or use Jump to top, and keep older prints available seamlessly beyond the hot window. Manual Pause/Resume control is now redundant and should be removed from live tape panes. This work should also fix the current regression where paused/held tapes still mutate, and align the options tape with a strict 100-row hot head backed by ClickHouse history.","notes":"Implemented live scroll-hold with no live pause button, demand-loaded ClickHouse history, a 100-row options hot head, and cache-first scoped snapshots. Validated with bun test apps/web/app/terminal.test.ts services/api/tests/live.test.ts and bun --cwd=apps/web run build.","status":"closed","priority":2,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T18:12:51Z","created_by":"dirtydishes","updated_at":"2026-05-16T18:23:43Z","started_at":"2026-05-16T18:12:54Z","closed_at":"2026-05-16T18:23:43Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-2db","title":"Manually remove stale islandflow local-infra containers from VPS","description":"The live VPS still has an older compose project named islandflow created from the repo-root docker-compose.yml. Inspection shows it is separate from the supported islandflow-vps deployment stack and exposes NATS, ClickHouse, and Redis on host ports. Container removal commands currently hang when run as the delta user through Docker, so cleanup likely needs a focused maintenance window and possibly host-level intervention or a Docker daemon restart.","notes":"The duplicate islandflow compose project on the VPS was confirmed live during inspection. Nginx Proxy Manager routes public traffic only to islandflow-vps web/api by Docker name, so the stale islandflow project appears to be stray local-infra state rather than part of the supported production path. Attempts to remove the stale containers with docker compose down and docker rm -f as the delta user hung and timed out, so manual cleanup likely needs a maintenance window and possibly Docker daemon intervention.","status":"open","priority":2,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:27:27Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:28:59Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-2db","title":"Manually remove stale islandflow local-infra containers from VPS","description":"The live VPS still has an older compose project named islandflow created from the repo-root docker-compose.yml. Inspection shows it is separate from the supported islandflow-vps deployment stack and exposes NATS, ClickHouse, and Redis on host ports. Container removal commands currently hang when run as the delta user through Docker, so cleanup likely needs a focused maintenance window and possibly host-level intervention or a Docker daemon restart.","notes":"The duplicate islandflow compose project on the VPS was confirmed live during inspection. Nginx Proxy Manager routes public traffic only to islandflow-vps web/api by Docker name, so the stale islandflow project appears to be stray local-infra state rather than part of the supported production path. Attempts to remove the stale containers with docker compose down and docker rm -f as the delta user hung and timed out, so manual cleanup likely needs a maintenance window and possibly Docker daemon intervention.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:27:27Z","created_by":"dirtydishes","updated_at":"2026-05-18T07:32:48Z","started_at":"2026-05-18T07:15:25Z","closed_at":"2026-05-18T07:32:48Z","close_reason":"Audited the live VPS on 2026-05-18: docker compose ls and container labels no longer show a duplicate islandflow compose project, so the stale local-infra stack cleanup appears to already be resolved on the host.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-c87","title":"Clean up duplicate Islandflow Docker infra on VPS","description":"The live VPS is currently running both the production-style islandflow-vps Docker stack and an older root-level islandflow infra stack that publishes NATS, ClickHouse, and Redis on host ports. Investigate whether the older stack is unused, remove it safely if so, and update docs/deploy guidance so the server topology is clearer.","notes":"Inspected the live VPS and confirmed the duplicate compose project: islandflow-vps is the supported deployment stack, while a separate islandflow project from the repo-root docker-compose.yml still runs exposed NATS/ClickHouse/Redis containers. Verified Nginx Proxy Manager routes only to islandflow-vps web/api by Docker name. Attempted cleanup via docker compose down and docker rm -f on the stale islandflow containers, but those commands hung for the delta user and timed out. Added repo guardrails and docs so deploy warns when the duplicate project exists, and opened islandflow-2db for manual host-level cleanup during a maintenance window.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:16:05Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:28:07Z","started_at":"2026-05-16T01:16:09Z","closed_at":"2026-05-16T01:28:07Z","close_reason":"Completed the repo-side investigation and guardrails. Actual server-side container removal is blocked by hanging Docker operations and is tracked separately in islandflow-2db for a maintenance window.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-4gj","title":"Clarify Docker-first deploy workflow and mark native runtime experimental","description":"After inspecting the live VPS, native deployment is not ready for routine use: Nginx Proxy Manager routes to Docker container names, Bun is not installed on the host, sudo systemctl is not passwordless, and no Islandflow units exist. Update deploy messaging and docs so Docker remains the clearly recommended deployment path and native runtime is labeled experimental/future-facing with server prerequisites called out.","notes":"Updated deploy messaging and docs after live VPS inspection. scripts/deploy.ts now marks Docker as the default and recommended runtime, labels native as experimental, switches native systemctl default to sudo -n systemctl, and prints explicit native precheck failures for missing Bun/systemctl access/units. Updated README.md, deployment/docker/README.md, and deployment/native/README.md to reflect the current Docker + Nginx Proxy Manager topology. Validation: ./deploy --help, ./deploy main --runtime native --no-build (fails fast with Bun-missing message), bun run check:docker-workspace.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T01:10:11Z","created_by":"dirtydishes","updated_at":"2026-05-16T01:12:39Z","started_at":"2026-05-16T01:10:14Z","closed_at":"2026-05-16T01:12:39Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-7p2","title":"Fix deploy wrapper argument forwarding for runtime flags","description":"The repo-root deploy wrapper currently invokes bun run without a -- separator, so flags like --runtime native are treated as Bun CLI flags instead of script arguments. Update the wrapper so ./deploy main --runtime native forwards arguments correctly to scripts/deploy.ts.","notes":"Cherry-picked the dual-runtime deploy workflow onto main and fixed the repo-root deploy wrapper to call Bun with a -- separator so flags like --runtime native are forwarded to scripts/deploy.ts correctly. Validation: ./deploy --help, ./deploy main --runtime native --force-recreate guard, bun run check:docker-workspace.","status":"closed","priority":2,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-16T00:51:05Z","created_by":"dirtydishes","updated_at":"2026-05-16T00:52:34Z","started_at":"2026-05-16T00:51:10Z","closed_at":"2026-05-16T00:52:34Z","close_reason":"Closed","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
@ -47,5 +44,5 @@
|
|||
{"_type":"issue","id":"islandflow-igk","title":"Add plan mode","description":"Implement a user-facing plan mode in the application so users can switch into planning before taking action. Scope to be clarified from existing app patterns.","status":"closed","priority":2,"issue_type":"feature","owner":"dishes@dpdrm.com","created_at":"2026-05-04T04:22:37Z","created_by":"dirtydishes","updated_at":"2026-05-04T04:26:18Z","started_at":"2026-05-04T04:22:40Z","closed_at":"2026-05-04T04:26:18Z","close_reason":"Implemented as a global pi extension toggled with Shift+P","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-biq","title":"Finish raw live options delivery and filter/backpressure observability","description":"The smart-money signal path and Tape filters are in place, but the next firehose pass should finish server-side selective raw live delivery for options subscriptions and add explicit filtered-out/backpressure observability for API/web counters. This was discovered while landing islandflow-e4r.\n","status":"in_progress","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:28:58Z","created_by":"dirtydishes","updated_at":"2026-04-29T03:54:12Z","started_at":"2026-04-29T03:54:12Z","dependencies":[{"issue_id":"islandflow-biq","depends_on_id":"islandflow-e4r","type":"discovered-from","created_at":"2026-04-28T16:28:58Z","created_by":"auto-import","metadata":"{}"}],"dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-zsy","title":"Expose Forgejo SSH on a direct DNS hostname","description":"git.deltaisland.io currently resolves through Cloudflare's proxy, so SSH on port 2222 does not complete even though the Forgejo container is listening on the host. If SSH-based git/beads workflows are desired, add a DNS-only hostname (or adjust the existing record) that points directly at the server for Forgejo SSH.","status":"open","priority":3,"issue_type":"task","created_at":"2026-05-17T10:34:06Z","created_by":"delta","updated_at":"2026-05-17T10:34:06Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-38p","title":"Add native deployment unit templates and rollback helpers","description":"The deploy helper now supports --runtime native, but the repo still relies on operator-managed systemd units and manual rollback. Add checked-in native deployment templates or provisioning guidance for the expected units, and consider lightweight rollback/smoke-test helpers once the host-native path is exercised on the real VPS.","status":"open","priority":3,"issue_type":"task","owner":"dishes@dpdrm.com","created_at":"2026-05-15T23:46:42Z","created_by":"dirtydishes","updated_at":"2026-05-15T23:46:42Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-38p","title":"Add native deployment unit templates and rollback helpers","description":"The deploy helper now supports --runtime native, but the repo still relies on operator-managed systemd units and manual rollback. Add checked-in native deployment templates or provisioning guidance for the expected units, and consider lightweight rollback/smoke-test helpers once the host-native path is exercised on the real VPS.","status":"closed","priority":3,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-15T23:46:42Z","created_by":"dirtydishes","updated_at":"2026-05-18T07:34:02Z","started_at":"2026-05-18T07:15:25Z","closed_at":"2026-05-18T07:34:02Z","close_reason":"Added checked-in native user unit templates, install/smoke-test/rollback helpers, updated native deploy docs with worker-first guidance, and installed the unit files onto the VPS in disabled form.","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
{"_type":"issue","id":"islandflow-575","title":"Document smart-money event calendar env","description":"Document smart-money event-calendar environment configuration in env examples and README.\n","status":"closed","priority":3,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T06:57:14Z","created_by":"dirtydishes","updated_at":"2026-05-05T06:57:57Z","started_at":"2026-05-05T06:57:17Z","closed_at":"2026-05-05T06:57:57Z","close_reason":"Documented event-calendar env variables","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||
|
|
|
|||
|
|
@ -129,8 +129,10 @@ This keeps Docker in the local workflow where it helps most (NATS, ClickHouse, R
|
|||
- `./deploy main` keeps the current VPS Docker rollout path as the default and recommended path.
|
||||
- Do not run the repo-root `docker-compose.yml` on the VPS. That file is for local infra only and can create duplicate exposed NATS, ClickHouse, and Redis containers on the server.
|
||||
- `./deploy main --runtime native` targets an experimental host-native Bun + systemd deployment.
|
||||
- Native deploys are now intended primarily for worker-only fast iteration until the public edge is cut over deliberately.
|
||||
- `./deploy current-branch` and `./deploy current-branch --runtime native` keep branch deploys available during the transition, but Docker remains the supported path for the current VPS.
|
||||
- Partial deploys are supported with `--web-only`, `--api-only`, `--services-only`, and `--no-build`.
|
||||
- Partial deploys are supported with `--web-only`, `--api-only`, `--services-only`, `--workers-only`, and `--no-build`.
|
||||
- When run from `/home/delta/islandflow` on the VPS itself, `./deploy` can execute locally instead of SSHing back into the same server.
|
||||
- Docker runtime details live in `deployment/docker/README.md`.
|
||||
- Native runtime expectations and prerequisites live in `deployment/native/README.md`.
|
||||
|
||||
|
|
|
|||
|
|
@ -217,13 +217,15 @@ The current live VPS uses Nginx Proxy Manager on the shared Docker network and r
|
|||
|
||||
The deploy helper also warns if it detects a second compose project named `islandflow` on the server, because that usually means the repo-root local-infra stack was started on the VPS by mistake.
|
||||
|
||||
The checked-in deploy helper is meant to run from your local repo checkout, not from the VPS shell. It always targets:
|
||||
The checked-in deploy helper normally runs from your local repo checkout and targets:
|
||||
|
||||
- SSH host: `delta@152.53.80.229`
|
||||
- SSH key: `~/.ssh/delta_ed25519`
|
||||
- SSH key: `~/.ssh/delta_ed25519` by default
|
||||
- Live repo checkout: `/home/delta/islandflow`
|
||||
- Live compose directory: `/home/delta/islandflow/deployment/docker`
|
||||
|
||||
If you run `./deploy` from `/home/delta/islandflow` on the VPS itself, it now executes the remote steps locally instead of SSHing back into the same machine. You can still force SSH with `DEPLOY_FORCE_SSH=1`, or override the key path with `DEPLOY_SSH_KEY_PATH=/path/to/key`.
|
||||
|
||||
It preserves the current Docker Compose project and avoids destructive cleanup on the server.
|
||||
|
||||
### Deploy `origin/main`
|
||||
|
|
@ -271,6 +273,7 @@ Examples:
|
|||
./deploy main --runtime docker --web-only
|
||||
./deploy main --runtime docker --api-only
|
||||
./deploy current-branch --runtime docker --services-only
|
||||
./deploy main --runtime docker --workers-only
|
||||
./deploy main --runtime docker --fast
|
||||
./deploy main --runtime docker --web-only --no-build
|
||||
```
|
||||
|
|
@ -280,6 +283,7 @@ Scoped Docker deploys now build only the selected image set and then restart onl
|
|||
- `--web-only`: `docker compose build web`, then `docker compose up -d web`
|
||||
- `--api-only`: `docker compose build api`, then `docker compose up -d api`
|
||||
- `--services-only`: builds and restarts `api`, `compute`, `candles`, `ingest-options`, and `ingest-equities`
|
||||
- `--workers-only`: builds and restarts `compute`, `candles`, `ingest-options`, and `ingest-equities` without touching `web` or `api`
|
||||
- `--fast`: when no explicit scope flag is given, treats the deploy as `--services-only` and skips the public API route suite for quicker completion. It still runs remote service health checks.
|
||||
|
||||
Use `--no-build` only when the image is already correct and you need Compose to recreate or restart containers, such as after changing server-side environment values that do not affect a Next.js build-time variable. Do not use `--no-build` for dependency changes, application source changes, or `NEXT_PUBLIC_*` changes.
|
||||
|
|
|
|||
|
|
@ -1,29 +1,114 @@
|
|||
# Native Deployment
|
||||
|
||||
This directory documents the experimental host-native Islandflow rollout path used by:
|
||||
This directory documents the host-native Islandflow rollout path used by:
|
||||
|
||||
```bash
|
||||
./deploy main --runtime native
|
||||
./deploy current-branch --runtime native
|
||||
```
|
||||
|
||||
This runtime is intended for faster server iteration during the transition away from Docker-only app rollouts. It is not the recommended path for the current production VPS, which still uses Nginx Proxy Manager to reach the Docker `web` and `api` containers by container name on the shared Docker network. Local development should still prefer:
|
||||
## Current operating model
|
||||
|
||||
- Docker for infra (`bun run dev:infra`)
|
||||
- native Bun services (`bun run dev:services`)
|
||||
- native Next.js web (`bun run dev:web`)
|
||||
Native runtime is now intended for **fast iterative backend deploys first**, while Docker remains the supported public production edge until a deliberate cutover is completed.
|
||||
|
||||
Today, the recommended split is:
|
||||
|
||||
- **Docker runtime** for the live public `web` + `api` path
|
||||
- **Native runtime** for worker-only iteration (`compute`, `candles`, `ingest-options`, `ingest-equities`)
|
||||
- local development stays:
|
||||
- Docker infra: `bun run dev:infra`
|
||||
- native backend services: `bun run dev:services`
|
||||
- native web: `bun run dev:web`
|
||||
|
||||
## What native deploy means here
|
||||
|
||||
The checked-in `deploy` helper assumes:
|
||||
|
||||
- the live repo checkout is still `/home/delta/islandflow`
|
||||
- the live repo checkout is `/home/delta/islandflow`
|
||||
- Bun is installed on the VPS
|
||||
- app processes are managed by `systemd`
|
||||
- infrastructure services such as NATS, ClickHouse, and Redis are already reachable from the host
|
||||
- app processes are managed by `systemd --user`
|
||||
- infrastructure services such as NATS, ClickHouse, and Redis are reachable from the host
|
||||
- the web app runs from `apps/web` and is served with `next start -p 3000`
|
||||
|
||||
The deploy script updates the repo checkout, optionally runs `bun install --frozen-lockfile`, optionally rebuilds the web app, restarts the target systemd units, and then verifies the services locally on the VPS plus through the public app URL.
|
||||
The deploy script updates the repo checkout, optionally runs `bun install --frozen-lockfile`, optionally rebuilds the web app, restarts the target user units, verifies local health, and then runs public verification when the selected scope includes the public edge.
|
||||
|
||||
## Live audit status on 2026-05-18
|
||||
|
||||
The plan assumptions were audited on the VPS:
|
||||
|
||||
- `bun` is installed and available at `/home/delta/.bun/bin/bun`
|
||||
- `systemctl --user` is available and the `delta` user has lingering enabled
|
||||
- `/home/delta/islandflow/.env` exists
|
||||
- public `https://flow.deltaisland.io/replay/options` routing is healthy again
|
||||
- the previously reported duplicate `islandflow` compose project is not currently present in `docker compose ls`
|
||||
- native Islandflow user units were not installed at the start of the audit; this change now provides and installs the checked-in user unit files, but they remain disabled until an operator enables a scope intentionally
|
||||
|
||||
That means native worker deploy support is now provisioned on the host, but native runtime should still be enabled scope-by-scope rather than started wholesale.
|
||||
|
||||
## Checked-in native ops assets
|
||||
|
||||
### User unit templates
|
||||
|
||||
Checked-in unit files live under:
|
||||
|
||||
- `deployment/native/systemd/user/islandflow-web.service`
|
||||
- `deployment/native/systemd/user/islandflow-api.service`
|
||||
- `deployment/native/systemd/user/islandflow-compute.service`
|
||||
- `deployment/native/systemd/user/islandflow-candles.service`
|
||||
- `deployment/native/systemd/user/islandflow-ingest-options.service`
|
||||
- `deployment/native/systemd/user/islandflow-ingest-equities.service`
|
||||
|
||||
These are written for the current VPS layout:
|
||||
|
||||
- repo root: `/home/delta/islandflow`
|
||||
- Bun binary: `/home/delta/.bun/bin/bun`
|
||||
- env file: `/home/delta/islandflow/.env`
|
||||
|
||||
### Install the units
|
||||
|
||||
```bash
|
||||
./deployment/native/install-user-units.sh
|
||||
./deployment/native/install-user-units.sh workers
|
||||
systemctl --user start islandflow-compute.service
|
||||
```
|
||||
|
||||
Install script behavior:
|
||||
|
||||
- copies the checked-in unit files into `~/.config/systemd/user`
|
||||
- reloads the user systemd daemon
|
||||
- enables only the scope you explicitly request
|
||||
- defaults to installing without enabling anything yet
|
||||
|
||||
### Smoke test helper
|
||||
|
||||
```bash
|
||||
./deployment/native/check-native-health.sh workers
|
||||
./deployment/native/check-native-health.sh services
|
||||
./deployment/native/check-native-health.sh full
|
||||
```
|
||||
|
||||
This validates:
|
||||
|
||||
- `systemctl --user is-active` for the selected units
|
||||
- local API health at `http://127.0.0.1:4000/health` when API scope is included
|
||||
- local web health at `http://127.0.0.1:3000/` when web scope is included
|
||||
|
||||
### Rollback helper
|
||||
|
||||
```bash
|
||||
./deployment/native/rollback.sh <git-ref> workers
|
||||
./deployment/native/rollback.sh <git-ref> services
|
||||
```
|
||||
|
||||
Rollback helper behavior:
|
||||
|
||||
- requires a clean repo state
|
||||
- fetches refs
|
||||
- switches the checkout to a detached target ref
|
||||
- reruns `bun install --frozen-lockfile`
|
||||
- rebuilds the web app only when web scope is included
|
||||
- restarts the selected user units
|
||||
- runs the native smoke checks
|
||||
|
||||
## Expected unit names
|
||||
|
||||
|
|
@ -54,87 +139,104 @@ Available overrides:
|
|||
|
||||
## systemctl invocation
|
||||
|
||||
By default the deploy helper uses:
|
||||
|
||||
```bash
|
||||
sudo -n systemctl
|
||||
```
|
||||
|
||||
If the server uses user units or another wrapper, override it locally before invoking `./deploy`:
|
||||
For the checked-in user units, use:
|
||||
|
||||
```bash
|
||||
export DEPLOY_NATIVE_SYSTEMCTL_PREFIX="systemctl --user"
|
||||
./deploy main --runtime native
|
||||
```
|
||||
|
||||
The deploy helper defaults to `sudo -n systemctl`, but that is only appropriate if you intentionally install matching system units.
|
||||
|
||||
## Partial native rollouts
|
||||
|
||||
Examples:
|
||||
|
||||
```bash
|
||||
./deploy main --runtime native --web-only
|
||||
./deploy main --runtime native --api-only
|
||||
./deploy current-branch --runtime native --services-only
|
||||
./deploy main --runtime native --workers-only
|
||||
./deploy main --runtime native --fast
|
||||
./deploy main --runtime native --web-only --no-build
|
||||
./deploy main --runtime native --services-only
|
||||
./deploy main --runtime native --web-only
|
||||
./deploy current-branch --runtime native --workers-only --no-build
|
||||
```
|
||||
|
||||
Scope behavior:
|
||||
|
||||
- default: restart web + API + backend services
|
||||
- default: restart web + API + worker services
|
||||
- `--web-only`: rebuild/restart only the web unit
|
||||
- `--api-only`: restart only the API unit
|
||||
- `--services-only`: restart API + backend units without touching the web unit
|
||||
- `--fast`: when no explicit scope flag is provided, uses the same `--services-only` scope and trims verbose verification output for quicker completion
|
||||
- `--services-only`: restart API + worker units without touching the web unit
|
||||
- `--workers-only`: restart only `compute`, `candles`, `ingest-options`, and `ingest-equities`
|
||||
- `--fast`: when no explicit scope flag is provided, native deploys now default to `--workers-only`
|
||||
- `--no-build`: skip `bun install --frozen-lockfile` and skip the web build step
|
||||
|
||||
## Current status
|
||||
## Edge-cutover guardrail
|
||||
|
||||
On the current live VPS, native deploys should be treated as opt-in infrastructure work, not the default rollout path. Before a native deploy can succeed there, all of the following must be true at the same time:
|
||||
|
||||
- Bun is installed on the host.
|
||||
- The selected `systemctl` command works non-interactively.
|
||||
- Islandflow systemd units exist for the requested scope.
|
||||
- Host-native services can reach the intended NATS, ClickHouse, and Redis endpoints.
|
||||
- If `web` or `api` move native, the reverse proxy topology is updated deliberately.
|
||||
|
||||
Until that is prepared intentionally, prefer:
|
||||
Native deploys that touch the public web or API edge are intentionally blocked unless you acknowledge cutover readiness:
|
||||
|
||||
```bash
|
||||
./deploy main --runtime docker
|
||||
./deploy current-branch --runtime docker
|
||||
export DEPLOY_NATIVE_EDGE_READY=1
|
||||
```
|
||||
|
||||
## Server preparation checklist
|
||||
Without that variable, these commands are refused:
|
||||
|
||||
Before the first native rollout, ensure the VPS has:
|
||||
- `./deploy main --runtime native`
|
||||
- `./deploy main --runtime native --web-only`
|
||||
- `./deploy main --runtime native --api-only`
|
||||
- `./deploy main --runtime native --services-only`
|
||||
|
||||
1. Bun installed and on `PATH`
|
||||
2. a working `/home/delta/islandflow/.env` (or unit-managed equivalent env source)
|
||||
3. systemd units for each target service
|
||||
4. the web unit configured to serve the built app on port `3000`
|
||||
5. the API unit configured to serve health checks on port `4000`
|
||||
6. infrastructure endpoints configured so the native services can reach NATS, ClickHouse, and Redis
|
||||
This keeps the native path focused on safe worker iteration until proxy routing and public unit ownership are switched deliberately.
|
||||
|
||||
## Verification
|
||||
## Running deploy from the VPS itself
|
||||
|
||||
Native deploys verify:
|
||||
If you run `./deploy` from `/home/delta/islandflow` on the live server, the deploy helper now executes the remote steps locally instead of SSHing back into the same machine.
|
||||
|
||||
- target units are active via `systemctl`
|
||||
- recent unit status and journal output can be collected
|
||||
- local `http://127.0.0.1:4000/health` when API scope is included
|
||||
- local `http://127.0.0.1:3000/` when web scope is included
|
||||
- the public app URL from the local machine after the rollout finishes
|
||||
That means:
|
||||
|
||||
## Rollback
|
||||
- no SSH key is required for on-server deploy execution
|
||||
- timing and verification behavior stay the same
|
||||
- you can still force SSH with `DEPLOY_FORCE_SSH=1`
|
||||
- you can override the SSH key path with `DEPLOY_SSH_KEY_PATH=/path/to/key`
|
||||
|
||||
Rollback remains manual for now:
|
||||
## Validation matrix
|
||||
|
||||
1. switch the server checkout back to the last known-good branch or commit
|
||||
2. rerun the appropriate native deploy command
|
||||
3. if needed, restart only the affected units with `systemctl`
|
||||
| Area | Native workers-only | Native edge cutover |
|
||||
| --- | --- | --- |
|
||||
| Bun installed | required | required |
|
||||
| `systemctl --user` works | required | required |
|
||||
| Islandflow user units installed | worker units only | all units |
|
||||
| Host access to NATS/ClickHouse/Redis | required | required |
|
||||
| Proxy routes updated for `/prints`, `/history`, `/replay`, `/nbbo`, `/ws`, `/flow`, `/candles` | not required | required |
|
||||
| Public app check | not required | required |
|
||||
| Public API route suite | not required | required |
|
||||
|
||||
Docker remains the fallback and currently recommended runtime during the transition:
|
||||
## Staged cutover plan
|
||||
|
||||
1. **Stage 1: native workers only**
|
||||
- install user units
|
||||
- validate `./deployment/native/check-native-health.sh workers`
|
||||
- use `./deploy main --runtime native --fast`
|
||||
2. **Stage 2: native API behind local-only verification**
|
||||
- start `islandflow-api.service`
|
||||
- confirm `curl http://127.0.0.1:4000/health`
|
||||
- do not switch public routing yet
|
||||
3. **Stage 3: deliberate public edge cutover**
|
||||
- update proxy routing to native `web`/`api`
|
||||
- export `DEPLOY_NATIVE_EDGE_READY=1`
|
||||
- run full native deploy
|
||||
- validate `bun run scripts/check-public-api-routes.ts https://flow.deltaisland.io`
|
||||
4. **Stage 4: decide final default runtime**
|
||||
- keep Docker as fallback until native edge has proven stable
|
||||
|
||||
## Recommended current commands
|
||||
|
||||
Fast backend iteration before edge cutover:
|
||||
|
||||
```bash
|
||||
export DEPLOY_NATIVE_SYSTEMCTL_PREFIX="systemctl --user"
|
||||
./deploy main --runtime native --fast
|
||||
```
|
||||
|
||||
Supported production path today:
|
||||
|
||||
```bash
|
||||
./deploy main --runtime docker
|
||||
|
|
|
|||
43
deployment/native/check-native-health.sh
Executable file
43
deployment/native/check-native-health.sh
Executable file
|
|
@ -0,0 +1,43 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
scope="${1:-full}"
|
||||
units=()
|
||||
|
||||
case "$scope" in
|
||||
full)
|
||||
units=(islandflow-web.service islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
web)
|
||||
units=(islandflow-web.service)
|
||||
;;
|
||||
api)
|
||||
units=(islandflow-api.service)
|
||||
;;
|
||||
services)
|
||||
units=(islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
workers)
|
||||
units=(islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
*)
|
||||
echo "Unknown scope: $scope" >&2
|
||||
echo "Expected one of: full, web, api, services, workers" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
for unit in "${units[@]}"; do
|
||||
systemctl --user is-active --quiet "$unit"
|
||||
echo "ok $unit"
|
||||
done
|
||||
|
||||
if [[ " ${units[*]} " == *" islandflow-api.service "* ]]; then
|
||||
curl -fksS http://127.0.0.1:4000/health >/dev/null
|
||||
echo "ok api-health"
|
||||
fi
|
||||
|
||||
if [[ " ${units[*]} " == *" islandflow-web.service "* ]]; then
|
||||
curl -I -fksS http://127.0.0.1:3000/ >/dev/null
|
||||
echo "ok web-health"
|
||||
fi
|
||||
49
deployment/native/install-user-units.sh
Executable file
49
deployment/native/install-user-units.sh
Executable file
|
|
@ -0,0 +1,49 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
scope="${1:-none}"
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
unit_source_dir="$repo_root/deployment/native/systemd/user"
|
||||
unit_target_dir="${XDG_CONFIG_HOME:-$HOME/.config}/systemd/user"
|
||||
units=()
|
||||
|
||||
case "$scope" in
|
||||
none)
|
||||
;;
|
||||
full)
|
||||
units=(islandflow-web.service islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
web)
|
||||
units=(islandflow-web.service)
|
||||
;;
|
||||
api)
|
||||
units=(islandflow-api.service)
|
||||
;;
|
||||
services)
|
||||
units=(islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
workers)
|
||||
units=(islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
*)
|
||||
echo "Unknown scope: $scope" >&2
|
||||
echo "Expected one of: none, full, web, api, services, workers" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$unit_target_dir"
|
||||
cp "$unit_source_dir"/*.service "$unit_target_dir"/
|
||||
|
||||
systemctl --user daemon-reload
|
||||
|
||||
if [[ ${#units[@]} -gt 0 ]]; then
|
||||
systemctl --user enable "${units[@]}"
|
||||
fi
|
||||
|
||||
echo "Installed Islandflow user units into $unit_target_dir"
|
||||
if [[ ${#units[@]} -gt 0 ]]; then
|
||||
echo "Enabled scope: $scope"
|
||||
else
|
||||
echo "No units enabled yet. Pass a scope such as workers when you are ready."
|
||||
fi
|
||||
57
deployment/native/rollback.sh
Executable file
57
deployment/native/rollback.sh
Executable file
|
|
@ -0,0 +1,57 @@
|
|||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
if [[ $# -lt 1 || $# -gt 2 ]]; then
|
||||
echo "Usage: deployment/native/rollback.sh <git-ref> [full|web|api|services|workers]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ref="$1"
|
||||
scope="${2:-services}"
|
||||
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
|
||||
cd "$repo_root"
|
||||
|
||||
if [[ -n "$(git status --porcelain=v1)" ]]; then
|
||||
echo "Refusing rollback with a dirty working tree." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
current_ref="$(git rev-parse --short HEAD)"
|
||||
echo "Rolling back from $current_ref to $ref (scope: $scope)"
|
||||
|
||||
git fetch --all --prune
|
||||
git switch --detach "$ref"
|
||||
bun install --frozen-lockfile
|
||||
|
||||
if [[ "$scope" == "full" || "$scope" == "web" ]]; then
|
||||
bun --cwd=apps/web run build
|
||||
fi
|
||||
|
||||
case "$scope" in
|
||||
full)
|
||||
units=(islandflow-web.service islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
web)
|
||||
units=(islandflow-web.service)
|
||||
;;
|
||||
api)
|
||||
units=(islandflow-api.service)
|
||||
;;
|
||||
services)
|
||||
units=(islandflow-api.service islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
workers)
|
||||
units=(islandflow-compute.service islandflow-candles.service islandflow-ingest-options.service islandflow-ingest-equities.service)
|
||||
;;
|
||||
*)
|
||||
echo "Unknown scope: $scope" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
systemctl --user restart "${units[@]}"
|
||||
"$repo_root/deployment/native/check-native-health.sh" "$scope"
|
||||
|
||||
echo "Rollback complete. Repo is now detached at $(git rev-parse --short HEAD)."
|
||||
echo "Return to tracked main later with: git switch main && git pull --ff-only <remote> main"
|
||||
17
deployment/native/systemd/user/islandflow-api.service
Normal file
17
deployment/native/systemd/user/islandflow-api.service
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow API
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun services/api/src/index.ts
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
17
deployment/native/systemd/user/islandflow-candles.service
Normal file
17
deployment/native/systemd/user/islandflow-candles.service
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow candles
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun services/candles/src/index.ts
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
17
deployment/native/systemd/user/islandflow-compute.service
Normal file
17
deployment/native/systemd/user/islandflow-compute.service
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow compute
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun services/compute/src/index.ts
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow ingest-equities
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun services/ingest-equities/src/index.ts
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow ingest-options
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun services/ingest-options/src/index.ts
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
17
deployment/native/systemd/user/islandflow-web.service
Normal file
17
deployment/native/systemd/user/islandflow-web.service
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
[Unit]
|
||||
Description=Islandflow web
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory=/home/delta/islandflow
|
||||
EnvironmentFile=/home/delta/islandflow/.env
|
||||
ExecStart=/home/delta/.bun/bin/bun --cwd apps/web run start
|
||||
Restart=always
|
||||
RestartSec=2
|
||||
KillSignal=SIGINT
|
||||
TimeoutStopSec=20
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
93
docs/plans/2026-05-18-native-fast-iterative-deploy-plan.html
Normal file
93
docs/plans/2026-05-18-native-fast-iterative-deploy-plan.html
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Plan: Native Fast Iterative Deployment</title>
|
||||
<style>
|
||||
body { font-family: Inter, system-ui, sans-serif; margin: 40px auto; max-width: 860px; line-height: 1.55; padding: 0 16px; }
|
||||
h1, h2 { line-height: 1.2; }
|
||||
.meta { color: #555; margin-bottom: 20px; }
|
||||
section { margin: 22px 0; }
|
||||
ul { padding-left: 20px; }
|
||||
code { background: #f3f4f6; padding: 2px 6px; border-radius: 6px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Plan: Native, Fast, Iterative Deployment (Docker Optional)</h1>
|
||||
<p class="meta">Date: 2026-05-18</p>
|
||||
|
||||
<section>
|
||||
<h2>Plan Summary</h2>
|
||||
<p>Define and execute a fast iteration deployment path centered on host-native services, while preserving Docker as a fallback/runtime option.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Goals</h2>
|
||||
<ul>
|
||||
<li>Reduce deploy turnaround time immediately.</li>
|
||||
<li>Identify concrete bottlenecks with timing evidence.</li>
|
||||
<li>Stabilize proxy/runtime topology for reliable production rollouts.</li>
|
||||
<li>Support both native and Docker strategies with explicit guardrails.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Proposed Changes</h2>
|
||||
<ul>
|
||||
<li>Use scoped fast deploys short-term.</li>
|
||||
<li>Audit and remediate server-state blockers (duplicate compose/project drift).</li>
|
||||
<li>Prepare native runtime prerequisites and checked-in operational assets.</li>
|
||||
<li>Add deployment strategy prechecks, validation matrix, and staged cutover.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Relevant Context</h2>
|
||||
<ul>
|
||||
<li>Open issue <code>islandflow-2db</code>: stale duplicate compose stack cleanup.</li>
|
||||
<li>Open issue <code>islandflow-sz8</code>: public <code>/replay/options</code> proxy regression.</li>
|
||||
<li>Open issue <code>islandflow-38p</code>: native unit templates and rollback helpers.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Implementation Steps</h2>
|
||||
<ol>
|
||||
<li>Stop the bleeding immediately (current deploy loop).</li>
|
||||
<li>Get hard timing data per deploy phase.</li>
|
||||
<li>Live server state audit (when plan mode is off).</li>
|
||||
<li>Resolve duplicate compose stack first (<code>islandflow-2db</code>).</li>
|
||||
<li>Fix NPM proxy route regression (<code>islandflow-sz8</code>).</li>
|
||||
<li>Define target iterative deployment model.</li>
|
||||
<li>Prepare native runtime prerequisites on VPS.</li>
|
||||
<li>Checked-in native ops assets (<code>islandflow-38p</code>).</li>
|
||||
<li>Switch proxy topology for native mode carefully.</li>
|
||||
<li>Deploy strategy guardrails.</li>
|
||||
<li>Validation matrix.</li>
|
||||
<li>Staged cutover plan.</li>
|
||||
<li>Decision: final default runtime.</li>
|
||||
<li>Decision: optimization priority.</li>
|
||||
<li>Decision: immediate live audit kickoff.</li>
|
||||
</ol>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Risks, Limitations, and Mitigations</h2>
|
||||
<ul>
|
||||
<li>Risk: native runtime not yet production-hardened. Mitigation: keep Docker fallback and explicit gating.</li>
|
||||
<li>Risk: proxy misrouting breaks API routes. Mitigation: route checks and post-change smoke validation.</li>
|
||||
<li>Risk: operational drift on VPS. Mitigation: preflight audits and documented rollback steps.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Open Questions</h2>
|
||||
<ul>
|
||||
<li>Should native become the default runtime now, or after hardening milestones?</li>
|
||||
<li>Should backend iteration speed be prioritized ahead of web deploy speed?</li>
|
||||
<li>Do we start immediate live server audit as soon as plan mode is disabled?</li>
|
||||
</ul>
|
||||
</section>
|
||||
</body>
|
||||
</html>
|
||||
153
docs/turns/2026-05-18-native-fast-iterative-deploy.html
Normal file
153
docs/turns/2026-05-18-native-fast-iterative-deploy.html
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>2026-05-18: Native fast iterative deploy</title>
|
||||
<style>
|
||||
:root {
|
||||
color-scheme: dark;
|
||||
--bg: #0b1020;
|
||||
--panel: #131a2b;
|
||||
--panel-2: #182237;
|
||||
--text: #eef3ff;
|
||||
--muted: #a7b4d4;
|
||||
--line: #2a3651;
|
||||
--accent: #7dd3fc;
|
||||
--good: #86efac;
|
||||
--warn: #fbbf24;
|
||||
}
|
||||
* { box-sizing: border-box; }
|
||||
body {
|
||||
margin: 0;
|
||||
background: linear-gradient(180deg, #060914, var(--bg));
|
||||
color: var(--text);
|
||||
font: 16px/1.6 Inter, system-ui, sans-serif;
|
||||
}
|
||||
main {
|
||||
max-width: 920px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px 64px;
|
||||
}
|
||||
section {
|
||||
margin-top: 22px;
|
||||
padding: 22px 24px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 18px;
|
||||
background: linear-gradient(180deg, var(--panel), var(--panel-2));
|
||||
}
|
||||
h1, h2 { line-height: 1.15; }
|
||||
h1 { margin: 0 0 12px; font-size: 2rem; }
|
||||
h2 { margin: 0 0 12px; font-size: 1.15rem; }
|
||||
p, li { color: var(--text); }
|
||||
.meta { color: var(--muted); margin-bottom: 18px; }
|
||||
.lede { color: var(--muted); max-width: 72ch; }
|
||||
code, pre { font: 13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
||||
code {
|
||||
padding: 0.15rem 0.35rem;
|
||||
border-radius: 8px;
|
||||
background: rgba(125, 211, 252, 0.12);
|
||||
color: var(--accent);
|
||||
}
|
||||
pre {
|
||||
margin: 12px 0 0;
|
||||
padding: 14px 16px;
|
||||
overflow: auto;
|
||||
border-radius: 14px;
|
||||
border: 1px solid var(--line);
|
||||
background: #0a0f1d;
|
||||
}
|
||||
ul { margin: 0; padding-left: 1.2rem; }
|
||||
.good { color: var(--good); }
|
||||
.warn { color: var(--warn); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
<div class="meta">Turn document · 2026-05-18 03:29 EDT · Issues: islandflow-9rc, islandflow-38p, islandflow-bsg, islandflow-2db</div>
|
||||
<h1>Native fast iterative deploy</h1>
|
||||
<p class="lede">Implemented the native-first iterative deploy plan by adding deploy timing output, a safe worker-only native fast path, checked-in systemd user units and rollback helpers, server-local deploy execution, and updated live-operational documentation based on a fresh VPS audit.</p>
|
||||
|
||||
<section>
|
||||
<h2>Summary</h2>
|
||||
<p>The deploy flow now supports a safer native worker iteration model without requiring public edge cutover first. It can run directly from the VPS checkout without SSH, emits phase timings, includes checked-in native unit files plus install/rollback/smoke-test helpers, and documents the staged cutover path. During live audit, the previously reported <code>/replay/options</code> proxy issue and duplicate <code>islandflow</code> compose stack were both confirmed resolved on the host.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Changes Made</h2>
|
||||
<ul>
|
||||
<li>Extended <code>scripts/deploy.ts</code> with deploy timing summaries for precheck, rollout, and verification phases.</li>
|
||||
<li>Added <code>--workers-only</code> deploy scope for Docker and native runtimes.</li>
|
||||
<li>Changed native <code>--fast</code> behavior so default full-scope fast deploys become worker-only instead of touching web/API.</li>
|
||||
<li>Added native edge guardrails via <code>DEPLOY_NATIVE_EDGE_READY=1</code> before web/API native deploys are allowed.</li>
|
||||
<li>Added local-server execution mode so <code>./deploy</code> can run from <code>/home/delta/islandflow</code> without SSHing back into the same host.</li>
|
||||
<li>Added <code>DEPLOY_SSH_KEY_PATH</code> and <code>DEPLOY_FORCE_SSH</code> overrides for operators with non-default SSH setups.</li>
|
||||
<li>Checked in native ops assets under <code>deployment/native/</code>:</li>
|
||||
<li><code>install-user-units.sh</code>, <code>check-native-health.sh</code>, <code>rollback.sh</code></li>
|
||||
<li>six user unit files in <code>deployment/native/systemd/user/</code></li>
|
||||
<li>Updated <code>README.md</code>, <code>deployment/docker/README.md</code>, and <code>deployment/native/README.md</code> to document the worker-first model, local execution mode, validation matrix, and staged cutover guidance.</li>
|
||||
<li>Synced <code>deployment/docker/workspace-root/package.json</code> so Docker workspace validation passes again.</li>
|
||||
<li>Installed the checked-in user unit files onto the live VPS in disabled form under <code>~/.config/systemd/user</code>.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Context</h2>
|
||||
<p>The plan targeted faster deployment iteration while avoiding a premature move of the public edge away from the current Docker + Nginx Proxy Manager topology. The practical target was to make native runtime useful immediately for backend-worker iteration, while leaving web/API cutover deliberate and reversible.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Important Implementation Details</h2>
|
||||
<ul>
|
||||
<li>Native fast mode now defaults to <code>--workers-only</code>; Docker fast mode still defaults to <code>--services-only</code>.</li>
|
||||
<li>Native deploys that include public web/API scope now fail fast unless <code>DEPLOY_NATIVE_EDGE_READY=1</code> is set.</li>
|
||||
<li>Running from the live VPS checkout automatically switches deploy execution from SSH mode to local mode.</li>
|
||||
<li>The checked-in native unit files are user units aimed at the current VPS layout: <code>/home/delta/islandflow</code> and <code>/home/delta/.bun/bin/bun</code>.</li>
|
||||
<li><code>install-user-units.sh</code> now installs units safely without enabling anything by default; enabling is explicit and scope-based.</li>
|
||||
<li><code>rollback.sh</code> intentionally uses a detached git ref to make one-off native rollback practical without rewriting branch history.</li>
|
||||
</ul>
|
||||
<pre>export DEPLOY_NATIVE_SYSTEMCTL_PREFIX="systemctl --user"
|
||||
./deploy main --runtime native --fast
|
||||
# resolves to worker-only native deploy before public edge cutover</pre>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Expected Impact for End-Users</h2>
|
||||
<p>End-users should see indirect benefits first: faster backend iteration, safer operational changes, and clearer rollback paths. Public traffic behavior should remain unchanged until a deliberate native edge cutover is performed.</p>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Validation</h2>
|
||||
<ul>
|
||||
<li class="good">Passed: <code>bun run scripts/check-public-api-routes.ts https://flow.deltaisland.io</code></li>
|
||||
<li class="good">Passed: direct public <code>/replay/options</code> curl returned JSON</li>
|
||||
<li class="good">Passed: live Nginx Proxy Manager config contains <code>/replay</code> in the API route matcher</li>
|
||||
<li class="good">Passed: <code>docker compose ls</code> shows no duplicate <code>islandflow</code> project</li>
|
||||
<li class="good">Passed: <code>bash -n deployment/native/install-user-units.sh deployment/native/check-native-health.sh deployment/native/rollback.sh</code></li>
|
||||
<li class="good">Passed: <code>systemd-analyze verify deployment/native/systemd/user/*.service</code></li>
|
||||
<li class="good">Passed: <code>bun run check:docker-workspace</code> after syncing workspace snapshot</li>
|
||||
<li class="good">Passed: native edge guard refusal for <code>bun run scripts/deploy.ts main --runtime native --web-only --no-build</code></li>
|
||||
<li class="good">Passed: <code>./deployment/native/install-user-units.sh</code> followed by <code>systemctl --user list-unit-files 'islandflow*'</code></li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Issues, Limitations, and Mitigations</h2>
|
||||
<ul>
|
||||
<li><span class="warn">Native units were installed but not enabled or started.</span> This is intentional to avoid conflicting with the current Docker production edge.</li>
|
||||
<li><span class="warn">Public web/API native deploys are still gated.</span> Mitigation: explicit <code>DEPLOY_NATIVE_EDGE_READY=1</code> acknowledgment and staged cutover documentation.</li>
|
||||
<li><span class="warn">Native worker runtime has not yet been exercised live against the existing Docker worker stack.</span> Mitigation: follow-up issue to soak worker-only native units before any default-runtime decision.</li>
|
||||
<li><span class="warn">The known untracked Signal CLI tarball remains in the repo checkout.</span> This is already tolerated by the deploy helper allowlist and was not changed here.</li>
|
||||
</ul>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<h2>Follow-up Work</h2>
|
||||
<ul>
|
||||
<li>Open follow-up: <code>islandflow-vvw</code> — stage native public-edge cutover after worker soak.</li>
|
||||
<li>Decide whether native should ever replace Docker as the default runtime only after worker soak data and deliberate edge cutover validation.</li>
|
||||
</ul>
|
||||
</section>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
21
plans/2026-05-18-native-fast-iterative-deploy-plan.md
Normal file
21
plans/2026-05-18-native-fast-iterative-deploy-plan.md
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
# Native, Fast, Iterative Deployment Plan (Docker Optional)
|
||||
|
||||
Date: 2026-05-18
|
||||
|
||||
## Plan Steps (15)
|
||||
|
||||
1. ☐ Stop the bleeding immediately (current deploy loop).
|
||||
2. ☐ Get hard timing data per deploy phase.
|
||||
3. ☐ Live server state audit (when plan mode is off).
|
||||
4. ☐ Resolve duplicate compose stack first (islandflow-2db).
|
||||
5. ☐ Fix NPM proxy route regression (islandflow-sz8).
|
||||
6. ☐ Define target iterative deployment model.
|
||||
7. ☐ Prepare native runtime prerequisites on VPS.
|
||||
8. ☐ Checked-in native ops assets (islandflow-38p).
|
||||
9. ☐ Switch proxy topology for native mode carefully.
|
||||
10. ☐ Deploy strategy guardrails.
|
||||
11. ☐ Validation matrix.
|
||||
12. ☐ Staged cutover plan.
|
||||
13. ☐ Decision: final default runtime.
|
||||
14. ☐ Decision: optimization priority.
|
||||
15. ☐ Decision: immediate live audit kickoff.
|
||||
|
|
@ -7,7 +7,7 @@ import { fileURLToPath } from "node:url";
|
|||
|
||||
type DeployMode = "main" | "current-branch";
|
||||
type DeployRuntime = "docker" | "native";
|
||||
type DeployScope = "full" | "web" | "api" | "services";
|
||||
type DeployScope = "full" | "web" | "api" | "services" | "workers";
|
||||
|
||||
type DeployOptions = {
|
||||
mode: DeployMode;
|
||||
|
|
@ -18,10 +18,18 @@ type DeployOptions = {
|
|||
noBuild: boolean;
|
||||
};
|
||||
|
||||
type PhaseTiming = {
|
||||
name: string;
|
||||
durationMs: number;
|
||||
};
|
||||
|
||||
const REMOTE_HOST = "delta@152.53.80.229";
|
||||
const REMOTE_REPO = "/home/delta/islandflow";
|
||||
const REMOTE_DOCKER_DEPLOYMENT = "/home/delta/islandflow/deployment/docker";
|
||||
const SSH_KEY = path.join(process.env.HOME ?? "", ".ssh", "delta_ed25519");
|
||||
const SSH_KEY =
|
||||
process.env.DEPLOY_SSH_KEY_PATH?.trim() ||
|
||||
path.join(process.env.HOME ?? "", ".ssh", "delta_ed25519");
|
||||
const DEPLOY_FORCE_SSH = process.env.DEPLOY_FORCE_SSH?.trim() === "1";
|
||||
const SSH_OPTIONS = [
|
||||
"-i",
|
||||
SSH_KEY,
|
||||
|
|
@ -38,6 +46,7 @@ const PUBLIC_APP_URL =
|
|||
const PUBLIC_API_HEALTH_URL =
|
||||
process.env.DEPLOY_PUBLIC_API_HEALTH_URL?.trim() || null;
|
||||
const DEPLOY_GIT_REMOTE_OVERRIDE = process.env.DEPLOY_GIT_REMOTE?.trim() || null;
|
||||
const DEPLOY_NATIVE_EDGE_READY = process.env.DEPLOY_NATIVE_EDGE_READY?.trim() === "1";
|
||||
const NATIVE_SYSTEMCTL_PREFIX =
|
||||
process.env.DEPLOY_NATIVE_SYSTEMCTL_PREFIX?.trim() || "sudo -n systemctl";
|
||||
const NATIVE_UNITS = {
|
||||
|
|
@ -65,15 +74,22 @@ const DOCKER_BACKEND_SERVICES = [
|
|||
"ingest-options",
|
||||
"ingest-equities"
|
||||
] as const;
|
||||
const DOCKER_WORKER_SERVICES = [
|
||||
"compute",
|
||||
"candles",
|
||||
"ingest-options",
|
||||
"ingest-equities"
|
||||
] as const;
|
||||
|
||||
const scriptPath = fileURLToPath(import.meta.url);
|
||||
const repoRoot = path.resolve(path.dirname(scriptPath), "..");
|
||||
const isLocalServerExecution = !DEPLOY_FORCE_SSH && repoRoot === REMOTE_REPO;
|
||||
|
||||
function usage(exitCode = 1): never {
|
||||
console.error(`Usage:
|
||||
./deploy main [--runtime docker|native] [--web-only|--api-only|--services-only] [--fast] [--no-build] [--force-recreate]
|
||||
./deploy current-branch [--runtime docker|native] [--web-only|--api-only|--services-only] [--fast] [--no-build] [--force-recreate]
|
||||
./deploy current branch [--runtime docker|native] [--web-only|--api-only|--services-only] [--fast] [--no-build] [--force-recreate]
|
||||
./deploy main [--runtime docker|native] [--web-only|--api-only|--services-only|--workers-only] [--fast] [--no-build] [--force-recreate]
|
||||
./deploy current-branch [--runtime docker|native] [--web-only|--api-only|--services-only|--workers-only] [--fast] [--no-build] [--force-recreate]
|
||||
./deploy current branch [--runtime docker|native] [--web-only|--api-only|--services-only|--workers-only] [--fast] [--no-build] [--force-recreate]
|
||||
|
||||
Modes:
|
||||
main Deploy <remote>/main to the live server checkout.
|
||||
|
|
@ -88,18 +104,22 @@ Scopes:
|
|||
--web-only Deploy only the Next.js web surface.
|
||||
--api-only Deploy only the API service.
|
||||
--services-only Deploy API + backend services without the web service.
|
||||
--workers-only Deploy compute/candles/ingest workers without touching web or API.
|
||||
|
||||
Options:
|
||||
--runtime <name> Explicit runtime selector (docker or native).
|
||||
--fast Prefer a quicker rollout profile (defaults full scope to --services-only and skips public API route suite).
|
||||
--fast Prefer a quicker rollout profile (defaults full scope to --services-only for docker and --workers-only for native, and skips the public API route suite when API scope is included).
|
||||
--no-build Skip docker image builds or native bun install/web build steps.
|
||||
--force-recreate Docker-only escalation path for docker compose when a normal refresh is not enough.
|
||||
--help Show this help text.
|
||||
|
||||
Environment:
|
||||
DEPLOY_GIT_REMOTE Override git remote used for deploy fetch/pull/push (auto-detected by default).
|
||||
DEPLOY_SSH_KEY_PATH Override the SSH key used for remote execution.
|
||||
DEPLOY_FORCE_SSH Set to 1 to force SSH even when running from the live server checkout.
|
||||
DEPLOY_PUBLIC_APP_URL Override the public app URL (default: https://flow.deltaisland.io).
|
||||
DEPLOY_PUBLIC_API_HEALTH_URL Optional separate public API health URL for two-origin deployments.
|
||||
DEPLOY_NATIVE_EDGE_READY Set to 1 to allow native rollouts that include the public web or API edge.
|
||||
DEPLOY_NATIVE_SYSTEMCTL_PREFIX Override systemctl invocation for native rollouts (default: sudo -n systemctl).
|
||||
DEPLOY_NATIVE_WEB_UNIT Override native web systemd unit name.
|
||||
DEPLOY_NATIVE_API_UNIT Override native api systemd unit name.
|
||||
|
|
@ -114,6 +134,32 @@ function section(title: string): void {
|
|||
console.log(`\n== ${title} ==`);
|
||||
}
|
||||
|
||||
function formatDuration(durationMs: number): string {
|
||||
if (durationMs < 1000) {
|
||||
return `${durationMs}ms`;
|
||||
}
|
||||
|
||||
return `${(durationMs / 1000).toFixed(2)}s`;
|
||||
}
|
||||
|
||||
function timedPhase<T>(timings: PhaseTiming[], name: string, fn: () => T): T {
|
||||
const startedAt = Date.now();
|
||||
try {
|
||||
return fn();
|
||||
} finally {
|
||||
timings.push({ name, durationMs: Date.now() - startedAt });
|
||||
}
|
||||
}
|
||||
|
||||
function printTimingSummary(timings: PhaseTiming[]): void {
|
||||
section("Deploy Timings");
|
||||
const totalMs = timings.reduce((sum, timing) => sum + timing.durationMs, 0);
|
||||
for (const timing of timings) {
|
||||
console.log(`[deploy] ${timing.name}: ${formatDuration(timing.durationMs)}`);
|
||||
}
|
||||
console.log(`[deploy] total: ${formatDuration(totalMs)}`);
|
||||
}
|
||||
|
||||
function formatCommand(command: string, args: string[]): string {
|
||||
return [command, ...args]
|
||||
.map((part) => (/\s/.test(part) ? JSON.stringify(part) : part))
|
||||
|
|
@ -180,6 +226,23 @@ function runRemoteScript(
|
|||
args: string[] = []
|
||||
): void {
|
||||
section(title);
|
||||
|
||||
if (isLocalServerExecution) {
|
||||
const localArgs = ["-s", "--", ...args];
|
||||
console.log(`$ ${formatCommand("bash", localArgs)} # local server execution`);
|
||||
const result = spawnSync("bash", localArgs, {
|
||||
cwd: repoRoot,
|
||||
input: script,
|
||||
encoding: "utf8",
|
||||
stdio: ["pipe", "inherit", "inherit"]
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
process.exit(result.status ?? 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const sshArgs = [...SSH_OPTIONS, REMOTE_HOST, "bash", "-s", "--", ...args];
|
||||
console.log(`$ ${formatCommand("ssh", sshArgs)}`);
|
||||
const result = spawnSync("ssh", sshArgs, {
|
||||
|
|
@ -221,11 +284,14 @@ function parseScope(rawArgs: string[]): DeployScope {
|
|||
const scopes = [
|
||||
rawArgs.includes("--web-only") ? "web" : null,
|
||||
rawArgs.includes("--api-only") ? "api" : null,
|
||||
rawArgs.includes("--services-only") ? "services" : null
|
||||
rawArgs.includes("--services-only") ? "services" : null,
|
||||
rawArgs.includes("--workers-only") ? "workers" : null
|
||||
].filter((value): value is Exclude<DeployScope, "full"> => value !== null);
|
||||
|
||||
if (scopes.length > 1) {
|
||||
console.error("Choose only one deploy scope flag: --web-only, --api-only, or --services-only.");
|
||||
console.error(
|
||||
"Choose only one deploy scope flag: --web-only, --api-only, --services-only, or --workers-only."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +316,7 @@ function parseArgs(rawArgs: string[]): DeployOptions {
|
|||
arg !== "--web-only" &&
|
||||
arg !== "--api-only" &&
|
||||
arg !== "--services-only" &&
|
||||
arg !== "--workers-only" &&
|
||||
arg !== "--runtime" &&
|
||||
rawArgs[index - 1] !== "--runtime" &&
|
||||
!arg.startsWith("--runtime=")
|
||||
|
|
@ -282,8 +349,13 @@ function parseArgs(rawArgs: string[]): DeployOptions {
|
|||
}
|
||||
|
||||
function assertSshKeyExists(): void {
|
||||
if (isLocalServerExecution) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!existsSync(SSH_KEY)) {
|
||||
console.error(`Missing SSH key: ${SSH_KEY}`);
|
||||
console.error("Set DEPLOY_SSH_KEY_PATH or run from the live server checkout without DEPLOY_FORCE_SSH.");
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
|
@ -398,14 +470,16 @@ function describeScope(scope: DeployScope): string {
|
|||
return "api only";
|
||||
case "services":
|
||||
return "api + backend services";
|
||||
case "workers":
|
||||
return "worker services only";
|
||||
default:
|
||||
return "full stack";
|
||||
}
|
||||
}
|
||||
|
||||
function effectiveScope(scope: DeployScope, fast: boolean): DeployScope {
|
||||
function effectiveScope(scope: DeployScope, runtime: DeployRuntime, fast: boolean): DeployScope {
|
||||
if (fast && scope === "full") {
|
||||
return "services";
|
||||
return runtime === "native" ? "workers" : "services";
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
|
@ -418,6 +492,10 @@ function scopeIncludesApi(scope: DeployScope): boolean {
|
|||
return scope === "full" || scope === "api" || scope === "services";
|
||||
}
|
||||
|
||||
function scopeTouchesPublicEdge(scope: DeployScope): boolean {
|
||||
return scopeIncludesWeb(scope) || scopeIncludesApi(scope);
|
||||
}
|
||||
|
||||
function dockerServicesForScope(scope: DeployScope): string[] {
|
||||
switch (scope) {
|
||||
case "web":
|
||||
|
|
@ -426,6 +504,8 @@ function dockerServicesForScope(scope: DeployScope): string[] {
|
|||
return ["api"];
|
||||
case "services":
|
||||
return [...DOCKER_BACKEND_SERVICES];
|
||||
case "workers":
|
||||
return [...DOCKER_WORKER_SERVICES];
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
|
|
@ -448,6 +528,8 @@ function dockerLogServicesForScope(scope: DeployScope): string[] {
|
|||
return ["api"];
|
||||
case "services":
|
||||
return [...DOCKER_BACKEND_SERVICES];
|
||||
case "workers":
|
||||
return [...DOCKER_WORKER_SERVICES];
|
||||
default:
|
||||
return [...DOCKER_CORE_SERVICES];
|
||||
}
|
||||
|
|
@ -467,6 +549,13 @@ function nativeUnitsForScope(scope: DeployScope): string[] {
|
|||
NATIVE_UNITS.ingestOptions,
|
||||
NATIVE_UNITS.ingestEquities
|
||||
];
|
||||
case "workers":
|
||||
return [
|
||||
NATIVE_UNITS.compute,
|
||||
NATIVE_UNITS.candles,
|
||||
NATIVE_UNITS.ingestOptions,
|
||||
NATIVE_UNITS.ingestEquities
|
||||
];
|
||||
default:
|
||||
return [
|
||||
NATIVE_UNITS.web,
|
||||
|
|
@ -494,19 +583,46 @@ function localDockerWorkspaceSnapshotPrecheck(): void {
|
|||
}
|
||||
}
|
||||
|
||||
function localRuntimePrecheck(runtime: DeployRuntime, noBuild: boolean): void {
|
||||
function assertNativeEdgeReady(scope: DeployScope): void {
|
||||
if (!scopeTouchesPublicEdge(scope) || DEPLOY_NATIVE_EDGE_READY) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.error(
|
||||
"Refusing native deploy that touches public web/API scope before edge cutover is acknowledged."
|
||||
);
|
||||
console.error(
|
||||
"Set DEPLOY_NATIVE_EDGE_READY=1 only after proxy routing and native units for the public edge are intentionally prepared."
|
||||
);
|
||||
console.error(
|
||||
"For fast iterative backend deploys before cutover, use --runtime native --workers-only or --runtime native --fast."
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function localRuntimePrecheck(runtime: DeployRuntime, scope: DeployScope, noBuild: boolean): void {
|
||||
if (runtime === "docker" && !noBuild) {
|
||||
localDockerWorkspaceSnapshotPrecheck();
|
||||
return;
|
||||
}
|
||||
|
||||
if (runtime === "native") {
|
||||
assertNativeEdgeReady(scope);
|
||||
}
|
||||
}
|
||||
|
||||
function localMainPrecheck(remote: string, runtime: DeployRuntime, noBuild: boolean): void {
|
||||
function localMainPrecheck(
|
||||
remote: string,
|
||||
runtime: DeployRuntime,
|
||||
scope: DeployScope,
|
||||
noBuild: boolean
|
||||
): void {
|
||||
section("Local Precheck");
|
||||
runChecked("git", ["fetch", remote]);
|
||||
runChecked("git", ["status", "--short", "--branch"]);
|
||||
runChecked("git", ["rev-parse", "--verify", "HEAD"]);
|
||||
runChecked("git", ["rev-parse", `${remote}/main`]);
|
||||
localRuntimePrecheck(runtime, noBuild);
|
||||
localRuntimePrecheck(runtime, scope, noBuild);
|
||||
}
|
||||
|
||||
function currentBranchName(): string {
|
||||
|
|
@ -522,6 +638,7 @@ function localBranchPrecheck(
|
|||
remote: string,
|
||||
branch: string,
|
||||
runtime: DeployRuntime,
|
||||
scope: DeployScope,
|
||||
noBuild: boolean
|
||||
): void {
|
||||
section("Local Precheck");
|
||||
|
|
@ -537,7 +654,7 @@ function localBranchPrecheck(
|
|||
process.exit(1);
|
||||
}
|
||||
|
||||
localRuntimePrecheck(runtime, noBuild);
|
||||
localRuntimePrecheck(runtime, scope, noBuild);
|
||||
}
|
||||
|
||||
function publishCurrentBranch(remote: string, branch: string): void {
|
||||
|
|
@ -861,7 +978,8 @@ function publicVerification(scope: DeployScope, fast: boolean): void {
|
|||
|
||||
function main(): void {
|
||||
const options = parseArgs(process.argv.slice(2));
|
||||
const scope = effectiveScope(options.scope, options.fast);
|
||||
const scope = effectiveScope(options.scope, options.runtime, options.fast);
|
||||
const timings: PhaseTiming[] = [];
|
||||
const currentBranch = options.mode === "current-branch" ? currentBranchName() : null;
|
||||
const deployRemote = resolveDeployRemote(options.mode, currentBranch);
|
||||
assertSshKeyExists();
|
||||
|
|
@ -872,22 +990,33 @@ function main(): void {
|
|||
`via ${describeRuntime(options.runtime)} (${describeScope(scope)}${options.fast ? ", fast mode" : ""}).`
|
||||
);
|
||||
console.log(`[deploy] Using git remote: ${deployRemote}`);
|
||||
console.log(
|
||||
`[deploy] Execution mode: ${isLocalServerExecution ? "local server checkout" : `ssh to ${REMOTE_HOST}`}`
|
||||
);
|
||||
if (options.fast && options.scope === "full") {
|
||||
console.log("[deploy] Fast mode changed default full scope to --services-only.");
|
||||
console.log(
|
||||
`[deploy] Fast mode changed default full scope to ${options.runtime === "native" ? "--workers-only" : "--services-only"}.`
|
||||
);
|
||||
}
|
||||
|
||||
if (options.mode === "main") {
|
||||
localMainPrecheck(deployRemote, options.runtime, options.noBuild);
|
||||
remoteGitPrecheck();
|
||||
remoteRuntimePrecheck(options.runtime, scope);
|
||||
remoteRollout(
|
||||
options.mode,
|
||||
deployRemote,
|
||||
options.runtime,
|
||||
null,
|
||||
scope,
|
||||
options.forceRecreate,
|
||||
options.noBuild
|
||||
timedPhase(timings, "local precheck", () =>
|
||||
localMainPrecheck(deployRemote, options.runtime, scope, options.noBuild)
|
||||
);
|
||||
timedPhase(timings, "remote git precheck", () => remoteGitPrecheck());
|
||||
timedPhase(timings, "remote runtime precheck", () =>
|
||||
remoteRuntimePrecheck(options.runtime, scope)
|
||||
);
|
||||
timedPhase(timings, "remote rollout", () =>
|
||||
remoteRollout(
|
||||
options.mode,
|
||||
deployRemote,
|
||||
options.runtime,
|
||||
null,
|
||||
scope,
|
||||
options.forceRecreate,
|
||||
options.noBuild
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const branch = currentBranch;
|
||||
|
|
@ -895,23 +1024,34 @@ function main(): void {
|
|||
console.error("Unable to resolve current branch for current-branch deploy mode.");
|
||||
process.exit(1);
|
||||
}
|
||||
localBranchPrecheck(deployRemote, branch, options.runtime, options.noBuild);
|
||||
publishCurrentBranch(deployRemote, branch);
|
||||
remoteGitPrecheck();
|
||||
remoteRuntimePrecheck(options.runtime, scope);
|
||||
remoteRollout(
|
||||
options.mode,
|
||||
deployRemote,
|
||||
options.runtime,
|
||||
branch,
|
||||
scope,
|
||||
options.forceRecreate,
|
||||
options.noBuild
|
||||
timedPhase(timings, "local precheck", () =>
|
||||
localBranchPrecheck(deployRemote, branch, options.runtime, scope, options.noBuild)
|
||||
);
|
||||
timedPhase(timings, "local publish", () => publishCurrentBranch(deployRemote, branch));
|
||||
timedPhase(timings, "remote git precheck", () => remoteGitPrecheck());
|
||||
timedPhase(timings, "remote runtime precheck", () =>
|
||||
remoteRuntimePrecheck(options.runtime, scope)
|
||||
);
|
||||
timedPhase(timings, "remote rollout", () =>
|
||||
remoteRollout(
|
||||
options.mode,
|
||||
deployRemote,
|
||||
options.runtime,
|
||||
branch,
|
||||
scope,
|
||||
options.forceRecreate,
|
||||
options.noBuild
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
remoteVerification(options.runtime, scope, options.fast);
|
||||
publicVerification(scope, options.fast);
|
||||
timedPhase(timings, "remote verification", () =>
|
||||
remoteVerification(options.runtime, scope, options.fast)
|
||||
);
|
||||
timedPhase(timings, "public verification", () =>
|
||||
publicVerification(scope, options.fast)
|
||||
);
|
||||
printTimingSummary(timings);
|
||||
}
|
||||
|
||||
main();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue