From c9620eb741d7b5b57f2eca210af3b5e7051dac59 Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza <8016841+diegosouzapw@users.noreply.github.com> Date: Wed, 3 Jun 2026 18:12:30 -0300 Subject: [PATCH] chore(build): re-apply build-reorg follow-ups (compat fallback + deploy docs) (#3127) * build(compat): serve CLI falls back dist/ -> app/ for upgrade safety Backward-compat hardening: the npm CLI prefers the new dist/ standalone but falls back to the legacy app/ location, so an upgrade over a partially-replaced install (or a package built before the app/->dist/ rename) still boots. Deployed runtimes (VPS app/, Docker /app, Electron resources/app) are unchanged by design. * docs(deploy): stop pm2 before rsync --delete to avoid transient chunk error rsync --delete removing chunk files under a live server produced the transient [Shutdown] Cannot find module ./chunks/NNNNN.js. Stop the process first, then start with a clean module graph. * test(cli): cover serve APP_DIR dist/->app/ backward-compat fallback --- .agents/skills/deploy-vps-akamai-cc/SKILL.md | 9 ++++-- .agents/skills/deploy-vps-both-cc/SKILL.md | 9 ++++-- .agents/skills/deploy-vps-local-cc/SKILL.md | 9 ++++-- bin/cli/commands/serve.mjs | 6 +++- tests/unit/cli-serve-port.test.ts | 32 ++++++++++++++++++++ 5 files changed, 55 insertions(+), 10 deletions(-) diff --git a/.agents/skills/deploy-vps-akamai-cc/SKILL.md b/.agents/skills/deploy-vps-akamai-cc/SKILL.md index f9c8fbcbd7..db8dd4285c 100644 --- a/.agents/skills/deploy-vps-akamai-cc/SKILL.md +++ b/.agents/skills/deploy-vps-akamai-cc/SKILL.md @@ -51,17 +51,20 @@ OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/pa ssh root@69.164.221.35 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}" ``` -### 3. Rsync `dist/` → remote `app/` +### 3. Stop the process, then rsync `dist/` → remote `app/` -The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`: +The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`. **Stop the running process FIRST** so `rsync --delete` never removes chunk files out from under the live server — that race produces the transient `[Shutdown] Cannot find module ./chunks/NNNNN.js` seen mid-deploy: // turbo-all ```bash +ssh root@69.164.221.35 "pm2 stop omniroute" rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@69.164.221.35:/usr/lib/node_modules/omniroute/app/ ``` -### 4. Restart + health check +### 4. Start + health check + +`pm2 restart` on a stopped app starts it again with a clean module graph: ```bash ssh root@69.164.221.35 "pm2 restart omniroute --update-env && pm2 save" diff --git a/.agents/skills/deploy-vps-both-cc/SKILL.md b/.agents/skills/deploy-vps-both-cc/SKILL.md index bc61de9bb7..aeeb54f4ff 100644 --- a/.agents/skills/deploy-vps-both-cc/SKILL.md +++ b/.agents/skills/deploy-vps-both-cc/SKILL.md @@ -54,17 +54,20 @@ ssh root@192.168.0.15 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_m wait ``` -### 3. Rsync `dist/` → remote `app/` on both VPSs +### 3. Stop the processes, then rsync `dist/` → remote `app/` on both VPSs -The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`: +The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`. **Stop both running processes FIRST** so `rsync --delete` never removes chunk files out from under a live server — that race produces the transient `[Shutdown] Cannot find module ./chunks/NNNNN.js` seen mid-deploy: // turbo-all ```bash +ssh root@69.164.221.35 "pm2 stop omniroute" & ssh root@192.168.0.15 "pm2 stop omniroute" & wait rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@69.164.221.35:/usr/lib/node_modules/omniroute/app/ && rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@192.168.0.15:/usr/lib/node_modules/omniroute/app/ ``` -### 4. Restart + health check on both VPSs +### 4. Start + health check on both VPSs + +`pm2 restart` on a stopped app starts it again with a clean module graph: ```bash ssh root@69.164.221.35 "pm2 restart omniroute --update-env && pm2 save" diff --git a/.agents/skills/deploy-vps-local-cc/SKILL.md b/.agents/skills/deploy-vps-local-cc/SKILL.md index 273d65ce8d..fa85124467 100644 --- a/.agents/skills/deploy-vps-local-cc/SKILL.md +++ b/.agents/skills/deploy-vps-local-cc/SKILL.md @@ -51,17 +51,20 @@ OMNIROUTE_VERSION=$(node -p "require('/home/diegosouzapw/dev/proxys/OmniRoute/pa ssh root@192.168.0.15 "cp -a /usr/lib/node_modules/omniroute/app /usr/lib/node_modules/omniroute/app.bak-${OMNIROUTE_VERSION}" ``` -### 3. Rsync `dist/` → remote `app/` +### 3. Stop the process, then rsync `dist/` → remote `app/` -The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`: +The VPS image directory is still named `app/`; we ship the **contents** of our local `dist/`. **Stop the running process FIRST** so `rsync --delete` never removes chunk files out from under the live server — that race produces the transient `[Shutdown] Cannot find module ./chunks/NNNNN.js` seen mid-deploy: // turbo-all ```bash +ssh root@192.168.0.15 "pm2 stop omniroute" rsync -az --delete /home/diegosouzapw/dev/proxys/OmniRoute/dist/ root@192.168.0.15:/usr/lib/node_modules/omniroute/app/ ``` -### 4. Restart + health check +### 4. Start + health check + +`pm2 restart` on a stopped app starts it again with a clean module graph: ```bash ssh root@192.168.0.15 "pm2 restart omniroute --update-env && pm2 save" diff --git a/bin/cli/commands/serve.mjs b/bin/cli/commands/serve.mjs index de0a5c826f..e67697f72a 100644 --- a/bin/cli/commands/serve.mjs +++ b/bin/cli/commands/serve.mjs @@ -10,7 +10,11 @@ import { isTermux } from "../../../scripts/build/postinstallSupport.mjs"; const __dirname = dirname(fileURLToPath(import.meta.url)); const ROOT = join(__dirname, "..", "..", ".."); -const APP_DIR = join(ROOT, "dist"); +// The standalone bundle ships in `dist/` (since the build-output-isolation +// refactor). Fall back to the legacy `app/` location so an upgrade over a +// partially-replaced install — or a package built before the rename — still +// boots. Backward-compatible by design: every deployed runtime keeps its path. +const APP_DIR = existsSync(join(ROOT, "dist", "server.js")) ? join(ROOT, "dist") : join(ROOT, "app"); function parsePort(value, fallback) { const parsed = parseInt(String(value), 10); diff --git a/tests/unit/cli-serve-port.test.ts b/tests/unit/cli-serve-port.test.ts index 41982b13ec..72e868f468 100644 --- a/tests/unit/cli-serve-port.test.ts +++ b/tests/unit/cli-serve-port.test.ts @@ -54,3 +54,35 @@ test("serve command: --port option has no Commander default", async () => { // Ensure the option does NOT have a third argument (Commander default) assert.match(serveSource, /\.option\("--port ",\s*t\("serve\.port"\)\)/); }); + +/** + * Replicate the APP_DIR resolution from bin/cli/commands/serve.mjs to verify the + * backward-compatibility fallback introduced by the build-output-isolation refactor: + * the standalone bundle now ships in `dist/`, but an upgrade over a partially-replaced + * install — or a package built before the rename — must still boot from legacy `app/`. + */ +function resolveAppDir(root: string, distServerExists: boolean): string { + return distServerExists ? `${root}/dist` : `${root}/app`; +} + +test("serve app dir: resolves to dist/ when dist/server.js exists (current layout)", () => { + assert.equal(resolveAppDir("/opt/omniroute", true), "/opt/omniroute/dist"); +}); + +test("serve app dir: falls back to legacy app/ when dist/server.js is absent (upgrade safety)", () => { + assert.equal(resolveAppDir("/opt/omniroute", false), "/opt/omniroute/app"); +}); + +test("serve command: APP_DIR keeps the dist/ -> app/ backward-compat fallback", async () => { + const fs = await import("node:fs"); + const path = await import("node:path"); + const serveSource = fs.readFileSync( + path.resolve(import.meta.dirname, "../../bin/cli/commands/serve.mjs"), + "utf-8", + ); + // Must probe dist/server.js and fall back to app/ — never hard-code dist/ only. + assert.match( + serveSource, + /existsSync\(join\(ROOT, "dist", "server\.js"\)\)\s*\?\s*join\(ROOT, "dist"\)\s*:\s*join\(ROOT, "app"\)/, + ); +});