@milaboratories/pl-deployments 1.7.2 → 1.8.1
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/index.js +9 -9
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +356 -334
- package/dist/index.mjs.map +1 -1
- package/dist/local/pl.d.ts +8 -0
- package/dist/local/pl.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/local/config.test.yaml +2 -0
- package/src/local/pl.test.ts +318 -4
- package/src/local/pl.ts +72 -19
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@milaboratories/pl-deployments",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"pl-version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
|
+
"pl-version": "1.31.2",
|
|
5
5
|
"description": "MiLaboratories Platforma Backend code service run wrapper",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": ">=20.16.0"
|
|
@@ -53,7 +53,7 @@
|
|
|
53
53
|
"undici": "~7.5.0",
|
|
54
54
|
"yaml": "^2.7.0",
|
|
55
55
|
"zod": "~3.23.8",
|
|
56
|
-
"@milaboratories/pl-config": "^1.4.
|
|
56
|
+
"@milaboratories/pl-config": "^1.4.6",
|
|
57
57
|
"@milaboratories/ts-helpers": "^1.1.5"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
package/src/local/pl.test.ts
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { test } from 'vitest';
|
|
2
|
-
import type { LocalPlOptions } from './pl';
|
|
1
|
+
import { test, expect } from 'vitest';
|
|
3
2
|
import { localPlatformaInit } from './pl';
|
|
4
3
|
import { ConsoleLoggerAdapter, sleep } from '@milaboratories/ts-helpers';
|
|
5
4
|
import * as fs from 'fs/promises';
|
|
6
5
|
import upath from 'upath';
|
|
7
|
-
import { processStop } from './process';
|
|
6
|
+
import { ProcessOptions, processStop } from './process';
|
|
8
7
|
import * as yaml from 'yaml';
|
|
9
8
|
import * as os from 'os';
|
|
9
|
+
import { mergeDefaultOps } from './pl';
|
|
10
|
+
import type { LocalPlOptions, LocalPlOptionsFull } from './pl';
|
|
11
|
+
import { plProcessOps } from './pl';
|
|
12
|
+
import { describe, it, beforeEach, afterEach } from 'vitest';
|
|
10
13
|
|
|
11
14
|
test(
|
|
12
15
|
'should start and stop platforma of the current version with hardcoded config',
|
|
@@ -37,7 +40,7 @@ test(
|
|
|
37
40
|
|
|
38
41
|
test(
|
|
39
42
|
'should close old platforma when starting a new one if the option is set',
|
|
40
|
-
{ timeout:
|
|
43
|
+
{ timeout: 35000 },
|
|
41
44
|
async ({ expect }) => {
|
|
42
45
|
const logger = new ConsoleLoggerAdapter();
|
|
43
46
|
|
|
@@ -127,3 +130,314 @@ async function prepareDirForTestConfig() {
|
|
|
127
130
|
|
|
128
131
|
return dir;
|
|
129
132
|
}
|
|
133
|
+
|
|
134
|
+
const mergeDefaultOpsCases: {
|
|
135
|
+
name: string;
|
|
136
|
+
input: {
|
|
137
|
+
ops: LocalPlOptions;
|
|
138
|
+
numCpu: number;
|
|
139
|
+
};
|
|
140
|
+
expected: LocalPlOptionsFull;
|
|
141
|
+
}[] = [
|
|
142
|
+
{
|
|
143
|
+
name: 'should set default values when minimal input is provided',
|
|
144
|
+
input: {
|
|
145
|
+
ops: {
|
|
146
|
+
workingDir: '/test',
|
|
147
|
+
config: 'config',
|
|
148
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
149
|
+
},
|
|
150
|
+
numCpu: 4,
|
|
151
|
+
},
|
|
152
|
+
expected: {
|
|
153
|
+
workingDir: '/test',
|
|
154
|
+
config: 'config',
|
|
155
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
156
|
+
spawnOptions: {
|
|
157
|
+
env: {
|
|
158
|
+
GOMAXPROCS: '4',
|
|
159
|
+
},
|
|
160
|
+
},
|
|
161
|
+
closeOld: true,
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
name: 'should override outermost options when provided',
|
|
166
|
+
input: {
|
|
167
|
+
ops: {
|
|
168
|
+
workingDir: '/test',
|
|
169
|
+
config: 'config',
|
|
170
|
+
// we provided plBinary and closeOld, they should appear in the result
|
|
171
|
+
plBinary: { type: 'Local', path: '/custom/binary' },
|
|
172
|
+
closeOld: false,
|
|
173
|
+
},
|
|
174
|
+
numCpu: 2,
|
|
175
|
+
},
|
|
176
|
+
expected: {
|
|
177
|
+
workingDir: '/test',
|
|
178
|
+
config: 'config',
|
|
179
|
+
spawnOptions: {
|
|
180
|
+
env: {
|
|
181
|
+
GOMAXPROCS: '2',
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
plBinary: { type: 'Local', path: '/custom/binary' },
|
|
185
|
+
closeOld: false,
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
name: 'should merge env variables when provided',
|
|
190
|
+
input: {
|
|
191
|
+
ops: {
|
|
192
|
+
workingDir: '/test',
|
|
193
|
+
config: 'config',
|
|
194
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
195
|
+
spawnOptions: {
|
|
196
|
+
env: {
|
|
197
|
+
NODE_ENV: 'test',
|
|
198
|
+
DEBUG: 'true',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
numCpu: 3,
|
|
203
|
+
},
|
|
204
|
+
expected: {
|
|
205
|
+
workingDir: '/test',
|
|
206
|
+
config: 'config',
|
|
207
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
208
|
+
spawnOptions: {
|
|
209
|
+
env: {
|
|
210
|
+
GOMAXPROCS: '3',
|
|
211
|
+
NODE_ENV: 'test',
|
|
212
|
+
DEBUG: 'true',
|
|
213
|
+
},
|
|
214
|
+
},
|
|
215
|
+
closeOld: true,
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
name: 'should override other spawnOptions properties',
|
|
220
|
+
input: {
|
|
221
|
+
ops: {
|
|
222
|
+
workingDir: '/test',
|
|
223
|
+
config: 'config',
|
|
224
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
225
|
+
spawnOptions: {
|
|
226
|
+
stdio: 'inherit',
|
|
227
|
+
detached: true,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
numCpu: 2,
|
|
231
|
+
},
|
|
232
|
+
expected: {
|
|
233
|
+
workingDir: '/test',
|
|
234
|
+
config: 'config',
|
|
235
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
236
|
+
spawnOptions: {
|
|
237
|
+
env: {
|
|
238
|
+
GOMAXPROCS: '2',
|
|
239
|
+
},
|
|
240
|
+
stdio: 'inherit',
|
|
241
|
+
detached: true,
|
|
242
|
+
},
|
|
243
|
+
closeOld: true,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
name: 'should handle complex case with multiple overrides',
|
|
248
|
+
input: {
|
|
249
|
+
ops: {
|
|
250
|
+
workingDir: '/test',
|
|
251
|
+
config: 'config',
|
|
252
|
+
plBinary: { type: 'Local', path: '/custom/binary' },
|
|
253
|
+
closeOld: false,
|
|
254
|
+
spawnOptions: {
|
|
255
|
+
env: {
|
|
256
|
+
NODE_ENV: 'production',
|
|
257
|
+
LOG_LEVEL: 'debug',
|
|
258
|
+
},
|
|
259
|
+
cwd: '/custom/dir',
|
|
260
|
+
windowsHide: false,
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
numCpu: 6,
|
|
264
|
+
},
|
|
265
|
+
expected: {
|
|
266
|
+
workingDir: '/test',
|
|
267
|
+
config: 'config',
|
|
268
|
+
plBinary: { type: 'Local', path: '/custom/binary' },
|
|
269
|
+
closeOld: false,
|
|
270
|
+
spawnOptions: {
|
|
271
|
+
env: {
|
|
272
|
+
GOMAXPROCS: '6',
|
|
273
|
+
NODE_ENV: 'production',
|
|
274
|
+
LOG_LEVEL: 'debug',
|
|
275
|
+
},
|
|
276
|
+
cwd: '/custom/dir',
|
|
277
|
+
windowsHide: false,
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
test.each(mergeDefaultOpsCases)('mergeDefaultOps: $name', ({ name, input, expected }) => {
|
|
284
|
+
const result = mergeDefaultOps(input.ops, input.numCpu);
|
|
285
|
+
|
|
286
|
+
expect(result).toEqual(expected);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const plProcessOpsCases: {
|
|
290
|
+
name: string;
|
|
291
|
+
input: {
|
|
292
|
+
binaryPath: string;
|
|
293
|
+
configPath: string;
|
|
294
|
+
ops: LocalPlOptionsFull;
|
|
295
|
+
workDir: string;
|
|
296
|
+
};
|
|
297
|
+
expected: ProcessOptions;
|
|
298
|
+
}[] = [
|
|
299
|
+
{
|
|
300
|
+
name: 'should set basic options with minimal input',
|
|
301
|
+
input: {
|
|
302
|
+
binaryPath: '/path/to/binary',
|
|
303
|
+
configPath: '/path/to/config.yaml',
|
|
304
|
+
ops: {
|
|
305
|
+
workingDir: '/work/dir',
|
|
306
|
+
config: 'config-content',
|
|
307
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
308
|
+
spawnOptions: {},
|
|
309
|
+
closeOld: true,
|
|
310
|
+
},
|
|
311
|
+
workDir: '/work/dir',
|
|
312
|
+
},
|
|
313
|
+
expected: {
|
|
314
|
+
cmd: '/path/to/binary',
|
|
315
|
+
args: ['--config', '/path/to/config.yaml'],
|
|
316
|
+
opts: {
|
|
317
|
+
env: {},
|
|
318
|
+
cwd: '/work/dir',
|
|
319
|
+
stdio: ['pipe', 'ignore', 'inherit'],
|
|
320
|
+
windowsHide: true,
|
|
321
|
+
},
|
|
322
|
+
},
|
|
323
|
+
},
|
|
324
|
+
{
|
|
325
|
+
name: 'should merge environment variables when provided',
|
|
326
|
+
input: {
|
|
327
|
+
binaryPath: '/path/to/binary',
|
|
328
|
+
configPath: '/config.yaml',
|
|
329
|
+
ops: {
|
|
330
|
+
workingDir: '/work',
|
|
331
|
+
config: 'content',
|
|
332
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
333
|
+
spawnOptions: {
|
|
334
|
+
env: {
|
|
335
|
+
DEBUG: 'true',
|
|
336
|
+
LOG_LEVEL: 'info',
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
closeOld: true,
|
|
340
|
+
},
|
|
341
|
+
workDir: '/work',
|
|
342
|
+
},
|
|
343
|
+
expected: {
|
|
344
|
+
cmd: '/path/to/binary',
|
|
345
|
+
args: ['--config', '/config.yaml'],
|
|
346
|
+
opts: {
|
|
347
|
+
env: {
|
|
348
|
+
DEBUG: 'true',
|
|
349
|
+
LOG_LEVEL: 'info',
|
|
350
|
+
},
|
|
351
|
+
cwd: '/work',
|
|
352
|
+
stdio: ['pipe', 'ignore', 'inherit'],
|
|
353
|
+
windowsHide: true,
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
name: 'should override spawn options when provided',
|
|
359
|
+
input: {
|
|
360
|
+
binaryPath: '/binary',
|
|
361
|
+
configPath: '/config.yaml',
|
|
362
|
+
ops: {
|
|
363
|
+
workingDir: '/work',
|
|
364
|
+
config: 'content',
|
|
365
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
366
|
+
spawnOptions: {
|
|
367
|
+
stdio: 'inherit',
|
|
368
|
+
detached: true,
|
|
369
|
+
shell: true,
|
|
370
|
+
},
|
|
371
|
+
closeOld: true,
|
|
372
|
+
},
|
|
373
|
+
workDir: '/work',
|
|
374
|
+
},
|
|
375
|
+
expected: {
|
|
376
|
+
cmd: '/binary',
|
|
377
|
+
args: ['--config', '/config.yaml'],
|
|
378
|
+
opts: {
|
|
379
|
+
env: {},
|
|
380
|
+
cwd: '/work',
|
|
381
|
+
stdio: 'inherit',
|
|
382
|
+
detached: true,
|
|
383
|
+
shell: true,
|
|
384
|
+
windowsHide: true,
|
|
385
|
+
},
|
|
386
|
+
},
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
name: 'should handle complex case with multiple options',
|
|
390
|
+
input: {
|
|
391
|
+
binaryPath: '/bin/platforma',
|
|
392
|
+
configPath: '/etc/platforma/config.yaml',
|
|
393
|
+
ops: {
|
|
394
|
+
workingDir: '/var/platforma',
|
|
395
|
+
config: 'yaml content',
|
|
396
|
+
plBinary: { type: 'Download', version: '1.29.2' },
|
|
397
|
+
spawnOptions: {
|
|
398
|
+
env: {
|
|
399
|
+
PL_DEBUG: 'true',
|
|
400
|
+
PL_MODE: 'development',
|
|
401
|
+
GOMAXPROCS: '4',
|
|
402
|
+
},
|
|
403
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
404
|
+
detached: false,
|
|
405
|
+
windowsHide: false,
|
|
406
|
+
uid: 1000,
|
|
407
|
+
gid: 1000,
|
|
408
|
+
},
|
|
409
|
+
closeOld: true,
|
|
410
|
+
},
|
|
411
|
+
workDir: '/var/platforma/runtime',
|
|
412
|
+
},
|
|
413
|
+
expected: {
|
|
414
|
+
cmd: '/bin/platforma',
|
|
415
|
+
args: ['--config', '/etc/platforma/config.yaml'],
|
|
416
|
+
opts: {
|
|
417
|
+
env: {
|
|
418
|
+
PL_DEBUG: 'true',
|
|
419
|
+
PL_MODE: 'development',
|
|
420
|
+
GOMAXPROCS: '4',
|
|
421
|
+
},
|
|
422
|
+
cwd: '/var/platforma/runtime',
|
|
423
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
424
|
+
detached: false,
|
|
425
|
+
windowsHide: false,
|
|
426
|
+
uid: 1000,
|
|
427
|
+
gid: 1000,
|
|
428
|
+
},
|
|
429
|
+
},
|
|
430
|
+
},
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
test.each(plProcessOpsCases)('plProcessOps: $name', ({ name, input, expected }) => {
|
|
434
|
+
const result = plProcessOps(
|
|
435
|
+
input.binaryPath,
|
|
436
|
+
input.configPath,
|
|
437
|
+
input.ops,
|
|
438
|
+
input.workDir,
|
|
439
|
+
{},
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
expect(result).toEqual(expected);
|
|
443
|
+
});
|
package/src/local/pl.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { withTrace } from './trace';
|
|
|
17
17
|
import upath from 'upath';
|
|
18
18
|
import fsp from 'node:fs/promises';
|
|
19
19
|
import type { Required } from 'utility-types';
|
|
20
|
+
import * as os from 'node:os';
|
|
20
21
|
|
|
21
22
|
export const LocalConfigYaml = 'config-local.yaml';
|
|
22
23
|
|
|
@@ -134,19 +135,18 @@ export type LocalPlOptions = {
|
|
|
134
135
|
readonly onCloseAndErrorNoStop?: (pl: LocalPl) => Promise<void>;
|
|
135
136
|
};
|
|
136
137
|
|
|
137
|
-
type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;
|
|
138
|
+
export type LocalPlOptionsFull = Required<LocalPlOptions, 'plBinary' | 'spawnOptions' | 'closeOld'>;
|
|
138
139
|
|
|
139
140
|
/**
|
|
140
141
|
* Starts pl-core, if the option was provided downloads a binary, reads license environments etc.
|
|
141
142
|
*/
|
|
142
143
|
export async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions): Promise<LocalPl> {
|
|
143
144
|
// filling-in default values
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
} satisfies LocalPlOptionsFull;
|
|
145
|
+
|
|
146
|
+
// Backend could consume a lot of CPU power,
|
|
147
|
+
// we want to keep at least a couple for UI and other apps to work.
|
|
148
|
+
const numCpu = Math.max(os.cpus().length - 2, 1);
|
|
149
|
+
const ops = mergeDefaultOps(_ops, numCpu);
|
|
150
150
|
|
|
151
151
|
return await withTrace(logger, async (trace, t) => {
|
|
152
152
|
trace('startOptions', { ...ops, config: 'too wordy' });
|
|
@@ -164,20 +164,9 @@ export async function localPlatformaInit(logger: MiLogger, _ops: LocalPlOptions)
|
|
|
164
164
|
|
|
165
165
|
const plBinPath = upath.join(workDir, 'binaries');
|
|
166
166
|
const baseBinaryPath = await resolveLocalPlBinaryPath(logger, plBinPath, ops.plBinary);
|
|
167
|
-
|
|
168
167
|
const binaryPath = trace('binaryPath', upath.join('binaries', baseBinaryPath));
|
|
169
168
|
|
|
170
|
-
const processOpts
|
|
171
|
-
cmd: binaryPath,
|
|
172
|
-
args: ['--config', configPath],
|
|
173
|
-
opts: {
|
|
174
|
-
env: { ...process.env },
|
|
175
|
-
cwd: workDir,
|
|
176
|
-
stdio: ['pipe', 'ignore', 'inherit'],
|
|
177
|
-
windowsHide: true, // hide a terminal on Windows
|
|
178
|
-
...ops.spawnOptions,
|
|
179
|
-
},
|
|
180
|
-
};
|
|
169
|
+
const processOpts = plProcessOps(binaryPath, configPath, ops, workDir, process.env);
|
|
181
170
|
trace('processOpts', {
|
|
182
171
|
cmd: processOpts.cmd,
|
|
183
172
|
args: processOpts.args,
|
|
@@ -220,3 +209,67 @@ async function localPlatformaReadPidAndStop(
|
|
|
220
209
|
return t;
|
|
221
210
|
});
|
|
222
211
|
}
|
|
212
|
+
|
|
213
|
+
/** Gets default options for the whole init process
|
|
214
|
+
* and overrides them with the provided options. */
|
|
215
|
+
export function mergeDefaultOps(ops: LocalPlOptions, numCpu: number): LocalPlOptionsFull {
|
|
216
|
+
const result: {
|
|
217
|
+
plBinary: PlBinarySource;
|
|
218
|
+
spawnOptions: SpawnOptions;
|
|
219
|
+
closeOld: boolean;
|
|
220
|
+
} = {
|
|
221
|
+
plBinary: newDefaultPlBinarySource(),
|
|
222
|
+
spawnOptions: {
|
|
223
|
+
env: {
|
|
224
|
+
GOMAXPROCS: String(numCpu),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
closeOld: true,
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
if (ops.spawnOptions?.env) {
|
|
231
|
+
result.spawnOptions.env = { ...result.spawnOptions.env, ...ops.spawnOptions.env };
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
if (ops.spawnOptions) {
|
|
235
|
+
const withoutEnv = { ...ops.spawnOptions };
|
|
236
|
+
delete withoutEnv['env'];
|
|
237
|
+
result.spawnOptions = { ...result.spawnOptions, ...withoutEnv };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const withoutSpawnOps = { ...ops };
|
|
241
|
+
delete withoutSpawnOps['spawnOptions'];
|
|
242
|
+
|
|
243
|
+
return { ...result, ...withoutSpawnOps };
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/** Gets default options for a platforma local binary
|
|
247
|
+
* and overrides them with the provided options. */
|
|
248
|
+
export function plProcessOps(
|
|
249
|
+
binaryPath: any,
|
|
250
|
+
configPath: string,
|
|
251
|
+
ops: LocalPlOptionsFull,
|
|
252
|
+
workDir: string,
|
|
253
|
+
defaultEnv: Record<string, string | undefined>,
|
|
254
|
+
): ProcessOptions {
|
|
255
|
+
const result: ProcessOptions = {
|
|
256
|
+
cmd: binaryPath,
|
|
257
|
+
args: ['--config', configPath],
|
|
258
|
+
opts: {
|
|
259
|
+
env: { ...defaultEnv },
|
|
260
|
+
cwd: workDir,
|
|
261
|
+
stdio: ['pipe', 'ignore', 'inherit'],
|
|
262
|
+
windowsHide: true, // hide a terminal on Windows
|
|
263
|
+
},
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
if (ops.spawnOptions?.env) {
|
|
267
|
+
result.opts.env = { ...result.opts.env, ...ops.spawnOptions.env };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const withoutEnv = { ...ops.spawnOptions };
|
|
271
|
+
delete withoutEnv['env'];
|
|
272
|
+
result.opts = { ...result.opts, ...withoutEnv };
|
|
273
|
+
|
|
274
|
+
return result;
|
|
275
|
+
}
|