@milaboratories/pl-deployments 2.4.8 → 2.4.9

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/local/pl.cjs CHANGED
@@ -158,7 +158,13 @@ async function localPlatformaReadPidAndStop(logger, workingDir) {
158
158
  const alive = trace('wasAlive', await process$1.isProcessAlive(oldPid));
159
159
  if (oldPid !== undefined && alive) {
160
160
  trace('stopped', process$1.processStop(oldPid));
161
- trace('waitStopped', await process$1.processWaitStopped(oldPid, 10_000));
161
+ try {
162
+ trace('waitStopped', await process$1.processWaitStopped(oldPid, 15_000)); // larger, that 10s we provide to backend in config.
163
+ }
164
+ catch (_e) {
165
+ trace('forceStopped', process$1.processStop(oldPid, true));
166
+ trace('waitForceStopped', await process$1.processWaitStopped(oldPid, 5_000));
167
+ }
162
168
  }
163
169
  return t;
164
170
  });
@@ -1 +1 @@
1
- {"version":3,"file":"pl.cjs","sources":["../../src/local/pl.ts"],"sourcesContent":["import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'node:child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'node:fs/promises';\nimport type { Required } from 'utility-types';\nimport * as os from 'node:os';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) void this.onError(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) void this.onClose(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\nexport type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n\n // Backend could consume a lot of CPU power,\n // we want to keep at least a couple for UI and other apps to work.\n const numCpu = Math.max(os.cpus().length - 2, 1);\n const ops = mergeDefaultOps(_ops, numCpu);\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n\n/** Gets default options for the whole init process\n * and overrides them with the provided options. */\nexport function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {\n const result: {\n plBinary: PlBinarySource;\n spawnOptions: SpawnOptions;\n closeOld: boolean;\n } = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {\n env: {\n GOMAXPROCS: String(numCpu),\n },\n },\n closeOld: true,\n };\n\n if (ops.spawnOptions?.env) {\n result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };\n }\n\n if (ops.spawnOptions) {\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };\n }\n\n const withoutSpawnOps = { ...ops };\n delete withoutSpawnOps['spawnOptions'];\n\n return { ...result, ...withoutSpawnOps };\n}\n\n/** Gets default options for a platforma local binary\n * and overrides them with the provided options. */\nexport function plProcessOps(\n binaryPath: any,\n configPath: string,\n ops: LocalPlOptionsFull,\n workDir: string,\n defaultEnv: Record<string, string | undefined>,\n): ProcessOptions {\n const result: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...defaultEnv },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n },\n };\n\n if (ops.spawnOptions?.env) {\n result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };\n }\n\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.opts = { ...result.opts, ...withoutEnv };\n\n return result;\n}\n"],"names":["withTrace","processRun","filePid","notEmpty","writePid","processStop","processWaitStopped","isProcessAlive","os","resolveLocalPlBinaryPath","readPid","newDefaultPlBinarySource"],"mappings":"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"}
1
+ {"version":3,"file":"pl.cjs","sources":["../../src/local/pl.ts"],"sourcesContent":["import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'node:child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'node:fs/promises';\nimport type { Required } from 'utility-types';\nimport * as os from 'node:os';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) void this.onError(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) void this.onClose(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\nexport type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n\n // Backend could consume a lot of CPU power,\n // we want to keep at least a couple for UI and other apps to work.\n const numCpu = Math.max(os.cpus().length - 2, 1);\n const ops = mergeDefaultOps(_ops, numCpu);\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n try {\n trace('waitStopped', await processWaitStopped(oldPid, 15_000)); // larger, that 10s we provide to backend in config.\n } catch (_e) {\n trace('forceStopped', processStop(oldPid, true));\n trace('waitForceStopped', await processWaitStopped(oldPid, 5_000));\n }\n }\n\n return t;\n });\n}\n\n/** Gets default options for the whole init process\n * and overrides them with the provided options. */\nexport function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {\n const result: {\n plBinary: PlBinarySource;\n spawnOptions: SpawnOptions;\n closeOld: boolean;\n } = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {\n env: {\n GOMAXPROCS: String(numCpu),\n },\n },\n closeOld: true,\n };\n\n if (ops.spawnOptions?.env) {\n result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };\n }\n\n if (ops.spawnOptions) {\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };\n }\n\n const withoutSpawnOps = { ...ops };\n delete withoutSpawnOps['spawnOptions'];\n\n return { ...result, ...withoutSpawnOps };\n}\n\n/** Gets default options for a platforma local binary\n * and overrides them with the provided options. */\nexport function plProcessOps(\n binaryPath: any,\n configPath: string,\n ops: LocalPlOptionsFull,\n workDir: string,\n defaultEnv: Record<string, string | undefined>,\n): ProcessOptions {\n const result: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...defaultEnv },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n },\n };\n\n if (ops.spawnOptions?.env) {\n result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };\n }\n\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.opts = { ...result.opts, ...withoutEnv };\n\n return result;\n}\n"],"names":["withTrace","processRun","filePid","notEmpty","writePid","processStop","processWaitStopped","isProcessAlive","os","resolveLocalPlBinaryPath","readPid","newDefaultPlBinarySource"],"mappings":"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"}
package/dist/local/pl.js CHANGED
@@ -137,7 +137,13 @@ async function localPlatformaReadPidAndStop(logger, workingDir) {
137
137
  const alive = trace('wasAlive', await isProcessAlive(oldPid));
138
138
  if (oldPid !== undefined && alive) {
139
139
  trace('stopped', processStop(oldPid));
140
- trace('waitStopped', await processWaitStopped(oldPid, 10_000));
140
+ try {
141
+ trace('waitStopped', await processWaitStopped(oldPid, 15_000)); // larger, that 10s we provide to backend in config.
142
+ }
143
+ catch (_e) {
144
+ trace('forceStopped', processStop(oldPid, true));
145
+ trace('waitForceStopped', await processWaitStopped(oldPid, 5_000));
146
+ }
141
147
  }
142
148
  return t;
143
149
  });
