diff --git a/src/templates/__tests__/generate.test.ts b/src/templates/__tests__/generate.test.ts new file mode 100644 index 0000000..42fe8a2 --- /dev/null +++ b/src/templates/__tests__/generate.test.ts @@ -0,0 +1,55 @@ +import { + assertStringIncludes, +} from 'https://deno.land/std@0.224.0/assert/mod.ts'; +import { TimerOptions } from '../../types/mod.ts'; +import { generateUnits } from '../unit-generator.ts'; + +Deno.test('generateUnits erzeugt Basis-Service und Timer korrekt', () => { + const opts: TimerOptions = { + exec: '/usr/bin/my-script', + calendar: 'daily', + }; + const { serviceUnit, timerUnit } = generateUnits('myjob', opts); + + assertStringIncludes(serviceUnit, 'ExecStart=/usr/bin/my-script'); + assertStringIncludes(timerUnit, 'OnCalendar=daily'); + assertStringIncludes(timerUnit, '[Install]'); +}); + +Deno.test('generateUnits setzt Beschreibung aus Option', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + description: 'Meine Unit', + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'Description=Meine Unit'); +}); + +Deno.test('generateUnits berücksichtigt after=', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + after: ['network-online.target', 'docker.service'], + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'After=network-online.target'); + assertStringIncludes(serviceUnit, 'After=docker.service'); +}); + +Deno.test('generateUnits berücksichtigt environment und logfile', () => { + const opts: TimerOptions = { + exec: '/bin/true', + calendar: 'daily', + environment: ['FOO=bar', 'DEBUG=1'], + logfile: '/var/log/job.log', + }; + const { serviceUnit } = generateUnits('job', opts); + + assertStringIncludes(serviceUnit, 'Environment=FOO=bar'); + assertStringIncludes(serviceUnit, 'Environment=DEBUG=1'); + assertStringIncludes(serviceUnit, 'StandardOutput=append:/var/log/job.log'); + assertStringIncludes(serviceUnit, 'StandardError=append:/var/log/job.log'); +}); diff --git a/src/templates/unit-generator.ts b/src/templates/unit-generator.ts new file mode 100644 index 0000000..8e6a0a6 --- /dev/null +++ b/src/templates/unit-generator.ts @@ -0,0 +1,63 @@ +import { TimerOptions } from '../types/mod.ts'; +import { deriveNameFromExec, writeUnitFiles } from '../utils/mod.ts'; + +export async function generateUnitFiles(options: TimerOptions): Promise { + const name = options.name || deriveNameFromExec(options.exec); + + const { serviceUnit, timerUnit } = generateUnits(name, options); + + if (options.dryRun) { + console.log(`===== ${name}.service =====`); + console.log(serviceUnit); + console.log(`\n===== ${name}.timer =====`); + console.log(timerUnit); + } else { + const { servicePath, timerPath } = await writeUnitFiles( + name, + serviceUnit, + timerUnit, + options, + ); + console.log(`Service unit written to: ${servicePath}`); + console.log(`Timer unit written to: ${timerPath}`); + } +} + +export function generateUnits(name: string, options: TimerOptions): { + serviceUnit: string; + timerUnit: string; +} { + const unitParts = [ + `[Unit]`, + `Description=${options.description ?? name}`, + ...(options.after?.map((a) => `After=${a}`) ?? []), + ``, + `[Service]`, + `Type=oneshot`, + `ExecStart=${options.exec}`, + ...(options.environment?.map((e) => `Environment=${e}`) ?? []), + ]; + + if (options.logfile) { + unitParts.push(`StandardOutput=append:${options.logfile}`); + unitParts.push(`StandardError=append:${options.logfile}`); + } + + const serviceUnit = unitParts.join('\n'); + + const timerParts = [ + `[Unit]`, + `Description=Timer for ${name}`, + ``, + `[Timer]`, + `OnCalendar=${options.calendar}`, + `Persistent=true`, + ``, + `[Install]`, + `WantedBy=timers.target`, + ]; + + const timerUnit = timerParts.join('\n'); + + return { serviceUnit, timerUnit }; +}