From 113103f368ead3014165cc708f016a04749f59be Mon Sep 17 00:00:00 2001 From: "Max P." Date: Wed, 28 May 2025 14:24:28 +0200 Subject: [PATCH] feat(cli): add options for user, home, and working directory - Add `--run-as` option to specify user for system-wide timers - Add `--home` option to set the HOME environment variable - Add `--cwd` option to define the working directory - Update tests to validate new options and behavior --- src/cli/create.ts | 12 +++++ src/templates/__tests__/generate.test.ts | 61 ++++++++++++++++++++++++ src/templates/unit-generator.ts | 16 ++++--- src/types/options.ts | 3 ++ 4 files changed, 86 insertions(+), 6 deletions(-) diff --git a/src/cli/create.ts b/src/cli/create.ts index 20820f6..bc8fa73 100644 --- a/src/cli/create.ts +++ b/src/cli/create.ts @@ -18,6 +18,18 @@ export const createCommand = new Command() }) .option('--description ', 'Beschreibung des Timers') .option('--user', 'Erstellt die Unit als User-Timer') + .option( + '--run-as ', + 'Führe den systemweiten Timer als bestimmter Benutzer aus (setzt User= in der Service-Unit)', + ) + .option( + '--home ', + 'HOME-Variable für den Service setzen', + ) + .option( + '--cwd ', + 'Arbeitsverzeichnis (WorkingDirectory) für den Service-Prozess', + ) .option('--output ', 'Zielverzeichnis der Unit-Dateien') .option( '--after ', diff --git a/src/templates/__tests__/generate.test.ts b/src/templates/__tests__/generate.test.ts index 42fe8a2..edb1231 100644 --- a/src/templates/__tests__/generate.test.ts +++ b/src/templates/__tests__/generate.test.ts @@ -1,4 +1,5 @@ import { + assert, assertStringIncludes, } from 'https://deno.land/std@0.224.0/assert/mod.ts'; import { TimerOptions } from '../../types/mod.ts'; @@ -53,3 +54,63 @@ Deno.test('generateUnits berücksichtigt environment und logfile', () => { assertStringIncludes(serviceUnit, 'StandardOutput=append:/var/log/job.log'); assertStringIncludes(serviceUnit, 'StandardError=append:/var/log/job.log'); }); + +Deno.test('generateUnits berücksichtigt runAs', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + runAs: 'myuser', + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'User=myuser'); +}); + +Deno.test('generateUnits berücksichtigt home', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + home: '/home/myuser', + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'Environment=HOME=/home/myuser'); +}); + +Deno.test('generateUnits berücksichtigt cwd', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + cwd: '/srv/app', + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'WorkingDirectory=/srv/app'); +}); + +Deno.test('generateUnits verwendet default.target bei User-Timern', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + user: true, + }; + const { timerUnit } = generateUnits('job', opts); + + assertStringIncludes(timerUnit, 'WantedBy=default.target'); +}); + +Deno.test('generateUnits ignoriert runAs bei --user', () => { + const opts = { + exec: '/bin/true', + calendar: 'daily', + user: true, + runAs: 'should-not-appear', + }; + + const { serviceUnit } = generateUnits('job', opts); + + assert( + !serviceUnit.includes('User=should-not-appear'), + 'User= sollte bei --user nicht enthalten sein', + ); +}); diff --git a/src/templates/unit-generator.ts b/src/templates/unit-generator.ts index 9674b0a..4f009e4 100644 --- a/src/templates/unit-generator.ts +++ b/src/templates/unit-generator.ts @@ -51,14 +51,18 @@ export function generateUnits(name: string, options: TimerOptions): { `[Service]`, `Type=oneshot`, `ExecStart=${options.exec}`, + ...(options.cwd ? [`WorkingDirectory=${options.cwd}`] : []), ...(options.environment?.map((e) => `Environment=${e}`) ?? []), + ...(options.home ? [`Environment=HOME=${options.home}`] : []), + ...(options.logfile + ? [ + `StandardOutput=append:${options.logfile}`, + `StandardError=append:${options.logfile}`, + ] + : []), + ...(options.runAs && !options.user ? [`User=${options.runAs}`] : []), ]; - if (options.logfile) { - unitParts.push(`StandardOutput=append:${options.logfile}`); - unitParts.push(`StandardError=append:${options.logfile}`); - } - const serviceUnit = unitParts.join('\n'); const timerParts = [ @@ -70,7 +74,7 @@ export function generateUnits(name: string, options: TimerOptions): { `Persistent=true`, ``, `[Install]`, - `WantedBy=timers.target`, + `WantedBy=${options.user ? 'default.target' : 'timers.target'}`, ]; const timerUnit = timerParts.join('\n'); diff --git a/src/types/options.ts b/src/types/options.ts index a8c617e..f82abdf 100644 --- a/src/types/options.ts +++ b/src/types/options.ts @@ -4,6 +4,9 @@ export interface TimerOptions { calendar: string; description?: string; user?: boolean; + runAs?: string; + home?: string; + cwd?: string; output?: string; after?: string[]; environment?: string[];