unitup 0.1.1 → 0.1.3
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/package.json +1 -1
- package/src/cli.js +8 -2
- package/src/schedule.js +4 -5
- package/src/unit.js +3 -10
- package/src/utils.js +47 -5
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -108,12 +108,15 @@ export function parseArgs(argv) {
|
|
|
108
108
|
} else if (arg.startsWith('--runtime=')) {
|
|
109
109
|
result.flags.runtime = arg.slice(10);
|
|
110
110
|
i++;
|
|
111
|
-
} else if (arg === '--runtime-arg') {
|
|
111
|
+
} else if (arg === '--runtime-arg' || arg === '--runtime-args') {
|
|
112
112
|
if (argv[i + 1]) result.flags.runtimeArgs.push(argv[i + 1]);
|
|
113
113
|
i += 2;
|
|
114
114
|
} else if (arg.startsWith('--runtime-arg=')) {
|
|
115
115
|
result.flags.runtimeArgs.push(arg.slice(14));
|
|
116
116
|
i++;
|
|
117
|
+
} else if (arg.startsWith('--runtime-args=')) {
|
|
118
|
+
result.flags.runtimeArgs.push(arg.slice(15));
|
|
119
|
+
i++;
|
|
117
120
|
} else if (arg === '--command') {
|
|
118
121
|
result.flags.command = argv[i + 1] || '';
|
|
119
122
|
i += 2;
|
|
@@ -162,12 +165,15 @@ export function parseArgs(argv) {
|
|
|
162
165
|
} else if (arg.startsWith('--restart=')) {
|
|
163
166
|
result.flags.restart = arg.slice(10);
|
|
164
167
|
i++;
|
|
165
|
-
} else if (arg === '--arg') {
|
|
168
|
+
} else if (arg === '--arg' || arg === '--args') {
|
|
166
169
|
if (argv[i + 1]) result.flags.args.push(argv[i + 1]);
|
|
167
170
|
i += 2;
|
|
168
171
|
} else if (arg.startsWith('--arg=')) {
|
|
169
172
|
result.flags.args.push(arg.slice(6));
|
|
170
173
|
i++;
|
|
174
|
+
} else if (arg.startsWith('--args=')) {
|
|
175
|
+
result.flags.args.push(arg.slice(7));
|
|
176
|
+
i++;
|
|
171
177
|
} else if (arg === '--lines' || arg === '-n') {
|
|
172
178
|
result.flags.lines = parseInt(argv[i + 1] || '100', 10);
|
|
173
179
|
i += 2;
|
package/src/schedule.js
CHANGED
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
getUnitFilename,
|
|
7
7
|
getTimerFilename,
|
|
8
8
|
resolveAbsolutePath,
|
|
9
|
+
resolveWorkingDirectory,
|
|
9
10
|
validateDuration,
|
|
10
11
|
formatFutureTime,
|
|
11
12
|
formatRelativeTime,
|
|
@@ -125,11 +126,7 @@ export async function createSchedule(opts = {}) {
|
|
|
125
126
|
const execArgs = resolved.args;
|
|
126
127
|
const detectedRuntime = resolved.runtime;
|
|
127
128
|
|
|
128
|
-
const cwd = opts.cwd
|
|
129
|
-
? resolveAbsolutePath(opts.cwd)
|
|
130
|
-
: scriptPath
|
|
131
|
-
? path.dirname(scriptPath)
|
|
132
|
-
: process.cwd();
|
|
129
|
+
const cwd = resolveWorkingDirectory({ cwd: opts.cwd, script: scriptPath });
|
|
133
130
|
|
|
134
131
|
const envObj = {};
|
|
135
132
|
if (opts.env) {
|
|
@@ -173,6 +170,7 @@ export async function createSchedule(opts = {}) {
|
|
|
173
170
|
|
|
174
171
|
if (opts.start || opts.enable) {
|
|
175
172
|
await runCommand('systemctl', ['--user', 'daemon-reload']);
|
|
173
|
+
await runCommand('systemctl', ['--user', 'reset-failed', getUnitFilename(safeName), getTimerFilename(safeName)]);
|
|
176
174
|
await runCommand('systemctl', ['--user', 'enable', getTimerFilename(safeName)]);
|
|
177
175
|
await runCommand('systemctl', ['--user', 'start', getTimerFilename(safeName)]);
|
|
178
176
|
}
|
|
@@ -212,6 +210,7 @@ export async function enableSchedule(name) {
|
|
|
212
210
|
throw new Error(`Schedule timer unit up for "${name}" does not exist.`);
|
|
213
211
|
}
|
|
214
212
|
await runCommand('systemctl', ['--user', 'daemon-reload']);
|
|
213
|
+
await runCommand('systemctl', ['--user', 'reset-failed', getUnitFilename(safeName), getTimerFilename(safeName)]);
|
|
215
214
|
const res = await runCommand('systemctl', ['--user', 'enable', '--now', getTimerFilename(safeName)]);
|
|
216
215
|
return res.code === 0;
|
|
217
216
|
}
|
package/src/unit.js
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
formatSystemdEnv,
|
|
10
10
|
escapeExecArg,
|
|
11
11
|
resolveAbsolutePath,
|
|
12
|
+
resolveWorkingDirectory,
|
|
12
13
|
saveAppMetadata,
|
|
13
14
|
deleteAppMetadata,
|
|
14
15
|
saveScheduleMetadata,
|
|
@@ -83,11 +84,7 @@ export function generateUnitContent(opts) {
|
|
|
83
84
|
throw new Error('Either "command" or "script" must be provided to generate systemd unit file.');
|
|
84
85
|
}
|
|
85
86
|
|
|
86
|
-
const cwd = opts
|
|
87
|
-
? resolveAbsolutePath(opts.cwd)
|
|
88
|
-
: opts.script
|
|
89
|
-
? path.dirname(resolveAbsolutePath(opts.script))
|
|
90
|
-
: process.cwd();
|
|
87
|
+
const cwd = resolveWorkingDirectory(opts);
|
|
91
88
|
const restartPolicy = opts.restart || 'on-failure';
|
|
92
89
|
|
|
93
90
|
const execStartTokens = [commandExec, ...execArgs];
|
|
@@ -309,11 +306,7 @@ export function generateScheduleServiceContent(opts) {
|
|
|
309
306
|
throw new Error('Either "command" or "script" must be provided to generate scheduled unit file.');
|
|
310
307
|
}
|
|
311
308
|
|
|
312
|
-
const cwd = opts
|
|
313
|
-
? resolveAbsolutePath(opts.cwd)
|
|
314
|
-
: opts.script
|
|
315
|
-
? path.dirname(resolveAbsolutePath(opts.script))
|
|
316
|
-
: process.cwd();
|
|
309
|
+
const cwd = resolveWorkingDirectory(opts);
|
|
317
310
|
|
|
318
311
|
const execStartTokens = [commandExec, ...execArgs];
|
|
319
312
|
const execStartLine = execStartTokens.map(escapeExecArg).join(' ');
|
package/src/utils.js
CHANGED
|
@@ -141,7 +141,7 @@ export function saveAppMetadata(meta) {
|
|
|
141
141
|
const rawArgs = Array.isArray(meta.args) ? meta.args : (meta.script ? [meta.script] : []);
|
|
142
142
|
const args = rawArgs.map(a => (a.startsWith('/') || a.startsWith('./') || a.startsWith('../') || a.startsWith('~/')) ? resolveAbsolutePath(a) : a);
|
|
143
143
|
const scriptPath = meta.script ? resolveAbsolutePath(meta.script) : (args[0] ? resolveAbsolutePath(args[0]) : command);
|
|
144
|
-
const cwd =
|
|
144
|
+
const cwd = resolveWorkingDirectory(meta);
|
|
145
145
|
|
|
146
146
|
// Extract memory resources if present
|
|
147
147
|
const resources = meta.resources || {};
|
|
@@ -180,10 +180,17 @@ export function readAppMetadata(name) {
|
|
|
180
180
|
const data = JSON.parse(content);
|
|
181
181
|
if (!data) return null;
|
|
182
182
|
|
|
183
|
-
// Normalization for legacy metadata format
|
|
184
183
|
const command = data.command || data.node || process.execPath;
|
|
185
184
|
const args = data.args || (data.script ? [data.script] : []);
|
|
186
|
-
|
|
185
|
+
let runtime = data.runtime;
|
|
186
|
+
if (!runtime || (runtime === 'node' && data.command)) {
|
|
187
|
+
const baseCmd = path.basename(command).toLowerCase();
|
|
188
|
+
if (baseCmd !== 'node' && baseCmd !== 'nodejs') {
|
|
189
|
+
runtime = 'custom';
|
|
190
|
+
} else {
|
|
191
|
+
runtime = 'node';
|
|
192
|
+
}
|
|
193
|
+
}
|
|
187
194
|
const script = data.script || args[0] || command;
|
|
188
195
|
const cwd = data.cwd || (script ? path.dirname(script) : process.cwd());
|
|
189
196
|
|
|
@@ -302,6 +309,31 @@ export function resolveAbsolutePath(filepath, baseDir = process.cwd()) {
|
|
|
302
309
|
return path.resolve(baseDir, p);
|
|
303
310
|
}
|
|
304
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Safely resolves the working directory for a service or schedule unit.
|
|
314
|
+
*
|
|
315
|
+
* @param {Object} [opts]
|
|
316
|
+
* @returns {string}
|
|
317
|
+
*/
|
|
318
|
+
export function resolveWorkingDirectory(opts = {}) {
|
|
319
|
+
if (opts.cwd) {
|
|
320
|
+
return resolveAbsolutePath(opts.cwd);
|
|
321
|
+
}
|
|
322
|
+
if (opts.script) {
|
|
323
|
+
return path.dirname(resolveAbsolutePath(opts.script));
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
const cwd = process.cwd();
|
|
327
|
+
if (fs.existsSync(cwd)) {
|
|
328
|
+
fs.accessSync(cwd, fs.constants.R_OK | fs.constants.X_OK);
|
|
329
|
+
return cwd;
|
|
330
|
+
}
|
|
331
|
+
} catch {
|
|
332
|
+
// fallback if process.cwd() is inaccessible
|
|
333
|
+
}
|
|
334
|
+
return os.homedir();
|
|
335
|
+
}
|
|
336
|
+
|
|
305
337
|
/**
|
|
306
338
|
* Escapes values for systemd Environment= line entries.
|
|
307
339
|
* E.g. KEY="val with spaces"
|
|
@@ -447,7 +479,7 @@ export function saveScheduleMetadata(meta) {
|
|
|
447
479
|
name: safeName,
|
|
448
480
|
group: meta.group || 'default',
|
|
449
481
|
type: 'timer',
|
|
450
|
-
runtime: meta.runtime || 'node',
|
|
482
|
+
runtime: meta.runtime || (meta.command ? 'custom' : 'node'),
|
|
451
483
|
command,
|
|
452
484
|
args,
|
|
453
485
|
cwd: meta.cwd ? resolveAbsolutePath(meta.cwd) : process.cwd(),
|
|
@@ -477,7 +509,17 @@ export function readScheduleMetadata(name) {
|
|
|
477
509
|
const filePath = getScheduleMetadataPath(name);
|
|
478
510
|
if (!fs.existsSync(filePath)) return null;
|
|
479
511
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
480
|
-
|
|
512
|
+
const meta = JSON.parse(content);
|
|
513
|
+
if (!meta) return null;
|
|
514
|
+
|
|
515
|
+
// Normalization for legacy metadata format where runtime defaulted to 'node' for custom commands
|
|
516
|
+
if (meta.runtime === 'node' && meta.command) {
|
|
517
|
+
const baseCmd = path.basename(meta.command).toLowerCase();
|
|
518
|
+
if (baseCmd !== 'node' && baseCmd !== 'nodejs') {
|
|
519
|
+
meta.runtime = 'custom';
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
return meta;
|
|
481
523
|
} catch {
|
|
482
524
|
return null;
|
|
483
525
|
}
|