unitup 0.0.10 → 0.0.11
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 +15 -2
- package/index.d.ts +7 -1
- package/package.json +15 -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
|
@@ -43,7 +43,10 @@
|
|
|
43
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
44
|
<div class="flex items-center gap-3">
|
|
45
45
|
<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.
|
|
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.10</span>
|
|
47
|
+
<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" aria-label="GitHub Repository">
|
|
48
|
+
<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>
|
|
49
|
+
</a>
|
|
47
50
|
</div>
|
|
48
51
|
<nav class="flex flex-wrap gap-x-4 gap-y-2 text-sm font-medium" aria-label="Page sections">
|
|
49
52
|
<a href="#overview" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400">Overview</a>
|
|
@@ -54,6 +57,7 @@
|
|
|
54
57
|
<a href="#api" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400">API</a>
|
|
55
58
|
<a href="#declarative" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400">Roadmap</a>
|
|
56
59
|
<a href="#troubleshooting" class="text-slate-600 hover:text-cyan-600 dark:text-slate-400 dark:hover:text-cyan-400">Lingering</a>
|
|
60
|
+
<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">GitHub →</a>
|
|
57
61
|
</nav>
|
|
58
62
|
<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
63
|
<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>
|
|
@@ -332,6 +336,7 @@ WantedBy=default.target</code></pre>
|
|
|
332
336
|
# --env KEY=val Set environment variable
|
|
333
337
|
# --env-file <path> Load environment file
|
|
334
338
|
# --start Enable and start service immediately
|
|
339
|
+
# --force, -f Force overwrite of currently running service
|
|
335
340
|
|
|
336
341
|
unitup add worker.py --name worker --group backend --env APP_ENV=production --start</code></pre>
|
|
337
342
|
</div>
|
|
@@ -384,6 +389,14 @@ unitup journal rotate
|
|
|
384
389
|
unitup journal vacuum --size 500M --yes
|
|
385
390
|
unitup journal vacuum --time 14d --dry-run</code></pre>
|
|
386
391
|
</div>
|
|
392
|
+
|
|
393
|
+
<!-- remove -->
|
|
394
|
+
<div class="p-5 rounded-xl border border-slate-200 dark:border-slate-800 bg-white/70 dark:bg-slate-900/50">
|
|
395
|
+
<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>
|
|
396
|
+
<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>
|
|
397
|
+
<pre><code class="language-bash">unitup remove worker
|
|
398
|
+
unitup remove @backend --force</code></pre>
|
|
399
|
+
</div>
|
|
387
400
|
</div>
|
|
388
401
|
</section>
|
|
389
402
|
|
|
@@ -511,7 +524,7 @@ unitup restart @default</code></pre>
|
|
|
511
524
|
|
|
512
525
|
<!-- Footer -->
|
|
513
526
|
<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
|
|
527
|
+
<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
528
|
</footer>
|
|
516
529
|
</div>
|
|
517
530
|
|
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.11",
|
|
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,9 +19,20 @@
|
|
|
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",
|
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