unitup 0.0.10 → 0.0.12
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/README.md +7 -2
- package/docs/index.html +62 -24
- package/index.d.ts +7 -1
- package/package.json +23 -4
- package/src/cli.js +6 -3
- package/src/systemd.js +41 -2
- package/tsconfig.json +13 -0
package/README.md
CHANGED
|
@@ -327,6 +327,7 @@ unitup add worker.py --name worker --group backend --start
|
|
|
327
327
|
- `--env-file <file>`: Path to environment file (adds `EnvironmentFile=...`).
|
|
328
328
|
- `--restart <policy>`: Systemd restart policy (`on-failure`, `always`, `no`, `on-abnormal`. Default: `on-failure`).
|
|
329
329
|
- `--start`: Automatically enables and starts the service immediately (`systemctl --user enable --now`).
|
|
330
|
+
- `--force`, `-f`: Force overwrite of an existing service if it is currently running.
|
|
330
331
|
|
|
331
332
|
Generated unit file example (`~/.config/systemd/user/unitup-worker.service`):
|
|
332
333
|
|
|
@@ -477,11 +478,15 @@ unitup failures
|
|
|
477
478
|
|
|
478
479
|
### `unitup remove <name|@group>`
|
|
479
480
|
|
|
480
|
-
Stops, disables, and deletes the unit file, then reloads systemd
|
|
481
|
+
Stops, disables, and deletes the unit file, then reloads systemd. If the service is currently running, `unitup` prevents accidental deletion and requires `--force` / `-f`:
|
|
481
482
|
|
|
482
483
|
```bash
|
|
484
|
+
# Remove stopped service
|
|
483
485
|
unitup remove worker
|
|
484
|
-
|
|
486
|
+
|
|
487
|
+
# Force remove an actively running service or group
|
|
488
|
+
unitup remove worker --force
|
|
489
|
+
unitup remove @backend -f
|
|
485
490
|
```
|
|
486
491
|
|
|
487
492
|
---
|
package/docs/index.html
CHANGED
|
@@ -26,11 +26,13 @@
|
|
|
26
26
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/languages/ini.min.js"></script>
|
|
27
27
|
<style>
|
|
28
28
|
html { scroll-behavior: smooth; }
|
|
29
|
-
pre { tab-size: 2; margin: 0; }
|
|
30
|
-
pre code { padding: 1rem; background: #0f172a; border-radius: 0.75rem; font-size: 0.
|
|
31
|
-
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; }
|
|
29
|
+
pre { tab-size: 2; margin: 0; overflow-x: auto; max-width: 100%; }
|
|
30
|
+
pre code { padding: 1rem; background: #0f172a; border-radius: 0.75rem; font-size: 0.85rem; display: block; overflow-x: auto; white-space: pre; }
|
|
31
|
+
code { font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace; word-break: break-word; }
|
|
32
32
|
.doc-section { scroll-margin-top: 2rem; }
|
|
33
|
-
table { border-
|
|
33
|
+
.table-container { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; border-radius: 0.75rem; border: 1px solid #e2e8f0; margin-bottom: 1rem; }
|
|
34
|
+
.dark .table-container { border-color: #334155; }
|
|
35
|
+
table { border-collapse: collapse; width: 100%; min-width: 580px; }
|
|
34
36
|
th, td { border: 1px solid #e2e8f0; padding: 0.75rem 1rem; text-align: left; }
|
|
35
37
|
th { background: #f8fafc; font-weight: 600; }
|
|
36
38
|
.dark th, .dark td { border-color: #334155; }
|
|
@@ -38,27 +40,38 @@
|
|
|
38
40
|
</style>
|
|
39
41
|
</head>
|
|
40
42
|
<body class="bg-slate-50 text-slate-900 antialiased dark:bg-slate-950 dark:text-slate-100 transition-colors">
|
|
41
|
-
<div class="max-w-6xl mx-auto px-6 py-10">
|
|
43
|
+
<div class="max-w-6xl mx-auto px-4 sm:px-6 py-6 sm:py-10">
|
|
42
44
|
<!-- Top Navigation Bar -->
|
|
43
|
-
<div class="flex items-center justify-between mb-10 gap-4 flex-wrap pb-6 border-b border-slate-200 dark:border-slate-800">
|
|
44
|
-
<div class="flex items-center gap-3">
|
|
45
|
+
<div class="flex items-center justify-between mb-8 sm:mb-10 gap-4 flex-wrap pb-6 border-b border-slate-200 dark:border-slate-800">
|
|
46
|
+
<div class="flex items-center gap-2.5 sm:gap-3">
|
|
45
47
|
<span class="text-2xl font-black tracking-tight text-cyan-600 dark:text-cyan-400">unitup</span>
|
|
46
|
-
<span class="text-xs px-2.5 py-0.5 rounded-full bg-cyan-100 text-cyan-800 dark:bg-cyan-950 dark:text-cyan-300 font-semibold border border-cyan-300 dark:border-cyan-800">v0.0.
|
|
48
|
+
<span class="text-xs px-2.5 py-0.5 rounded-full bg-cyan-100 text-cyan-800 dark:bg-cyan-950 dark:text-cyan-300 font-semibold border border-cyan-300 dark:border-cyan-800">v0.0.11</span>
|
|
49
|
+
<a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-slate-500 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 transition-colors p-1" aria-label="GitHub Repository">
|
|
50
|
+
<svg class="w-5 h-5" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" clip-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.53 1.032 1.53 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z"/></svg>
|
|
51
|
+
</a>
|
|
47
52
|
</div>
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
53
|
+
|
|
54
|
+
<div class="flex items-center gap-2">
|
|
55
|
+
<button id="mobile-menu-btn" type="button" class="md:hidden p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400" aria-label="Toggle navigation menu">
|
|
56
|
+
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/></svg>
|
|
57
|
+
</button>
|
|
58
|
+
<button id="theme-toggle" type="button" class="p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400 shrink-0" aria-label="Toggle dark mode">
|
|
59
|
+
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
60
|
+
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
|
61
|
+
</button>
|
|
62
|
+
</div>
|
|
63
|
+
|
|
64
|
+
<nav id="nav-menu" class="hidden md:flex w-full md:w-auto flex-col md:flex-row flex-wrap gap-x-4 gap-y-2 text-sm font-medium pt-3 md:pt-0 border-t md:border-t-0 border-slate-200 dark:border-slate-800" aria-label="Page sections">
|
|
65
|
+
<a href="#overview" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Overview</a>
|
|
66
|
+
<a href="#comparison" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">vs PM2</a>
|
|
67
|
+
<a href="#architecture" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Architecture</a>
|
|
68
|
+
<a href="#runtimes" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Runtimes</a>
|
|
69
|
+
<a href="#cli" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">CLI Reference</a>
|
|
70
|
+
<a href="#api" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">API</a>
|
|
71
|
+
<a href="#declarative" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Roadmap</a>
|
|
72
|
+
<a href="#troubleshooting" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400 py-1">Lingering</a>
|
|
73
|
+
<a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-cyan-600 dark:text-cyan-400 hover:underline flex items-center gap-1 font-semibold py-1">GitHub →</a>
|
|
57
74
|
</nav>
|
|
58
|
-
<button id="theme-toggle" type="button" class="p-2 rounded-lg border border-slate-200 dark:border-slate-800 text-slate-500 hover:bg-slate-200 dark:hover:bg-slate-800 dark:text-slate-400 shrink-0" aria-label="Toggle dark mode">
|
|
59
|
-
<svg class="w-5 h-5 hidden dark:block" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"/></svg>
|
|
60
|
-
<svg class="w-5 h-5 block dark:hidden" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z"/></svg>
|
|
61
|
-
</button>
|
|
62
75
|
</div>
|
|
63
76
|
|
|
64
77
|
<!-- Header / Hero Section -->
|
|
@@ -142,7 +155,7 @@ unitup add ./server --runtime native --name api --start</code></pre>
|
|
|
142
155
|
<strong class="text-slate-800 dark:text-slate-200">unitup</strong> is not a PM2 replacement or an independent process manager. While PM2 runs its own master daemon, unitup is strictly a thin, transparent CLI layer on top of Linux OS <code class="text-cyan-600 dark:text-cyan-400">systemd</code>.
|
|
143
156
|
</p>
|
|
144
157
|
|
|
145
|
-
<div class="
|
|
158
|
+
<div class="table-container">
|
|
146
159
|
<table>
|
|
147
160
|
<thead>
|
|
148
161
|
<tr>
|
|
@@ -237,7 +250,7 @@ WantedBy=default.target</code></pre>
|
|
|
237
250
|
<section id="runtimes" class="doc-section mb-14">
|
|
238
251
|
<h2 class="text-2xl font-bold text-slate-900 dark:text-white mb-4">Supported Runtimes & Auto-Detection</h2>
|
|
239
252
|
|
|
240
|
-
<div class="
|
|
253
|
+
<div class="table-container">
|
|
241
254
|
<table>
|
|
242
255
|
<thead>
|
|
243
256
|
<tr>
|
|
@@ -332,6 +345,7 @@ WantedBy=default.target</code></pre>
|
|
|
332
345
|
# --env KEY=val Set environment variable
|
|
333
346
|
# --env-file <path> Load environment file
|
|
334
347
|
# --start Enable and start service immediately
|
|
348
|
+
# --force, -f Force overwrite of currently running service
|
|
335
349
|
|
|
336
350
|
unitup add worker.py --name worker --group backend --env APP_ENV=production --start</code></pre>
|
|
337
351
|
</div>
|
|
@@ -384,6 +398,14 @@ unitup journal rotate
|
|
|
384
398
|
unitup journal vacuum --size 500M --yes
|
|
385
399
|
unitup journal vacuum --time 14d --dry-run</code></pre>
|
|
386
400
|
</div>
|
|
401
|
+
|
|
402
|
+
<!-- remove -->
|
|
403
|
+
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
404
|
+
<h3 class="text-lg font-bold text-cyan-600 dark:text-cyan-400 mb-2"><code class="text-base">unitup remove <name|@group> [--force]</code></h3>
|
|
405
|
+
<p class="text-sm text-slate-600 dark:text-slate-400 mb-3">Stops, disables, and deletes a service or group. Active running services require <code class="text-xs">--force</code> / <code class="text-xs">-f</code> to prevent accidental deletion.</p>
|
|
406
|
+
<pre><code class="language-bash">unitup remove worker
|
|
407
|
+
unitup remove @backend --force</code></pre>
|
|
408
|
+
</div>
|
|
387
409
|
</div>
|
|
388
410
|
</section>
|
|
389
411
|
|
|
@@ -511,7 +533,7 @@ unitup restart @default</code></pre>
|
|
|
511
533
|
|
|
512
534
|
<!-- Footer -->
|
|
513
535
|
<footer class="pt-8 border-t border-slate-200 dark:border-slate-800 text-center text-sm text-slate-500 dark:text-slate-500">
|
|
514
|
-
<p>unitup
|
|
536
|
+
<p><a href="https://github.com/litepacks/unitup" target="_blank" rel="noopener noreferrer" class="text-cyan-600 dark:text-cyan-400 hover:underline">github.com/litepacks/unitup</a> · Released under <a href="https://opensource.org/licenses/MIT" target="_blank" rel="noopener noreferrer" class="underline hover:text-slate-700 dark:hover:text-slate-300">MIT License</a>.</p>
|
|
515
537
|
</footer>
|
|
516
538
|
</div>
|
|
517
539
|
|
|
@@ -522,6 +544,22 @@ unitup restart @default</code></pre>
|
|
|
522
544
|
document.documentElement.classList.toggle('dark');
|
|
523
545
|
});
|
|
524
546
|
|
|
547
|
+
// Mobile navigation toggle
|
|
548
|
+
const mobileMenuBtn = document.getElementById('mobile-menu-btn');
|
|
549
|
+
const navMenu = document.getElementById('nav-menu');
|
|
550
|
+
if (mobileMenuBtn && navMenu) {
|
|
551
|
+
mobileMenuBtn.addEventListener('click', () => {
|
|
552
|
+
navMenu.classList.toggle('hidden');
|
|
553
|
+
});
|
|
554
|
+
navMenu.querySelectorAll('a').forEach(link => {
|
|
555
|
+
link.addEventListener('click', () => {
|
|
556
|
+
if (window.innerWidth < 768) {
|
|
557
|
+
navMenu.classList.add('hidden');
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
|
|
525
563
|
// Highlight code blocks
|
|
526
564
|
document.addEventListener('DOMContentLoaded', () => {
|
|
527
565
|
hljs.highlightAll();
|
package/index.d.ts
CHANGED
|
@@ -77,6 +77,12 @@ export interface CreateServiceOptions {
|
|
|
77
77
|
*/
|
|
78
78
|
start?: boolean;
|
|
79
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Force overwrite of currently running service.
|
|
82
|
+
* @default false
|
|
83
|
+
*/
|
|
84
|
+
force?: boolean;
|
|
85
|
+
|
|
80
86
|
/**
|
|
81
87
|
* Soft memory limit directive (e.g. '400M').
|
|
82
88
|
*/
|
|
@@ -406,7 +412,7 @@ export function addService(options: CreateServiceOptions): Promise<AddServiceRes
|
|
|
406
412
|
export function startService(name: string, enable?: boolean): Promise<boolean>;
|
|
407
413
|
export function stopService(name: string): Promise<boolean>;
|
|
408
414
|
export function restartService(name: string): Promise<boolean>;
|
|
409
|
-
export function removeService(name: string): Promise<boolean>;
|
|
415
|
+
export function removeService(name: string, options?: { force?: boolean } | boolean): Promise<boolean>;
|
|
410
416
|
export function getServiceStatus(name: string): Promise<ServiceStatus>;
|
|
411
417
|
export function getServiceStatusRaw(name: string): Promise<string>;
|
|
412
418
|
export function listServices(options?: { group?: string }): Promise<ListServiceItem[]>;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "unitup",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "Minimal systemd
|
|
3
|
+
"version": "0.0.12",
|
|
4
|
+
"description": "Minimal, zero-dependency systemd-native service manager for any runtime, script, or executable",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
7
7
|
"type": "module",
|
|
@@ -19,12 +19,31 @@
|
|
|
19
19
|
"service",
|
|
20
20
|
"user-service",
|
|
21
21
|
"systemctl",
|
|
22
|
+
"process-manager",
|
|
23
|
+
"daemon-less",
|
|
24
|
+
"multi-runtime",
|
|
22
25
|
"node",
|
|
23
|
-
"
|
|
24
|
-
"
|
|
26
|
+
"python",
|
|
27
|
+
"ruby",
|
|
28
|
+
"php",
|
|
29
|
+
"bun",
|
|
30
|
+
"deno",
|
|
31
|
+
"go",
|
|
32
|
+
"elixir",
|
|
33
|
+
"shell",
|
|
34
|
+
"journald",
|
|
35
|
+
"cli"
|
|
25
36
|
],
|
|
26
37
|
"author": "",
|
|
27
38
|
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/litepacks/unitup.git"
|
|
42
|
+
},
|
|
43
|
+
"bugs": {
|
|
44
|
+
"url": "https://github.com/litepacks/unitup/issues"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/litepacks/unitup#readme",
|
|
28
47
|
"engines": {
|
|
29
48
|
"node": ">=20.0.0"
|
|
30
49
|
}
|
package/src/cli.js
CHANGED
|
@@ -53,6 +53,7 @@ export function parseArgs(argv) {
|
|
|
53
53
|
lines: 100,
|
|
54
54
|
help: false,
|
|
55
55
|
version: false,
|
|
56
|
+
force: false,
|
|
56
57
|
memoryHigh: '',
|
|
57
58
|
memoryMax: '',
|
|
58
59
|
memorySwapMax: '',
|
|
@@ -163,8 +164,9 @@ export function parseArgs(argv) {
|
|
|
163
164
|
} else if (arg === '--raw') {
|
|
164
165
|
result.flags.raw = true;
|
|
165
166
|
i++;
|
|
166
|
-
} else if (arg === '-f' || arg === '--follow') {
|
|
167
|
+
} else if (arg === '-f' || arg === '--follow' || arg === '--force') {
|
|
167
168
|
result.flags.follow = true;
|
|
169
|
+
result.flags.force = true;
|
|
168
170
|
i++;
|
|
169
171
|
} else if (arg === '-c' || arg === '--cat') {
|
|
170
172
|
result.flags.cat = true;
|
|
@@ -414,7 +416,8 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
414
416
|
start: flags.start,
|
|
415
417
|
memoryHigh: flags.memoryHigh,
|
|
416
418
|
memoryMax: flags.memoryMax,
|
|
417
|
-
memorySwapMax: flags.memorySwapMax
|
|
419
|
+
memorySwapMax: flags.memorySwapMax,
|
|
420
|
+
force: flags.force
|
|
418
421
|
});
|
|
419
422
|
|
|
420
423
|
console.log(`✓ Service "${res.name}" created at ${res.unitPath}`);
|
|
@@ -583,7 +586,7 @@ export async function runCli(argv = process.argv.slice(2)) {
|
|
|
583
586
|
}
|
|
584
587
|
const targetNames = await resolveTargetNames(nameArg);
|
|
585
588
|
for (const name of targetNames) {
|
|
586
|
-
await removeService(name);
|
|
589
|
+
await removeService(name, { force: flags.force });
|
|
587
590
|
console.log(`✓ Service "${sanitizeServiceName(name)}" removed.`);
|
|
588
591
|
}
|
|
589
592
|
break;
|
package/src/systemd.js
CHANGED
|
@@ -280,6 +280,26 @@ export async function resetFailed() {
|
|
|
280
280
|
|
|
281
281
|
export async function addService(opts) {
|
|
282
282
|
const safeName = sanitizeServiceName(opts.name);
|
|
283
|
+
|
|
284
|
+
if (unitFileExists(safeName)) {
|
|
285
|
+
try {
|
|
286
|
+
const show = await getServiceShow(safeName);
|
|
287
|
+
if (show.ActiveState === 'active') {
|
|
288
|
+
if (!opts.force) {
|
|
289
|
+
throw new Error(
|
|
290
|
+
`Service "${safeName}" is currently running.\n` +
|
|
291
|
+
`Use --force (-f) to overwrite running services, or stop it first:\n` +
|
|
292
|
+
` unitup stop ${safeName}`
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} catch (err) {
|
|
297
|
+
if (err.message.includes('currently running')) {
|
|
298
|
+
throw err;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
283
303
|
const runtimeConfig = await resolveRuntimeConfig({ ...opts, name: safeName });
|
|
284
304
|
|
|
285
305
|
const { path: unitPath } = writeUnitFile({
|
|
@@ -376,14 +396,16 @@ export async function restartService(name) {
|
|
|
376
396
|
return true;
|
|
377
397
|
}
|
|
378
398
|
|
|
379
|
-
export async function removeService(name) {
|
|
399
|
+
export async function removeService(name, opts = {}) {
|
|
400
|
+
const force = typeof opts === 'boolean' ? opts : !!opts?.force;
|
|
401
|
+
|
|
380
402
|
if (name && typeof name === 'string' && name.startsWith('@')) {
|
|
381
403
|
const list = await getServicesByGroup(name);
|
|
382
404
|
if (list.length === 0) {
|
|
383
405
|
throw new Error(`No services found in group "${name}".`);
|
|
384
406
|
}
|
|
385
407
|
for (const s of list) {
|
|
386
|
-
await removeService(s);
|
|
408
|
+
await removeService(s, { force });
|
|
387
409
|
}
|
|
388
410
|
return true;
|
|
389
411
|
}
|
|
@@ -393,6 +415,23 @@ export async function removeService(name) {
|
|
|
393
415
|
throw new Error(`Service "${safeName}" does not exist.`);
|
|
394
416
|
}
|
|
395
417
|
|
|
418
|
+
try {
|
|
419
|
+
const show = await getServiceShow(safeName);
|
|
420
|
+
if (show.ActiveState === 'active') {
|
|
421
|
+
if (!force) {
|
|
422
|
+
throw new Error(
|
|
423
|
+
`Service "${safeName}" is currently running.\n` +
|
|
424
|
+
`Use --force (-f) to remove running services, or stop it first:\n` +
|
|
425
|
+
` unitup stop ${safeName}`
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
} catch (err) {
|
|
430
|
+
if (err.message.includes('currently running')) {
|
|
431
|
+
throw err;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
396
435
|
// Attempt disable and stop
|
|
397
436
|
await runCommand('systemctl', ['--user', 'disable', '--now', getUnitFilename(safeName)]);
|
|
398
437
|
|
package/tsconfig.json
ADDED