unitup 0.1.4 → 0.2.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/CHANGELOG.md +5 -0
- package/README.md +89 -8
- package/biome.json +0 -1
- package/docs/index.html +188 -234
- package/index.d.ts +336 -445
- package/package.json +9 -3
- package/src/cli.js +187 -101
- package/src/doctor.js +123 -56
- package/src/errors.js +131 -0
- package/src/index.js +132 -59
- package/src/logs.js +148 -0
- package/src/platform/base.js +211 -0
- package/src/platform/index.js +43 -0
- package/src/platform/linux.js +155 -0
- package/src/platform/macos.js +723 -0
- package/src/platform/windows-host.js +246 -0
- package/src/platform/windows.js +495 -0
- package/src/runtimes/bun.js +4 -3
- package/src/runtimes/common.js +24 -6
- package/src/runtimes/deno.js +4 -3
- package/src/runtimes/elixir.js +4 -3
- package/src/runtimes/go.js +4 -3
- package/src/runtimes/index.js +14 -11
- package/src/runtimes/native.js +4 -5
- package/src/runtimes/node.js +4 -3
- package/src/runtimes/php.js +4 -3
- package/src/runtimes/python.js +4 -3
- package/src/runtimes/ruby.js +4 -3
- package/src/runtimes/shell.js +4 -3
- package/src/schedule.js +31 -24
- package/src/service/config.js +55 -0
- package/src/service/manager.js +233 -0
- package/src/service/normalize.js +216 -0
- package/src/systemd.js +84 -38
- package/src/unit.js +12 -18
- package/src/utils.js +32 -18
- package/tsconfig.json +1 -1
- package/types-test/index.ts +158 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unitup",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Minimal, zero-dependency
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Minimal, zero-dependency cross-platform native service manager (systemd, launchd, Windows Services) for any runtime, script, or executable",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"unitup": "bin/unitup.js"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"test": "node --test
|
|
12
|
+
"test": "node --test",
|
|
13
13
|
"lint": "biome check .",
|
|
14
14
|
"format": "biome format --write .",
|
|
15
15
|
"check": "biome check ."
|
|
@@ -20,9 +20,15 @@
|
|
|
20
20
|
},
|
|
21
21
|
"keywords": [
|
|
22
22
|
"systemd",
|
|
23
|
+
"launchd",
|
|
24
|
+
"windows-service",
|
|
25
|
+
"macos",
|
|
26
|
+
"windows",
|
|
27
|
+
"cross-platform",
|
|
23
28
|
"service",
|
|
24
29
|
"user-service",
|
|
25
30
|
"systemctl",
|
|
31
|
+
"launchctl",
|
|
26
32
|
"process-manager",
|
|
27
33
|
"daemon-less",
|
|
28
34
|
"multi-runtime",
|
package/src/cli.js
CHANGED
|
@@ -1,46 +1,48 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
1
|
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
3
|
import readline from 'node:readline';
|
|
4
4
|
import { runDoctor } from './doctor.js';
|
|
5
|
+
import { getAdapter } from './platform/index.js';
|
|
6
|
+
import {
|
|
7
|
+
createSchedule,
|
|
8
|
+
disableSchedule,
|
|
9
|
+
enableSchedule,
|
|
10
|
+
getScheduleStatus,
|
|
11
|
+
listSchedules,
|
|
12
|
+
removeSchedule,
|
|
13
|
+
runSchedule
|
|
14
|
+
} from './schedule.js';
|
|
15
|
+
import { defaultManager } from './service/manager.js';
|
|
5
16
|
import {
|
|
6
17
|
addService,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
18
|
+
executeJournalctlMaintenance,
|
|
19
|
+
findNodeExecutable,
|
|
20
|
+
getAllServicesMemoryUsage,
|
|
21
|
+
getServiceFailures,
|
|
22
|
+
getServiceMemoryUsage,
|
|
11
23
|
getServiceStatus,
|
|
12
24
|
getServiceStatusRaw,
|
|
13
|
-
listServices,
|
|
14
|
-
runJournalctlLogs,
|
|
15
|
-
isLinux,
|
|
16
|
-
isSystemctlAvailable,
|
|
17
|
-
findNodeExecutable,
|
|
18
25
|
getServicesByGroup,
|
|
19
26
|
inspectService,
|
|
20
|
-
|
|
27
|
+
isLinux,
|
|
28
|
+
isSystemctlAvailable,
|
|
29
|
+
listServices,
|
|
30
|
+
removeService,
|
|
31
|
+
restartService,
|
|
32
|
+
runJournalctlLogs,
|
|
21
33
|
setServiceLimits,
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
getAllServicesMemoryUsage
|
|
34
|
+
startService,
|
|
35
|
+
stopService
|
|
25
36
|
} from './systemd.js';
|
|
26
37
|
import {
|
|
27
|
-
|
|
28
|
-
listSchedules,
|
|
29
|
-
getScheduleStatus,
|
|
30
|
-
runSchedule,
|
|
31
|
-
enableSchedule,
|
|
32
|
-
disableSchedule,
|
|
33
|
-
removeSchedule
|
|
34
|
-
} from './schedule.js';
|
|
35
|
-
import {
|
|
36
|
-
sanitizeServiceName,
|
|
38
|
+
findProjectConfig,
|
|
37
39
|
formatTable,
|
|
38
40
|
readGlobalConfig,
|
|
39
|
-
saveGlobalConfig,
|
|
40
|
-
validateMemorySize,
|
|
41
|
-
findProjectConfig,
|
|
42
41
|
readProjectConfig,
|
|
43
|
-
|
|
42
|
+
sanitizeServiceName,
|
|
43
|
+
saveGlobalConfig,
|
|
44
|
+
saveProjectConfig,
|
|
45
|
+
validateMemorySize
|
|
44
46
|
} from './utils.js';
|
|
45
47
|
|
|
46
48
|
/**
|
|
@@ -75,6 +77,8 @@ export function parseArgs(argv) {
|
|
|
75
77
|
help: false,
|
|
76
78
|
version: false,
|
|
77
79
|
force: false,
|
|
80
|
+
system: false,
|
|
81
|
+
verbose: false,
|
|
78
82
|
memoryHigh: '',
|
|
79
83
|
memoryMax: '',
|
|
80
84
|
memorySwapMax: '',
|
|
@@ -112,6 +116,12 @@ export function parseArgs(argv) {
|
|
|
112
116
|
} else if (arg === '-v' || arg === '--version') {
|
|
113
117
|
result.flags.version = true;
|
|
114
118
|
i++;
|
|
119
|
+
} else if (arg === '--verbose') {
|
|
120
|
+
result.flags.verbose = true;
|
|
121
|
+
i++;
|
|
122
|
+
} else if (arg === '--system') {
|
|
123
|
+
result.flags.system = true;
|
|
124
|
+
i++;
|
|
115
125
|
} else if (arg === '--runtime') {
|
|
116
126
|
result.flags.runtime = argv[i + 1] || '';
|
|
117
127
|
i += 2;
|
|
@@ -191,10 +201,10 @@ export function parseArgs(argv) {
|
|
|
191
201
|
result.flags.args.push(arg.slice(7));
|
|
192
202
|
i++;
|
|
193
203
|
} else if (arg === '--lines' || arg === '-n') {
|
|
194
|
-
result.flags.lines = parseInt(argv[i + 1] || '100', 10);
|
|
204
|
+
result.flags.lines = Number.parseInt(argv[i + 1] || '100', 10);
|
|
195
205
|
i += 2;
|
|
196
206
|
} else if (arg.startsWith('--lines=')) {
|
|
197
|
-
result.flags.lines = parseInt(arg.slice(8), 10);
|
|
207
|
+
result.flags.lines = Number.parseInt(arg.slice(8), 10);
|
|
198
208
|
i++;
|
|
199
209
|
} else if (arg === '--start') {
|
|
200
210
|
result.flags.start = true;
|
|
@@ -352,7 +362,6 @@ export function parseArgs(argv) {
|
|
|
352
362
|
}
|
|
353
363
|
i++;
|
|
354
364
|
} else {
|
|
355
|
-
// Unknown flag, treat as positional or ignore
|
|
356
365
|
i++;
|
|
357
366
|
}
|
|
358
367
|
}
|
|
@@ -362,29 +371,33 @@ export function parseArgs(argv) {
|
|
|
362
371
|
|
|
363
372
|
export function printHelp() {
|
|
364
373
|
console.log(`
|
|
365
|
-
unitup - Minimal systemd user service wrapper
|
|
374
|
+
unitup - Minimal systemd user service wrapper & cross-platform service manager (Linux, macOS, Windows)
|
|
366
375
|
|
|
367
376
|
Usage:
|
|
368
377
|
unitup doctor Run system readiness and runtime check
|
|
369
378
|
unitup init [script] Create local project config file (unitup.config.json)
|
|
370
|
-
unitup add <script>
|
|
379
|
+
unitup install / add <script> Install script or executable as native service (--dry-run)
|
|
371
380
|
unitup start <name|@group> Start a service (--enable to enable on boot)
|
|
372
381
|
unitup stop <name|@group> Stop a service
|
|
373
382
|
unitup restart <name|@group> Restart a service
|
|
374
|
-
unitup status <name|@group> Show status summary (--raw
|
|
375
|
-
unitup
|
|
383
|
+
unitup status <name|@group> Show status summary (--raw, --verbose)
|
|
384
|
+
unitup enable <name> Enable service on startup/boot
|
|
385
|
+
unitup disable <name> Disable service on startup/boot
|
|
386
|
+
unitup logs <name|@group> Show service logs (-f/--follow, -n/--lines N, -c/--cat)
|
|
376
387
|
unitup inspect <name> View detailed app configuration and status
|
|
377
388
|
unitup failures List all failed services with exit code & restarts
|
|
378
|
-
unitup remove
|
|
389
|
+
unitup uninstall / remove Stop, disable and delete a service (--force)
|
|
379
390
|
unitup list / unitup ls List all services (--group <group>)
|
|
380
391
|
unitup memory / unitup top Show memory usage for all apps or a specific app
|
|
381
392
|
unitup config Manage global configuration (--default-memory 1G)
|
|
382
393
|
|
|
383
394
|
Config Options:
|
|
384
395
|
--config <path> Path to custom project config file (defaults to unitup.config.json)
|
|
396
|
+
--system Install as system-wide service (LaunchDaemons on macOS / root systemd)
|
|
397
|
+
--dry-run Display generated service configuration without installing
|
|
385
398
|
|
|
386
|
-
Schedule Commands:
|
|
387
|
-
unitup schedule <script> [options] Create a
|
|
399
|
+
Schedule Commands (systemd):
|
|
400
|
+
unitup schedule <script> [options] Create a timer schedule
|
|
388
401
|
unitup schedules / unitup timers List all schedule timers
|
|
389
402
|
unitup schedule-status <name> Show detailed schedule timer status
|
|
390
403
|
unitup schedule-run <name> Manually run schedule service unit once
|
|
@@ -398,21 +411,13 @@ Memory Options:
|
|
|
398
411
|
--memory-max <size> Hard memory limit (e.g. 512M, 1G)
|
|
399
412
|
--default-memory [sz] Apply default memory limit (defaults to 1G)
|
|
400
413
|
|
|
401
|
-
Schedule Options:
|
|
402
|
-
--every <duration> Execution interval (e.g., 30s, 10m, 2h, 1d)
|
|
403
|
-
--calendar <expr> Calendar expression (e.g., daily, "Mon..Fri 09:00")
|
|
404
|
-
--on-boot <duration> Delay relative to system boot (e.g., 5m)
|
|
405
|
-
--on-active <duration> Delay relative to timer activation
|
|
406
|
-
--random-delay <dur> Random delay to prevent stampedes (e.g., 10m)
|
|
407
|
-
--persistent Run immediately if a scheduled run was missed
|
|
408
|
-
--start Enable and start timer immediately after creation
|
|
409
|
-
|
|
410
414
|
Examples:
|
|
411
|
-
unitup
|
|
412
|
-
unitup
|
|
413
|
-
unitup
|
|
414
|
-
unitup
|
|
415
|
-
unitup
|
|
415
|
+
unitup install server.js --name api --start
|
|
416
|
+
unitup install ./app.js --dry-run
|
|
417
|
+
unitup start api
|
|
418
|
+
unitup status api --verbose
|
|
419
|
+
unitup logs api --follow
|
|
420
|
+
unitup uninstall api
|
|
416
421
|
`);
|
|
417
422
|
}
|
|
418
423
|
|
|
@@ -468,12 +473,12 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
468
473
|
if (fs.existsSync(targetConfigPath) && !flags.force) {
|
|
469
474
|
throw new Error(
|
|
470
475
|
`Configuration file "${path.basename(targetConfigPath)}" already exists at ${targetConfigPath}.\n` +
|
|
471
|
-
|
|
476
|
+
`Use --force (-f) to overwrite.`
|
|
472
477
|
);
|
|
473
478
|
}
|
|
474
479
|
|
|
475
480
|
const scriptArg = positionals[0];
|
|
476
|
-
|
|
481
|
+
const scriptPath = scriptArg || '';
|
|
477
482
|
let name = flags.name;
|
|
478
483
|
if (!name) {
|
|
479
484
|
if (scriptPath) {
|
|
@@ -521,6 +526,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
521
526
|
break;
|
|
522
527
|
}
|
|
523
528
|
|
|
529
|
+
case 'install':
|
|
524
530
|
case 'add': {
|
|
525
531
|
const targetCwd = flags.cwd ? path.resolve(process.cwd(), flags.cwd) : process.cwd();
|
|
526
532
|
const configPath = flags.config ? path.resolve(targetCwd, flags.config) : findProjectConfig(targetCwd);
|
|
@@ -528,18 +534,23 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
528
534
|
|
|
529
535
|
const scriptArg = positionals[0] || (projectCfg ? projectCfg.script : undefined);
|
|
530
536
|
let name = flags.name || (projectCfg ? projectCfg.name : undefined);
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
537
|
+
const cmdFlag = flags.command || (projectCfg ? projectCfg.command : undefined);
|
|
538
|
+
const runtimeFlag = flags.runtime || (projectCfg ? projectCfg.runtime : undefined);
|
|
539
|
+
const groupFlag = flags.group || (projectCfg ? projectCfg.group : undefined) || 'default';
|
|
540
|
+
const restartFlag =
|
|
541
|
+
flags.restart !== 'on-failure' ? flags.restart : (projectCfg && projectCfg.restart) || 'on-failure';
|
|
542
|
+
const envFileFlag = flags.envFile || (projectCfg ? projectCfg.envFile : undefined);
|
|
543
|
+
const argsFlag = flags.args && flags.args.length > 0 ? flags.args : (projectCfg && projectCfg.args) || [];
|
|
544
|
+
const runtimeArgsFlag =
|
|
545
|
+
flags.runtimeArgs && flags.runtimeArgs.length > 0
|
|
546
|
+
? flags.runtimeArgs
|
|
547
|
+
: (projectCfg && projectCfg.runtimeArgs) || [];
|
|
548
|
+
const memoryHighFlag = flags.memoryHigh || projectCfg?.resources?.memoryHigh || projectCfg?.memoryHigh || '';
|
|
549
|
+
const memoryMaxFlag = flags.memoryMax || projectCfg?.resources?.memoryMax || projectCfg?.memoryMax || '';
|
|
550
|
+
const memorySwapMaxFlag =
|
|
551
|
+
flags.memorySwapMax || projectCfg?.resources?.memorySwapMax || projectCfg?.memorySwapMax || '';
|
|
552
|
+
|
|
553
|
+
const absScriptPath = scriptArg ? path.resolve(targetCwd, scriptArg) : undefined;
|
|
543
554
|
|
|
544
555
|
if (flags.node) {
|
|
545
556
|
const resolvedNode = await findNodeExecutable(flags.node);
|
|
@@ -573,7 +584,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
573
584
|
}
|
|
574
585
|
}
|
|
575
586
|
|
|
576
|
-
const envObj =
|
|
587
|
+
const envObj = projectCfg && projectCfg.env && typeof projectCfg.env === 'object' ? { ...projectCfg.env } : {};
|
|
577
588
|
for (const e of flags.env) {
|
|
578
589
|
const idx = e.indexOf('=');
|
|
579
590
|
if (idx !== -1) {
|
|
@@ -583,7 +594,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
583
594
|
}
|
|
584
595
|
}
|
|
585
596
|
|
|
586
|
-
const
|
|
597
|
+
const installOptions = {
|
|
587
598
|
name,
|
|
588
599
|
group: groupFlag,
|
|
589
600
|
script: absScriptPath,
|
|
@@ -597,14 +608,27 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
597
608
|
restart: restartFlag,
|
|
598
609
|
args: argsFlag,
|
|
599
610
|
start: flags.start,
|
|
611
|
+
system: flags.system,
|
|
600
612
|
memoryHigh: memoryHighFlag,
|
|
601
613
|
memoryMax: memoryMaxFlag,
|
|
602
614
|
memorySwapMax: memorySwapMaxFlag,
|
|
603
615
|
defaultMemory: flags.defaultMemory,
|
|
604
616
|
force: flags.force
|
|
605
|
-
}
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
if (flags.dryRun) {
|
|
620
|
+
const generated = await defaultManager.generate(installOptions);
|
|
621
|
+
if (typeof generated === 'string') {
|
|
622
|
+
console.log(generated);
|
|
623
|
+
} else {
|
|
624
|
+
console.log(JSON.stringify(generated, null, 2));
|
|
625
|
+
}
|
|
626
|
+
break;
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
const res = await defaultManager.install(installOptions);
|
|
606
630
|
|
|
607
|
-
console.log(`✓ Service "${res.name}" created at ${res.unitPath}`);
|
|
631
|
+
console.log(`✓ Service "${res.name}" created at ${res.unitPath || res.serviceName || res.name}`);
|
|
608
632
|
if (flags.start) {
|
|
609
633
|
console.log(`✓ Service "${res.name}" enabled and started.`);
|
|
610
634
|
} else {
|
|
@@ -638,7 +662,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
638
662
|
}
|
|
639
663
|
const targetNames = await resolveTargetNames(nameArg);
|
|
640
664
|
for (const name of targetNames) {
|
|
641
|
-
await
|
|
665
|
+
await defaultManager.start(name, { enable: flags.enable, system: flags.system });
|
|
642
666
|
console.log(`✓ Service "${sanitizeServiceName(name)}" ${flags.enable ? 'enabled & ' : ''}started.`);
|
|
643
667
|
}
|
|
644
668
|
break;
|
|
@@ -651,7 +675,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
651
675
|
}
|
|
652
676
|
const targetNames = await resolveTargetNames(nameArg);
|
|
653
677
|
for (const name of targetNames) {
|
|
654
|
-
await
|
|
678
|
+
await defaultManager.stop(name, { system: flags.system });
|
|
655
679
|
console.log(`✓ Service "${sanitizeServiceName(name)}" stopped.`);
|
|
656
680
|
}
|
|
657
681
|
break;
|
|
@@ -660,20 +684,51 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
660
684
|
case 'restart': {
|
|
661
685
|
const nameArg = positionals[0];
|
|
662
686
|
if (!nameArg) {
|
|
663
|
-
throw new Error(
|
|
687
|
+
throw new Error(
|
|
688
|
+
'Service name or @group is required.\nExample: unitup restart api or unitup restart @myproject'
|
|
689
|
+
);
|
|
664
690
|
}
|
|
665
691
|
const targetNames = await resolveTargetNames(nameArg);
|
|
666
692
|
for (const name of targetNames) {
|
|
667
|
-
await
|
|
693
|
+
await defaultManager.restart(name, { system: flags.system });
|
|
668
694
|
console.log(`✓ Service "${sanitizeServiceName(name)}" restarted.`);
|
|
669
695
|
}
|
|
670
696
|
break;
|
|
671
697
|
}
|
|
672
698
|
|
|
699
|
+
case 'enable': {
|
|
700
|
+
const nameArg = positionals[0];
|
|
701
|
+
if (!nameArg) {
|
|
702
|
+
throw new Error('Service name or @group is required.\nExample: unitup enable api');
|
|
703
|
+
}
|
|
704
|
+
const targetNames = await resolveTargetNames(nameArg);
|
|
705
|
+
for (const name of targetNames) {
|
|
706
|
+
await defaultManager.enable(name, { system: flags.system });
|
|
707
|
+
console.log(`✓ Service "${sanitizeServiceName(name)}" enabled on startup.`);
|
|
708
|
+
}
|
|
709
|
+
break;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
case 'disable': {
|
|
713
|
+
const nameArg = positionals[0];
|
|
714
|
+
if (!nameArg) {
|
|
715
|
+
throw new Error('Service name or @group is required.\nExample: unitup disable api');
|
|
716
|
+
}
|
|
717
|
+
const targetNames = await resolveTargetNames(nameArg);
|
|
718
|
+
for (const name of targetNames) {
|
|
719
|
+
await defaultManager.disable(name, { system: flags.system });
|
|
720
|
+
console.log(`✓ Service "${sanitizeServiceName(name)}" disabled on startup.`);
|
|
721
|
+
}
|
|
722
|
+
break;
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
case 'ps':
|
|
673
726
|
case 'status': {
|
|
674
727
|
const nameArg = positionals[0];
|
|
675
728
|
if (!nameArg) {
|
|
676
|
-
throw new Error(
|
|
729
|
+
throw new Error(
|
|
730
|
+
'Service name or @group is required.\nExample: unitup status api or unitup status @myproject'
|
|
731
|
+
);
|
|
677
732
|
}
|
|
678
733
|
|
|
679
734
|
const targetNames = await resolveTargetNames(nameArg);
|
|
@@ -682,25 +737,31 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
682
737
|
const raw = await getServiceStatusRaw(name);
|
|
683
738
|
console.log(raw);
|
|
684
739
|
} else {
|
|
685
|
-
const status = await
|
|
740
|
+
const status = await defaultManager.status(name, { system: flags.system });
|
|
686
741
|
console.log(`${status.name}\n`);
|
|
687
|
-
console.log(`Status: ${status.status}`);
|
|
742
|
+
console.log(`Status: ${status.status || status.state}`);
|
|
688
743
|
console.log(`PID: ${status.pid}`);
|
|
689
744
|
console.log(`Started: ${status.started}`);
|
|
690
745
|
console.log(`Restarts: ${status.restarts}`);
|
|
691
746
|
console.log(`Command: ${status.command}`);
|
|
692
747
|
console.log(`Arguments: ${status.arguments}`);
|
|
693
748
|
console.log(`Working directory: ${status.cwd}`);
|
|
694
|
-
console.log(`Memory: ${status.memory}`);
|
|
695
|
-
console.log(`Memory Peak: ${status.memoryPeak}`);
|
|
696
|
-
console.log(`Memory High: ${status.memoryHigh}`);
|
|
697
|
-
console.log(`Memory Max: ${status.memoryMax}`);
|
|
698
|
-
console.log(`Swap Max: ${status.memorySwapMax}`);
|
|
749
|
+
if (status.memory) console.log(`Memory: ${status.memory}`);
|
|
750
|
+
if (status.memoryPeak) console.log(`Memory Peak: ${status.memoryPeak}`);
|
|
751
|
+
if (status.memoryHigh) console.log(`Memory High: ${status.memoryHigh}`);
|
|
752
|
+
if (status.memoryMax) console.log(`Memory Max: ${status.memoryMax}`);
|
|
753
|
+
if (status.memorySwapMax) console.log(`Swap Max: ${status.memorySwapMax}`);
|
|
754
|
+
if (flags.verbose) {
|
|
755
|
+
console.log(`Platform: ${status.platform}`);
|
|
756
|
+
console.log(`Manager: ${status.manager}`);
|
|
757
|
+
if (status.unitPath) console.log(`Unit Path: ${status.unitPath}`);
|
|
758
|
+
}
|
|
699
759
|
}
|
|
700
760
|
}
|
|
701
761
|
break;
|
|
702
762
|
}
|
|
703
763
|
|
|
764
|
+
case 'log':
|
|
704
765
|
case 'logs': {
|
|
705
766
|
const nameArg = positionals[0];
|
|
706
767
|
if (!nameArg) {
|
|
@@ -712,7 +773,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
712
773
|
if (targetNames.length > 1) {
|
|
713
774
|
console.log(`=== Logs for ${name} ===`);
|
|
714
775
|
}
|
|
715
|
-
const output = await
|
|
776
|
+
const output = await defaultManager.logs(name, {
|
|
716
777
|
follow: flags.follow,
|
|
717
778
|
lines: flags.lines,
|
|
718
779
|
cat: flags.cat,
|
|
@@ -723,9 +784,24 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
723
784
|
grep: flags.grep,
|
|
724
785
|
boot: flags.boot,
|
|
725
786
|
json: flags.json,
|
|
726
|
-
diskUsage: flags.diskUsage
|
|
787
|
+
diskUsage: flags.diskUsage,
|
|
788
|
+
system: flags.system
|
|
727
789
|
});
|
|
728
|
-
if (typeof output === '
|
|
790
|
+
if (output && typeof output.on === 'function') {
|
|
791
|
+
await new Promise((resolve) => {
|
|
792
|
+
const cleanup = () => {
|
|
793
|
+
if (output.stop) output.stop();
|
|
794
|
+
resolve();
|
|
795
|
+
};
|
|
796
|
+
process.on('SIGINT', cleanup);
|
|
797
|
+
process.on('SIGTERM', cleanup);
|
|
798
|
+
output.on('close', resolve);
|
|
799
|
+
output.on('error', (err) => {
|
|
800
|
+
console.error(err.message);
|
|
801
|
+
cleanup();
|
|
802
|
+
});
|
|
803
|
+
});
|
|
804
|
+
} else if (typeof output === 'string') {
|
|
729
805
|
console.log(output);
|
|
730
806
|
}
|
|
731
807
|
}
|
|
@@ -735,16 +811,21 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
735
811
|
case 'journal': {
|
|
736
812
|
const action = positionals[0];
|
|
737
813
|
if (!action) {
|
|
738
|
-
throw new Error(
|
|
814
|
+
throw new Error(
|
|
815
|
+
'Journal action is required (disk-usage, rotate, vacuum).\nExample: unitup journal vacuum --size 500M'
|
|
816
|
+
);
|
|
739
817
|
}
|
|
740
818
|
|
|
741
819
|
if (action === 'vacuum' && !flags.yes && !flags.dryRun) {
|
|
742
820
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
743
821
|
const answer = await new Promise((resolve) => {
|
|
744
|
-
rl.question(
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
822
|
+
rl.question(
|
|
823
|
+
'This operation affects archived journal logs system-wide,\nnot only services managed by unitup.\n\nContinue? [y/N] ',
|
|
824
|
+
(ans) => {
|
|
825
|
+
rl.close();
|
|
826
|
+
resolve(ans.trim());
|
|
827
|
+
}
|
|
828
|
+
);
|
|
748
829
|
});
|
|
749
830
|
if (answer.toLowerCase() !== 'y' && answer.toLowerCase() !== 'yes') {
|
|
750
831
|
console.log('Aborted.');
|
|
@@ -763,14 +844,19 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
763
844
|
break;
|
|
764
845
|
}
|
|
765
846
|
|
|
847
|
+
case 'uninstall':
|
|
848
|
+
case 'rm':
|
|
849
|
+
case 'delete':
|
|
766
850
|
case 'remove': {
|
|
767
851
|
const nameArg = positionals[0];
|
|
768
852
|
if (!nameArg) {
|
|
769
|
-
throw new Error(
|
|
853
|
+
throw new Error(
|
|
854
|
+
'Service name or @group is required.\nExample: unitup remove api or unitup remove @myproject'
|
|
855
|
+
);
|
|
770
856
|
}
|
|
771
857
|
const targetNames = await resolveTargetNames(nameArg);
|
|
772
858
|
for (const name of targetNames) {
|
|
773
|
-
await
|
|
859
|
+
await defaultManager.uninstall(name, { force: flags.force, system: flags.system });
|
|
774
860
|
console.log(`✓ Service "${sanitizeServiceName(name)}" removed.`);
|
|
775
861
|
}
|
|
776
862
|
break;
|
|
@@ -778,7 +864,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
778
864
|
|
|
779
865
|
case 'ls':
|
|
780
866
|
case 'list': {
|
|
781
|
-
const services = await
|
|
867
|
+
const services = await defaultManager.list({ group: flags.group });
|
|
782
868
|
if (services.length === 0) {
|
|
783
869
|
if (flags.group) {
|
|
784
870
|
console.log(`No services found in group "${flags.group}".`);
|
|
@@ -804,7 +890,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
804
890
|
if (!nameArg) {
|
|
805
891
|
throw new Error('Service name is required.\nExample: unitup inspect api');
|
|
806
892
|
}
|
|
807
|
-
const info = await
|
|
893
|
+
const info = await defaultManager.inspect(nameArg, { system: flags.system });
|
|
808
894
|
console.log(`Name: ${info.name}`);
|
|
809
895
|
console.log(`Runtime: ${info.runtime}`);
|
|
810
896
|
console.log(`Group: ${info.group}`);
|
|
@@ -812,17 +898,17 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
812
898
|
console.log(`Command: ${info.command}`);
|
|
813
899
|
console.log(`Arguments: ${info.arguments}`);
|
|
814
900
|
console.log(`Working directory: ${info.cwd}`);
|
|
815
|
-
console.log(`Unit: ${info.unit}`);
|
|
816
|
-
console.log(`Memory: ${info.memory}`);
|
|
817
|
-
console.log(`Memory Peak: ${info.memoryPeak}`);
|
|
818
|
-
console.log(`Memory High: ${info.memoryHigh}`);
|
|
819
|
-
console.log(`Memory Max: ${info.memoryMax}`);
|
|
820
|
-
console.log(`Swap Max: ${info.memorySwapMax}`);
|
|
901
|
+
if (info.unit || info.unitFile) console.log(`Unit: ${info.unit || info.unitFile}`);
|
|
902
|
+
if (info.memory) console.log(`Memory: ${info.memory}`);
|
|
903
|
+
if (info.memoryPeak) console.log(`Memory Peak: ${info.memoryPeak}`);
|
|
904
|
+
if (info.memoryHigh) console.log(`Memory High: ${info.memoryHigh}`);
|
|
905
|
+
if (info.memoryMax) console.log(`Memory Max: ${info.memoryMax}`);
|
|
906
|
+
if (info.memorySwapMax) console.log(`Swap Max: ${info.memorySwapMax}`);
|
|
821
907
|
break;
|
|
822
908
|
}
|
|
823
909
|
|
|
824
910
|
case 'failures': {
|
|
825
|
-
const failures = await
|
|
911
|
+
const failures = await defaultManager.failures();
|
|
826
912
|
if (failures.length === 0) {
|
|
827
913
|
console.log('✓ No failed services.');
|
|
828
914
|
break;
|