Skip to content

Commit

Permalink
feat(cli): add server.cron.logPath option
Browse files Browse the repository at this point in the history
  • Loading branch information
juanrgm committed Mar 26, 2024
1 parent fb105d5 commit 5cf1657
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/tall-foxes-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@datatruck/cli": minor
---

Add `server.cron.logPath` option
1 change: 1 addition & 0 deletions packages/cli/src/commands/RunCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export class RunCommand extends CommandAbstract<
log,
verbose: verbose,
configPath: config.path!,
logPath: config.data.server?.cron?.logPath,
});

return { exitCode: 0 };
Expand Down
13 changes: 9 additions & 4 deletions packages/cli/src/commands/StartServerCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,25 @@ export class StartServerCommand extends CommandAbstract<
server.listen(port, address);
}
const cronOptions = config.server?.cron || {};
const logPath = config.server?.cron?.logPath;

if (cronOptions.enabled ?? true) {
if (typeof configPath !== "string")
throw new AppError(`Config path is required by cron server`);
const server = await createCronServer({
configPath,
verbose,
log,
logPath,
});
server.start();
logJson("cron-server", `server started`);
}
process.on("SIGINT", () => process.exit(1));
process.on("SIGTERM", () => process.exit(1));
await new Promise<void>(() => setInterval(() => {}, 60_000));
return { exitCode: 0 };

const exitCode = await new Promise<number>((resolve) => {
process.on("SIGINT", () => resolve(1)).on("SIGTERM", () => resolve(1));
});

return { exitCode };
}
}
26 changes: 17 additions & 9 deletions packages/cli/src/utils/datatruck/cron-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@ import { formatCronScheduleObject } from "../cron";
import { compareJsons } from "../string";
import { createWatcher } from "../watcher";
import { Config } from "./config-type";
import { Job, runJob } from "./job";
import { Job, JobConfig, runJob } from "./job";
import { Cron } from "croner";
import { platform } from "os";
import { join } from "path";

export const defaultsLogPath =
platform() === "win32"
? join(
process.env.APPDATA ?? `${process.env.HOMEDRIVE ?? "C:"}\\ProgramData`,
"datatruck\\logs",
)
: "/var/logs/datatruck";

export type DatatruckCronServerOptions = {
enabled?: boolean;
/**
* @default '/var/logs/datatruck'
*/
logPath?: string | boolean;
};

type Options = {
log: boolean;
verbose: boolean;
configPath: string;
};

function createCrons(jobs: Record<string, Job>, options: Options) {
function createCrons(jobs: Record<string, Job>, options: JobConfig) {
const crons: Cron[] = [];
for (const name in jobs) {
const job = jobs[name];
Expand All @@ -40,7 +48,7 @@ function createCrons(jobs: Record<string, Job>, options: Options) {
return crons;
}

export async function createCronServer(options: Options) {
export async function createCronServer(options: JobConfig) {
const config = await ConfigAction.fromGlobalOptions({
config: options.configPath,
});
Expand Down
87 changes: 70 additions & 17 deletions packages/cli/src/utils/datatruck/job.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import { CopyCommandOptions } from "../../commands/CopyCommand";
import { PruneCommandOptions } from "../../commands/PruneCommand";
import { AsyncProcess } from "../async-process";
import { logJson, stringifyOptions } from "../cli";
import { safeRename } from "../fs";
import { datatruckCommandMap } from "./command";
import { defaultsLogPath } from "./cron-server";
import { WriteStream, createWriteStream } from "fs";
import { mkdir } from "fs/promises";
import { join } from "path";

export type JobScheduleObject = {
minute?: number | { each: number };
Expand All @@ -13,9 +18,7 @@ export type JobScheduleObject = {
weekDay?: number | { each: number };
};

export type Job = {
schedule?: string | JobScheduleObject;
} & (
export type JobAction =
| {
action: "backup";
options: BackupCommandOptions;
Expand All @@ -27,18 +30,21 @@ export type Job = {
| {
action: "prune";
options: Omit<PruneCommandOptions, "confirm">;
}
);
};

export type JobSchedule = string | JobScheduleObject;
export type Job = JobAction & {
schedule?: JobSchedule;
};

export async function runJob(
job: Job,
name: string,
config: {
log: boolean;
verbose: boolean;
configPath: string;
},
) {
export type JobConfig = {
log: boolean;
logPath: string | boolean | undefined;
verbose: boolean;
configPath: string;
};

export async function runJob(job: Job, name: string, config: JobConfig) {
let pid = 0;
try {
const Command = datatruckCommandMap[job.action];
Expand All @@ -53,22 +59,69 @@ export async function runJob(
: job.options,
);
const [node, bin] = process.argv;

const baseLogPath =
typeof config.logPath === "string"
? config.logPath
: config.logPath === true || config.logPath === undefined
? defaultsLogPath
: config.logPath;

let stream: WriteStream | undefined;
let logPath: string | undefined;
const dt = new Date().toISOString().replaceAll(":", "-");

if (baseLogPath) {
const tmpLogPath = join(baseLogPath, dt) + ".log";
await mkdir(baseLogPath, { recursive: true });
stream = createWriteStream(tmpLogPath);
}
const p = new AsyncProcess(
node,
[
process.env.pm_exec_path ?? bin,
"--tty",
"false",
"--progress",
"interval:3000",
"-c",
config.configPath,
job.action,
...cliOptions,
],
{ $log: config.verbose, $exitCode: false },
{
$log: config.verbose,
$exitCode: false,
env: {
...process.env,
COLUMNS: "160",
NO_COLOR: "1",
},
},
);

pid = p.child.pid || 0;

if (config.log) logJson("job", `'${name}' started`, { pid });
const exitCode = await p.waitForClose();
if (config.log) logJson("job", `'${name}' finished`, { pid, exitCode });

const [exitCode] = await Promise.all([
p.waitForClose(),
stream && p.stderr.pipe(stream),
stream && p.stdout.pipe(stream),
]);

if (stream)
await safeRename(
stream.path.toString(),
(logPath = join(stream.path.toString(), `${dt}-${pid}.log`)),
);

if (config.log)
logJson("job", `'${name}' finished`, {
pid,
exitCode,
...(logPath && { log: logPath }),
});
} catch (error) {
if (config.log) logJson("job", `'${name}' failed`, { pid });
console.error(error);
Expand Down

0 comments on commit 5cf1657

Please sign in to comment.