unitup 0.1.1 → 0.1.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "unitup",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
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",
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.cwd
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.cwd
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 = meta.cwd ? resolveAbsolutePath(meta.cwd) : (meta.script ? path.dirname(resolveAbsolutePath(meta.script)) : process.cwd());
144
+ const cwd = resolveWorkingDirectory(meta);
145
145
 
146
146
  // Extract memory resources if present
147
147
  const resources = meta.resources || {};
@@ -302,6 +302,31 @@ export function resolveAbsolutePath(filepath, baseDir = process.cwd()) {
302
302
  return path.resolve(baseDir, p);
303
303
  }
304
304
 
305
+ /**
306
+ * Safely resolves the working directory for a service or schedule unit.
307
+ *
308
+ * @param {Object} [opts]
309
+ * @returns {string}
310
+ */
311
+ export function resolveWorkingDirectory(opts = {}) {
312
+ if (opts.cwd) {
313
+ return resolveAbsolutePath(opts.cwd);
314
+ }
315
+ if (opts.script) {
316
+ return path.dirname(resolveAbsolutePath(opts.script));
317
+ }
318
+ try {
319
+ const cwd = process.cwd();
320
+ if (fs.existsSync(cwd)) {
321
+ fs.accessSync(cwd, fs.constants.R_OK | fs.constants.X_OK);
322
+ return cwd;
323
+ }
324
+ } catch {
325
+ // fallback if process.cwd() is inaccessible
326
+ }
327
+ return os.homedir();
328
+ }
329
+
305
330
  /**
306
331
  * Escapes values for systemd Environment= line entries.
307
332
  * E.g. KEY="val with spaces"