@@ -1 +1 @@
1
- {"version":3,"file":"pl.js","sources":["../../src/local/pl.ts"],"sourcesContent":["import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'node:child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'node:fs/promises';\nimport type { Required } from 'utility-types';\nimport * as os from 'node:os';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) void this.onError(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) void this.onClose(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\nexport type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n\n // Backend could consume a lot of CPU power,\n // we want to keep at least a couple for UI and other apps to work.\n const numCpu = Math.max(os.cpus().length - 2, 1);\n const ops = mergeDefaultOps(_ops, numCpu);\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n trace('waitStopped', await processWaitStopped(oldPid, 10_000));\n }\n\n return t;\n });\n}\n\n/** Gets default options for the whole init process\n * and overrides them with the provided options. */\nexport function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {\n const result: {\n plBinary: PlBinarySource;\n spawnOptions: SpawnOptions;\n closeOld: boolean;\n } = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {\n env: {\n GOMAXPROCS: String(numCpu),\n },\n },\n closeOld: true,\n };\n\n if (ops.spawnOptions?.env) {\n result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };\n }\n\n if (ops.spawnOptions) {\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };\n }\n\n const withoutSpawnOps = { ...ops };\n delete withoutSpawnOps['spawnOptions'];\n\n return { ...result, ...withoutSpawnOps };\n}\n\n/** Gets default options for a platforma local binary\n * and overrides them with the provided options. */\nexport function plProcessOps(\n binaryPath: any,\n configPath: string,\n ops: LocalPlOptionsFull,\n workDir: string,\n defaultEnv: Record<string, string | undefined>,\n): ProcessOptions {\n const result: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...defaultEnv },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n },\n };\n\n if (ops.spawnOptions?.env) {\n result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };\n }\n\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.opts = { ...result.opts, ...withoutEnv };\n\n return result;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"pl.js","sources":["../../src/local/pl.ts"],"sourcesContent":["import type {\n ProcessOptions } from './process';\nimport {\n isProcessAlive,\n processStop,\n processWaitStopped,\n processRun,\n} from './process';\nimport type { PlBinarySource } from '../common/pl_binary';\nimport { newDefaultPlBinarySource, resolveLocalPlBinaryPath } from '../common/pl_binary';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { notEmpty } from '@milaboratories/ts-helpers';\nimport type { ChildProcess, SpawnOptions } from 'node:child_process';\nimport { filePid, readPid, writePid } from './pid';\nimport type { Trace } from './trace';\nimport { withTrace } from './trace';\nimport upath from 'upath';\nimport fsp from 'node:fs/promises';\nimport type { Required } from 'utility-types';\nimport * as os from 'node:os';\n\nexport const LocalConfigYaml = 'config-local.yaml';\n\n/**\n * Represents a local running pl-core,\n * and has methods to start, check if it's running, stop and wait for stopping it.\n * Also, a hook on pl-core closed can be provided.\n */\nexport class LocalPl {\n private instance?: ChildProcess;\n public pid?: number;\n private nRuns: number = 0;\n private lastRunHistory: Trace = {};\n private wasStopped = false;\n\n constructor(\n private readonly logger: MiLogger,\n private readonly workingDir: string,\n private readonly startOptions: ProcessOptions,\n private readonly initialStartHistory: Trace,\n private readonly onClose?: (pl: LocalPl) => Promise<void>,\n private readonly onError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndError?: (pl: LocalPl) => Promise<void>,\n private readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>,\n ) {}\n\n async start() {\n await withTrace(this.logger, async (trace, t) => {\n this.wasStopped = false;\n const instance = processRun(this.logger, this.startOptions);\n instance.on('error', (e: any) => {\n this.logger.error(\n `error '${e}', while running platforma, started opts: ${JSON.stringify(this.debugInfo())}`,\n );\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onError !== undefined) void this.onError(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n instance.on('close', () => {\n this.logger.warn(`platforma was closed, started opts: ${JSON.stringify(this.debugInfo())}`);\n\n // keep in mind there are no awaits here, it will be asynchronous\n if (this.onClose !== undefined) void this.onClose(this);\n if (this.onCloseAndError !== undefined) void this.onCloseAndError(this);\n if (this.onCloseAndErrorNoStop !== undefined && !this.wasStopped)\n void this.onCloseAndErrorNoStop(this);\n });\n\n trace('started', true);\n\n const pidFile = trace('pidFile', filePid(this.workingDir));\n trace('pid', notEmpty(instance.pid));\n trace('pidWritten', await writePid(pidFile, notEmpty(instance.pid)));\n\n this.nRuns++;\n this.instance = instance;\n this.pid = instance.pid;\n this.lastRunHistory = t;\n });\n }\n\n stop() {\n // TODO use this.instance to stop the process\n this.wasStopped = true;\n processStop(notEmpty(this.pid));\n }\n\n async waitStopped() {\n await processWaitStopped(notEmpty(this.pid), 15000);\n }\n\n stopped() {\n return this.wasStopped;\n }\n\n async isAlive(): Promise<boolean> {\n return await isProcessAlive(notEmpty(this.pid));\n }\n\n debugInfo() {\n return {\n lastRunHistory: this.lastRunHistory,\n nRuns: this.nRuns,\n pid: this.pid,\n workingDir: this.workingDir,\n initialStartHistory: this.initialStartHistory,\n wasStopped: this.wasStopped,\n };\n }\n}\n\n/** Options to start a local pl-core. */\nexport type LocalPlOptions = {\n /** From what directory start a process. */\n readonly workingDir: string;\n /** A string representation of yaml config. */\n readonly config: string;\n /** How to get a binary, download it or get an existing one (default: download latest version) */\n readonly plBinary?: PlBinarySource;\n /** Additional options for a process, environments, stdout, stderr etc. */\n readonly spawnOptions?: SpawnOptions;\n /**\n * If the previous pl-core was started from the same directory,\n * we can check if it's still running and then stop it before starting a new one.\n * (default: true)\n */\n readonly closeOld?: boolean;\n\n readonly onClose?: (pl: LocalPl) => Promise<void>;\n readonly onError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndError?: (pl: LocalPl) => Promise<void>;\n readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;\n};\n\nexport type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;\n\n/**\n * Starts pl-core, if the option was provided downloads a binary, reads license environments etc.\n */\nexport async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {\n // filling-in default values\n\n // Backend could consume a lot of CPU power,\n // we want to keep at least a couple for UI and other apps to work.\n const numCpu = Math.max(os.cpus().length - 2, 1);\n const ops = mergeDefaultOps(_ops, numCpu);\n\n return await withTrace(logger, async (trace, t) => {\n trace('startOptions', { ...ops, config: 'too wordy' });\n\n const workDir = upath.resolve(ops.workingDir);\n\n if (ops.closeOld) {\n trace('closeOld', await localPlatformaReadPidAndStop(logger, workDir));\n }\n\n const configPath = upath.join(workDir, LocalConfigYaml);\n\n logger.info(`writing configuration '${configPath}'...`);\n await fsp.writeFile(configPath, ops.config);\n\n const plBinPath = upath.join(workDir, 'binaries');\n const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);\n const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));\n\n const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);\n trace('processOpts', {\n cmd: processOpts.cmd,\n args: processOpts.args,\n cwd: processOpts.opts.cwd,\n });\n\n const pl = new LocalPl(\n logger,\n ops.workingDir,\n processOpts,\n t,\n ops.onClose,\n ops.onError,\n ops.onCloseAndError,\n ops.onCloseAndErrorNoStop,\n );\n await pl.start();\n\n return pl;\n });\n}\n\n/** Reads a pid of the old pl-core if it was started in the same working directory,\n * and closes it. */\nasync function localPlatformaReadPidAndStop(\n logger: MiLogger,\n workingDir: string,\n): Promise<Record<string, any>> {\n return await withTrace(logger, async (trace, t) => {\n const file = trace('pidFilePath', filePid(workingDir));\n\n const oldPid = trace('pid', await readPid(file));\n const alive = trace('wasAlive', await isProcessAlive(oldPid));\n\n if (oldPid !== undefined && alive) {\n trace('stopped', processStop(oldPid));\n try {\n trace('waitStopped', await processWaitStopped(oldPid, 15_000)); // larger, that 10s we provide to backend in config.\n } catch (_e) {\n trace('forceStopped', processStop(oldPid, true));\n trace('waitForceStopped', await processWaitStopped(oldPid, 5_000));\n }\n }\n\n return t;\n });\n}\n\n/** Gets default options for the whole init process\n * and overrides them with the provided options. */\nexport function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {\n const result: {\n plBinary: PlBinarySource;\n spawnOptions: SpawnOptions;\n closeOld: boolean;\n } = {\n plBinary: newDefaultPlBinarySource(),\n spawnOptions: {\n env: {\n GOMAXPROCS: String(numCpu),\n },\n },\n closeOld: true,\n };\n\n if (ops.spawnOptions?.env) {\n result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };\n }\n\n if (ops.spawnOptions) {\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };\n }\n\n const withoutSpawnOps = { ...ops };\n delete withoutSpawnOps['spawnOptions'];\n\n return { ...result, ...withoutSpawnOps };\n}\n\n/** Gets default options for a platforma local binary\n * and overrides them with the provided options. */\nexport function plProcessOps(\n binaryPath: any,\n configPath: string,\n ops: LocalPlOptionsFull,\n workDir: string,\n defaultEnv: Record<string, string | undefined>,\n): ProcessOptions {\n const result: ProcessOptions = {\n cmd: binaryPath,\n args: ['--config', configPath],\n opts: {\n env: { ...defaultEnv },\n cwd: workDir,\n stdio: ['pipe', 'ignore', 'inherit'],\n windowsHide: true, // hide a terminal on Windows\n },\n };\n\n if (ops.spawnOptions?.env) {\n result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };\n }\n\n const withoutEnv = { ...ops.spawnOptions };\n delete withoutEnv['env'];\n result.opts = { ...result.opts, ...withoutEnv };\n\n return result;\n}\n"],"names":[],"mappings":"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"}
@@ -14,14 +14,49 @@ wd: ${opts.opts.cwd}`);
14
14
  async function isProcessAlive(pid) {
15
15
  try {
16
16
  process.kill(pid, 0);
17
- return true;
17
+ // Check we look at 'platforma' to not kill wrong process.
18
+ const processName = getProcessName(pid);
19
+ if (process.platform === 'win32') {
20
+ return processName === 'platforma.exe'; // process name does not contain path to the file.
21
+ }
22
+ // Linux and Mac OS X behave differently (so can different Linux distributions).
23
+ // Process name can contain original path to the binary file or just its name.
24
+ return processName.endsWith('/platforma') || processName === 'platforma';
18
25
  }
19
26
  catch (_e) {
20
27
  return false;
21
28
  }
22
29
  }
23
- function processStop(pid) {
24
- return process.kill(pid, 'SIGINT');
30
+ function getProcessName(pid) {
31
+ try {
32
+ if (process.platform === 'win32') {
33
+ // Windows: use tasklist command
34
+ const output = node_child_process.execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: 'utf8' });
35
+ const lines = output.trim().split('\n');
36
+ if (lines.length > 0 && lines[0].includes(',')) {
37
+ const parts = lines[0].split(',');
38
+ if (parts.length >= 1) {
39
+ // Remove quotes and get the executable name
40
+ const exeName = parts[0].replace(/^"|"$/g, '').trim();
41
+ return exeName;
42
+ }
43
+ }
44
+ }
45
+ else {
46
+ // Unix-like systems: use ps command
47
+ const output = node_child_process.execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });
48
+ const processName = output.trim();
49
+ return processName;
50
+ }
51
+ }
52
+ catch (_error) {
53
+ // If we can't get the process name, return empty string
54
+ return '';
55
+ }
56
+ return '';
57
+ }
58
+ function processStop(pid, force = false) {
59
+ return process.kill(pid, force ? 'SIGKILL' : 'SIGINT');
25
60
  }
26
61
  async function processWaitStopped(pid, maxMs) {
27
62
  const sleepMs = 100;
@@ -1 +1 @@
1
- {"version":3,"file":"process.cjs","sources":["../../src/local/process.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (_e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n"],"names":["spawn","sleep"],"mappings":";;;;;AAWM,SAAU,UAAU,CAAC,MAAgB,EAAE,IAAoB,EAAA;IAC/D,MAAM,CAAC,IAAI,CAAC,CAAA;AACP,KAAA,EAAA,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;AACzC,IAAA,EAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA,CAAE,CAAC;AAEpB,IAAA,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC;AACvC,IAAA,OAAOA,wBAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;AAC9C;AAEA;AACO,eAAe,cAAc,CAAC,GAAW,EAAA;AAC9C,IAAA,IAAI;AACF,QAAA,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACpB,QAAA,OAAO,IAAI;IACb;IAAE,OAAO,EAAE,EAAE;AACX,QAAA,OAAO,KAAK;IACd;AACF;AAEM,SAAU,WAAW,CAAC,GAAW,EAAA;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;AACpC;AAEO,eAAe,kBAAkB,CAAC,GAAW,EAAE,KAAa,EAAA;IACjE,MAAM,OAAO,GAAG,GAAG;IACnB,IAAI,KAAK,GAAG,CAAC;AACb,IAAA,OAAO,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE;AAChC,QAAA,MAAMC,eAAK,CAAC,OAAO,CAAC;QACpB,KAAK,IAAI,OAAO;AAChB,QAAA,IAAI,KAAK,GAAG,KAAK,EAAE;AACjB,YAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,CAAA,IAAA,CAAM,CAAC;QACnE;IACF;AACF;;;;;;;"}
1
+ {"version":3,"file":"process.cjs","sources":["../../src/local/process.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { execSync } from 'node:child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n\n // Check we look at 'platforma' to not kill wrong process.\n const processName = getProcessName(pid);\n if (process.platform === 'win32') {\n return processName === 'platforma.exe'; // process name does not contain path to the file.\n }\n\n // Linux and Mac OS X behave differently (so can different Linux distributions).\n // Process name can contain original path to the binary file or just its name.\n return processName.endsWith('/platforma') || processName === 'platforma';\n } catch (_e) {\n return false;\n }\n}\n\nfunction getProcessName(pid: number): string {\n try {\n if (process.platform === 'win32') {\n // Windows: use tasklist command\n const output = execSync(`tasklist /FI \"PID eq ${pid}\" /FO CSV /NH`, { encoding: 'utf8' });\n const lines = output.trim().split('\\n');\n if (lines.length > 0 && lines[0].includes(',')) {\n const parts = lines[0].split(',');\n if (parts.length >= 1) {\n // Remove quotes and get the executable name\n const exeName = parts[0].replace(/^\"|\"$/g, '').trim();\n return exeName;\n }\n }\n } else {\n // Unix-like systems: use ps command\n const output = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });\n const processName = output.trim();\n return processName;\n }\n } catch (_error) {\n // If we can't get the process name, return empty string\n return '';\n }\n return '';\n}\n\nexport function processStop(pid: number, force: boolean = false) {\n return process.kill(pid, force ? 'SIGKILL' : 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n"],"names":["spawn","execSync","sleep"],"mappings":"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"}
@@ -7,5 +7,5 @@ export type ProcessOptions = {
7
7
  };
8
8
  export declare function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess;
9
9
  export declare function isProcessAlive(pid: number): Promise<boolean>;
10
- export declare function processStop(pid: number): true;
10
+ export declare function processStop(pid: number, force?: boolean): true;
11
11
  export declare function processWaitStopped(pid: number, maxMs: number): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { spawn } from 'node:child_process';
1
+ import { execSync, spawn } from 'node:child_process';
2
2
  import { sleep } from '@milaboratories/ts-helpers';
3
3
 
4
4
  function processRun(logger, opts) {
@@ -12,14 +12,49 @@ wd: ${opts.opts.cwd}`);
12
12
  async function isProcessAlive(pid) {
13
13
  try {
14
14
  process.kill(pid, 0);
15
- return true;
15
+ // Check we look at 'platforma' to not kill wrong process.
16
+ const processName = getProcessName(pid);
17
+ if (process.platform === 'win32') {
18
+ return processName === 'platforma.exe'; // process name does not contain path to the file.
19
+ }
20
+ // Linux and Mac OS X behave differently (so can different Linux distributions).
21
+ // Process name can contain original path to the binary file or just its name.
22
+ return processName.endsWith('/platforma') || processName === 'platforma';
16
23
  }
17
24
  catch (_e) {
18
25
  return false;
19
26
  }
20
27
  }
21
- function processStop(pid) {
22
- return process.kill(pid, 'SIGINT');
28
+ function getProcessName(pid) {
29
+ try {
30
+ if (process.platform === 'win32') {
31
+ // Windows: use tasklist command
32
+ const output = execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: 'utf8' });
33
+ const lines = output.trim().split('\n');
34
+ if (lines.length > 0 && lines[0].includes(',')) {
35
+ const parts = lines[0].split(',');
36
+ if (parts.length >= 1) {
37
+ // Remove quotes and get the executable name
38
+ const exeName = parts[0].replace(/^"|"$/g, '').trim();
39
+ return exeName;
40
+ }
41
+ }
42
+ }
43
+ else {
44
+ // Unix-like systems: use ps command
45
+ const output = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });
46
+ const processName = output.trim();
47
+ return processName;
48
+ }
49
+ }
50
+ catch (_error) {
51
+ // If we can't get the process name, return empty string
52
+ return '';
53
+ }
54
+ return '';
55
+ }
56
+ function processStop(pid, force = false) {
57
+ return process.kill(pid, force ? 'SIGKILL' : 'SIGINT');
23
58
  }
24
59
  async function processWaitStopped(pid, maxMs) {
25
60
  const sleepMs = 100;
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sources":["../../src/local/process.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n return true;\n } catch (_e) {\n return false;\n }\n}\n\nexport function processStop(pid: number) {\n return process.kill(pid, 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n"],"names":[],"mappings":";;;AAWM,SAAU,UAAU,CAAC,MAAgB,EAAE,IAAoB,EAAA;IAC/D,MAAM,CAAC,IAAI,CAAC,CAAA;AACP,KAAA,EAAA,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;AACzC,IAAA,EAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAA,CAAE,CAAC;AAEpB,IAAA,MAAM,CAAC,IAAI,CAAC,0BAA0B,CAAC;AACvC,IAAA,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC;AAC9C;AAEA;AACO,eAAe,cAAc,CAAC,GAAW,EAAA;AAC9C,IAAA,IAAI;AACF,QAAA,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;AACpB,QAAA,OAAO,IAAI;IACb;IAAE,OAAO,EAAE,EAAE;AACX,QAAA,OAAO,KAAK;IACd;AACF;AAEM,SAAU,WAAW,CAAC,GAAW,EAAA;IACrC,OAAO,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC;AACpC;AAEO,eAAe,kBAAkB,CAAC,GAAW,EAAE,KAAa,EAAA;IACjE,MAAM,OAAO,GAAG,GAAG;IACnB,IAAI,KAAK,GAAG,CAAC;AACb,IAAA,OAAO,MAAM,cAAc,CAAC,GAAG,CAAC,EAAE;AAChC,QAAA,MAAM,KAAK,CAAC,OAAO,CAAC;QACpB,KAAK,IAAI,OAAO;AAChB,QAAA,IAAI,KAAK,GAAG,KAAK,EAAE;AACjB,YAAA,MAAM,IAAI,KAAK,CAAC,qCAAqC,KAAK,CAAA,IAAA,CAAM,CAAC;QACnE;IACF;AACF;;;;"}
1
+ {"version":3,"file":"process.js","sources":["../../src/local/process.ts"],"sourcesContent":["import type { SpawnOptions, ChildProcess } from 'node:child_process';\nimport { spawn } from 'node:child_process';\nimport { execSync } from 'node:child_process';\nimport type { MiLogger } from '@milaboratories/ts-helpers';\nimport { sleep } from '@milaboratories/ts-helpers';\n\nexport type ProcessOptions = {\n cmd: string;\n args: string[];\n opts: SpawnOptions;\n};\n\nexport function processRun(logger: MiLogger, opts: ProcessOptions): ChildProcess {\n logger.info(`Running:\ncmd: ${JSON.stringify([opts.cmd, ...opts.args])}\nwd: ${opts.opts.cwd}`);\n\n logger.info(' spawning child process');\n return spawn(opts.cmd, opts.args, opts.opts);\n}\n\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function isProcessAlive(pid: number) {\n try {\n process.kill(pid, 0);\n\n // Check we look at 'platforma' to not kill wrong process.\n const processName = getProcessName(pid);\n if (process.platform === 'win32') {\n return processName === 'platforma.exe'; // process name does not contain path to the file.\n }\n\n // Linux and Mac OS X behave differently (so can different Linux distributions).\n // Process name can contain original path to the binary file or just its name.\n return processName.endsWith('/platforma') || processName === 'platforma';\n } catch (_e) {\n return false;\n }\n}\n\nfunction getProcessName(pid: number): string {\n try {\n if (process.platform === 'win32') {\n // Windows: use tasklist command\n const output = execSync(`tasklist /FI \"PID eq ${pid}\" /FO CSV /NH`, { encoding: 'utf8' });\n const lines = output.trim().split('\\n');\n if (lines.length > 0 && lines[0].includes(',')) {\n const parts = lines[0].split(',');\n if (parts.length >= 1) {\n // Remove quotes and get the executable name\n const exeName = parts[0].replace(/^\"|\"$/g, '').trim();\n return exeName;\n }\n }\n } else {\n // Unix-like systems: use ps command\n const output = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });\n const processName = output.trim();\n return processName;\n }\n } catch (_error) {\n // If we can't get the process name, return empty string\n return '';\n }\n return '';\n}\n\nexport function processStop(pid: number, force: boolean = false) {\n return process.kill(pid, force ? 'SIGKILL' : 'SIGINT');\n}\n\nexport async function processWaitStopped(pid: number, maxMs: number) {\n const sleepMs = 100;\n let total = 0;\n while (await isProcessAlive(pid)) {\n await sleep(sleepMs);\n total += sleepMs;\n if (total > maxMs) {\n throw new Error(`The process did not stopped after ${maxMs} ms.`);\n }\n }\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@milaboratories/pl-deployments",
3
- "version": "2.4.8",
3
+ "version": "2.4.9",
4
4
  "pl-version": "1.35.2",
5
5
  "description": "MiLaboratories Platforma Backend code service run wrapper",
6
6
  "engines": {
@@ -41,9 +41,9 @@
41
41
  "typescript": "~5.6.3",
42
42
  "utility-types": "^3.11.0",
43
43
  "vitest": "^2.1.9",
44
- "@milaboratories/eslint-config": "^1.0.4",
45
44
  "@milaboratories/ts-configs": "1.0.6",
46
- "@milaboratories/ts-builder": "1.0.1",
45
+ "@milaboratories/eslint-config": "^1.0.4",
46
+ "@milaboratories/ts-builder": "1.0.2",
47
47
  "@milaboratories/build-configs": "1.0.6"
48
48
  },
49
49
  "dependencies": {
@@ -55,7 +55,7 @@
55
55
  "yaml": "^2.8.0",
56
56
  "zod": "~3.23.8",
57
57
  "@milaboratories/ts-helpers": "^1.4.4",
58
- "@milaboratories/pl-config": "^1.6.4"
58
+ "@milaboratories/pl-config": "^1.7.0"
59
59
  },
60
60
  "scripts": {
61
61
  "type-check": "ts-builder types --target node",
package/src/local/pl.ts CHANGED
@@ -203,7 +203,12 @@ async function localPlatformaReadPidAndStop(
203
203
 
204
204
  if (oldPid !== undefined && alive) {
205
205
  trace('stopped', processStop(oldPid));
206
- trace('waitStopped', await processWaitStopped(oldPid, 10_000));
206
+ try {
207
+ trace('waitStopped', await processWaitStopped(oldPid, 15_000)); // larger, that 10s we provide to backend in config.
208
+ } catch (_e) {
209
+ trace('forceStopped', processStop(oldPid, true));
210
+ trace('waitForceStopped', await processWaitStopped(oldPid, 5_000));
211
+ }
207
212
  }
208
213
 
209
214
  return t;
@@ -1,5 +1,6 @@
1
1
  import type { SpawnOptions, ChildProcess } from 'node:child_process';
2
2
  import { spawn } from 'node:child_process';
3
+ import { execSync } from 'node:child_process';
3
4
  import type { MiLogger } from '@milaboratories/ts-helpers';
4
5
  import { sleep } from '@milaboratories/ts-helpers';
5
6
 
@@ -22,14 +23,50 @@ wd: ${opts.opts.cwd}`);
22
23
  export async function isProcessAlive(pid: number) {
23
24
  try {
24
25
  process.kill(pid, 0);
25
- return true;
26
+
27
+ // Check we look at 'platforma' to not kill wrong process.
28
+ const processName = getProcessName(pid);
29
+ if (process.platform === 'win32') {
30
+ return processName === 'platforma.exe'; // process name does not contain path to the file.
31
+ }
32
+
33
+ // Linux and Mac OS X behave differently (so can different Linux distributions).
34
+ // Process name can contain original path to the binary file or just its name.
35
+ return processName.endsWith('/platforma') || processName === 'platforma';
26
36
  } catch (_e) {
27
37
  return false;
28
38
  }
29
39
  }
30
40
 
31
- export function processStop(pid: number) {
32
- return process.kill(pid, 'SIGINT');
41
+ function getProcessName(pid: number): string {
42
+ try {
43
+ if (process.platform === 'win32') {
44
+ // Windows: use tasklist command
45
+ const output = execSync(`tasklist /FI "PID eq ${pid}" /FO CSV /NH`, { encoding: 'utf8' });
46
+ const lines = output.trim().split('\n');
47
+ if (lines.length > 0 && lines[0].includes(',')) {
48
+ const parts = lines[0].split(',');
49
+ if (parts.length >= 1) {
50
+ // Remove quotes and get the executable name
51
+ const exeName = parts[0].replace(/^"|"$/g, '').trim();
52
+ return exeName;
53
+ }
54
+ }
55
+ } else {
56
+ // Unix-like systems: use ps command
57
+ const output = execSync(`ps -p ${pid} -o comm=`, { encoding: 'utf8' });
58
+ const processName = output.trim();
59
+ return processName;
60
+ }
61
+ } catch (_error) {
62
+ // If we can't get the process name, return empty string
63
+ return '';
64
+ }
65
+ return '';
66
+ }
67
+
68
+ export function processStop(pid: number, force: boolean = false) {
69
+ return process.kill(pid, force ? 'SIGKILL' : 'SIGINT');
33
70
  }
34
71
 
35
72
  export async function processWaitStopped(pid: number, maxMs: number) {