gcp-job-runner 1.3.0 → 1.4.0
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/dist/run-job.mjs +14 -0
- package/dist/run-job.mjs.map +1 -1
- package/package.json +1 -1
- package/src/run-job.ts +25 -0
package/dist/run-job.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { formatDuration } from "./format.mjs";
|
|
|
3
3
|
import { promptForArgs, selectJob } from "./interactive.mjs";
|
|
4
4
|
import { consola } from "consola";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
6
7
|
import process from "node:process";
|
|
7
8
|
|
|
8
9
|
//#region src/run-job.ts
|
|
@@ -51,6 +52,19 @@ Usage: ${prefix} <job-name> [options]\n ${prefix} --list\n`);
|
|
|
51
52
|
const subDirectories = parts;
|
|
52
53
|
const fileLocation = path.join(jobsDirectory, ...subDirectories);
|
|
53
54
|
const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);
|
|
55
|
+
/** Check if the job file exists before attempting to import */
|
|
56
|
+
if (!existsSync(modulePath)) {
|
|
57
|
+
const matches = (await discoverJobs(jobsDirectory)).filter((job) => job.name.split("/").pop() === fileName);
|
|
58
|
+
let message = `Job "${jobName}" not found.\n`;
|
|
59
|
+
if (matches.length === 1) message += `\nDid you mean "${matches[0].name}"?\n`;
|
|
60
|
+
else if (matches.length > 1) {
|
|
61
|
+
message += "\nFound similar jobs:\n";
|
|
62
|
+
for (const match of matches) message += ` ${match.name}\n`;
|
|
63
|
+
}
|
|
64
|
+
message += "\nRun with --list to see all available jobs.";
|
|
65
|
+
logger.error(message);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
54
68
|
/** Get argv after the job name (flags to pass to the job) */
|
|
55
69
|
const jobArgvIndex = process.argv.indexOf(jobName);
|
|
56
70
|
const argv = jobArgvIndex >= 0 ? process.argv.slice(jobArgvIndex + 1) : [];
|
package/dist/run-job.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-job.mjs","names":[],"sources":["../src/run-job.ts"],"sourcesContent":["import path from \"node:path\";\nimport process from \"node:process\";\nimport { consola } from \"consola\";\nimport type { ZodObject, ZodRawShape } from \"zod\";\nimport { discoverJobs } from \"./discover-jobs\";\nimport { formatDuration } from \"./format\";\nimport { promptForArgs, selectJob } from \"./interactive\";\nimport type { JobFunction, RunJobOptions } from \"./types\";\n\n/**\n * Entry point for job execution. Each service calls this from its own thin\n * `cli/run-jobs.ts` wrapper with a configured jobsDirectory.\n *\n * Parses argv to determine the job name, handles --list for discovery,\n * optionally runs initialization, then dynamically imports and executes\n * the target job.\n */\nexport async function runJob(options: RunJobOptions): Promise<void> {\n const { jobsDirectory, initialize, logger = console } = options;\n\n process.on(\"uncaughtException\", (err) => {\n const message =\n err instanceof Error ? (err.stack ?? err.message) : String(err);\n logger.error(`Uncaught exception: ${message}`);\n process.exitCode = 1;\n });\n\n /** Find the job name from argv (first non-flag argument after the entry script) */\n const jobName = findJobName(process.argv);\n\n /** Handle --list: discover and print all available jobs */\n if (jobName === \"--list\" || process.argv.includes(\"--list\")) {\n const jobs = await discoverJobs(jobsDirectory);\n if (jobs.length === 0) {\n consola.info(\"No jobs found.\");\n } else {\n consola.info(\"Available jobs:\");\n for (const job of jobs) {\n consola.log(` ${job.name}`);\n }\n }\n return;\n }\n\n /** Handle --interactive: guide user through job selection and args */\n if (process.argv.includes(\"--interactive\") || process.argv.includes(\"-i\")) {\n await runInteractive(options);\n return;\n }\n\n if (!jobName) {\n const prefix = options.commandPrefix ?? \"<command>\";\n logger.error(\n \"No job name provided.\\n\\n\" +\n `Usage: ${prefix} <job-name> [options]\\n` +\n ` ${prefix} --list\\n`,\n );\n process.exit(1);\n }\n\n /** Build the module path (add .mjs extension for ESM resolution) */\n const parts = jobName.split(\"/\");\n const fileName = parts.pop() ?? \"\";\n const subDirectories = parts;\n const fileLocation = path.join(jobsDirectory, ...subDirectories);\n const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);\n\n /** Get argv after the job name (flags to pass to the job) */\n const jobArgvIndex = process.argv.indexOf(jobName);\n const argv = jobArgvIndex >= 0 ? process.argv.slice(jobArgvIndex + 1) : [];\n\n /** Check for --help before doing initialization (faster help output) */\n const isHelp = argv.includes(\"--help\") || argv.includes(\"-h\");\n\n if (!isHelp && initialize) {\n await initialize();\n }\n\n if (!isHelp) {\n logger.info(`Executing: ${jobName}`);\n }\n\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default;\n\n if (typeof fn !== \"function\") {\n logger.error(\n `Module ${modulePath} does not have a default export function`,\n );\n process.exit(1);\n }\n\n const start = performance.now();\n\n await (\n fn as (\n argv: string[],\n jobName: string,\n commandPrefix?: string,\n ) => Promise<void>\n )(argv, jobName, options.commandPrefix);\n\n consola.info(`Completed in ${formatDuration(performance.now() - start)}`);\n process.exit(0);\n } catch (error) {\n const message =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n logger.error(message);\n process.exit(1);\n }\n}\n\n/**\n * Find the job name from argv.\n * Returns the first non-flag argument after the Node binary and entry script.\n */\nfunction findJobName(argv: string[]): string | undefined {\n const argsAfterEntry = argv.slice(2);\n return argsAfterEntry.find((arg) => !arg.startsWith(\"-\"));\n}\n\n/**\n * Run interactive mode: guide user through job selection and argument input.\n */\nasync function runInteractive(options: RunJobOptions): Promise<void> {\n const { jobsDirectory, initialize, logger = console } = options;\n\n /** Select job interactively */\n const jobName = await selectJob(jobsDirectory);\n\n consola.info(`Selected job: ${jobName}`);\n\n /** Load the job module to get its schema */\n const parts = jobName.split(\"/\");\n const fileName = parts.pop() ?? \"\";\n const subDirectories = parts;\n const fileLocation = path.join(jobsDirectory, ...subDirectories);\n const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);\n\n let schema: ZodObject<ZodRawShape> | undefined;\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default as JobFunction | undefined;\n schema = fn?.__metadata?.schema;\n } catch {\n /** Module might not exist yet or have errors - proceed without schema */\n }\n\n /** Prompt for arguments if schema exists */\n let args: Record<string, unknown> = {};\n if (schema && Object.keys(schema.shape).length > 0) {\n consola.info(\"Enter arguments for the job:\");\n args = await promptForArgs(schema);\n }\n\n /** Run initialization */\n if (initialize) {\n await initialize();\n }\n\n logger.info(`Executing: ${jobName}`);\n\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default;\n\n if (typeof fn !== \"function\") {\n logger.error(\n `Module ${modulePath} does not have a default export function`,\n );\n process.exit(1);\n }\n\n /** Build argv with --args JSON */\n const argv =\n Object.keys(args).length > 0 ? [\"--args\", JSON.stringify(args)] : [];\n\n const start = performance.now();\n\n await (\n fn as (\n argv: string[],\n jobName: string,\n commandPrefix?: string,\n ) => Promise<void>\n )(argv, jobName, options.commandPrefix);\n\n consola.info(`Completed in ${formatDuration(performance.now() - start)}`);\n process.exit(0);\n } catch (error) {\n const message =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n logger.error(message);\n process.exit(1);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAiBA,eAAsB,OAAO,SAAuC;CAClE,MAAM,EAAE,eAAe,YAAY,SAAS,YAAY;AAExD,SAAQ,GAAG,sBAAsB,QAAQ;EACvC,MAAM,UACJ,eAAe,QAAS,IAAI,SAAS,IAAI,UAAW,OAAO,IAAI;AACjE,SAAO,MAAM,uBAAuB,UAAU;AAC9C,UAAQ,WAAW;GACnB;;CAGF,MAAM,UAAU,YAAY,QAAQ,KAAK;;AAGzC,KAAI,YAAY,YAAY,QAAQ,KAAK,SAAS,SAAS,EAAE;EAC3D,MAAM,OAAO,MAAM,aAAa,cAAc;AAC9C,MAAI,KAAK,WAAW,EAClB,SAAQ,KAAK,iBAAiB;OACzB;AACL,WAAQ,KAAK,kBAAkB;AAC/B,QAAK,MAAM,OAAO,KAChB,SAAQ,IAAI,KAAK,IAAI,OAAO;;AAGhC;;;AAIF,KAAI,QAAQ,KAAK,SAAS,gBAAgB,IAAI,QAAQ,KAAK,SAAS,KAAK,EAAE;AACzE,QAAM,eAAe,QAAQ;AAC7B;;AAGF,KAAI,CAAC,SAAS;EACZ,MAAM,SAAS,QAAQ,iBAAiB;AACxC,SAAO,MACL;;SACY,OAAO,gCACP,OAAO,WACpB;AACD,UAAQ,KAAK,EAAE;;;CAIjB,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,MAAM,WAAW,MAAM,KAAK,IAAI;CAChC,MAAM,iBAAiB;CACvB,MAAM,eAAe,KAAK,KAAK,eAAe,GAAG,eAAe;CAChE,MAAM,aAAa,KAAK,QAAQ,cAAc,GAAG,SAAS,MAAM;;CAGhE,MAAM,eAAe,QAAQ,KAAK,QAAQ,QAAQ;CAClD,MAAM,OAAO,gBAAgB,IAAI,QAAQ,KAAK,MAAM,eAAe,EAAE,GAAG,EAAE;;CAG1E,MAAM,SAAS,KAAK,SAAS,SAAS,IAAI,KAAK,SAAS,KAAK;AAE7D,KAAI,CAAC,UAAU,WACb,OAAM,YAAY;AAGpB,KAAI,CAAC,OACH,QAAO,KAAK,cAAc,UAAU;AAGtC,KAAI;EAEF,MAAM,MADgB,MAAM,OAAO,aACX;AAExB,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAO,MACL,UAAU,WAAW,0CACtB;AACD,WAAQ,KAAK,EAAE;;EAGjB,MAAM,QAAQ,YAAY,KAAK;AAE/B,QACE,GAKA,MAAM,SAAS,QAAQ,cAAc;AAEvC,UAAQ,KAAK,gBAAgB,eAAe,YAAY,KAAK,GAAG,MAAM,GAAG;AACzE,UAAQ,KAAK,EAAE;UACR,OAAO;EACd,MAAM,UACJ,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,MAAM;AACzE,SAAO,MAAM,QAAQ;AACrB,UAAQ,KAAK,EAAE;;;;;;;AAQnB,SAAS,YAAY,MAAoC;AAEvD,QADuB,KAAK,MAAM,EAAE,CACd,MAAM,QAAQ,CAAC,IAAI,WAAW,IAAI,CAAC;;;;;AAM3D,eAAe,eAAe,SAAuC;CACnE,MAAM,EAAE,eAAe,YAAY,SAAS,YAAY;;CAGxD,MAAM,UAAU,MAAM,UAAU,cAAc;AAE9C,SAAQ,KAAK,iBAAiB,UAAU;;CAGxC,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,MAAM,WAAW,MAAM,KAAK,IAAI;CAChC,MAAM,iBAAiB;CACvB,MAAM,eAAe,KAAK,KAAK,eAAe,GAAG,eAAe;CAChE,MAAM,aAAa,KAAK,QAAQ,cAAc,GAAG,SAAS,MAAM;CAEhE,IAAI;AACJ,KAAI;AAGF,YAFsB,MAAM,OAAO,aACX,SACX,YAAY;SACnB;;CAKR,IAAI,OAAgC,EAAE;AACtC,KAAI,UAAU,OAAO,KAAK,OAAO,MAAM,CAAC,SAAS,GAAG;AAClD,UAAQ,KAAK,+BAA+B;AAC5C,SAAO,MAAM,cAAc,OAAO;;;AAIpC,KAAI,WACF,OAAM,YAAY;AAGpB,QAAO,KAAK,cAAc,UAAU;AAEpC,KAAI;EAEF,MAAM,MADgB,MAAM,OAAO,aACX;AAExB,MAAI,OAAO,OAAO,YAAY;AAC5B,UAAO,MACL,UAAU,WAAW,0CACtB;AACD,WAAQ,KAAK,EAAE;;;EAIjB,MAAM,OACJ,OAAO,KAAK,KAAK,CAAC,SAAS,IAAI,CAAC,UAAU,KAAK,UAAU,KAAK,CAAC,GAAG,EAAE;EAEtE,MAAM,QAAQ,YAAY,KAAK;AAE/B,QACE,GAKA,MAAM,SAAS,QAAQ,cAAc;AAEvC,UAAQ,KAAK,gBAAgB,eAAe,YAAY,KAAK,GAAG,MAAM,GAAG;AACzE,UAAQ,KAAK,EAAE;UACR,OAAO;EACd,MAAM,UACJ,iBAAiB,QAAS,MAAM,SAAS,MAAM,UAAW,OAAO,MAAM;AACzE,SAAO,MAAM,QAAQ;AACrB,UAAQ,KAAK,EAAE"}
|
|
1
|
+
{"version":3,"file":"run-job.mjs","names":[],"sources":["../src/run-job.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport path from \"node:path\";\nimport process from \"node:process\";\nimport { consola } from \"consola\";\nimport type { ZodObject, ZodRawShape } from \"zod\";\nimport { discoverJobs } from \"./discover-jobs\";\nimport { formatDuration } from \"./format\";\nimport { promptForArgs, selectJob } from \"./interactive\";\nimport type { JobFunction, RunJobOptions } from \"./types\";\n\n/**\n * Entry point for job execution. Each service calls this from its own thin\n * `cli/run-jobs.ts` wrapper with a configured jobsDirectory.\n *\n * Parses argv to determine the job name, handles --list for discovery,\n * optionally runs initialization, then dynamically imports and executes\n * the target job.\n */\nexport async function runJob(options: RunJobOptions): Promise<void> {\n const { jobsDirectory, initialize, logger = console } = options;\n\n process.on(\"uncaughtException\", (err) => {\n const message =\n err instanceof Error ? (err.stack ?? err.message) : String(err);\n logger.error(`Uncaught exception: ${message}`);\n process.exitCode = 1;\n });\n\n /** Find the job name from argv (first non-flag argument after the entry script) */\n const jobName = findJobName(process.argv);\n\n /** Handle --list: discover and print all available jobs */\n if (jobName === \"--list\" || process.argv.includes(\"--list\")) {\n const jobs = await discoverJobs(jobsDirectory);\n if (jobs.length === 0) {\n consola.info(\"No jobs found.\");\n } else {\n consola.info(\"Available jobs:\");\n for (const job of jobs) {\n consola.log(` ${job.name}`);\n }\n }\n return;\n }\n\n /** Handle --interactive: guide user through job selection and args */\n if (process.argv.includes(\"--interactive\") || process.argv.includes(\"-i\")) {\n await runInteractive(options);\n return;\n }\n\n if (!jobName) {\n const prefix = options.commandPrefix ?? \"<command>\";\n logger.error(\n \"No job name provided.\\n\\n\" +\n `Usage: ${prefix} <job-name> [options]\\n` +\n ` ${prefix} --list\\n`,\n );\n process.exit(1);\n }\n\n /** Build the module path (add .mjs extension for ESM resolution) */\n const parts = jobName.split(\"/\");\n const fileName = parts.pop() ?? \"\";\n const subDirectories = parts;\n const fileLocation = path.join(jobsDirectory, ...subDirectories);\n const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);\n\n /** Check if the job file exists before attempting to import */\n if (!existsSync(modulePath)) {\n const jobs = await discoverJobs(jobsDirectory);\n const matches = jobs.filter(\n (job) => job.name.split(\"/\").pop() === fileName,\n );\n\n let message = `Job \"${jobName}\" not found.\\n`;\n\n if (matches.length === 1) {\n message += `\\nDid you mean \"${matches[0]!.name}\"?\\n`;\n } else if (matches.length > 1) {\n message += \"\\nFound similar jobs:\\n\";\n for (const match of matches) {\n message += ` ${match.name}\\n`;\n }\n }\n\n message += \"\\nRun with --list to see all available jobs.\";\n\n logger.error(message);\n process.exit(1);\n }\n\n /** Get argv after the job name (flags to pass to the job) */\n const jobArgvIndex = process.argv.indexOf(jobName);\n const argv = jobArgvIndex >= 0 ? process.argv.slice(jobArgvIndex + 1) : [];\n\n /** Check for --help before doing initialization (faster help output) */\n const isHelp = argv.includes(\"--help\") || argv.includes(\"-h\");\n\n if (!isHelp && initialize) {\n await initialize();\n }\n\n if (!isHelp) {\n logger.info(`Executing: ${jobName}`);\n }\n\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default;\n\n if (typeof fn !== \"function\") {\n logger.error(\n `Module ${modulePath} does not have a default export function`,\n );\n process.exit(1);\n }\n\n const start = performance.now();\n\n await (\n fn as (\n argv: string[],\n jobName: string,\n commandPrefix?: string,\n ) => Promise<void>\n )(argv, jobName, options.commandPrefix);\n\n consola.info(`Completed in ${formatDuration(performance.now() - start)}`);\n process.exit(0);\n } catch (error) {\n const message =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n logger.error(message);\n process.exit(1);\n }\n}\n\n/**\n * Find the job name from argv.\n * Returns the first non-flag argument after the Node binary and entry script.\n */\nfunction findJobName(argv: string[]): string | undefined {\n const argsAfterEntry = argv.slice(2);\n return argsAfterEntry.find((arg) => !arg.startsWith(\"-\"));\n}\n\n/**\n * Run interactive mode: guide user through job selection and argument input.\n */\nasync function runInteractive(options: RunJobOptions): Promise<void> {\n const { jobsDirectory, initialize, logger = console } = options;\n\n /** Select job interactively */\n const jobName = await selectJob(jobsDirectory);\n\n consola.info(`Selected job: ${jobName}`);\n\n /** Load the job module to get its schema */\n const parts = jobName.split(\"/\");\n const fileName = parts.pop() ?? \"\";\n const subDirectories = parts;\n const fileLocation = path.join(jobsDirectory, ...subDirectories);\n const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);\n\n let schema: ZodObject<ZodRawShape> | undefined;\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default as JobFunction | undefined;\n schema = fn?.__metadata?.schema;\n } catch {\n /** Module might not exist yet or have errors - proceed without schema */\n }\n\n /** Prompt for arguments if schema exists */\n let args: Record<string, unknown> = {};\n if (schema && Object.keys(schema.shape).length > 0) {\n consola.info(\"Enter arguments for the job:\");\n args = await promptForArgs(schema);\n }\n\n /** Run initialization */\n if (initialize) {\n await initialize();\n }\n\n logger.info(`Executing: ${jobName}`);\n\n try {\n const moduleObject = (await import(modulePath)) as Record<string, unknown>;\n const fn = moduleObject.default;\n\n if (typeof fn !== \"function\") {\n logger.error(\n `Module ${modulePath} does not have a default export function`,\n );\n process.exit(1);\n }\n\n /** Build argv with --args JSON */\n const argv =\n Object.keys(args).length > 0 ? [\"--args\", JSON.stringify(args)] : [];\n\n const start = performance.now();\n\n await (\n fn as (\n argv: string[],\n jobName: string,\n commandPrefix?: string,\n ) => Promise<void>\n )(argv, jobName, options.commandPrefix);\n\n consola.info(`Completed in ${formatDuration(performance.now() - start)}`);\n process.exit(0);\n } catch (error) {\n const message =\n error instanceof Error ? (error.stack ?? error.message) : String(error);\n logger.error(message);\n process.exit(1);\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
package/src/run-job.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import process from "node:process";
|
|
3
4
|
import { consola } from "consola";
|
|
@@ -65,6 +66,30 @@ export async function runJob(options: RunJobOptions): Promise<void> {
|
|
|
65
66
|
const fileLocation = path.join(jobsDirectory, ...subDirectories);
|
|
66
67
|
const modulePath = path.resolve(fileLocation, `${fileName}.mjs`);
|
|
67
68
|
|
|
69
|
+
/** Check if the job file exists before attempting to import */
|
|
70
|
+
if (!existsSync(modulePath)) {
|
|
71
|
+
const jobs = await discoverJobs(jobsDirectory);
|
|
72
|
+
const matches = jobs.filter(
|
|
73
|
+
(job) => job.name.split("/").pop() === fileName,
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
let message = `Job "${jobName}" not found.\n`;
|
|
77
|
+
|
|
78
|
+
if (matches.length === 1) {
|
|
79
|
+
message += `\nDid you mean "${matches[0]!.name}"?\n`;
|
|
80
|
+
} else if (matches.length > 1) {
|
|
81
|
+
message += "\nFound similar jobs:\n";
|
|
82
|
+
for (const match of matches) {
|
|
83
|
+
message += ` ${match.name}\n`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
message += "\nRun with --list to see all available jobs.";
|
|
88
|
+
|
|
89
|
+
logger.error(message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
|
|
68
93
|
/** Get argv after the job name (flags to pass to the job) */
|
|
69
94
|
const jobArgvIndex = process.argv.indexOf(jobName);
|
|
70
95
|
const argv = jobArgvIndex >= 0 ? process.argv.slice(jobArgvIndex + 1) : [];
|