@nmakarov/cli-toolkit 0.76.0 → 0.77.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deploy.cjs +16 -2
- package/dist/deploy.cjs.map +1 -1
- package/dist/deploy.js +15 -2
- package/dist/deploy.js.map +1 -1
- package/dist/index.cjs +16 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +15 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/deploy.cjs
CHANGED
|
@@ -45,6 +45,7 @@ __export(deploy_exports, {
|
|
|
45
45
|
npmEnv: () => npmEnv,
|
|
46
46
|
npmInstallEnv: () => npmInstallEnv,
|
|
47
47
|
parseGitHost: () => parseGitHost,
|
|
48
|
+
parsePm2Jlist: () => parsePm2Jlist,
|
|
48
49
|
prepareGitHost: () => prepareGitHost,
|
|
49
50
|
provisionService: () => provisionService,
|
|
50
51
|
pruneReleases: () => pruneReleases,
|
|
@@ -461,16 +462,28 @@ function isPidAlive(pid) {
|
|
|
461
462
|
return false;
|
|
462
463
|
}
|
|
463
464
|
}
|
|
465
|
+
function parsePm2Jlist(stdout) {
|
|
466
|
+
const raw = String(stdout ?? "");
|
|
467
|
+
const text = raw.split("\n").filter((line) => !line.startsWith("[PM2]")).join("\n").trim();
|
|
468
|
+
if (!text) return [];
|
|
469
|
+
try {
|
|
470
|
+
const parsed = JSON.parse(text);
|
|
471
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
472
|
+
} catch (err) {
|
|
473
|
+
throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
464
476
|
async function getPm2Process(appName, options = {}) {
|
|
465
477
|
const { logger } = options;
|
|
478
|
+
await runCapture("bash", ["-lc", "pm2 ping >/dev/null 2>&1 || true"], { logger }).catch(() => {
|
|
479
|
+
});
|
|
466
480
|
const { stdout } = await runCapture("bash", ["-lc", "pm2 jlist"], { logger });
|
|
467
481
|
let list;
|
|
468
482
|
try {
|
|
469
|
-
list =
|
|
483
|
+
list = parsePm2Jlist(stdout);
|
|
470
484
|
} catch (err) {
|
|
471
485
|
throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);
|
|
472
486
|
}
|
|
473
|
-
if (!Array.isArray(list)) return null;
|
|
474
487
|
return list.find((p) => p?.name === appName) ?? null;
|
|
475
488
|
}
|
|
476
489
|
async function waitPm2(appName, predicate, options = {}) {
|
|
@@ -1470,6 +1483,7 @@ function resolveServiceFrom(serviceMap, name, { appsRoot } = {}) {
|
|
|
1470
1483
|
npmEnv,
|
|
1471
1484
|
npmInstallEnv,
|
|
1472
1485
|
parseGitHost,
|
|
1486
|
+
parsePm2Jlist,
|
|
1473
1487
|
prepareGitHost,
|
|
1474
1488
|
provisionService,
|
|
1475
1489
|
pruneReleases,
|
package/dist/deploy.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/deploy/index.js","../src/deploy/service.js","../src/deploy/paths.js","../src/deploy/run.js","../src/deploy/log.js","../src/deploy/git.js","../src/deploy/release.js","../src/deploy/activate.js","../src/deploy/deps.js","../src/deploy/test.js","../src/deploy/prune.js","../src/deploy/pm2.js","../src/deploy/nginx.js","../src/deploy/sync-env.js","../src/deploy/build-info.js","../src/deploy/init-structure.js","../src/deploy/bootstrap-host.js","../src/deploy/deploy-service.js","../src/deploy/provision-service.js","../src/deploy/rollback-service.js","../src/deploy/ssh-remote.js","../src/deploy/manifests.js"],"sourcesContent":["/**\n * Deploy ecosystem — manifest-driven EC2 release deploys (git checkout → versioned\n * releases under /apps/<svc>/ → pm2 + nginx upstream switch, with rollback).\n *\n * Projects describe each app as a \"service\" manifest (see ./service.js) and drive\n * everything from one CLI (scripts/deploy/cli.js, exposed as the `cli-deploy` bin).\n * The engine itself is project-agnostic: all specifics come from the manifest.\n */\nexport { defineService, deriveRepoDirName } from \"./service.js\";\nexport { servicePaths, releaseStamp, releaseDir } from \"./paths.js\";\nexport { run, runShell, runCapture, npmEnv, npmInstallEnv } from \"./run.js\";\nexport { appendDeployLog } from \"./log.js\";\nexport { cloneRepo, pullRepo } from \"./git.js\";\nexport { createRelease, readCurrentRelease, listReleases } from \"./release.js\";\nexport { activateRelease } from \"./activate.js\";\nexport { installDeps } from \"./deps.js\";\nexport { runReleaseTests } from \"./test.js\";\nexport { pruneReleases } from \"./prune.js\";\nexport {\n reloadPm2,\n stopPm2,\n startPm2,\n getPm2Process,\n waitPm2,\n isPidAlive,\n isFreshOnline,\n} from \"./pm2.js\";\nexport { enableNginxUpstream } from \"./nginx.js\";\nexport { syncEnv, scrubEnvContent } from \"./sync-env.js\";\nexport {\n bumpPatchVersion,\n resolveNextVersion,\n readReleaseBuildInfo,\n writeReleaseBuildInfo,\n} from \"./build-info.js\";\nexport { initServiceStructure } from \"./init-structure.js\";\nexport { bootstrapHost, installOperatorShell } from \"./bootstrap-host.js\";\nexport { deployService } from \"./deploy-service.js\";\nexport { provisionService } from \"./provision-service.js\";\nexport { rollbackService } from \"./rollback-service.js\";\nexport {\n REMOTE_CLI_REL,\n parseGitHost,\n sshRun,\n shellQuote,\n ensureDeployKeyOnRemote,\n prepareGitHost,\n ensureRepoDependencies,\n ensureEnvOnRemote,\n ensureRemoteRepo,\n runRemoteCli,\n runRemoteStatus,\n} from \"./ssh-remote.js\";\nexport { loadServices, resolveServiceFrom } from \"./manifests.js\";\n","/**\n * A \"service\" is a plain manifest object describing how to deploy one app onto a\n * host. Projects define these; the engine is otherwise project-agnostic.\n *\n * Required:\n * name unique id, also the default pm2 app + nginx site name\n * appsRoot base dir on the host, e.g. /apps/<name>\n * repoUrl git url to clone (ssh git@… or https://…)\n * pm2.script entry script (relative to the release/cwd)\n *\n * Optional (sensible defaults applied by defineService):\n * repoDirName default: repo name derived from repoUrl\n * repoSubdir default: \"\" (release = repo root). Set \"v2\" for a monorepo subdir.\n * keepReleases default: 3\n * testCommand default: null (shell run in the release before activating; null = skip)\n * envScrubPatterns default: [] (regex source strings; matching .env lines are dropped)\n * legacyRepoEnv default: null (fallback .env path on a pre-migration host)\n * buildInfoPath default: \"build-info.json\" (written into each release, relative to release root)\n * deployKey default: null (path to a git deploy key; only needed for ssh repoUrl)\n * requireEnv default: false (true = fail if no .env found; false = continue with empty)\n * pm2: {\n * appName=name, script, args=\"\", port,\n * stopAllowance=60, // seconds → injected as --stopAllowance=N on the process\n * killTimeout=65000, // ms; pm2 kill_timeout (default: stopAllowance*1000+5000)\n * }\n * nginx: { siteName=name, fqdn } (omit nginx entirely to skip the nginx step)\n */\nexport function deriveRepoDirName(repoUrl) {\n const tail = String(repoUrl ?? \"\").split(\"/\").pop() ?? \"repo\";\n return tail.replace(/\\.git$/, \"\") || \"repo\";\n}\n\nexport function defineService(service) {\n if (!service?.name) throw new Error(\"service manifest needs a `name`\");\n if (!service.appsRoot) throw new Error(`service \"${service.name}\" needs an appsRoot`);\n if (!service.repoUrl) throw new Error(`service \"${service.name}\" needs a repoUrl`);\n if (!service.pm2?.script) throw new Error(`service \"${service.name}\" needs pm2.script`);\n\n const pm2 = {\n appName: service.name,\n args: \"\",\n stopAllowance: 60,\n ...service.pm2,\n };\n if (pm2.killTimeout == null) {\n const stopSec = Number(pm2.stopAllowance);\n pm2.killTimeout = Number.isFinite(stopSec) && stopSec > 0\n ? Math.floor(stopSec * 1000) + 5000\n : 65_000;\n }\n const nginx = service.nginx\n ? { siteName: service.name, ...service.nginx }\n : null;\n\n return {\n repoDirName: deriveRepoDirName(service.repoUrl),\n repoSubdir: \"\",\n keepReleases: 3,\n testCommand: null,\n envScrubPatterns: [],\n legacyRepoEnv: null,\n buildInfoPath: \"build-info.json\",\n deployKey: null,\n requireEnv: false,\n ...service,\n pm2,\n nginx,\n };\n}\n","import { join } from \"node:path\";\n\n/**\n * Resolve on-disk paths for a service under <appsRoot>/.\n *\n * <appsRoot>/\n * <repoDirName>/[<repoSubdir>/] git checkout (build source only)\n * releases/<stamp>/ one release = contents of the run dir\n * shared/.env, ecosystem.config.cjs\n * logs/deploy.log\n * current -> releases/<stamp>\n */\nexport function servicePaths(service) {\n const root = service.appsRoot;\n const repoDirName = service.repoDirName ?? \"repo\";\n const repoSubdir = service.repoSubdir ?? \"\";\n\n const repo = join(root, repoDirName);\n const repoRun = repoSubdir ? join(repo, repoSubdir) : repo;\n\n return {\n root,\n repo,\n repoRun,\n repoEnv: join(repoRun, \".env\"),\n releases: join(root, \"releases\"),\n shared: join(root, \"shared\"),\n logs: join(root, \"logs\"),\n current: join(root, \"current\"),\n sharedEnv: join(root, \"shared\", \".env\"),\n ecosystem: join(root, \"shared\", \"ecosystem.config.cjs\"),\n /** Preloaded by pm2 `node_args --require` so ENV survives flaky restarts. */\n ensureEnv: join(root, \"shared\", \"ensure-env.cjs\"),\n deployLog: join(root, \"logs\", \"deploy.log\"),\n lockHashFile: join(root, \"shared\", \".package-lock.sha256\"),\n };\n}\n\n/** ISO-8601 release stamp with `:` (safe on Linux ext4). */\nexport function releaseStamp(date = new Date()) {\n return date.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n}\n\nexport function releaseDir(releasesRoot, stamp) {\n return join(releasesRoot, stamp);\n}\n","import { spawn } from \"node:child_process\";\n\n/** npm omits devDependencies when NODE_ENV=production — unset it for ci + tests. */\nexport function npmEnv(baseEnv = process.env) {\n const env = { ...baseEnv };\n delete env.NODE_ENV;\n return env;\n}\n\n/** Env for npm ci/install so devDependencies are always installed. */\nexport function npmInstallEnv(baseEnv = process.env) {\n return {\n ...npmEnv(baseEnv),\n NPM_CONFIG_PRODUCTION: \"false\",\n npm_config_production: \"false\",\n };\n}\n\n/** Run a command with inherited stdio. Rejects on non-zero exit. */\nexport function run(cmd, args, options = {}) {\n const { cwd, env, logger } = options;\n\n return new Promise((resolve, reject) => {\n logger?.info?.(`$ ${cmd} ${args.join(\" \")}${cwd ? ` (cwd=${cwd})` : \"\"}`);\n const child = spawn(cmd, args, {\n cwd,\n env: env ?? process.env,\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`${cmd} exited with code ${code}`));\n });\n });\n}\n\n/** Run a shell one-liner via bash -lc. */\nexport function runShell(command, options = {}) {\n return run(\"bash\", [\"-lc\", command], options);\n}\n\n/**\n * Run a command capturing stdout/stderr (for `pm2 jlist`, etc.).\n * Rejects on non-zero exit unless `allowFail` is set.\n *\n * @returns {Promise<{ stdout: string, stderr: string, code: number|null }>}\n */\nexport function runCapture(cmd, args, options = {}) {\n const { cwd, env, logger, allowFail = false } = options;\n\n return new Promise((resolve, reject) => {\n logger?.info?.(`$ ${cmd} ${args.join(\" \")}${cwd ? ` (cwd=${cwd})` : \"\"}`);\n const child = spawn(cmd, args, {\n cwd,\n env: env ?? process.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0 || allowFail) resolve({ stdout, stderr, code });\n else reject(new Error(`${cmd} exited with code ${code}${stderr ? `: ${stderr.trim()}` : \"\"}`));\n });\n });\n}\n","import { appendFile, mkdir } from \"node:fs/promises\";\n\nexport async function appendDeployLog(deployLogPath, message) {\n await mkdir(deployLogPath.replace(/\\/[^/]+$/, \"\"), { recursive: true });\n const line = `[${new Date().toISOString()}] ${message}\\n`;\n await appendFile(deployLogPath, line);\n}\n","import { access } from \"node:fs/promises\";\n\nimport { servicePaths } from \"./paths.js\";\nimport { run } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function cloneRepo(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n\n if (await pathExists(paths.repo)) {\n throw new Error(`Repo already exists at ${paths.repo} — use git pull instead`);\n }\n if (dryRun) {\n logger.info(`[dryRun] would git clone ${service.repoUrl} ${paths.repo}`);\n return;\n }\n\n await run(\"git\", [\"clone\", service.repoUrl, paths.repo], { logger });\n logger.info(`cloned ${service.repoUrl} → ${paths.repo}`);\n}\n\nexport async function pullRepo(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n\n if (!(await pathExists(paths.repo))) {\n throw new Error(`Repo missing at ${paths.repo} — run provision first`);\n }\n if (dryRun) {\n logger.info(`[dryRun] would git -C ${paths.repo} pull --ff-only`);\n return;\n }\n\n await run(\"git\", [\"-C\", paths.repo, \"pull\", \"--ff-only\"], { logger });\n logger.info(`pulled ${paths.repo}`);\n}\n","import { cp, mkdir, readlink, readdir, stat } from \"node:fs/promises\";\nimport { join, dirname as pathDirname, sep } from \"node:path\";\n\nimport { releaseDir, releaseStamp, servicePaths } from \"./paths.js\";\n\nconst SKIP_TOP = new Set([\"node_modules\", \".git\"]);\n\nfunction shouldCopyEntry(srcPath) {\n const parts = srcPath.split(sep);\n if (parts.some((p) => p === \"node_modules\" || p === \".git\")) return false;\n const top = parts.at(-1);\n if (parts.length === 1 && SKIP_TOP.has(top)) return false;\n return true;\n}\n\n/** Copy the run dir into releases/<stamp>/ (excluding node_modules and .git). */\nexport async function createRelease(service, options = {}) {\n const { stamp = releaseStamp(), dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const dest = releaseDir(paths.releases, stamp);\n\n if (dryRun) {\n logger.info(`[dryRun] would copy ${paths.repoRun} → ${dest}`);\n return { stamp, path: dest };\n }\n\n await mkdir(paths.releases, { recursive: true });\n await cp(paths.repoRun, dest, {\n recursive: true,\n filter: (src) => shouldCopyEntry(src),\n });\n\n logger.info(`release ${stamp} created at ${dest}`);\n return { stamp, path: dest };\n}\n\n/** Resolve current release path if symlink exists. */\nexport async function readCurrentRelease(paths) {\n try {\n const target = await readlink(paths.current);\n return target.startsWith(\"/\") ? target : join(pathDirname(paths.current), target);\n } catch {\n return null;\n }\n}\n\nexport async function listReleases(paths) {\n let names;\n try {\n names = await readdir(paths.releases);\n } catch {\n return [];\n }\n\n const entries = [];\n for (const name of names) {\n const full = releaseDir(paths.releases, name);\n const s = await stat(full);\n if (s.isDirectory()) entries.push({ name, path: full, mtime: s.mtime });\n }\n\n entries.sort((a, b) => b.name.localeCompare(a.name));\n return entries;\n}\n","import { symlink, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** Point current → release and link shared/.env inside the release. */\nexport async function activateRelease(releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would activate ${releasePath} → ${paths.current}`);\n return;\n }\n\n try {\n await unlink(paths.current);\n } catch {\n // first deploy\n }\n await symlink(releasePath, paths.current);\n\n const envLink = join(releasePath, \".env\");\n try {\n await unlink(envLink);\n } catch {\n // ok\n }\n await symlink(paths.sharedEnv, envLink);\n\n logger.info(`active release: ${releasePath}`);\n}\n","import { createHash } from \"node:crypto\";\nimport { access, cp, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { readCurrentRelease } from \"./release.js\";\nimport { npmInstallEnv, run } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function lockfileHash(releasePath) {\n const lockPath = join(releasePath, \"package-lock.json\");\n try {\n const buf = await readFile(lockPath);\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n } catch {\n return null; // no lockfile → can't fingerprint; always (re)install\n }\n}\n\nasync function readHashFile(path) {\n try {\n return (await readFile(path, \"utf8\")).trim();\n } catch {\n return null;\n }\n}\n\nasync function npmInstall(releasePath, hasLock, logger) {\n await rm(join(releasePath, \"node_modules\"), { recursive: true, force: true });\n const cmd = hasLock ? [\"ci\", \"--include=dev\"] : [\"install\", \"--include=dev\", \"--no-audit\", \"--no-fund\"];\n logger.info(`running npm ${cmd.join(\" \")} in ${releasePath}`);\n await run(\"npm\", cmd, { cwd: releasePath, logger, env: npmInstallEnv() });\n}\n\n/**\n * Install deps in the release. If the lockfile is unchanged from the active\n * release and its node_modules exists, copy it (fast path); otherwise npm ci.\n */\nexport async function installDeps(service, releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would install deps in ${releasePath}`);\n return { hash: null, copied: false };\n }\n\n const hash = await lockfileHash(releasePath);\n const hasLock = hash !== null;\n const hashMarker = join(releasePath, \".deploy-package-lock.sha256\");\n\n const currentPath = await readCurrentRelease(paths);\n let copied = false;\n\n if (hasLock && currentPath && (await pathExists(join(currentPath, \"node_modules\")))) {\n const currentHash = await readHashFile(join(currentPath, \".deploy-package-lock.sha256\"));\n if (currentHash === hash) {\n logger.info(\"lockfile unchanged — copying node_modules from current release\");\n await cp(join(currentPath, \"node_modules\"), join(releasePath, \"node_modules\"), {\n recursive: true,\n force: true,\n });\n copied = true;\n }\n }\n\n if (!copied) {\n await npmInstall(releasePath, hasLock, logger);\n }\n\n if (hasLock) {\n await writeFile(hashMarker, `${hash}\\n`, \"utf8\");\n await mkdir(paths.shared, { recursive: true });\n await writeFile(paths.lockHashFile, `${hash}\\n`, \"utf8\");\n }\n\n return { hash, copied };\n}\n","import { symlink, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { npmInstallEnv, runShell } from \"./run.js\";\n\n/**\n * Symlink shared/.env into the release and run the service's testCommand (a shell\n * one-liner, e.g. \"npm run test:ci\"). No-op when the service defines no command.\n */\nexport async function runReleaseTests(service, releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (!service.testCommand) {\n logger.info(\"no testCommand configured — skipping tests\");\n return;\n }\n\n if (dryRun) {\n logger.info(`[dryRun] would run \"${service.testCommand}\" in ${releasePath}`);\n return;\n }\n\n const envLink = join(releasePath, \".env\");\n try {\n await unlink(envLink);\n } catch {\n // no existing link\n }\n await symlink(paths.sharedEnv, envLink);\n\n logger.info(`running \"${service.testCommand}\" in ${releasePath}`);\n await runShell(service.testCommand, { cwd: releasePath, logger, env: npmInstallEnv() });\n}\n","import { rm, readlink } from \"node:fs/promises\";\n\nimport { listReleases } from \"./release.js\";\n\n/**\n * Remove old releases, keeping the newest N (always keeps the active one).\n *\n * @param {object} service\n * @param {object} paths\n * @param {{ dryRun?: boolean, logger?: object, protect?: string[] }} [options]\n * `protect` — release stamp names that must not be deleted (e.g. still-running\n * previous release during a rolling deploy).\n */\nexport async function pruneReleases(service, paths, options = {}) {\n const { dryRun = false, logger = console, protect = [] } = options;\n const keep = service.keepReleases ?? 3;\n const releases = await listReleases(paths);\n\n let activeName = null;\n try {\n const target = await readlink(paths.current);\n activeName = target.split(\"/\").pop();\n } catch {\n // no current\n }\n\n const keepSet = new Set();\n for (const rel of releases) {\n if (keepSet.size < keep) keepSet.add(rel.name);\n }\n if (activeName) keepSet.add(activeName);\n for (const name of protect) {\n if (name) keepSet.add(name);\n }\n\n const toRemove = releases.filter((rel) => !keepSet.has(rel.name));\n for (const rel of toRemove) {\n if (dryRun) {\n logger.info(`[dryRun] would rm -rf ${rel.path}`);\n } else {\n await rm(rel.path, { recursive: true, force: true });\n logger.info(`pruned ${rel.path}`);\n }\n }\n\n return { removed: toRemove.map((r) => r.name) };\n}\n","import { runShell, runCapture } from \"./run.js\";\n\nfunction sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** True if `pid` still exists (same host). */\nexport function isPidAlive(pid) {\n const n = Number(pid);\n if (!Number.isFinite(n) || n <= 0) return false;\n try {\n process.kill(n, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse `pm2 jlist` and return the process entry for `appName`, or null.\n *\n * @param {string} appName\n * @param {{ logger?: { info?: Function, warn?: Function } }} [options]\n * @returns {Promise<object|null>}\n */\nexport async function getPm2Process(appName, options = {}) {\n const { logger } = options;\n const { stdout } = await runCapture(\"bash\", [\"-lc\", \"pm2 jlist\"], { logger });\n let list;\n try {\n list = JSON.parse(stdout || \"[]\");\n } catch (err) {\n throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);\n }\n if (!Array.isArray(list)) return null;\n return list.find((p) => p?.name === appName) ?? null;\n}\n\n/**\n * Poll pm2 until `predicate(proc)` is true (proc may be null if missing), or timeout.\n *\n * @param {string} appName\n * @param {(proc: object|null) => boolean} predicate\n * @param {{ timeoutMs?: number, pollMs?: number, dryRun?: boolean, logger?: object, label?: string }} [options]\n */\nexport async function waitPm2(appName, predicate, options = {}) {\n const {\n timeoutMs = 65_000,\n pollMs = 500,\n dryRun = false,\n logger = console,\n label = \"condition\",\n } = options;\n\n if (dryRun) {\n logger.info?.(`[dryRun] would wait pm2 ${appName} for ${label}`);\n return null;\n }\n\n const deadline = Date.now() + timeoutMs;\n let lastStatus = \"(unknown)\";\n while (Date.now() < deadline) {\n const proc = await getPm2Process(appName, { logger });\n lastStatus = proc?.pm2_env?.status ?? \"(missing)\";\n if (predicate(proc)) return proc;\n await sleep(pollMs);\n }\n throw new Error(\n `pm2 wait timed out after ${timeoutMs}ms for ${appName} (${label}); last status=${lastStatus}`\n );\n}\n\n/**\n * True when pm2 reports the app online on a *new* pid (old process fully gone).\n * Avoids treating a not-yet-dead pre-reload process as a successful restart.\n */\nexport function isFreshOnline(proc, oldPid) {\n if (proc?.pm2_env?.status !== \"online\") return false;\n const pid = Number(proc.pid);\n if (!Number.isFinite(pid) || pid <= 0) return false;\n if (oldPid != null) {\n if (pid === oldPid) return false;\n if (isPidAlive(oldPid)) return false;\n }\n return true;\n}\n\n/**\n * Remove app from pm2 (SIGINT + kill_timeout drain) and wait until gone.\n * No-op if the app is not present.\n */\nasync function deletePm2App(appName, options = {}) {\n const { dryRun = false, logger = console, waitTimeoutMs = 65_000, oldPid = null } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 delete ${appName}`);\n return;\n }\n\n const before = await getPm2Process(appName, { logger });\n if (!before) {\n logger.info(`pm2 ${appName}: not present (skip delete)`);\n return;\n }\n\n await runShell(`pm2 delete \"${appName}\"`, { logger });\n await waitPm2(\n appName,\n (proc) => {\n if (proc) return false;\n if (oldPid != null && isPidAlive(oldPid)) return false;\n return true;\n },\n { timeoutMs: waitTimeoutMs, logger, label: \"deleted (process gone)\" }\n );\n logger.info(`pm2 ${appName} deleted`);\n}\n\n/** Fresh `pm2 start <ecosystem> --update-env` with production ENV on the CLI. */\nasync function startFromEcosystem(paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 start ${paths.ecosystem} --update-env`);\n return;\n }\n\n // Prefix ENV on the pm2 CLI: --update-env copies the invoking shell env.\n await runShell(\n `ENV=production NODE_ENV=production pm2 start \"${paths.ecosystem}\" --update-env`,\n { logger }\n );\n logger.info(\"pm2 started from ecosystem\");\n}\n\n/**\n * Rolling restart: `pm2 delete` then `pm2 start <ecosystem> --update-env`.\n *\n * Prefer delete+start over `startOrReload --update-env`: reload can leave a first\n * spawn with a stale/wrong ENV (e.g. S3 profile=local) and does not refresh\n * immutable fields like `script` / `node_args`. Delete honors kill_timeout so the\n * old process can drain, then start applies the full ecosystem.\n */\nexport async function reloadPm2(paths, options = {}) {\n const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(\n `[dryRun] would pm2 delete${appName ? ` ${appName}` : \"\"} + start ${paths.ecosystem} --update-env`\n );\n return;\n }\n\n let oldPid = null;\n if (appName) {\n const before = await getPm2Process(appName, { logger });\n const n = Number(before?.pid);\n if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === \"online\") {\n oldPid = n;\n logger.info(`pm2 ${appName}: recreate (old pid=${oldPid})`);\n }\n await deletePm2App(appName, { dryRun, logger, waitTimeoutMs, oldPid });\n } else {\n logger.warn?.(\n \"reloadPm2: appName missing; starting ecosystem without delete \" +\n \"(pass appName so script/env always recreate cleanly)\"\n );\n }\n\n await startFromEcosystem(paths, { dryRun, logger });\n\n if (appName) {\n const proc = await waitPm2(\n appName,\n (p) => isFreshOnline(p, oldPid),\n {\n timeoutMs: waitTimeoutMs,\n logger,\n label: oldPid\n ? `online on new pid (old ${oldPid} gone)`\n : \"online after recreate\",\n }\n );\n logger.info(`pm2 ${appName} online pid=${proc?.pid ?? \"?\"}`);\n }\n}\n\n/** `pm2 stop <app>` and wait until status is stopped (or process missing). */\nexport async function stopPm2(appName, options = {}) {\n const { dryRun = false, logger = console, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 stop ${appName}`);\n return;\n }\n\n const before = await getPm2Process(appName, { logger });\n if (!before) {\n logger.info(`pm2 ${appName}: not present (already stopped)`);\n return;\n }\n if (before.pm2_env?.status === \"stopped\") {\n logger.info(`pm2 ${appName}: already stopped`);\n return;\n }\n\n const oldPid = Number(before.pid);\n await runShell(`pm2 stop \"${appName}\"`, { logger });\n await waitPm2(\n appName,\n (proc) => {\n if (proc && proc.pm2_env?.status !== \"stopped\") return false;\n if (Number.isFinite(oldPid) && oldPid > 0 && isPidAlive(oldPid)) return false;\n return true;\n },\n { timeoutMs: waitTimeoutMs, logger, label: \"stopped\" }\n );\n logger.info(`pm2 ${appName} stopped`);\n}\n\n/**\n * `pm2 start <ecosystem> --update-env` after deleting any existing registration\n * so immutable fields (script, node_args) and env always come from the ecosystem.\n */\nexport async function startPm2(paths, options = {}) {\n const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(\n `[dryRun] would pm2 delete${appName ? ` ${appName}` : \"\"} + start ${paths.ecosystem} --update-env`\n );\n return;\n }\n\n if (appName) {\n // stopFirst leaves a stopped registration; delete so start is a full recreate.\n await deletePm2App(appName, { dryRun, logger, waitTimeoutMs });\n }\n\n await startFromEcosystem(paths, { dryRun, logger });\n\n if (appName) {\n const proc = await waitPm2(\n appName,\n (p) => isFreshOnline(p, null),\n { timeoutMs: waitTimeoutMs, logger, label: \"online after start\" }\n );\n logger.info(`pm2 ${appName} online pid=${proc?.pid ?? \"?\"}`);\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { runShell } from \"./run.js\";\n\nasync function hasTlsCert(certPath) {\n try {\n await runShell(`sudo test -f '${certPath}'`, {});\n return true;\n } catch {\n return false;\n }\n}\n\nfunction proxyBlock(port) {\n return ` location / {\n proxy_pass http://127.0.0.1:${port};\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_read_timeout 120s;\n }`;\n}\n\nfunction buildNginxConfig(service) {\n const { nginx, pm2 } = service;\n const certDir = `/etc/letsencrypt/live/${nginx.fqdn}`;\n\n return `# ${nginx.siteName} — managed by cli-toolkit deploy (proxy mode)\n# ${nginx.fqdn} → 127.0.0.1:${pm2.port}\n\nserver {\n listen 80;\n listen [::]:80;\n server_name ${nginx.fqdn};\n location / { return 301 https://$host$request_uri; }\n}\n\nserver {\n listen 443 ssl;\n listen [::]:443 ssl;\n server_name ${nginx.fqdn};\n\n ssl_certificate ${certDir}/fullchain.pem;\n ssl_certificate_key ${certDir}/privkey.pem;\n include /etc/letsencrypt/options-ssl-nginx.conf;\n ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n client_max_body_size 25m;\n\n${proxyBlock(pm2.port)}\n}\n`;\n}\n\nfunction buildNginxConfigHttpOnly(service) {\n const { nginx, pm2 } = service;\n return `# ${nginx.siteName} — managed by cli-toolkit deploy (HTTP proxy, no TLS cert yet)\n\nserver {\n listen 80;\n listen [::]:80;\n server_name ${nginx.fqdn};\n\n client_max_body_size 25m;\n\n${proxyBlock(pm2.port)}\n}\n`;\n}\n\n/** Replace the stub nginx site with a reverse-proxy to the app. */\nexport async function enableNginxUpstream(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const { nginx } = service;\n if (!nginx) {\n logger.info(\"no nginx config on service — skipping nginx step\");\n return { skipped: true };\n }\n\n const siteAvailable = `/etc/nginx/sites-available/${nginx.siteName}`;\n const siteEnabled = `/etc/nginx/sites-enabled/${nginx.siteName}`;\n const cert = `/etc/letsencrypt/live/${nginx.fqdn}/fullchain.pem`;\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${siteAvailable} (proxy → 127.0.0.1:${service.pm2.port}) and reload nginx`);\n return { hasCert: null };\n }\n\n const hasCert = await hasTlsCert(cert);\n const config = hasCert ? buildNginxConfig(service) : buildNginxConfigHttpOnly(service);\n\n const tmp = join(tmpdir(), `${service.name}-nginx.conf`);\n await writeFile(tmp, config);\n await runShell(\n `sudo cp '${tmp}' '${siteAvailable}' && sudo ln -sf '${siteAvailable}' '${siteEnabled}' && sudo nginx -t && sudo systemctl reload nginx`,\n { logger },\n );\n\n logger.info(`nginx upstream enabled for ${nginx.fqdn} → 127.0.0.1:${service.pm2.port} (tls=${hasCert})`);\n return { hasCert };\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\n\nimport { servicePaths } from \"./paths.js\";\n\n/**\n * Drop .env lines matching any of the service's envScrubPatterns (regex source\n * strings). Useful to strip empty keys that would crash strict param validation.\n */\nexport function scrubEnvContent(content, patterns = []) {\n const regexes = patterns.map((p) => (p instanceof RegExp ? p : new RegExp(p)));\n if (regexes.length === 0) return content;\n return content\n .split(\"\\n\")\n .filter((line) => !regexes.some((re) => re.test(line.trim())))\n .join(\"\\n\");\n}\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Copy <repoRun>/.env → shared/.env (with optional legacy fallback + scrubbing). */\nexport async function syncEnv(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const patterns = service.envScrubPatterns ?? [];\n\n if (dryRun) {\n logger.info(`[dryRun] would sync ${paths.repoEnv} → ${paths.sharedEnv}`);\n return { source: paths.repoEnv, dest: paths.sharedEnv };\n }\n\n let source = paths.repoEnv;\n if (!(await pathExists(source)) && service.legacyRepoEnv && (await pathExists(service.legacyRepoEnv))) {\n logger.info(`using legacy env: ${service.legacyRepoEnv}`);\n source = service.legacyRepoEnv;\n }\n\n await mkdir(paths.shared, { recursive: true });\n\n if (!(await pathExists(source))) {\n if (service.requireEnv) {\n throw new Error(\n `No .env found at ${paths.repoEnv}` +\n (service.legacyRepoEnv ? ` or ${service.legacyRepoEnv}` : \"\") +\n \" — place .env on the host or run the remote deploy from a laptop with a local .env (auto-scp)\",\n );\n }\n // Optional env: ensure shared/.env exists so the per-release symlink is valid.\n if (!(await pathExists(paths.sharedEnv))) {\n await writeFile(paths.sharedEnv, \"\", { mode: 0o600 });\n }\n logger.warn(`no .env found (source ${source}) — using empty ${paths.sharedEnv}`);\n return { source: null, dest: paths.sharedEnv };\n }\n\n const raw = await readFile(source, \"utf8\");\n await writeFile(paths.sharedEnv, scrubEnvContent(raw, patterns), { mode: 0o600 });\n logger.info(`synced ${source} → ${paths.sharedEnv}`);\n return { source, dest: paths.sharedEnv };\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { readCurrentRelease } from \"./release.js\";\nimport { servicePaths } from \"./paths.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Bump semver patch (0.1.0 → 0.1.1). */\nexport function bumpPatchVersion(version) {\n const parts = String(version).trim().split(\".\");\n const major = Number.parseInt(parts[0], 10) || 0;\n const minor = Number.parseInt(parts[1], 10) || 0;\n const patch = Number.parseInt(parts[2], 10) || 0;\n return `${major}.${minor}.${patch + 1}`;\n}\n\nasync function readJson(path) {\n try {\n return JSON.parse(await readFile(path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nasync function gitShortCommit(repoPath) {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"-C\", repoPath, \"rev-parse\", \"--short\", \"HEAD\"], {\n encoding: \"utf8\",\n });\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Next semver: bump patch from the **currently active** release's build-info,\n * else seed from package.json. Each release carries its own build-info.json so a\n * rollback restores that release's label.\n */\nexport async function resolveNextVersion(service, paths, pkgPath) {\n const pkg = (await readJson(pkgPath)) ?? {};\n const baseVersion = pkg.version || \"0.1.0\";\n\n const currentPath = await readCurrentRelease(paths);\n if (currentPath) {\n const active = await readJson(join(currentPath, service.buildInfoPath));\n if (active?.version) return bumpPatchVersion(active.version);\n }\n return baseVersion;\n}\n\n/** Read build-info from a release directory (for rollback logs / status). */\nexport async function readReleaseBuildInfo(service, releasePath) {\n return readJson(join(releasePath, service.buildInfoPath));\n}\n\n/** Write build-info.json into the new release directory. */\nexport async function writeReleaseBuildInfo(service, releasePath, { stamp, dryRun = false, logger = console }) {\n const paths = servicePaths(service);\n const pkgPath = join(paths.repoRun, \"package.json\");\n\n const version = await resolveNextVersion(service, paths, pkgPath);\n const gitCommit = await gitShortCommit(paths.repo);\n\n const buildInfo = {\n version,\n release: stamp,\n deployedAt: new Date().toISOString(),\n gitCommit,\n service: service.name,\n };\n\n const dest = join(releasePath, service.buildInfoPath);\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${dest} (${JSON.stringify(buildInfo)})`);\n return buildInfo;\n }\n\n await mkdir(dirname(dest), { recursive: true });\n await writeFile(dest, `${JSON.stringify(buildInfo, null, 2)}\\n`, { mode: 0o644 });\n\n const gitSuffix = gitCommit ? ` git=${gitCommit}` : \"\";\n logger.info(`build info: v${version} release=${stamp}${gitSuffix}`);\n return buildInfo;\n}\n","import { access, appendFile, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { servicePaths } from \"./paths.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fail fast with a useful message when not on a provisioned host. */\nasync function requireDeployRoot(parentDir, serviceRoot) {\n if (await pathExists(parentDir)) return;\n throw new Error(\n `Cannot create ${serviceRoot}: parent directory ${parentDir} does not exist.\\n` +\n `This is meant to run on the target host (where ${parentDir} exists). ` +\n `For a local dry run use --appsRoot=/tmp/<service>.`,\n );\n}\n\nfunction resolveKillTimeoutMs(pm2) {\n const explicit = Number(pm2?.killTimeout);\n if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);\n const stopSec = Number(pm2?.stopAllowance);\n if (Number.isFinite(stopSec) && stopSec > 0) return Math.floor(stopSec * 1000) + 5000;\n return 65_000;\n}\n\n/** Append `--stopAllowance=<sec>` to pm2 args when manifest sets pm2.stopAllowance. */\nfunction resolvePm2Args(pm2) {\n const base = String(pm2?.args ?? \"\").trim();\n const stopSec = Number(pm2?.stopAllowance);\n if (!Number.isFinite(stopSec) || stopSec <= 0) return base;\n if (/(?:^|\\s)--stopAllowance=/.test(base)) return base;\n return `${base}${base ? \" \" : \"\"}--stopAllowance=${Math.floor(stopSec)}`.trim();\n}\n\n/** CJS snippet required before the app — always force production on deployed hosts. */\nfunction buildEnsureEnvScript() {\n return `/**\n * Written by cli-toolkit deploy (init-structure). Do not hand-edit.\n * Preloaded via pm2 node_args --require. Always set (do not only fill when unset):\n * \\`pm2 start … --update-env\\` can inject a non-production ENV from the\n * deploy shell / prior dump, and a conditional assign would leave it in place.\n */\nprocess.env.ENV = \"production\";\nprocess.env.NODE_ENV = \"production\";\n`;\n}\n\nfunction buildEcosystemConfig(service, paths) {\n const { pm2 } = service;\n const outLog = join(paths.logs, `${pm2.appName}.out.log`);\n const errLog = join(paths.logs, `${pm2.appName}.err.log`);\n const killTimeoutMs = resolveKillTimeoutMs(pm2);\n const args = resolvePm2Args(pm2);\n const ensureEnv = paths.ensureEnv;\n\n return `/**\n * pm2 ecosystem for ${service.name} — written by cli-toolkit deploy from the\n * service manifest (init/deploy). Do not hand-edit; change pm2.* in the\n * manifest and redeploy. cwd → \\`current\\` symlink.\n */\nmodule.exports = {\n apps: [\n {\n name: \"${pm2.appName}\",\n script: \"${pm2.script}\",\n cwd: \"${paths.current}\",\n args: ${JSON.stringify(args)},\n // Absolute --require so ENV is set before Args constructs (see shared/ensure-env.cjs).\n node_args: ${JSON.stringify(`--require ${ensureEnv}`)},\n instances: 1,\n exec_mode: \"fork\",\n autorestart: true,\n min_uptime: \"10s\",\n max_restarts: 10,\n restart_delay: 2000,\n max_memory_restart: \"1500M\",\n // Grace window after SIGINT/SIGTERM before SIGKILL (ms). Align with --stopAllowance.\n kill_timeout: ${killTimeoutMs},\n out_file: \"${outLog}\",\n error_file: \"${errLog}\",\n merge_logs: true,\n time: true,\n env: {\n // cli-toolkit Args: ENV selects env-suffixed defaults (DB_NAME_PRODUCTION, BUCKET_PRODUCTION, …)\n ENV: \"production\",\n NODE_ENV: \"production\",\n },\n },\n ],\n};\n`;\n}\n\n/** Create <appsRoot>/{releases,shared,logs} and sync shared/ecosystem.config.cjs from the manifest. */\nexport async function initServiceStructure(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const dirs = [paths.releases, paths.shared, paths.logs];\n const created = [];\n const skipped = [];\n\n if (!dryRun) {\n await requireDeployRoot(dirname(paths.root), paths.root);\n }\n\n for (const dir of dirs) {\n if (await pathExists(dir)) {\n skipped.push(dir);\n continue;\n }\n if (!dryRun) await mkdir(dir, { recursive: true });\n created.push(dir);\n }\n\n const ecosystemExisted = await pathExists(paths.ecosystem);\n const ensureEnvExisted = await pathExists(paths.ensureEnv);\n if (!dryRun) {\n await writeFile(paths.ensureEnv, buildEnsureEnvScript(), { mode: 0o644 });\n await writeFile(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 0o644 });\n }\n if (ensureEnvExisted) {\n logger.info(`synced ${paths.ensureEnv}`);\n } else {\n created.push(paths.ensureEnv);\n logger.info(`seeded ${paths.ensureEnv}`);\n }\n if (ecosystemExisted) {\n logger.info(`synced ${paths.ecosystem} from manifest`);\n } else {\n created.push(paths.ecosystem);\n logger.info(`seeded ${paths.ecosystem}`);\n }\n\n const line = `[${new Date().toISOString()}] init-structure service=${service.name} dryRun=${dryRun} created=${created.length} skipped=${skipped.length} ecosystem=synced\\n`;\n if (!dryRun) {\n await mkdir(paths.logs, { recursive: true });\n await appendFile(paths.deployLog, line);\n }\n\n logger.info(`service=${service.name} appsRoot=${paths.root}`);\n logger.info(`created: ${created.length ? created.join(\", \") : \"(none)\"}`);\n logger.info(`already present: ${skipped.length ? skipped.join(\", \") : \"(none)\"}`);\n\n return { paths, created, skipped, ecosystemCreated: !ecosystemExisted, ecosystemSynced: true };\n}\n","import { execSync } from \"node:child_process\";\nimport { access, chmod, copyFile, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\n\nimport { runShell } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction expandHome(path) {\n return path.startsWith(\"~/\") ? join(homedir(), path.slice(2)) : path;\n}\n\nasync function ensurePm2Startup(options = {}) {\n const { user = \"ubuntu\", dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(\"[dryRun] would configure pm2 startup systemd\");\n return;\n }\n\n try {\n execSync(\"pm2 ping\", { stdio: \"ignore\" });\n } catch {\n // pm2 daemon may not be up yet\n }\n\n try {\n const out = execSync(`pm2 startup systemd -u ${user} --hp /home/${user}`, { encoding: \"utf8\" });\n const sudoLine = out.split(\"\\n\").find((l) => l.trim().startsWith(\"sudo\"));\n if (sudoLine) {\n execSync(sudoLine.trim(), { stdio: \"inherit\" });\n logger.info(\"pm2 startup systemd configured\");\n }\n } catch (err) {\n logger.warn(`pm2 startup skipped or already configured: ${err.message}`);\n }\n}\n\nasync function installDeployKey(deployKeyPath, options = {}) {\n const { dryRun = false, logger = console } = options;\n const keyPath = expandHome(deployKeyPath);\n\n if (!(await pathExists(keyPath))) {\n logger.warn(`deploy key not found at ${keyPath} — skipping git ssh setup`);\n return;\n }\n\n const keyBase = basename(keyPath);\n const sshDir = join(homedir(), \".ssh\");\n const destKey = join(sshDir, keyBase);\n const configPath = join(sshDir, \"config\");\n const block = `\nHost github.com\n HostName github.com\n User git\n IdentityFile ${destKey}\n IdentitiesOnly yes\n`;\n\n if (dryRun) {\n logger.info(`[dryRun] would install deploy key ${keyPath} → ${destKey}`);\n return;\n }\n\n await mkdir(sshDir, { recursive: true, mode: 0o700 });\n await copyFile(keyPath, destKey);\n await chmod(destKey, 0o600);\n\n let config = \"\";\n if (await pathExists(configPath)) config = await readFile(configPath, \"utf8\");\n if (!config.includes(\"Host github.com\")) {\n await writeFile(configPath, `${config.trimEnd()}\\n${block}\\n`, { mode: 0o600 });\n logger.info(\"updated ~/.ssh/config for github.com\");\n }\n logger.info(`deploy key installed at ${destKey}`);\n}\n\nasync function installLogrotate(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const conf = `/etc/logrotate.d/${service.name}`;\n const body = `${service.appsRoot}/logs/*.log {\n daily\n rotate 14\n compress\n delaycompress\n missingok\n notifempty\n copytruncate\n}\n`;\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${conf}`);\n return;\n }\n\n const tmp = join(\"/tmp\", `${service.name}-logrotate.conf`);\n await writeFile(tmp, body);\n await runShell(`sudo cp '${tmp}' '${conf}'`, { logger });\n logger.info(`logrotate config written: ${conf}`);\n}\n\nconst OPERATOR_SHELL_BEGIN = \"# >>> mlsfarm-operator >>>\";\nconst OPERATOR_SHELL_END = \"# <<< mlsfarm-operator <<<\";\n\n/**\n * Idempotent ~/.bashrc block: ENV=production + cd into /apps/<service>/current\n * on interactive login. Safe to re-run; replaces any prior managed block.\n */\nexport async function installOperatorShell(service, options = {}) {\n const { user = \"ubuntu\", dryRun = false, logger = console } = options;\n const appsRoot = service.appsRoot;\n if (!appsRoot) {\n logger.warn(\"installOperatorShell: service.appsRoot missing — skipped\");\n return;\n }\n\n const home = user === \"ubuntu\" || user === process.env.USER\n ? join(homedir(), \".bashrc\")\n : `/home/${user}/.bashrc`;\n const currentDir = join(appsRoot, \"current\");\n const block = [\n OPERATOR_SHELL_BEGIN,\n \"# Managed by cli-toolkit deploy bootstrap — do not edit by hand.\",\n 'export ENV=production',\n `if [ -d \"${currentDir}\" ]; then`,\n ` cd \"${currentDir}\" || true`,\n \"fi\",\n OPERATOR_SHELL_END,\n \"\",\n ].join(\"\\n\");\n\n if (dryRun) {\n logger.info(`[dryRun] would update ${home} (ENV=production, cd ${currentDir})`);\n return;\n }\n\n let existing = \"\";\n if (await pathExists(home)) {\n existing = await readFile(home, \"utf8\");\n }\n\n const begin = existing.indexOf(OPERATOR_SHELL_BEGIN);\n const end = existing.indexOf(OPERATOR_SHELL_END);\n let next;\n if (begin !== -1 && end !== -1 && end > begin) {\n const afterEnd = end + OPERATOR_SHELL_END.length;\n const rest = existing.slice(afterEnd).replace(/^\\n/, \"\");\n next = `${existing.slice(0, begin).replace(/\\s*$/, \"\\n\")}${block}${rest}`;\n } else {\n next = `${existing.replace(/\\s*$/, \"\\n\")}\\n${block}`;\n }\n\n await writeFile(home, next, { mode: 0o644 });\n logger.info(`operator shell: ${home} → ENV=production, cd ${currentDir}`);\n}\n\n/** One-time host prep: pm2 systemd, optional git deploy key, logrotate, operator shell. */\nexport async function bootstrapHost(service, options = {}) {\n const { deployKey = service.deployKey, user = \"ubuntu\", dryRun = false, logger = console } = options;\n\n await ensurePm2Startup({ user, dryRun, logger });\n if (deployKey) await installDeployKey(deployKey, { dryRun, logger });\n await installLogrotate(service, { dryRun, logger });\n await installOperatorShell(service, { user, dryRun, logger });\n logger.info(\"bootstrap-host complete\");\n}\n","import { readlink } from \"node:fs/promises\";\n\nimport { initServiceStructure } from \"./init-structure.js\";\nimport { appendDeployLog } from \"./log.js\";\nimport { pullRepo } from \"./git.js\";\nimport { createRelease } from \"./release.js\";\nimport { installDeps } from \"./deps.js\";\nimport { runReleaseTests } from \"./test.js\";\nimport { activateRelease } from \"./activate.js\";\nimport { pruneReleases } from \"./prune.js\";\nimport { reloadPm2, stopPm2, startPm2 } from \"./pm2.js\";\nimport { enableNginxUpstream } from \"./nginx.js\";\nimport { syncEnv } from \"./sync-env.js\";\nimport { writeReleaseBuildInfo } from \"./build-info.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/**\n * Resolve pm2 kill_timeout (ms). Prefer manifest `pm2.killTimeout`, else\n * stopAllowance seconds × 1000 + 5s buffer, else 65s.\n */\nfunction resolveKillTimeoutMs(service) {\n const explicit = Number(service.pm2?.killTimeout);\n if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);\n const stopSec = Number(service.pm2?.stopAllowance);\n if (Number.isFinite(stopSec) && stopSec > 0) return Math.floor(stopSec * 1000) + 5000;\n return 65_000;\n}\n\n/**\n * Full deploy: pull → env → release → deps → test → activate → pm2 → prune → nginx.\n *\n * Default (**rolling**): keep the process running while the new release is built and\n * `current` is flipped; then `pm2 delete` + `pm2 start` (SIGINT + kill_timeout drain,\n * then full ecosystem recreate) so script/node_args/env always apply. Previous\n * release is not pruned until after that restart.\n *\n * **`--stopFirst`**: `pm2 stop` + wait → activate/prune → delete + `pm2 start`.\n * Use when a runner is wedged and will not honor graceful stop.\n */\nexport async function deployService(service, options = {}) {\n const {\n dryRun = false,\n skipPull = false,\n skipTests = false,\n skipNginx = false,\n stopFirst = false,\n logger = console,\n } = options;\n\n const paths = servicePaths(service);\n const appName = service.pm2.appName;\n const killTimeoutMs = resolveKillTimeoutMs(service);\n const waitTimeoutMs = killTimeoutMs + 10_000;\n\n await initServiceStructure(service, { dryRun, logger });\n\n if (!skipPull) await pullRepo(service, { dryRun, logger });\n\n await syncEnv(service, { dryRun, logger });\n\n const { stamp, path: releasePath } = await createRelease(service, { dryRun, logger });\n await writeReleaseBuildInfo(service, releasePath, { stamp, dryRun, logger });\n await installDeps(service, releasePath, paths, { dryRun, logger });\n\n if (!skipTests) await runReleaseTests(service, releasePath, paths, { dryRun, logger });\n\n let previousReleaseName = null;\n try {\n const prev = await readlink(paths.current);\n previousReleaseName = prev.split(\"/\").pop() || null;\n } catch {\n // first deploy\n }\n\n if (stopFirst) {\n logger.info(`deploy mode=stopFirst app=${appName} killTimeoutMs=${killTimeoutMs}`);\n await stopPm2(appName, { dryRun, logger, waitTimeoutMs });\n await activateRelease(releasePath, paths, { dryRun, logger });\n await pruneReleases(service, paths, { dryRun, logger });\n await startPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n } else {\n logger.info(\n `deploy mode=rolling app=${appName} killTimeoutMs=${killTimeoutMs}` +\n (previousReleaseName ? ` previous=${previousReleaseName}` : \"\")\n );\n await activateRelease(releasePath, paths, { dryRun, logger });\n // Restart into new `current` before pruning the previous tree (still in use by the old process).\n await reloadPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n await pruneReleases(service, paths, { dryRun, logger });\n }\n\n if (!skipNginx) await enableNginxUpstream(service, { dryRun, logger });\n\n const summary = `deploy complete stamp=${stamp} mode=${stopFirst ? \"stopFirst\" : \"rolling\"} dryRun=${dryRun}`;\n logger.info(summary);\n if (!dryRun) await appendDeployLog(paths.deployLog, summary);\n\n return { stamp, releasePath, mode: stopFirst ? \"stopFirst\" : \"rolling\" };\n}\n","import { access } from \"node:fs/promises\";\n\nimport { bootstrapHost } from \"./bootstrap-host.js\";\nimport { initServiceStructure } from \"./init-structure.js\";\nimport { cloneRepo, pullRepo } from \"./git.js\";\nimport { deployService } from \"./deploy-service.js\";\nimport { syncEnv } from \"./sync-env.js\";\nimport { servicePaths } from \"./paths.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** First-time setup: optional bootstrap, init dirs, clone/pull, sync .env, first deploy. */\nexport async function provisionService(service, options = {}) {\n const {\n dryRun = false,\n deploy = true,\n skipBootstrap = true,\n deployKey,\n logger = console,\n } = options;\n\n if (!skipBootstrap) {\n await bootstrapHost(service, { deployKey, dryRun, logger });\n }\n\n await initServiceStructure(service, { dryRun, logger });\n const paths = servicePaths(service);\n\n if (await pathExists(paths.repo)) {\n logger.info(`repo exists at ${paths.repo} — pulling`);\n await pullRepo(service, { dryRun, logger });\n } else {\n await cloneRepo(service, { dryRun, logger });\n }\n\n await syncEnv(service, { dryRun, logger });\n\n if (deploy) {\n await deployService(service, { dryRun, logger, skipPull: true });\n } else {\n logger.info(\"provision complete (deploy skipped)\");\n }\n}\n","import { readlink } from \"node:fs/promises\";\n\nimport { activateRelease } from \"./activate.js\";\nimport { readReleaseBuildInfo } from \"./build-info.js\";\nimport { appendDeployLog } from \"./log.js\";\nimport { listReleases } from \"./release.js\";\nimport { reloadPm2 } from \"./pm2.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/** Flip current to a named release or the previous one. */\nexport async function rollbackService(service, options = {}) {\n const { release: targetName, dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const releases = await listReleases(paths);\n\n if (releases.length === 0) throw new Error(\"No releases to roll back to\");\n\n let activeName = null;\n try {\n const target = await readlink(paths.current);\n activeName = target.split(\"/\").pop();\n } catch {\n throw new Error(\"No active release (current symlink missing)\");\n }\n\n let rollbackTarget;\n if (targetName) {\n rollbackTarget = releases.find((r) => r.name === targetName);\n if (!rollbackTarget) throw new Error(`Release not found: ${targetName}`);\n } else {\n rollbackTarget = releases.find((r) => r.name !== activeName);\n if (!rollbackTarget) throw new Error(\"No previous release to roll back to\");\n }\n\n if (rollbackTarget.name === activeName) throw new Error(`Already on release ${activeName}`);\n\n logger.info(`rollback ${activeName} → ${rollbackTarget.name}`);\n const buildInfo = await readReleaseBuildInfo(service, rollbackTarget.path);\n if (buildInfo?.version) {\n logger.info(`rollback target: v${buildInfo.version} release=${buildInfo.release ?? rollbackTarget.name}`);\n }\n await activateRelease(rollbackTarget.path, paths, { dryRun, logger });\n const appName = service.pm2?.appName ?? null;\n const waitTimeoutMs = 65_000;\n await reloadPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n\n const summary = `rollback ${activeName} → ${rollbackTarget.name} dryRun=${dryRun}`;\n if (!dryRun) await appendDeployLog(paths.deployLog, summary);\n\n return { from: activeName, to: rollbackTarget.name, path: rollbackTarget.path };\n}\n","import { access, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { basename, dirname, join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nimport { scrubEnvContent } from \"./sync-env.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/** Path (relative to the run dir on the server) of the toolkit deploy CLI. */\nexport const REMOTE_CLI_REL = \"node_modules/@nmakarov/cli-toolkit/scripts/deploy/cli.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction expandHome(path) {\n return path.startsWith(\"~/\") ? join(homedir(), path.slice(2)) : path;\n}\n\nfunction resolveLocalEnvPath(envFile) {\n if (envFile) {\n const expanded = expandHome(envFile);\n return expanded.startsWith(\"/\") ? expanded : join(process.cwd(), expanded);\n }\n return join(process.cwd(), \".env\");\n}\n\n/** Parse the ssh host out of a git url (git@host:..., ssh://git@host/...). null for https. */\nexport function parseGitHost(repoUrl) {\n const u = String(repoUrl ?? \"\");\n let m = u.match(/^[^@]+@([^:]+):/); // git@github.com:org/repo.git\n if (m) return m[1];\n m = u.match(/^ssh:\\/\\/[^@]+@([^/:]+)/); // ssh://git@github.com/org/repo.git\n if (m) return m[1];\n return null;\n}\n\nexport function shellQuote(value) {\n return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport function sshRun(host, remoteCommand, options = {}) {\n const { logger } = options;\n return new Promise((resolve, reject) => {\n logger?.info?.(`ssh ${host} ${remoteCommand.slice(0, 120)}${remoteCommand.length > 120 ? \"…\" : \"\"}`);\n const child = spawn(\"ssh\", [host, remoteCommand], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => (code === 0 ? resolve() : reject(new Error(`ssh ${host} exited with code ${code}`))));\n });\n}\n\nfunction scp(localPath, remoteSpec) {\n return new Promise((resolve, reject) => {\n const child = spawn(\"scp\", [localPath, remoteSpec], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => (code === 0 ? resolve() : reject(new Error(`scp exited with code ${code}`))));\n });\n}\n\n/** Copy a git deploy key from the laptop to the server (no-op if not present locally). */\nexport async function ensureDeployKeyOnRemote(host, deployKeyPath, options = {}) {\n const { logger = console } = options;\n if (!deployKeyPath) return false;\n const localPath = expandHome(deployKeyPath);\n if (!(await pathExists(localPath))) return false;\n\n const keyBase = basename(localPath);\n logger.info(`copying deploy key ${localPath} → ${host}:~/.ssh/${keyBase}`);\n await sshRun(host, \"mkdir -p ~/.ssh && chmod 700 ~/.ssh\", { logger });\n await scp(localPath, `${host}:.ssh/${keyBase}`, { logger });\n await sshRun(host, `chmod 600 ~/.ssh/${keyBase}`, { logger });\n return true;\n}\n\n/** known_hosts for the git host + (optional) ~/.ssh/config IdentityFile for the deploy key. */\nexport async function prepareGitHost(host, options = {}) {\n const { logger = console, gitHost, keyBasename } = options;\n if (!gitHost) return;\n\n const configBlock = keyBasename\n ? `if ! grep -q 'Host ${gitHost}' ~/.ssh/config 2>/dev/null; then\n printf '%s\\\\n' '' 'Host ${gitHost}' ' HostName ${gitHost}' ' User git' ' IdentityFile ~/.ssh/${keyBasename}' ' IdentitiesOnly yes' >> ~/.ssh/config\n chmod 600 ~/.ssh/config\n echo \"configured ~/.ssh/config for ${gitHost}\"\nfi`\n : `:`;\n\n const script = `\nset -euo pipefail\nmkdir -p ~/.ssh\nchmod 700 ~/.ssh\nif ! grep -q '^${gitHost}' ~/.ssh/known_hosts 2>/dev/null; then\n ssh-keyscan -t ed25519,rsa ${gitHost} >> ~/.ssh/known_hosts 2>/dev/null\n echo \"added ${gitHost} to known_hosts\"\nfi\n${configBlock}\n`.trim();\n\n await sshRun(host, script, { logger });\n}\n\n/** Install the run-dir workspace deps so the toolkit CLI is available on the server. */\nexport async function ensureRepoDependencies(host, service, options = {}) {\n const { logger = console } = options;\n const paths = servicePaths(service);\n const run = shellQuote(paths.repoRun);\n await sshRun(\n host,\n `cd ${run} && if [ ! -d node_modules/@nmakarov/cli-toolkit ] || [ package-lock.json -nt node_modules/.package-lock.json ]; then npm ci; fi`,\n { logger },\n );\n}\n\n/** Copy laptop .env → server run-dir .env (scrubbed). sync-env then copies it to shared/. */\nexport async function ensureEnvOnRemote(host, service, options = {}) {\n const { logger = console, envFile } = options;\n const paths = servicePaths(service);\n const localPath = resolveLocalEnvPath(envFile);\n if (!(await pathExists(localPath))) return false;\n\n logger.info(`copying .env ${localPath} → ${host}:${paths.repoEnv}`);\n await sshRun(host, `mkdir -p ${shellQuote(dirname(paths.repoEnv))}`, { logger });\n\n const scrubbed = scrubEnvContent(await readFile(localPath, \"utf8\"), service.envScrubPatterns ?? []);\n const tmp = join(tmpdir(), `deploy-env-${Date.now()}`);\n await writeFile(tmp, scrubbed, { mode: 0o600 });\n await scp(tmp, `${host}:${paths.repoEnv}`, { logger });\n await sshRun(host, `chmod 600 ${shellQuote(paths.repoEnv)}`, { logger });\n return true;\n}\n\n/** Ensure a checkout exists on the server, then pull latest + deps + env. */\nexport async function ensureRemoteRepo(host, service, options = {}) {\n const { logger = console, deployKey = service.deployKey, envFile } = options;\n const paths = servicePaths(service);\n const repo = shellQuote(paths.repo);\n const repoUrl = shellQuote(service.repoUrl);\n const root = shellQuote(paths.root);\n\n const localKeyBase = deployKey ? basename(expandHome(deployKey)) : null;\n await ensureDeployKeyOnRemote(host, deployKey, { logger });\n await prepareGitHost(host, { logger, gitHost: parseGitHost(service.repoUrl), keyBasename: localKeyBase });\n\n await sshRun(\n host,\n `mkdir -p ${root} && if [ -d ${repo}/.git ]; then git -C ${repo} pull --ff-only; else git clone ${repoUrl} ${repo}; fi`,\n { logger },\n );\n\n await ensureRepoDependencies(host, service, { logger });\n await ensureEnvOnRemote(host, service, { logger, envFile });\n}\n\n/**\n * Invoke the toolkit deploy CLI on the server in local mode (no --host), passing\n * the same --service/--manifests + extra flags. Run after ensureRemoteRepo.\n */\nexport async function runRemoteCli(host, service, command, args = [], options = {}) {\n const { logger = console, manifests, skipPull = false, deployKey = service.deployKey, envFile } = options;\n const paths = servicePaths(service);\n const run = shellQuote(paths.repoRun);\n const cli = shellQuote(REMOTE_CLI_REL);\n\n if (!skipPull) {\n await ensureRemoteRepo(host, service, { logger, deployKey, envFile });\n } else {\n await ensureRepoDependencies(host, service, { logger });\n await ensureEnvOnRemote(host, service, { logger, envFile });\n }\n\n const passthrough = [\n `--service=${service.name}`,\n ...(manifests ? [`--manifests=${manifests}`] : []),\n ...args,\n ].map(shellQuote).join(\" \");\n\n await sshRun(host, `cd ${run} && node ${cli} ${shellQuote(command)} ${passthrough}`.trim(), { logger });\n}\n\nexport async function runRemoteStatus(host, service, options = {}) {\n const { logger = console } = options;\n const paths = servicePaths(service);\n const port = service.pm2.port;\n const app = service.pm2.appName;\n\n const script = `\necho \"=== apps root ===\"\nls -la ${shellQuote(paths.root)} 2>/dev/null || echo \"(missing)\"\necho \"\"\necho \"=== current ===\"\nreadlink ${shellQuote(paths.current)} 2>/dev/null || echo \"(not set)\"\necho \"\"\necho \"=== releases ===\"\nls -1 ${shellQuote(paths.releases)} 2>/dev/null || echo \"(none)\"\necho \"\"\necho \"=== pm2 ===\"\npm2 describe ${app} 2>/dev/null | head -20 || pm2 status ${app} 2>/dev/null || echo \"(not running)\"\n${port ? `echo \"\"\necho \"=== app health (localhost) ===\"\ncurl -sf http://127.0.0.1:${port}/healthz 2>/dev/null || echo \"(no /healthz on :${port})\"` : \"\"}\n`.trim();\n\n await sshRun(host, script, { logger });\n}\n","import { isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { defineService } from \"./service.js\";\n\n/**\n * Load a project's service manifests module. The module may export:\n * export const services = { \"my-svc\": { … }, … } // map\n * export default { \"my-svc\": { … } } // map as default\n * export const services = [ { name: \"my-svc\", … } ] // array\n * Every entry is normalized through defineService().\n *\n * @param {{ manifests?: string, cwd?: string }} opts manifests = path to the module\n * @returns {Promise<Record<string, object>>} name → normalized service\n */\nexport async function loadServices({ manifests = \"deploy/services.js\", cwd = process.cwd() } = {}) {\n const abs = isAbsolute(manifests) ? manifests : resolve(cwd, manifests);\n let mod;\n try {\n mod = await import(pathToFileURL(abs).href);\n } catch (err) {\n throw new Error(`Could not load deploy manifests from ${abs}: ${err.message}`);\n }\n\n const raw = mod.services ?? mod.default;\n if (!raw) {\n throw new Error(`Manifests module ${abs} must export \\`services\\` (or default): a map or array of service manifests`);\n }\n\n const list = Array.isArray(raw) ? raw : Object.values(raw);\n const out = {};\n for (const entry of list) {\n const svc = defineService(entry);\n out[svc.name] = svc;\n }\n return out;\n}\n\n/** Resolve one normalized service by name (optionally overriding appsRoot). */\nexport function resolveServiceFrom(serviceMap, name, { appsRoot } = {}) {\n const svc = serviceMap[name];\n if (!svc) {\n throw new Error(`Unknown service \"${name}\". Known: ${Object.keys(serviceMap).join(\", \") || \"(none)\"}`);\n }\n return appsRoot ? { ...svc, appsRoot } : svc;\n}\n"],"mappings":"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","names":["resolve","import_promises","import_promises","import_node_path","pathDirname","import_promises","import_node_path","import_promises","import_node_path","pathExists","import_promises","import_node_path","import_promises","resolve","import_promises","import_node_path","import_promises","pathExists","import_promises","import_node_path","import_node_child_process","import_promises","import_node_path","pathExists","import_node_child_process","import_promises","import_node_os","import_node_path","pathExists","import_promises","resolveKillTimeoutMs","import_promises","pathExists","import_promises","import_promises","import_node_os","import_node_path","import_node_child_process","pathExists","expandHome","resolve","run","import_node_path"]}
|
|
1
|
+
{"version":3,"sources":["../src/deploy/index.js","../src/deploy/service.js","../src/deploy/paths.js","../src/deploy/run.js","../src/deploy/log.js","../src/deploy/git.js","../src/deploy/release.js","../src/deploy/activate.js","../src/deploy/deps.js","../src/deploy/test.js","../src/deploy/prune.js","../src/deploy/pm2.js","../src/deploy/nginx.js","../src/deploy/sync-env.js","../src/deploy/build-info.js","../src/deploy/init-structure.js","../src/deploy/bootstrap-host.js","../src/deploy/deploy-service.js","../src/deploy/provision-service.js","../src/deploy/rollback-service.js","../src/deploy/ssh-remote.js","../src/deploy/manifests.js"],"sourcesContent":["/**\n * Deploy ecosystem — manifest-driven EC2 release deploys (git checkout → versioned\n * releases under /apps/<svc>/ → pm2 + nginx upstream switch, with rollback).\n *\n * Projects describe each app as a \"service\" manifest (see ./service.js) and drive\n * everything from one CLI (scripts/deploy/cli.js, exposed as the `cli-deploy` bin).\n * The engine itself is project-agnostic: all specifics come from the manifest.\n */\nexport { defineService, deriveRepoDirName } from \"./service.js\";\nexport { servicePaths, releaseStamp, releaseDir } from \"./paths.js\";\nexport { run, runShell, runCapture, npmEnv, npmInstallEnv } from \"./run.js\";\nexport { appendDeployLog } from \"./log.js\";\nexport { cloneRepo, pullRepo } from \"./git.js\";\nexport { createRelease, readCurrentRelease, listReleases } from \"./release.js\";\nexport { activateRelease } from \"./activate.js\";\nexport { installDeps } from \"./deps.js\";\nexport { runReleaseTests } from \"./test.js\";\nexport { pruneReleases } from \"./prune.js\";\nexport {\n reloadPm2,\n stopPm2,\n startPm2,\n getPm2Process,\n parsePm2Jlist,\n waitPm2,\n isPidAlive,\n isFreshOnline,\n} from \"./pm2.js\";\nexport { enableNginxUpstream } from \"./nginx.js\";\nexport { syncEnv, scrubEnvContent } from \"./sync-env.js\";\nexport {\n bumpPatchVersion,\n resolveNextVersion,\n readReleaseBuildInfo,\n writeReleaseBuildInfo,\n} from \"./build-info.js\";\nexport { initServiceStructure } from \"./init-structure.js\";\nexport { bootstrapHost, installOperatorShell } from \"./bootstrap-host.js\";\nexport { deployService } from \"./deploy-service.js\";\nexport { provisionService } from \"./provision-service.js\";\nexport { rollbackService } from \"./rollback-service.js\";\nexport {\n REMOTE_CLI_REL,\n parseGitHost,\n sshRun,\n shellQuote,\n ensureDeployKeyOnRemote,\n prepareGitHost,\n ensureRepoDependencies,\n ensureEnvOnRemote,\n ensureRemoteRepo,\n runRemoteCli,\n runRemoteStatus,\n} from \"./ssh-remote.js\";\nexport { loadServices, resolveServiceFrom } from \"./manifests.js\";\n","/**\n * A \"service\" is a plain manifest object describing how to deploy one app onto a\n * host. Projects define these; the engine is otherwise project-agnostic.\n *\n * Required:\n * name unique id, also the default pm2 app + nginx site name\n * appsRoot base dir on the host, e.g. /apps/<name>\n * repoUrl git url to clone (ssh git@… or https://…)\n * pm2.script entry script (relative to the release/cwd)\n *\n * Optional (sensible defaults applied by defineService):\n * repoDirName default: repo name derived from repoUrl\n * repoSubdir default: \"\" (release = repo root). Set \"v2\" for a monorepo subdir.\n * keepReleases default: 3\n * testCommand default: null (shell run in the release before activating; null = skip)\n * envScrubPatterns default: [] (regex source strings; matching .env lines are dropped)\n * legacyRepoEnv default: null (fallback .env path on a pre-migration host)\n * buildInfoPath default: \"build-info.json\" (written into each release, relative to release root)\n * deployKey default: null (path to a git deploy key; only needed for ssh repoUrl)\n * requireEnv default: false (true = fail if no .env found; false = continue with empty)\n * pm2: {\n * appName=name, script, args=\"\", port,\n * stopAllowance=60, // seconds → injected as --stopAllowance=N on the process\n * killTimeout=65000, // ms; pm2 kill_timeout (default: stopAllowance*1000+5000)\n * }\n * nginx: { siteName=name, fqdn } (omit nginx entirely to skip the nginx step)\n */\nexport function deriveRepoDirName(repoUrl) {\n const tail = String(repoUrl ?? \"\").split(\"/\").pop() ?? \"repo\";\n return tail.replace(/\\.git$/, \"\") || \"repo\";\n}\n\nexport function defineService(service) {\n if (!service?.name) throw new Error(\"service manifest needs a `name`\");\n if (!service.appsRoot) throw new Error(`service \"${service.name}\" needs an appsRoot`);\n if (!service.repoUrl) throw new Error(`service \"${service.name}\" needs a repoUrl`);\n if (!service.pm2?.script) throw new Error(`service \"${service.name}\" needs pm2.script`);\n\n const pm2 = {\n appName: service.name,\n args: \"\",\n stopAllowance: 60,\n ...service.pm2,\n };\n if (pm2.killTimeout == null) {\n const stopSec = Number(pm2.stopAllowance);\n pm2.killTimeout = Number.isFinite(stopSec) && stopSec > 0\n ? Math.floor(stopSec * 1000) + 5000\n : 65_000;\n }\n const nginx = service.nginx\n ? { siteName: service.name, ...service.nginx }\n : null;\n\n return {\n repoDirName: deriveRepoDirName(service.repoUrl),\n repoSubdir: \"\",\n keepReleases: 3,\n testCommand: null,\n envScrubPatterns: [],\n legacyRepoEnv: null,\n buildInfoPath: \"build-info.json\",\n deployKey: null,\n requireEnv: false,\n ...service,\n pm2,\n nginx,\n };\n}\n","import { join } from \"node:path\";\n\n/**\n * Resolve on-disk paths for a service under <appsRoot>/.\n *\n * <appsRoot>/\n * <repoDirName>/[<repoSubdir>/] git checkout (build source only)\n * releases/<stamp>/ one release = contents of the run dir\n * shared/.env, ecosystem.config.cjs\n * logs/deploy.log\n * current -> releases/<stamp>\n */\nexport function servicePaths(service) {\n const root = service.appsRoot;\n const repoDirName = service.repoDirName ?? \"repo\";\n const repoSubdir = service.repoSubdir ?? \"\";\n\n const repo = join(root, repoDirName);\n const repoRun = repoSubdir ? join(repo, repoSubdir) : repo;\n\n return {\n root,\n repo,\n repoRun,\n repoEnv: join(repoRun, \".env\"),\n releases: join(root, \"releases\"),\n shared: join(root, \"shared\"),\n logs: join(root, \"logs\"),\n current: join(root, \"current\"),\n sharedEnv: join(root, \"shared\", \".env\"),\n ecosystem: join(root, \"shared\", \"ecosystem.config.cjs\"),\n /** Preloaded by pm2 `node_args --require` so ENV survives flaky restarts. */\n ensureEnv: join(root, \"shared\", \"ensure-env.cjs\"),\n deployLog: join(root, \"logs\", \"deploy.log\"),\n lockHashFile: join(root, \"shared\", \".package-lock.sha256\"),\n };\n}\n\n/** ISO-8601 release stamp with `:` (safe on Linux ext4). */\nexport function releaseStamp(date = new Date()) {\n return date.toISOString().replace(/\\.\\d{3}Z$/, \"Z\");\n}\n\nexport function releaseDir(releasesRoot, stamp) {\n return join(releasesRoot, stamp);\n}\n","import { spawn } from \"node:child_process\";\n\n/** npm omits devDependencies when NODE_ENV=production — unset it for ci + tests. */\nexport function npmEnv(baseEnv = process.env) {\n const env = { ...baseEnv };\n delete env.NODE_ENV;\n return env;\n}\n\n/** Env for npm ci/install so devDependencies are always installed. */\nexport function npmInstallEnv(baseEnv = process.env) {\n return {\n ...npmEnv(baseEnv),\n NPM_CONFIG_PRODUCTION: \"false\",\n npm_config_production: \"false\",\n };\n}\n\n/** Run a command with inherited stdio. Rejects on non-zero exit. */\nexport function run(cmd, args, options = {}) {\n const { cwd, env, logger } = options;\n\n return new Promise((resolve, reject) => {\n logger?.info?.(`$ ${cmd} ${args.join(\" \")}${cwd ? ` (cwd=${cwd})` : \"\"}`);\n const child = spawn(cmd, args, {\n cwd,\n env: env ?? process.env,\n stdio: \"inherit\",\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0) resolve();\n else reject(new Error(`${cmd} exited with code ${code}`));\n });\n });\n}\n\n/** Run a shell one-liner via bash -lc. */\nexport function runShell(command, options = {}) {\n return run(\"bash\", [\"-lc\", command], options);\n}\n\n/**\n * Run a command capturing stdout/stderr (for `pm2 jlist`, etc.).\n * Rejects on non-zero exit unless `allowFail` is set.\n *\n * @returns {Promise<{ stdout: string, stderr: string, code: number|null }>}\n */\nexport function runCapture(cmd, args, options = {}) {\n const { cwd, env, logger, allowFail = false } = options;\n\n return new Promise((resolve, reject) => {\n logger?.info?.(`$ ${cmd} ${args.join(\" \")}${cwd ? ` (cwd=${cwd})` : \"\"}`);\n const child = spawn(cmd, args, {\n cwd,\n env: env ?? process.env,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n let stdout = \"\";\n let stderr = \"\";\n child.stdout?.on(\"data\", (chunk) => {\n stdout += chunk.toString();\n });\n child.stderr?.on(\"data\", (chunk) => {\n stderr += chunk.toString();\n });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => {\n if (code === 0 || allowFail) resolve({ stdout, stderr, code });\n else reject(new Error(`${cmd} exited with code ${code}${stderr ? `: ${stderr.trim()}` : \"\"}`));\n });\n });\n}\n","import { appendFile, mkdir } from \"node:fs/promises\";\n\nexport async function appendDeployLog(deployLogPath, message) {\n await mkdir(deployLogPath.replace(/\\/[^/]+$/, \"\"), { recursive: true });\n const line = `[${new Date().toISOString()}] ${message}\\n`;\n await appendFile(deployLogPath, line);\n}\n","import { access } from \"node:fs/promises\";\n\nimport { servicePaths } from \"./paths.js\";\nimport { run } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport async function cloneRepo(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n\n if (await pathExists(paths.repo)) {\n throw new Error(`Repo already exists at ${paths.repo} — use git pull instead`);\n }\n if (dryRun) {\n logger.info(`[dryRun] would git clone ${service.repoUrl} ${paths.repo}`);\n return;\n }\n\n await run(\"git\", [\"clone\", service.repoUrl, paths.repo], { logger });\n logger.info(`cloned ${service.repoUrl} → ${paths.repo}`);\n}\n\nexport async function pullRepo(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n\n if (!(await pathExists(paths.repo))) {\n throw new Error(`Repo missing at ${paths.repo} — run provision first`);\n }\n if (dryRun) {\n logger.info(`[dryRun] would git -C ${paths.repo} pull --ff-only`);\n return;\n }\n\n await run(\"git\", [\"-C\", paths.repo, \"pull\", \"--ff-only\"], { logger });\n logger.info(`pulled ${paths.repo}`);\n}\n","import { cp, mkdir, readlink, readdir, stat } from \"node:fs/promises\";\nimport { join, dirname as pathDirname, sep } from \"node:path\";\n\nimport { releaseDir, releaseStamp, servicePaths } from \"./paths.js\";\n\nconst SKIP_TOP = new Set([\"node_modules\", \".git\"]);\n\nfunction shouldCopyEntry(srcPath) {\n const parts = srcPath.split(sep);\n if (parts.some((p) => p === \"node_modules\" || p === \".git\")) return false;\n const top = parts.at(-1);\n if (parts.length === 1 && SKIP_TOP.has(top)) return false;\n return true;\n}\n\n/** Copy the run dir into releases/<stamp>/ (excluding node_modules and .git). */\nexport async function createRelease(service, options = {}) {\n const { stamp = releaseStamp(), dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const dest = releaseDir(paths.releases, stamp);\n\n if (dryRun) {\n logger.info(`[dryRun] would copy ${paths.repoRun} → ${dest}`);\n return { stamp, path: dest };\n }\n\n await mkdir(paths.releases, { recursive: true });\n await cp(paths.repoRun, dest, {\n recursive: true,\n filter: (src) => shouldCopyEntry(src),\n });\n\n logger.info(`release ${stamp} created at ${dest}`);\n return { stamp, path: dest };\n}\n\n/** Resolve current release path if symlink exists. */\nexport async function readCurrentRelease(paths) {\n try {\n const target = await readlink(paths.current);\n return target.startsWith(\"/\") ? target : join(pathDirname(paths.current), target);\n } catch {\n return null;\n }\n}\n\nexport async function listReleases(paths) {\n let names;\n try {\n names = await readdir(paths.releases);\n } catch {\n return [];\n }\n\n const entries = [];\n for (const name of names) {\n const full = releaseDir(paths.releases, name);\n const s = await stat(full);\n if (s.isDirectory()) entries.push({ name, path: full, mtime: s.mtime });\n }\n\n entries.sort((a, b) => b.name.localeCompare(a.name));\n return entries;\n}\n","import { symlink, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\n/** Point current → release and link shared/.env inside the release. */\nexport async function activateRelease(releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would activate ${releasePath} → ${paths.current}`);\n return;\n }\n\n try {\n await unlink(paths.current);\n } catch {\n // first deploy\n }\n await symlink(releasePath, paths.current);\n\n const envLink = join(releasePath, \".env\");\n try {\n await unlink(envLink);\n } catch {\n // ok\n }\n await symlink(paths.sharedEnv, envLink);\n\n logger.info(`active release: ${releasePath}`);\n}\n","import { createHash } from \"node:crypto\";\nimport { access, cp, mkdir, readFile, rm, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { readCurrentRelease } from \"./release.js\";\nimport { npmInstallEnv, run } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function lockfileHash(releasePath) {\n const lockPath = join(releasePath, \"package-lock.json\");\n try {\n const buf = await readFile(lockPath);\n return createHash(\"sha256\").update(buf).digest(\"hex\");\n } catch {\n return null; // no lockfile → can't fingerprint; always (re)install\n }\n}\n\nasync function readHashFile(path) {\n try {\n return (await readFile(path, \"utf8\")).trim();\n } catch {\n return null;\n }\n}\n\nasync function npmInstall(releasePath, hasLock, logger) {\n await rm(join(releasePath, \"node_modules\"), { recursive: true, force: true });\n const cmd = hasLock ? [\"ci\", \"--include=dev\"] : [\"install\", \"--include=dev\", \"--no-audit\", \"--no-fund\"];\n logger.info(`running npm ${cmd.join(\" \")} in ${releasePath}`);\n await run(\"npm\", cmd, { cwd: releasePath, logger, env: npmInstallEnv() });\n}\n\n/**\n * Install deps in the release. If the lockfile is unchanged from the active\n * release and its node_modules exists, copy it (fast path); otherwise npm ci.\n */\nexport async function installDeps(service, releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would install deps in ${releasePath}`);\n return { hash: null, copied: false };\n }\n\n const hash = await lockfileHash(releasePath);\n const hasLock = hash !== null;\n const hashMarker = join(releasePath, \".deploy-package-lock.sha256\");\n\n const currentPath = await readCurrentRelease(paths);\n let copied = false;\n\n if (hasLock && currentPath && (await pathExists(join(currentPath, \"node_modules\")))) {\n const currentHash = await readHashFile(join(currentPath, \".deploy-package-lock.sha256\"));\n if (currentHash === hash) {\n logger.info(\"lockfile unchanged — copying node_modules from current release\");\n await cp(join(currentPath, \"node_modules\"), join(releasePath, \"node_modules\"), {\n recursive: true,\n force: true,\n });\n copied = true;\n }\n }\n\n if (!copied) {\n await npmInstall(releasePath, hasLock, logger);\n }\n\n if (hasLock) {\n await writeFile(hashMarker, `${hash}\\n`, \"utf8\");\n await mkdir(paths.shared, { recursive: true });\n await writeFile(paths.lockHashFile, `${hash}\\n`, \"utf8\");\n }\n\n return { hash, copied };\n}\n","import { symlink, unlink } from \"node:fs/promises\";\nimport { join } from \"node:path\";\n\nimport { npmInstallEnv, runShell } from \"./run.js\";\n\n/**\n * Symlink shared/.env into the release and run the service's testCommand (a shell\n * one-liner, e.g. \"npm run test:ci\"). No-op when the service defines no command.\n */\nexport async function runReleaseTests(service, releasePath, paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (!service.testCommand) {\n logger.info(\"no testCommand configured — skipping tests\");\n return;\n }\n\n if (dryRun) {\n logger.info(`[dryRun] would run \"${service.testCommand}\" in ${releasePath}`);\n return;\n }\n\n const envLink = join(releasePath, \".env\");\n try {\n await unlink(envLink);\n } catch {\n // no existing link\n }\n await symlink(paths.sharedEnv, envLink);\n\n logger.info(`running \"${service.testCommand}\" in ${releasePath}`);\n await runShell(service.testCommand, { cwd: releasePath, logger, env: npmInstallEnv() });\n}\n","import { rm, readlink } from \"node:fs/promises\";\n\nimport { listReleases } from \"./release.js\";\n\n/**\n * Remove old releases, keeping the newest N (always keeps the active one).\n *\n * @param {object} service\n * @param {object} paths\n * @param {{ dryRun?: boolean, logger?: object, protect?: string[] }} [options]\n * `protect` — release stamp names that must not be deleted (e.g. still-running\n * previous release during a rolling deploy).\n */\nexport async function pruneReleases(service, paths, options = {}) {\n const { dryRun = false, logger = console, protect = [] } = options;\n const keep = service.keepReleases ?? 3;\n const releases = await listReleases(paths);\n\n let activeName = null;\n try {\n const target = await readlink(paths.current);\n activeName = target.split(\"/\").pop();\n } catch {\n // no current\n }\n\n const keepSet = new Set();\n for (const rel of releases) {\n if (keepSet.size < keep) keepSet.add(rel.name);\n }\n if (activeName) keepSet.add(activeName);\n for (const name of protect) {\n if (name) keepSet.add(name);\n }\n\n const toRemove = releases.filter((rel) => !keepSet.has(rel.name));\n for (const rel of toRemove) {\n if (dryRun) {\n logger.info(`[dryRun] would rm -rf ${rel.path}`);\n } else {\n await rm(rel.path, { recursive: true, force: true });\n logger.info(`pruned ${rel.path}`);\n }\n }\n\n return { removed: toRemove.map((r) => r.name) };\n}\n","import { runShell, runCapture } from \"./run.js\";\n\nfunction sleep(ms) {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/** True if `pid` still exists (same host). */\nexport function isPidAlive(pid) {\n const n = Number(pid);\n if (!Number.isFinite(n) || n <= 0) return false;\n try {\n process.kill(n, 0);\n return true;\n } catch {\n return false;\n }\n}\n\n/**\n * Parse `pm2 jlist` stdout. When the daemon was not running, pm2 prints\n * \"[PM2] Spawning PM2 daemon…\" on stdout before the JSON array.\n *\n * @param {string} stdout\n * @returns {unknown[]}\n */\nexport function parsePm2Jlist(stdout) {\n const raw = String(stdout ?? \"\");\n // Drop \"[PM2] Spawning…\" banners (stdout when the daemon was not running).\n const text = raw\n .split(\"\\n\")\n .filter((line) => !line.startsWith(\"[PM2]\"))\n .join(\"\\n\")\n .trim();\n if (!text) return [];\n try {\n const parsed = JSON.parse(text);\n return Array.isArray(parsed) ? parsed : [];\n } catch (err) {\n throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);\n }\n}\n\n/**\n * Parse `pm2 jlist` and return the process entry for `appName`, or null.\n *\n * @param {string} appName\n * @param {{ logger?: { info?: Function, warn?: Function } }} [options]\n * @returns {Promise<object|null>}\n */\nexport async function getPm2Process(appName, options = {}) {\n const { logger } = options;\n // Warm the daemon so spawn banners do not race the next jlist (best-effort).\n await runCapture(\"bash\", [\"-lc\", \"pm2 ping >/dev/null 2>&1 || true\"], { logger }).catch(() => {});\n const { stdout } = await runCapture(\"bash\", [\"-lc\", \"pm2 jlist\"], { logger });\n let list;\n try {\n list = parsePm2Jlist(stdout);\n } catch (err) {\n throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);\n }\n return list.find((p) => p?.name === appName) ?? null;\n}\n\n/**\n * Poll pm2 until `predicate(proc)` is true (proc may be null if missing), or timeout.\n *\n * @param {string} appName\n * @param {(proc: object|null) => boolean} predicate\n * @param {{ timeoutMs?: number, pollMs?: number, dryRun?: boolean, logger?: object, label?: string }} [options]\n */\nexport async function waitPm2(appName, predicate, options = {}) {\n const {\n timeoutMs = 65_000,\n pollMs = 500,\n dryRun = false,\n logger = console,\n label = \"condition\",\n } = options;\n\n if (dryRun) {\n logger.info?.(`[dryRun] would wait pm2 ${appName} for ${label}`);\n return null;\n }\n\n const deadline = Date.now() + timeoutMs;\n let lastStatus = \"(unknown)\";\n while (Date.now() < deadline) {\n const proc = await getPm2Process(appName, { logger });\n lastStatus = proc?.pm2_env?.status ?? \"(missing)\";\n if (predicate(proc)) return proc;\n await sleep(pollMs);\n }\n throw new Error(\n `pm2 wait timed out after ${timeoutMs}ms for ${appName} (${label}); last status=${lastStatus}`\n );\n}\n\n/**\n * True when pm2 reports the app online on a *new* pid (old process fully gone).\n * Avoids treating a not-yet-dead pre-reload process as a successful restart.\n */\nexport function isFreshOnline(proc, oldPid) {\n if (proc?.pm2_env?.status !== \"online\") return false;\n const pid = Number(proc.pid);\n if (!Number.isFinite(pid) || pid <= 0) return false;\n if (oldPid != null) {\n if (pid === oldPid) return false;\n if (isPidAlive(oldPid)) return false;\n }\n return true;\n}\n\n/**\n * Remove app from pm2 (SIGINT + kill_timeout drain) and wait until gone.\n * No-op if the app is not present.\n */\nasync function deletePm2App(appName, options = {}) {\n const { dryRun = false, logger = console, waitTimeoutMs = 65_000, oldPid = null } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 delete ${appName}`);\n return;\n }\n\n const before = await getPm2Process(appName, { logger });\n if (!before) {\n logger.info(`pm2 ${appName}: not present (skip delete)`);\n return;\n }\n\n await runShell(`pm2 delete \"${appName}\"`, { logger });\n await waitPm2(\n appName,\n (proc) => {\n if (proc) return false;\n if (oldPid != null && isPidAlive(oldPid)) return false;\n return true;\n },\n { timeoutMs: waitTimeoutMs, logger, label: \"deleted (process gone)\" }\n );\n logger.info(`pm2 ${appName} deleted`);\n}\n\n/** Fresh `pm2 start <ecosystem> --update-env` with production ENV on the CLI. */\nasync function startFromEcosystem(paths, options = {}) {\n const { dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 start ${paths.ecosystem} --update-env`);\n return;\n }\n\n // Prefix ENV on the pm2 CLI: --update-env copies the invoking shell env.\n await runShell(\n `ENV=production NODE_ENV=production pm2 start \"${paths.ecosystem}\" --update-env`,\n { logger }\n );\n logger.info(\"pm2 started from ecosystem\");\n}\n\n/**\n * Rolling restart: `pm2 delete` then `pm2 start <ecosystem> --update-env`.\n *\n * Prefer delete+start over `startOrReload --update-env`: reload can leave a first\n * spawn with a stale/wrong ENV (e.g. S3 profile=local) and does not refresh\n * immutable fields like `script` / `node_args`. Delete honors kill_timeout so the\n * old process can drain, then start applies the full ecosystem.\n */\nexport async function reloadPm2(paths, options = {}) {\n const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(\n `[dryRun] would pm2 delete${appName ? ` ${appName}` : \"\"} + start ${paths.ecosystem} --update-env`\n );\n return;\n }\n\n let oldPid = null;\n if (appName) {\n const before = await getPm2Process(appName, { logger });\n const n = Number(before?.pid);\n if (Number.isFinite(n) && n > 0 && before?.pm2_env?.status === \"online\") {\n oldPid = n;\n logger.info(`pm2 ${appName}: recreate (old pid=${oldPid})`);\n }\n await deletePm2App(appName, { dryRun, logger, waitTimeoutMs, oldPid });\n } else {\n logger.warn?.(\n \"reloadPm2: appName missing; starting ecosystem without delete \" +\n \"(pass appName so script/env always recreate cleanly)\"\n );\n }\n\n await startFromEcosystem(paths, { dryRun, logger });\n\n if (appName) {\n const proc = await waitPm2(\n appName,\n (p) => isFreshOnline(p, oldPid),\n {\n timeoutMs: waitTimeoutMs,\n logger,\n label: oldPid\n ? `online on new pid (old ${oldPid} gone)`\n : \"online after recreate\",\n }\n );\n logger.info(`pm2 ${appName} online pid=${proc?.pid ?? \"?\"}`);\n }\n}\n\n/** `pm2 stop <app>` and wait until status is stopped (or process missing). */\nexport async function stopPm2(appName, options = {}) {\n const { dryRun = false, logger = console, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(`[dryRun] would pm2 stop ${appName}`);\n return;\n }\n\n const before = await getPm2Process(appName, { logger });\n if (!before) {\n logger.info(`pm2 ${appName}: not present (already stopped)`);\n return;\n }\n if (before.pm2_env?.status === \"stopped\") {\n logger.info(`pm2 ${appName}: already stopped`);\n return;\n }\n\n const oldPid = Number(before.pid);\n await runShell(`pm2 stop \"${appName}\"`, { logger });\n await waitPm2(\n appName,\n (proc) => {\n if (proc && proc.pm2_env?.status !== \"stopped\") return false;\n if (Number.isFinite(oldPid) && oldPid > 0 && isPidAlive(oldPid)) return false;\n return true;\n },\n { timeoutMs: waitTimeoutMs, logger, label: \"stopped\" }\n );\n logger.info(`pm2 ${appName} stopped`);\n}\n\n/**\n * `pm2 start <ecosystem> --update-env` after deleting any existing registration\n * so immutable fields (script, node_args) and env always come from the ecosystem.\n */\nexport async function startPm2(paths, options = {}) {\n const { dryRun = false, logger = console, appName = null, waitTimeoutMs = 65_000 } = options;\n\n if (dryRun) {\n logger.info(\n `[dryRun] would pm2 delete${appName ? ` ${appName}` : \"\"} + start ${paths.ecosystem} --update-env`\n );\n return;\n }\n\n if (appName) {\n // stopFirst leaves a stopped registration; delete so start is a full recreate.\n await deletePm2App(appName, { dryRun, logger, waitTimeoutMs });\n }\n\n await startFromEcosystem(paths, { dryRun, logger });\n\n if (appName) {\n const proc = await waitPm2(\n appName,\n (p) => isFreshOnline(p, null),\n { timeoutMs: waitTimeoutMs, logger, label: \"online after start\" }\n );\n logger.info(`pm2 ${appName} online pid=${proc?.pid ?? \"?\"}`);\n }\n}\n","import { writeFile } from \"node:fs/promises\";\nimport { tmpdir } from \"node:os\";\nimport { join } from \"node:path\";\n\nimport { runShell } from \"./run.js\";\n\nasync function hasTlsCert(certPath) {\n try {\n await runShell(`sudo test -f '${certPath}'`, {});\n return true;\n } catch {\n return false;\n }\n}\n\nfunction proxyBlock(port) {\n return ` location / {\n proxy_pass http://127.0.0.1:${port};\n proxy_http_version 1.1;\n proxy_set_header Host $host;\n proxy_set_header X-Real-IP $remote_addr;\n proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n proxy_set_header X-Forwarded-Proto $scheme;\n proxy_read_timeout 120s;\n }`;\n}\n\nfunction buildNginxConfig(service) {\n const { nginx, pm2 } = service;\n const certDir = `/etc/letsencrypt/live/${nginx.fqdn}`;\n\n return `# ${nginx.siteName} — managed by cli-toolkit deploy (proxy mode)\n# ${nginx.fqdn} → 127.0.0.1:${pm2.port}\n\nserver {\n listen 80;\n listen [::]:80;\n server_name ${nginx.fqdn};\n location / { return 301 https://$host$request_uri; }\n}\n\nserver {\n listen 443 ssl;\n listen [::]:443 ssl;\n server_name ${nginx.fqdn};\n\n ssl_certificate ${certDir}/fullchain.pem;\n ssl_certificate_key ${certDir}/privkey.pem;\n include /etc/letsencrypt/options-ssl-nginx.conf;\n ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;\n\n client_max_body_size 25m;\n\n${proxyBlock(pm2.port)}\n}\n`;\n}\n\nfunction buildNginxConfigHttpOnly(service) {\n const { nginx, pm2 } = service;\n return `# ${nginx.siteName} — managed by cli-toolkit deploy (HTTP proxy, no TLS cert yet)\n\nserver {\n listen 80;\n listen [::]:80;\n server_name ${nginx.fqdn};\n\n client_max_body_size 25m;\n\n${proxyBlock(pm2.port)}\n}\n`;\n}\n\n/** Replace the stub nginx site with a reverse-proxy to the app. */\nexport async function enableNginxUpstream(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const { nginx } = service;\n if (!nginx) {\n logger.info(\"no nginx config on service — skipping nginx step\");\n return { skipped: true };\n }\n\n const siteAvailable = `/etc/nginx/sites-available/${nginx.siteName}`;\n const siteEnabled = `/etc/nginx/sites-enabled/${nginx.siteName}`;\n const cert = `/etc/letsencrypt/live/${nginx.fqdn}/fullchain.pem`;\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${siteAvailable} (proxy → 127.0.0.1:${service.pm2.port}) and reload nginx`);\n return { hasCert: null };\n }\n\n const hasCert = await hasTlsCert(cert);\n const config = hasCert ? buildNginxConfig(service) : buildNginxConfigHttpOnly(service);\n\n const tmp = join(tmpdir(), `${service.name}-nginx.conf`);\n await writeFile(tmp, config);\n await runShell(\n `sudo cp '${tmp}' '${siteAvailable}' && sudo ln -sf '${siteAvailable}' '${siteEnabled}' && sudo nginx -t && sudo systemctl reload nginx`,\n { logger },\n );\n\n logger.info(`nginx upstream enabled for ${nginx.fqdn} → 127.0.0.1:${service.pm2.port} (tls=${hasCert})`);\n return { hasCert };\n}\n","import { access, mkdir, readFile, writeFile } from \"node:fs/promises\";\n\nimport { servicePaths } from \"./paths.js\";\n\n/**\n * Drop .env lines matching any of the service's envScrubPatterns (regex source\n * strings). Useful to strip empty keys that would crash strict param validation.\n */\nexport function scrubEnvContent(content, patterns = []) {\n const regexes = patterns.map((p) => (p instanceof RegExp ? p : new RegExp(p)));\n if (regexes.length === 0) return content;\n return content\n .split(\"\\n\")\n .filter((line) => !regexes.some((re) => re.test(line.trim())))\n .join(\"\\n\");\n}\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Copy <repoRun>/.env → shared/.env (with optional legacy fallback + scrubbing). */\nexport async function syncEnv(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const patterns = service.envScrubPatterns ?? [];\n\n if (dryRun) {\n logger.info(`[dryRun] would sync ${paths.repoEnv} → ${paths.sharedEnv}`);\n return { source: paths.repoEnv, dest: paths.sharedEnv };\n }\n\n let source = paths.repoEnv;\n if (!(await pathExists(source)) && service.legacyRepoEnv && (await pathExists(service.legacyRepoEnv))) {\n logger.info(`using legacy env: ${service.legacyRepoEnv}`);\n source = service.legacyRepoEnv;\n }\n\n await mkdir(paths.shared, { recursive: true });\n\n if (!(await pathExists(source))) {\n if (service.requireEnv) {\n throw new Error(\n `No .env found at ${paths.repoEnv}` +\n (service.legacyRepoEnv ? ` or ${service.legacyRepoEnv}` : \"\") +\n \" — place .env on the host or run the remote deploy from a laptop with a local .env (auto-scp)\",\n );\n }\n // Optional env: ensure shared/.env exists so the per-release symlink is valid.\n if (!(await pathExists(paths.sharedEnv))) {\n await writeFile(paths.sharedEnv, \"\", { mode: 0o600 });\n }\n logger.warn(`no .env found (source ${source}) — using empty ${paths.sharedEnv}`);\n return { source: null, dest: paths.sharedEnv };\n }\n\n const raw = await readFile(source, \"utf8\");\n await writeFile(paths.sharedEnv, scrubEnvContent(raw, patterns), { mode: 0o600 });\n logger.info(`synced ${source} → ${paths.sharedEnv}`);\n return { source, dest: paths.sharedEnv };\n}\n","import { readFile, writeFile, mkdir } from \"node:fs/promises\";\nimport { join, dirname } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { readCurrentRelease } from \"./release.js\";\nimport { servicePaths } from \"./paths.js\";\n\nconst execFileAsync = promisify(execFile);\n\n/** Bump semver patch (0.1.0 → 0.1.1). */\nexport function bumpPatchVersion(version) {\n const parts = String(version).trim().split(\".\");\n const major = Number.parseInt(parts[0], 10) || 0;\n const minor = Number.parseInt(parts[1], 10) || 0;\n const patch = Number.parseInt(parts[2], 10) || 0;\n return `${major}.${minor}.${patch + 1}`;\n}\n\nasync function readJson(path) {\n try {\n return JSON.parse(await readFile(path, \"utf8\"));\n } catch {\n return null;\n }\n}\n\nasync function gitShortCommit(repoPath) {\n try {\n const { stdout } = await execFileAsync(\"git\", [\"-C\", repoPath, \"rev-parse\", \"--short\", \"HEAD\"], {\n encoding: \"utf8\",\n });\n return stdout.trim() || null;\n } catch {\n return null;\n }\n}\n\n/**\n * Next semver: bump patch from the **currently active** release's build-info,\n * else seed from package.json. Each release carries its own build-info.json so a\n * rollback restores that release's label.\n */\nexport async function resolveNextVersion(service, paths, pkgPath) {\n const pkg = (await readJson(pkgPath)) ?? {};\n const baseVersion = pkg.version || \"0.1.0\";\n\n const currentPath = await readCurrentRelease(paths);\n if (currentPath) {\n const active = await readJson(join(currentPath, service.buildInfoPath));\n if (active?.version) return bumpPatchVersion(active.version);\n }\n return baseVersion;\n}\n\n/** Read build-info from a release directory (for rollback logs / status). */\nexport async function readReleaseBuildInfo(service, releasePath) {\n return readJson(join(releasePath, service.buildInfoPath));\n}\n\n/** Write build-info.json into the new release directory. */\nexport async function writeReleaseBuildInfo(service, releasePath, { stamp, dryRun = false, logger = console }) {\n const paths = servicePaths(service);\n const pkgPath = join(paths.repoRun, \"package.json\");\n\n const version = await resolveNextVersion(service, paths, pkgPath);\n const gitCommit = await gitShortCommit(paths.repo);\n\n const buildInfo = {\n version,\n release: stamp,\n deployedAt: new Date().toISOString(),\n gitCommit,\n service: service.name,\n };\n\n const dest = join(releasePath, service.buildInfoPath);\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${dest} (${JSON.stringify(buildInfo)})`);\n return buildInfo;\n }\n\n await mkdir(dirname(dest), { recursive: true });\n await writeFile(dest, `${JSON.stringify(buildInfo, null, 2)}\\n`, { mode: 0o644 });\n\n const gitSuffix = gitCommit ? ` git=${gitCommit}` : \"\";\n logger.info(`build info: v${version} release=${stamp}${gitSuffix}`);\n return buildInfo;\n}\n","import { access, appendFile, mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\n\nimport { servicePaths } from \"./paths.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Fail fast with a useful message when not on a provisioned host. */\nasync function requireDeployRoot(parentDir, serviceRoot) {\n if (await pathExists(parentDir)) return;\n throw new Error(\n `Cannot create ${serviceRoot}: parent directory ${parentDir} does not exist.\\n` +\n `This is meant to run on the target host (where ${parentDir} exists). ` +\n `For a local dry run use --appsRoot=/tmp/<service>.`,\n );\n}\n\nfunction resolveKillTimeoutMs(pm2) {\n const explicit = Number(pm2?.killTimeout);\n if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);\n const stopSec = Number(pm2?.stopAllowance);\n if (Number.isFinite(stopSec) && stopSec > 0) return Math.floor(stopSec * 1000) + 5000;\n return 65_000;\n}\n\n/** Append `--stopAllowance=<sec>` to pm2 args when manifest sets pm2.stopAllowance. */\nfunction resolvePm2Args(pm2) {\n const base = String(pm2?.args ?? \"\").trim();\n const stopSec = Number(pm2?.stopAllowance);\n if (!Number.isFinite(stopSec) || stopSec <= 0) return base;\n if (/(?:^|\\s)--stopAllowance=/.test(base)) return base;\n return `${base}${base ? \" \" : \"\"}--stopAllowance=${Math.floor(stopSec)}`.trim();\n}\n\n/** CJS snippet required before the app — always force production on deployed hosts. */\nfunction buildEnsureEnvScript() {\n return `/**\n * Written by cli-toolkit deploy (init-structure). Do not hand-edit.\n * Preloaded via pm2 node_args --require. Always set (do not only fill when unset):\n * \\`pm2 start … --update-env\\` can inject a non-production ENV from the\n * deploy shell / prior dump, and a conditional assign would leave it in place.\n */\nprocess.env.ENV = \"production\";\nprocess.env.NODE_ENV = \"production\";\n`;\n}\n\nfunction buildEcosystemConfig(service, paths) {\n const { pm2 } = service;\n const outLog = join(paths.logs, `${pm2.appName}.out.log`);\n const errLog = join(paths.logs, `${pm2.appName}.err.log`);\n const killTimeoutMs = resolveKillTimeoutMs(pm2);\n const args = resolvePm2Args(pm2);\n const ensureEnv = paths.ensureEnv;\n\n return `/**\n * pm2 ecosystem for ${service.name} — written by cli-toolkit deploy from the\n * service manifest (init/deploy). Do not hand-edit; change pm2.* in the\n * manifest and redeploy. cwd → \\`current\\` symlink.\n */\nmodule.exports = {\n apps: [\n {\n name: \"${pm2.appName}\",\n script: \"${pm2.script}\",\n cwd: \"${paths.current}\",\n args: ${JSON.stringify(args)},\n // Absolute --require so ENV is set before Args constructs (see shared/ensure-env.cjs).\n node_args: ${JSON.stringify(`--require ${ensureEnv}`)},\n instances: 1,\n exec_mode: \"fork\",\n autorestart: true,\n min_uptime: \"10s\",\n max_restarts: 10,\n restart_delay: 2000,\n max_memory_restart: \"1500M\",\n // Grace window after SIGINT/SIGTERM before SIGKILL (ms). Align with --stopAllowance.\n kill_timeout: ${killTimeoutMs},\n out_file: \"${outLog}\",\n error_file: \"${errLog}\",\n merge_logs: true,\n time: true,\n env: {\n // cli-toolkit Args: ENV selects env-suffixed defaults (DB_NAME_PRODUCTION, BUCKET_PRODUCTION, …)\n ENV: \"production\",\n NODE_ENV: \"production\",\n },\n },\n ],\n};\n`;\n}\n\n/** Create <appsRoot>/{releases,shared,logs} and sync shared/ecosystem.config.cjs from the manifest. */\nexport async function initServiceStructure(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const dirs = [paths.releases, paths.shared, paths.logs];\n const created = [];\n const skipped = [];\n\n if (!dryRun) {\n await requireDeployRoot(dirname(paths.root), paths.root);\n }\n\n for (const dir of dirs) {\n if (await pathExists(dir)) {\n skipped.push(dir);\n continue;\n }\n if (!dryRun) await mkdir(dir, { recursive: true });\n created.push(dir);\n }\n\n const ecosystemExisted = await pathExists(paths.ecosystem);\n const ensureEnvExisted = await pathExists(paths.ensureEnv);\n if (!dryRun) {\n await writeFile(paths.ensureEnv, buildEnsureEnvScript(), { mode: 0o644 });\n await writeFile(paths.ecosystem, buildEcosystemConfig(service, paths), { mode: 0o644 });\n }\n if (ensureEnvExisted) {\n logger.info(`synced ${paths.ensureEnv}`);\n } else {\n created.push(paths.ensureEnv);\n logger.info(`seeded ${paths.ensureEnv}`);\n }\n if (ecosystemExisted) {\n logger.info(`synced ${paths.ecosystem} from manifest`);\n } else {\n created.push(paths.ecosystem);\n logger.info(`seeded ${paths.ecosystem}`);\n }\n\n const line = `[${new Date().toISOString()}] init-structure service=${service.name} dryRun=${dryRun} created=${created.length} skipped=${skipped.length} ecosystem=synced\\n`;\n if (!dryRun) {\n await mkdir(paths.logs, { recursive: true });\n await appendFile(paths.deployLog, line);\n }\n\n logger.info(`service=${service.name} appsRoot=${paths.root}`);\n logger.info(`created: ${created.length ? created.join(\", \") : \"(none)\"}`);\n logger.info(`already present: ${skipped.length ? skipped.join(\", \") : \"(none)\"}`);\n\n return { paths, created, skipped, ecosystemCreated: !ecosystemExisted, ecosystemSynced: true };\n}\n","import { execSync } from \"node:child_process\";\nimport { access, chmod, copyFile, mkdir, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { basename, join } from \"node:path\";\n\nimport { runShell } from \"./run.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction expandHome(path) {\n return path.startsWith(\"~/\") ? join(homedir(), path.slice(2)) : path;\n}\n\nasync function ensurePm2Startup(options = {}) {\n const { user = \"ubuntu\", dryRun = false, logger = console } = options;\n\n if (dryRun) {\n logger.info(\"[dryRun] would configure pm2 startup systemd\");\n return;\n }\n\n try {\n execSync(\"pm2 ping\", { stdio: \"ignore\" });\n } catch {\n // pm2 daemon may not be up yet\n }\n\n try {\n const out = execSync(`pm2 startup systemd -u ${user} --hp /home/${user}`, { encoding: \"utf8\" });\n const sudoLine = out.split(\"\\n\").find((l) => l.trim().startsWith(\"sudo\"));\n if (sudoLine) {\n execSync(sudoLine.trim(), { stdio: \"inherit\" });\n logger.info(\"pm2 startup systemd configured\");\n }\n } catch (err) {\n logger.warn(`pm2 startup skipped or already configured: ${err.message}`);\n }\n}\n\nasync function installDeployKey(deployKeyPath, options = {}) {\n const { dryRun = false, logger = console } = options;\n const keyPath = expandHome(deployKeyPath);\n\n if (!(await pathExists(keyPath))) {\n logger.warn(`deploy key not found at ${keyPath} — skipping git ssh setup`);\n return;\n }\n\n const keyBase = basename(keyPath);\n const sshDir = join(homedir(), \".ssh\");\n const destKey = join(sshDir, keyBase);\n const configPath = join(sshDir, \"config\");\n const block = `\nHost github.com\n HostName github.com\n User git\n IdentityFile ${destKey}\n IdentitiesOnly yes\n`;\n\n if (dryRun) {\n logger.info(`[dryRun] would install deploy key ${keyPath} → ${destKey}`);\n return;\n }\n\n await mkdir(sshDir, { recursive: true, mode: 0o700 });\n await copyFile(keyPath, destKey);\n await chmod(destKey, 0o600);\n\n let config = \"\";\n if (await pathExists(configPath)) config = await readFile(configPath, \"utf8\");\n if (!config.includes(\"Host github.com\")) {\n await writeFile(configPath, `${config.trimEnd()}\\n${block}\\n`, { mode: 0o600 });\n logger.info(\"updated ~/.ssh/config for github.com\");\n }\n logger.info(`deploy key installed at ${destKey}`);\n}\n\nasync function installLogrotate(service, options = {}) {\n const { dryRun = false, logger = console } = options;\n const conf = `/etc/logrotate.d/${service.name}`;\n const body = `${service.appsRoot}/logs/*.log {\n daily\n rotate 14\n compress\n delaycompress\n missingok\n notifempty\n copytruncate\n}\n`;\n\n if (dryRun) {\n logger.info(`[dryRun] would write ${conf}`);\n return;\n }\n\n const tmp = join(\"/tmp\", `${service.name}-logrotate.conf`);\n await writeFile(tmp, body);\n await runShell(`sudo cp '${tmp}' '${conf}'`, { logger });\n logger.info(`logrotate config written: ${conf}`);\n}\n\nconst OPERATOR_SHELL_BEGIN = \"# >>> mlsfarm-operator >>>\";\nconst OPERATOR_SHELL_END = \"# <<< mlsfarm-operator <<<\";\n\n/**\n * Idempotent ~/.bashrc block: ENV=production + cd into /apps/<service>/current\n * on interactive login. Safe to re-run; replaces any prior managed block.\n */\nexport async function installOperatorShell(service, options = {}) {\n const { user = \"ubuntu\", dryRun = false, logger = console } = options;\n const appsRoot = service.appsRoot;\n if (!appsRoot) {\n logger.warn(\"installOperatorShell: service.appsRoot missing — skipped\");\n return;\n }\n\n const home = user === \"ubuntu\" || user === process.env.USER\n ? join(homedir(), \".bashrc\")\n : `/home/${user}/.bashrc`;\n const currentDir = join(appsRoot, \"current\");\n const block = [\n OPERATOR_SHELL_BEGIN,\n \"# Managed by cli-toolkit deploy bootstrap — do not edit by hand.\",\n 'export ENV=production',\n `if [ -d \"${currentDir}\" ]; then`,\n ` cd \"${currentDir}\" || true`,\n \"fi\",\n OPERATOR_SHELL_END,\n \"\",\n ].join(\"\\n\");\n\n if (dryRun) {\n logger.info(`[dryRun] would update ${home} (ENV=production, cd ${currentDir})`);\n return;\n }\n\n let existing = \"\";\n if (await pathExists(home)) {\n existing = await readFile(home, \"utf8\");\n }\n\n const begin = existing.indexOf(OPERATOR_SHELL_BEGIN);\n const end = existing.indexOf(OPERATOR_SHELL_END);\n let next;\n if (begin !== -1 && end !== -1 && end > begin) {\n const afterEnd = end + OPERATOR_SHELL_END.length;\n const rest = existing.slice(afterEnd).replace(/^\\n/, \"\");\n next = `${existing.slice(0, begin).replace(/\\s*$/, \"\\n\")}${block}${rest}`;\n } else {\n next = `${existing.replace(/\\s*$/, \"\\n\")}\\n${block}`;\n }\n\n await writeFile(home, next, { mode: 0o644 });\n logger.info(`operator shell: ${home} → ENV=production, cd ${currentDir}`);\n}\n\n/** One-time host prep: pm2 systemd, optional git deploy key, logrotate, operator shell. */\nexport async function bootstrapHost(service, options = {}) {\n const { deployKey = service.deployKey, user = \"ubuntu\", dryRun = false, logger = console } = options;\n\n await ensurePm2Startup({ user, dryRun, logger });\n if (deployKey) await installDeployKey(deployKey, { dryRun, logger });\n await installLogrotate(service, { dryRun, logger });\n await installOperatorShell(service, { user, dryRun, logger });\n logger.info(\"bootstrap-host complete\");\n}\n","import { readlink } from \"node:fs/promises\";\n\nimport { initServiceStructure } from \"./init-structure.js\";\nimport { appendDeployLog } from \"./log.js\";\nimport { pullRepo } from \"./git.js\";\nimport { createRelease } from \"./release.js\";\nimport { installDeps } from \"./deps.js\";\nimport { runReleaseTests } from \"./test.js\";\nimport { activateRelease } from \"./activate.js\";\nimport { pruneReleases } from \"./prune.js\";\nimport { reloadPm2, stopPm2, startPm2 } from \"./pm2.js\";\nimport { enableNginxUpstream } from \"./nginx.js\";\nimport { syncEnv } from \"./sync-env.js\";\nimport { writeReleaseBuildInfo } from \"./build-info.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/**\n * Resolve pm2 kill_timeout (ms). Prefer manifest `pm2.killTimeout`, else\n * stopAllowance seconds × 1000 + 5s buffer, else 65s.\n */\nfunction resolveKillTimeoutMs(service) {\n const explicit = Number(service.pm2?.killTimeout);\n if (Number.isFinite(explicit) && explicit > 0) return Math.floor(explicit);\n const stopSec = Number(service.pm2?.stopAllowance);\n if (Number.isFinite(stopSec) && stopSec > 0) return Math.floor(stopSec * 1000) + 5000;\n return 65_000;\n}\n\n/**\n * Full deploy: pull → env → release → deps → test → activate → pm2 → prune → nginx.\n *\n * Default (**rolling**): keep the process running while the new release is built and\n * `current` is flipped; then `pm2 delete` + `pm2 start` (SIGINT + kill_timeout drain,\n * then full ecosystem recreate) so script/node_args/env always apply. Previous\n * release is not pruned until after that restart.\n *\n * **`--stopFirst`**: `pm2 stop` + wait → activate/prune → delete + `pm2 start`.\n * Use when a runner is wedged and will not honor graceful stop.\n */\nexport async function deployService(service, options = {}) {\n const {\n dryRun = false,\n skipPull = false,\n skipTests = false,\n skipNginx = false,\n stopFirst = false,\n logger = console,\n } = options;\n\n const paths = servicePaths(service);\n const appName = service.pm2.appName;\n const killTimeoutMs = resolveKillTimeoutMs(service);\n const waitTimeoutMs = killTimeoutMs + 10_000;\n\n await initServiceStructure(service, { dryRun, logger });\n\n if (!skipPull) await pullRepo(service, { dryRun, logger });\n\n await syncEnv(service, { dryRun, logger });\n\n const { stamp, path: releasePath } = await createRelease(service, { dryRun, logger });\n await writeReleaseBuildInfo(service, releasePath, { stamp, dryRun, logger });\n await installDeps(service, releasePath, paths, { dryRun, logger });\n\n if (!skipTests) await runReleaseTests(service, releasePath, paths, { dryRun, logger });\n\n let previousReleaseName = null;\n try {\n const prev = await readlink(paths.current);\n previousReleaseName = prev.split(\"/\").pop() || null;\n } catch {\n // first deploy\n }\n\n if (stopFirst) {\n logger.info(`deploy mode=stopFirst app=${appName} killTimeoutMs=${killTimeoutMs}`);\n await stopPm2(appName, { dryRun, logger, waitTimeoutMs });\n await activateRelease(releasePath, paths, { dryRun, logger });\n await pruneReleases(service, paths, { dryRun, logger });\n await startPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n } else {\n logger.info(\n `deploy mode=rolling app=${appName} killTimeoutMs=${killTimeoutMs}` +\n (previousReleaseName ? ` previous=${previousReleaseName}` : \"\")\n );\n await activateRelease(releasePath, paths, { dryRun, logger });\n // Restart into new `current` before pruning the previous tree (still in use by the old process).\n await reloadPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n await pruneReleases(service, paths, { dryRun, logger });\n }\n\n if (!skipNginx) await enableNginxUpstream(service, { dryRun, logger });\n\n const summary = `deploy complete stamp=${stamp} mode=${stopFirst ? \"stopFirst\" : \"rolling\"} dryRun=${dryRun}`;\n logger.info(summary);\n if (!dryRun) await appendDeployLog(paths.deployLog, summary);\n\n return { stamp, releasePath, mode: stopFirst ? \"stopFirst\" : \"rolling\" };\n}\n","import { access } from \"node:fs/promises\";\n\nimport { bootstrapHost } from \"./bootstrap-host.js\";\nimport { initServiceStructure } from \"./init-structure.js\";\nimport { cloneRepo, pullRepo } from \"./git.js\";\nimport { deployService } from \"./deploy-service.js\";\nimport { syncEnv } from \"./sync-env.js\";\nimport { servicePaths } from \"./paths.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\n/** First-time setup: optional bootstrap, init dirs, clone/pull, sync .env, first deploy. */\nexport async function provisionService(service, options = {}) {\n const {\n dryRun = false,\n deploy = true,\n skipBootstrap = true,\n deployKey,\n logger = console,\n } = options;\n\n if (!skipBootstrap) {\n await bootstrapHost(service, { deployKey, dryRun, logger });\n }\n\n await initServiceStructure(service, { dryRun, logger });\n const paths = servicePaths(service);\n\n if (await pathExists(paths.repo)) {\n logger.info(`repo exists at ${paths.repo} — pulling`);\n await pullRepo(service, { dryRun, logger });\n } else {\n await cloneRepo(service, { dryRun, logger });\n }\n\n await syncEnv(service, { dryRun, logger });\n\n if (deploy) {\n await deployService(service, { dryRun, logger, skipPull: true });\n } else {\n logger.info(\"provision complete (deploy skipped)\");\n }\n}\n","import { readlink } from \"node:fs/promises\";\n\nimport { activateRelease } from \"./activate.js\";\nimport { readReleaseBuildInfo } from \"./build-info.js\";\nimport { appendDeployLog } from \"./log.js\";\nimport { listReleases } from \"./release.js\";\nimport { reloadPm2 } from \"./pm2.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/** Flip current to a named release or the previous one. */\nexport async function rollbackService(service, options = {}) {\n const { release: targetName, dryRun = false, logger = console } = options;\n const paths = servicePaths(service);\n const releases = await listReleases(paths);\n\n if (releases.length === 0) throw new Error(\"No releases to roll back to\");\n\n let activeName = null;\n try {\n const target = await readlink(paths.current);\n activeName = target.split(\"/\").pop();\n } catch {\n throw new Error(\"No active release (current symlink missing)\");\n }\n\n let rollbackTarget;\n if (targetName) {\n rollbackTarget = releases.find((r) => r.name === targetName);\n if (!rollbackTarget) throw new Error(`Release not found: ${targetName}`);\n } else {\n rollbackTarget = releases.find((r) => r.name !== activeName);\n if (!rollbackTarget) throw new Error(\"No previous release to roll back to\");\n }\n\n if (rollbackTarget.name === activeName) throw new Error(`Already on release ${activeName}`);\n\n logger.info(`rollback ${activeName} → ${rollbackTarget.name}`);\n const buildInfo = await readReleaseBuildInfo(service, rollbackTarget.path);\n if (buildInfo?.version) {\n logger.info(`rollback target: v${buildInfo.version} release=${buildInfo.release ?? rollbackTarget.name}`);\n }\n await activateRelease(rollbackTarget.path, paths, { dryRun, logger });\n const appName = service.pm2?.appName ?? null;\n const waitTimeoutMs = 65_000;\n await reloadPm2(paths, { dryRun, logger, appName, waitTimeoutMs });\n\n const summary = `rollback ${activeName} → ${rollbackTarget.name} dryRun=${dryRun}`;\n if (!dryRun) await appendDeployLog(paths.deployLog, summary);\n\n return { from: activeName, to: rollbackTarget.name, path: rollbackTarget.path };\n}\n","import { access, readFile, writeFile } from \"node:fs/promises\";\nimport { homedir, tmpdir } from \"node:os\";\nimport { basename, dirname, join } from \"node:path\";\nimport { spawn } from \"node:child_process\";\n\nimport { scrubEnvContent } from \"./sync-env.js\";\nimport { servicePaths } from \"./paths.js\";\n\n/** Path (relative to the run dir on the server) of the toolkit deploy CLI. */\nexport const REMOTE_CLI_REL = \"node_modules/@nmakarov/cli-toolkit/scripts/deploy/cli.js\";\n\nasync function pathExists(path) {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nfunction expandHome(path) {\n return path.startsWith(\"~/\") ? join(homedir(), path.slice(2)) : path;\n}\n\nfunction resolveLocalEnvPath(envFile) {\n if (envFile) {\n const expanded = expandHome(envFile);\n return expanded.startsWith(\"/\") ? expanded : join(process.cwd(), expanded);\n }\n return join(process.cwd(), \".env\");\n}\n\n/** Parse the ssh host out of a git url (git@host:..., ssh://git@host/...). null for https. */\nexport function parseGitHost(repoUrl) {\n const u = String(repoUrl ?? \"\");\n let m = u.match(/^[^@]+@([^:]+):/); // git@github.com:org/repo.git\n if (m) return m[1];\n m = u.match(/^ssh:\\/\\/[^@]+@([^/:]+)/); // ssh://git@github.com/org/repo.git\n if (m) return m[1];\n return null;\n}\n\nexport function shellQuote(value) {\n return `'${String(value).replace(/'/g, `'\\\\''`)}'`;\n}\n\nexport function sshRun(host, remoteCommand, options = {}) {\n const { logger } = options;\n return new Promise((resolve, reject) => {\n logger?.info?.(`ssh ${host} ${remoteCommand.slice(0, 120)}${remoteCommand.length > 120 ? \"…\" : \"\"}`);\n const child = spawn(\"ssh\", [host, remoteCommand], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => (code === 0 ? resolve() : reject(new Error(`ssh ${host} exited with code ${code}`))));\n });\n}\n\nfunction scp(localPath, remoteSpec) {\n return new Promise((resolve, reject) => {\n const child = spawn(\"scp\", [localPath, remoteSpec], { stdio: \"inherit\" });\n child.on(\"error\", reject);\n child.on(\"close\", (code) => (code === 0 ? resolve() : reject(new Error(`scp exited with code ${code}`))));\n });\n}\n\n/** Copy a git deploy key from the laptop to the server (no-op if not present locally). */\nexport async function ensureDeployKeyOnRemote(host, deployKeyPath, options = {}) {\n const { logger = console } = options;\n if (!deployKeyPath) return false;\n const localPath = expandHome(deployKeyPath);\n if (!(await pathExists(localPath))) return false;\n\n const keyBase = basename(localPath);\n logger.info(`copying deploy key ${localPath} → ${host}:~/.ssh/${keyBase}`);\n await sshRun(host, \"mkdir -p ~/.ssh && chmod 700 ~/.ssh\", { logger });\n await scp(localPath, `${host}:.ssh/${keyBase}`, { logger });\n await sshRun(host, `chmod 600 ~/.ssh/${keyBase}`, { logger });\n return true;\n}\n\n/** known_hosts for the git host + (optional) ~/.ssh/config IdentityFile for the deploy key. */\nexport async function prepareGitHost(host, options = {}) {\n const { logger = console, gitHost, keyBasename } = options;\n if (!gitHost) return;\n\n const configBlock = keyBasename\n ? `if ! grep -q 'Host ${gitHost}' ~/.ssh/config 2>/dev/null; then\n printf '%s\\\\n' '' 'Host ${gitHost}' ' HostName ${gitHost}' ' User git' ' IdentityFile ~/.ssh/${keyBasename}' ' IdentitiesOnly yes' >> ~/.ssh/config\n chmod 600 ~/.ssh/config\n echo \"configured ~/.ssh/config for ${gitHost}\"\nfi`\n : `:`;\n\n const script = `\nset -euo pipefail\nmkdir -p ~/.ssh\nchmod 700 ~/.ssh\nif ! grep -q '^${gitHost}' ~/.ssh/known_hosts 2>/dev/null; then\n ssh-keyscan -t ed25519,rsa ${gitHost} >> ~/.ssh/known_hosts 2>/dev/null\n echo \"added ${gitHost} to known_hosts\"\nfi\n${configBlock}\n`.trim();\n\n await sshRun(host, script, { logger });\n}\n\n/** Install the run-dir workspace deps so the toolkit CLI is available on the server. */\nexport async function ensureRepoDependencies(host, service, options = {}) {\n const { logger = console } = options;\n const paths = servicePaths(service);\n const run = shellQuote(paths.repoRun);\n await sshRun(\n host,\n `cd ${run} && if [ ! -d node_modules/@nmakarov/cli-toolkit ] || [ package-lock.json -nt node_modules/.package-lock.json ]; then npm ci; fi`,\n { logger },\n );\n}\n\n/** Copy laptop .env → server run-dir .env (scrubbed). sync-env then copies it to shared/. */\nexport async function ensureEnvOnRemote(host, service, options = {}) {\n const { logger = console, envFile } = options;\n const paths = servicePaths(service);\n const localPath = resolveLocalEnvPath(envFile);\n if (!(await pathExists(localPath))) return false;\n\n logger.info(`copying .env ${localPath} → ${host}:${paths.repoEnv}`);\n await sshRun(host, `mkdir -p ${shellQuote(dirname(paths.repoEnv))}`, { logger });\n\n const scrubbed = scrubEnvContent(await readFile(localPath, \"utf8\"), service.envScrubPatterns ?? []);\n const tmp = join(tmpdir(), `deploy-env-${Date.now()}`);\n await writeFile(tmp, scrubbed, { mode: 0o600 });\n await scp(tmp, `${host}:${paths.repoEnv}`, { logger });\n await sshRun(host, `chmod 600 ${shellQuote(paths.repoEnv)}`, { logger });\n return true;\n}\n\n/** Ensure a checkout exists on the server, then pull latest + deps + env. */\nexport async function ensureRemoteRepo(host, service, options = {}) {\n const { logger = console, deployKey = service.deployKey, envFile } = options;\n const paths = servicePaths(service);\n const repo = shellQuote(paths.repo);\n const repoUrl = shellQuote(service.repoUrl);\n const root = shellQuote(paths.root);\n\n const localKeyBase = deployKey ? basename(expandHome(deployKey)) : null;\n await ensureDeployKeyOnRemote(host, deployKey, { logger });\n await prepareGitHost(host, { logger, gitHost: parseGitHost(service.repoUrl), keyBasename: localKeyBase });\n\n await sshRun(\n host,\n `mkdir -p ${root} && if [ -d ${repo}/.git ]; then git -C ${repo} pull --ff-only; else git clone ${repoUrl} ${repo}; fi`,\n { logger },\n );\n\n await ensureRepoDependencies(host, service, { logger });\n await ensureEnvOnRemote(host, service, { logger, envFile });\n}\n\n/**\n * Invoke the toolkit deploy CLI on the server in local mode (no --host), passing\n * the same --service/--manifests + extra flags. Run after ensureRemoteRepo.\n */\nexport async function runRemoteCli(host, service, command, args = [], options = {}) {\n const { logger = console, manifests, skipPull = false, deployKey = service.deployKey, envFile } = options;\n const paths = servicePaths(service);\n const run = shellQuote(paths.repoRun);\n const cli = shellQuote(REMOTE_CLI_REL);\n\n if (!skipPull) {\n await ensureRemoteRepo(host, service, { logger, deployKey, envFile });\n } else {\n await ensureRepoDependencies(host, service, { logger });\n await ensureEnvOnRemote(host, service, { logger, envFile });\n }\n\n const passthrough = [\n `--service=${service.name}`,\n ...(manifests ? [`--manifests=${manifests}`] : []),\n ...args,\n ].map(shellQuote).join(\" \");\n\n await sshRun(host, `cd ${run} && node ${cli} ${shellQuote(command)} ${passthrough}`.trim(), { logger });\n}\n\nexport async function runRemoteStatus(host, service, options = {}) {\n const { logger = console } = options;\n const paths = servicePaths(service);\n const port = service.pm2.port;\n const app = service.pm2.appName;\n\n const script = `\necho \"=== apps root ===\"\nls -la ${shellQuote(paths.root)} 2>/dev/null || echo \"(missing)\"\necho \"\"\necho \"=== current ===\"\nreadlink ${shellQuote(paths.current)} 2>/dev/null || echo \"(not set)\"\necho \"\"\necho \"=== releases ===\"\nls -1 ${shellQuote(paths.releases)} 2>/dev/null || echo \"(none)\"\necho \"\"\necho \"=== pm2 ===\"\npm2 describe ${app} 2>/dev/null | head -20 || pm2 status ${app} 2>/dev/null || echo \"(not running)\"\n${port ? `echo \"\"\necho \"=== app health (localhost) ===\"\ncurl -sf http://127.0.0.1:${port}/healthz 2>/dev/null || echo \"(no /healthz on :${port})\"` : \"\"}\n`.trim();\n\n await sshRun(host, script, { logger });\n}\n","import { isAbsolute, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { defineService } from \"./service.js\";\n\n/**\n * Load a project's service manifests module. The module may export:\n * export const services = { \"my-svc\": { … }, … } // map\n * export default { \"my-svc\": { … } } // map as default\n * export const services = [ { name: \"my-svc\", … } ] // array\n * Every entry is normalized through defineService().\n *\n * @param {{ manifests?: string, cwd?: string }} opts manifests = path to the module\n * @returns {Promise<Record<string, object>>} name → normalized service\n */\nexport async function loadServices({ manifests = \"deploy/services.js\", cwd = process.cwd() } = {}) {\n const abs = isAbsolute(manifests) ? manifests : resolve(cwd, manifests);\n let mod;\n try {\n mod = await import(pathToFileURL(abs).href);\n } catch (err) {\n throw new Error(`Could not load deploy manifests from ${abs}: ${err.message}`);\n }\n\n const raw = mod.services ?? mod.default;\n if (!raw) {\n throw new Error(`Manifests module ${abs} must export \\`services\\` (or default): a map or array of service manifests`);\n }\n\n const list = Array.isArray(raw) ? raw : Object.values(raw);\n const out = {};\n for (const entry of list) {\n const svc = defineService(entry);\n out[svc.name] = svc;\n }\n return out;\n}\n\n/** Resolve one normalized service by name (optionally overriding appsRoot). */\nexport function resolveServiceFrom(serviceMap, name, { appsRoot } = {}) {\n const svc = serviceMap[name];\n if (!svc) {\n throw new Error(`Unknown service \"${name}\". Known: ${Object.keys(serviceMap).join(\", \") || \"(none)\"}`);\n }\n return appsRoot ? { ...svc, appsRoot } : svc;\n}\n"],"mappings":"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","names":["resolve","import_promises","import_promises","import_node_path","pathDirname","import_promises","import_node_path","import_promises","import_node_path","pathExists","import_promises","import_node_path","import_promises","resolve","import_promises","import_node_path","import_promises","pathExists","import_promises","import_node_path","import_node_child_process","import_promises","import_node_path","pathExists","import_node_child_process","import_promises","import_node_os","import_node_path","pathExists","import_promises","resolveKillTimeoutMs","import_promises","pathExists","import_promises","import_promises","import_node_os","import_node_path","import_node_child_process","pathExists","expandHome","resolve","run","import_node_path"]}
|
package/dist/deploy.js
CHANGED
|
@@ -384,16 +384,28 @@ function isPidAlive(pid) {
|
|
|
384
384
|
return false;
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
|
+
function parsePm2Jlist(stdout) {
|
|
388
|
+
const raw = String(stdout ?? "");
|
|
389
|
+
const text = raw.split("\n").filter((line) => !line.startsWith("[PM2]")).join("\n").trim();
|
|
390
|
+
if (!text) return [];
|
|
391
|
+
try {
|
|
392
|
+
const parsed = JSON.parse(text);
|
|
393
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
394
|
+
} catch (err) {
|
|
395
|
+
throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
387
398
|
async function getPm2Process(appName, options = {}) {
|
|
388
399
|
const { logger } = options;
|
|
400
|
+
await runCapture("bash", ["-lc", "pm2 ping >/dev/null 2>&1 || true"], { logger }).catch(() => {
|
|
401
|
+
});
|
|
389
402
|
const { stdout } = await runCapture("bash", ["-lc", "pm2 jlist"], { logger });
|
|
390
403
|
let list;
|
|
391
404
|
try {
|
|
392
|
-
list =
|
|
405
|
+
list = parsePm2Jlist(stdout);
|
|
393
406
|
} catch (err) {
|
|
394
407
|
throw new Error(`pm2 jlist: invalid JSON (${err?.message ?? err})`);
|
|
395
408
|
}
|
|
396
|
-
if (!Array.isArray(list)) return null;
|
|
397
409
|
return list.find((p) => p?.name === appName) ?? null;
|
|
398
410
|
}
|
|
399
411
|
async function waitPm2(appName, predicate, options = {}) {
|
|
@@ -1392,6 +1404,7 @@ export {
|
|
|
1392
1404
|
npmEnv,
|
|
1393
1405
|
npmInstallEnv,
|
|
1394
1406
|
parseGitHost,
|
|
1407
|
+
parsePm2Jlist,
|
|
1395
1408
|
prepareGitHost,
|
|
1396
1409
|
provisionService,
|
|
1397
1410
|
pruneReleases,
|