From ecdd4b6a71b2b61b4c81b9ea6ce6c73100dcea62 Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Thu, 19 Dec 2024 19:46:05 -0600 Subject: [PATCH 1/7] refactor: extract cli args before running cmd --- packages/scripts/src/cli.ts | 53 ++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 18 deletions(-) diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index c6a77cf3..e7cffd0d 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -9,22 +9,17 @@ import { runBuild } from "./commands/build"; import { ALL_PACKAGES, CATEGORY_VM } from "./common/cli.constants"; import { startDeleteFlow } from "./commands/delete"; import { log } from "./common/cli.utils"; +import { Options_Cli } from "./common/cli.types"; -const runScript = async () => { - const exitHelpfully = (msg?: string) => { - msg && log.error(msg); - console.log(program.helpInformation()); - process.exit(1); - }; - +const createProgram = () => { const program = new Command(); program.option( - `-e, --environment [${CATEGORY_VM.STAG}|${CATEGORY_VM.PROD}]`, + `-e, --environment [${CATEGORY_VM.STAG} | ${CATEGORY_VM.PROD}]`, "specify environment" ); program.option("-f, --force", "forces operation, no cautionary prompts"); program.option( - "-u, --user [id|email]", + "-u, --user [id | email]", "specifies which user to run script for" ); @@ -32,7 +27,7 @@ const runScript = async () => { .command("build") .description("build compass package(s)") .argument( - `[${ALL_PACKAGES.join("|")}]`, + `[${ALL_PACKAGES.join(" | ")}]`, "package(s) to build, separated by comma" ) .option("--skip-env", "skips copying env files to build"); @@ -40,30 +35,52 @@ const runScript = async () => { program .command("delete") .description("deletes users data from compass database"); + return program; +}; + +const exitHelpfully = (program: Command, msg?: string) => { + msg && log.error(msg); + console.log(program.helpInformation()); + process.exit(1); +}; + +const getCliOptions = (program: Command): Options_Cli => { + const _options = program.opts(); + const packages = program.args[1]?.split(","); + const options = { + ..._options, + packages, + force: _options["force"] === true, + user: _options["user"] as string, + }; + + return options; +}; + +const runScript = async () => { + const program = createProgram(); program.parse(process.argv); - const options = program.opts(); - const cmd = program.args[0]; + const options = getCliOptions(program); + const { user, force } = options; + const cmd = program.args[0]; switch (true) { case cmd === "build": { await runBuild(options); break; } case cmd === "delete": { - const force = options["force"] as boolean; - const user = options["user"] as string; - if (!user || typeof user !== "string") { - exitHelpfully("You must supply a user"); + exitHelpfully(program, "You must supply a user"); } - await startDeleteFlow(user, force); + await startDeleteFlow(user as string, force); break; } default: - exitHelpfully("Unsupported cmd"); + exitHelpfully(program, "Unsupported cmd"); } }; From a7706ca5e21df230517cb96cb0332931e3fe6882 Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Fri, 20 Dec 2024 06:27:07 -0600 Subject: [PATCH 2/7] feat: validate pckgs before building --- packages/scripts/src/cli.ts | 18 +++++++++++++++++- packages/scripts/src/commands/build.ts | 17 +++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index e7cffd0d..56a68139 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -6,7 +6,7 @@ dotenv.config({ import { Command } from "commander"; import { runBuild } from "./commands/build"; -import { ALL_PACKAGES, CATEGORY_VM } from "./common/cli.constants"; +import { ALL_PACKAGES, CATEGORY_VM, PCKG } from "./common/cli.constants"; import { startDeleteFlow } from "./commands/delete"; import { log } from "./common/cli.utils"; import { Options_Cli } from "./common/cli.types"; @@ -58,6 +58,21 @@ const getCliOptions = (program: Command): Options_Cli => { return options; }; +const validatePackages = (packages: string[] | undefined) => { + if (!packages) { + log.error("Packages must be defined"); + } + if (!packages?.includes(PCKG.NODE) && !packages?.includes(PCKG.WEB)) { + log.error( + `One or more of these pckgs isn't supported: ${( + packages as string[] + )?.toString()}` + ); + + process.exit(1); + } +}; + const runScript = async () => { const program = createProgram(); program.parse(process.argv); @@ -68,6 +83,7 @@ const runScript = async () => { const cmd = program.args[0]; switch (true) { case cmd === "build": { + validatePackages(options.packages); await runBuild(options); break; } diff --git a/packages/scripts/src/commands/build.ts b/packages/scripts/src/commands/build.ts index adb014a8..e3850ca1 100644 --- a/packages/scripts/src/commands/build.ts +++ b/packages/scripts/src/commands/build.ts @@ -18,24 +18,21 @@ import { } from "@scripts/common/cli.utils"; export const runBuild = async (options: Options_Cli) => { - const env = options["environment"]; - const vmInfo = await getVmInfo(env); + const vmInfo = await getVmInfo(options.environment); const pckgs = - options["packages"] === undefined + options?.packages?.length === 0 ? await getPckgsTo("build") - : options["packages"]; + : (options.packages as string[]); if (pckgs.includes(PCKG.NODE)) { - await buildNodePckgs(vmInfo, options["skipEnv"]); + await buildNodePckgs(vmInfo, options.skipEnv); } - if (pckgs.includes(PCKG.WEB)) { await buildWeb(vmInfo); } }; -// eslint-disable-next-line @typescript-eslint/require-await const buildNodePckgs = async (vmInfo: Info_VM, skipEnv?: boolean) => { removeOldBuildFor(PCKG.NODE); createNodeDirs(); @@ -78,10 +75,10 @@ const buildWeb = async (vmInfo: Info_VM) => { log.success(`Done building web files.`); log.tip(` Now you'll probably want to: - - zip the build/web dir + - zip the build dir - copy it to your ${destination} server - - unzip it - - run it`); + - unzip it and serve as the static assets + `); process.exit(0); }; From a0faf3702ad1f8fb20957dc69de6c2e80abf3bc6 Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Sat, 21 Dec 2024 07:23:40 -0600 Subject: [PATCH 3/7] chore: update delete cmd description --- packages/scripts/src/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index 56a68139..33275df2 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -34,7 +34,7 @@ const createProgram = () => { program .command("delete") - .description("deletes users data from compass database"); + .description("delete user data from compass database"); return program; }; From 407ef2d77f4432632d4aa0b6687bca372e38b570 Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Sat, 21 Dec 2024 07:24:07 -0600 Subject: [PATCH 4/7] feat: support force option when building node pckgs --- packages/scripts/src/commands/build.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/packages/scripts/src/commands/build.ts b/packages/scripts/src/commands/build.ts index e3850ca1..889f9223 100644 --- a/packages/scripts/src/commands/build.ts +++ b/packages/scripts/src/commands/build.ts @@ -26,17 +26,17 @@ export const runBuild = async (options: Options_Cli) => { : (options.packages as string[]); if (pckgs.includes(PCKG.NODE)) { - await buildNodePckgs(vmInfo, options.skipEnv); + await buildNodePckgs(vmInfo, options); } if (pckgs.includes(PCKG.WEB)) { await buildWeb(vmInfo); } }; -const buildNodePckgs = async (vmInfo: Info_VM, skipEnv?: boolean) => { +const buildNodePckgs = async (vmInfo: Info_VM, options: Options_Cli) => { removeOldBuildFor(PCKG.NODE); createNodeDirs(); - await copyNodeConfigsToBuild(vmInfo, skipEnv); + await copyNodeConfigsToBuild(vmInfo, options.skipEnv, options.force); log.info("Compiling node packages ..."); shell.exec( @@ -82,7 +82,11 @@ const buildWeb = async (vmInfo: Info_VM) => { process.exit(0); }; -const copyNodeConfigsToBuild = async (vmInfo: Info_VM, skipEnv?: boolean) => { +const copyNodeConfigsToBuild = async ( + vmInfo: Info_VM, + skipEnv?: boolean, + force?: boolean +) => { const envName = vmInfo.destination === "production" ? ".prod.env" : ".env"; const envPath = `${COMPASS_ROOT_DEV}/packages/backend/${envName}`; @@ -96,7 +100,9 @@ const copyNodeConfigsToBuild = async (vmInfo: Info_VM, skipEnv?: boolean) => { log.warning(`Env file does not exist: ${envPath}`); const keepGoing = - skipEnv === true ? true : await _confirm("Continue anyway?"); + skipEnv === true || force === true + ? true + : await _confirm("Continue anyway?"); if (!keepGoing) { log.error("Exiting due to missing env file"); From 2bda7f0f02a2db62bb5aba25a08715215fd1ec5f Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Sun, 29 Dec 2024 07:51:05 -0600 Subject: [PATCH 5/7] refactor(cli): convert CLI to class and use zod for validation --- packages/scripts/src/cli.ts | 164 ++++++++++++----------- packages/scripts/src/commands/build.ts | 44 +++--- packages/scripts/src/common/cli.types.ts | 25 ++-- packages/scripts/src/common/cli.utils.ts | 38 +++--- 4 files changed, 141 insertions(+), 130 deletions(-) diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index 33275df2..1ced114c 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -6,101 +6,113 @@ dotenv.config({ import { Command } from "commander"; import { runBuild } from "./commands/build"; -import { ALL_PACKAGES, CATEGORY_VM, PCKG } from "./common/cli.constants"; +import { ALL_PACKAGES, CATEGORY_VM } from "./common/cli.constants"; import { startDeleteFlow } from "./commands/delete"; +import { Options_Cli, Schema_Options_Cli } from "./common/cli.types"; import { log } from "./common/cli.utils"; -import { Options_Cli } from "./common/cli.types"; -const createProgram = () => { - const program = new Command(); - program.option( - `-e, --environment [${CATEGORY_VM.STAG} | ${CATEGORY_VM.PROD}]`, - "specify environment" - ); - program.option("-f, --force", "forces operation, no cautionary prompts"); - program.option( - "-u, --user [id | email]", - "specifies which user to run script for" - ); +class CompassCli { + private program: Command; + private options: Options_Cli; - program - .command("build") - .description("build compass package(s)") - .argument( - `[${ALL_PACKAGES.join(" | ")}]`, - "package(s) to build, separated by comma" - ) - .option("--skip-env", "skips copying env files to build"); + constructor(args: string[]) { + this.program = this.createProgram(); + this.program.parse(args); + this.options = this.getCliOptions(); + } - program - .command("delete") - .description("delete user data from compass database"); - return program; -}; + private createProgram(): Command { + const program = new Command(); + program.option( + `-e, --environment [${CATEGORY_VM.STAG} | ${CATEGORY_VM.PROD}]`, + "specify environment" + ); + program.option("-f, --force", "force operation, no cautionary prompts"); + program.option( + "-u, --user [id | email]", + "specify which user to run script for" + ); -const exitHelpfully = (program: Command, msg?: string) => { - msg && log.error(msg); - console.log(program.helpInformation()); - process.exit(1); -}; + program + .command("build") + .description("build compass package(s)") + .argument( + `[${ALL_PACKAGES.join(" | ")}]`, + "package(s) to build, separated by comma" + ) + .option("--skip-env", "skip copying env files to build"); -const getCliOptions = (program: Command): Options_Cli => { - const _options = program.opts(); - const packages = program.args[1]?.split(","); + program + .command("delete") + .description("delete user data from compass database"); + return program; + } - const options = { - ..._options, - packages, - force: _options["force"] === true, - user: _options["user"] as string, - }; + private getCliOptions(): Options_Cli { + const _options = this.program.opts(); + const packages = this.program.args[1]?.split(","); + const options: Options_Cli = { + ..._options, + force: _options["force"] === true, + packages, + }; - return options; -}; + const { data, error } = Schema_Options_Cli.safeParse(options); + if (error) { + log.error(`Invalid CLI options: ${JSON.stringify(error.format())}`); + process.exit(1); + } -const validatePackages = (packages: string[] | undefined) => { - if (!packages) { - log.error("Packages must be defined"); + return data; } - if (!packages?.includes(PCKG.NODE) && !packages?.includes(PCKG.WEB)) { - log.error( - `One or more of these pckgs isn't supported: ${( - packages as string[] - )?.toString()}` - ); - process.exit(1); + private validatePackages(packages: string[] | undefined) { + if (!packages) { + log.error("Packages must be defined"); + process.exit(1); + } + const unsupportedPackages = packages.filter( + (pkg) => !ALL_PACKAGES.includes(pkg) + ); + if (unsupportedPackages.length > 0) { + log.error( + `One or more of these packages isn't supported: ${unsupportedPackages.toString()}` + ); + process.exit(1); + } } -}; - -const runScript = async () => { - const program = createProgram(); - program.parse(process.argv); - const options = getCliOptions(program); - const { user, force } = options; + public async run() { + const { user, force, packages } = this.options; + const cmd = this.program.args[0]; - const cmd = program.args[0]; - switch (true) { - case cmd === "build": { - validatePackages(options.packages); - await runBuild(options); - break; - } - case cmd === "delete": { - if (!user || typeof user !== "string") { - exitHelpfully(program, "You must supply a user"); + switch (true) { + case cmd === "build": { + this.validatePackages(packages); + await runBuild(this.options); + break; } - - await startDeleteFlow(user as string, force); - break; + case cmd === "delete": { + if (!user || typeof user !== "string") { + this.exitHelpfully("You must supply a user"); + } + await startDeleteFlow(user as string, force); + break; + } + default: + this.exitHelpfully("Unsupported cmd"); } - default: - exitHelpfully(program, "Unsupported cmd"); } -}; -runScript().catch((err) => { + private exitHelpfully(msg?: string) { + msg && log.error(msg); + console.log(this.program.helpInformation()); + process.exit(1); + } +} + +const cli = new CompassCli(process.argv); +cli.run().catch((err) => { console.log(err); process.exit(1); }); diff --git a/packages/scripts/src/commands/build.ts b/packages/scripts/src/commands/build.ts index 889f9223..3c441294 100644 --- a/packages/scripts/src/commands/build.ts +++ b/packages/scripts/src/commands/build.ts @@ -1,7 +1,7 @@ import dotenv from "dotenv"; import path from "path"; import shell from "shelljs"; -import { Options_Cli, Info_VM } from "@scripts/common/cli.types"; +import { Options_Cli } from "@scripts/common/cli.types"; import { COMPASS_BUILD_DEV, COMPASS_ROOT_DEV, @@ -9,34 +9,33 @@ import { PCKG, } from "@scripts/common/cli.constants"; import { - getVmInfo, getPckgsTo, _confirm, log, fileExists, getClientId, + getApiBaseUrl, + getEnvironmentAnswer, } from "@scripts/common/cli.utils"; export const runBuild = async (options: Options_Cli) => { - const vmInfo = await getVmInfo(options.environment); - const pckgs = options?.packages?.length === 0 ? await getPckgsTo("build") : (options.packages as string[]); if (pckgs.includes(PCKG.NODE)) { - await buildNodePckgs(vmInfo, options); + await buildNodePckgs(options); } if (pckgs.includes(PCKG.WEB)) { - await buildWeb(vmInfo); + await buildWeb(options); } }; -const buildNodePckgs = async (vmInfo: Info_VM, options: Options_Cli) => { +const buildNodePckgs = async (options: Options_Cli) => { removeOldBuildFor(PCKG.NODE); createNodeDirs(); - await copyNodeConfigsToBuild(vmInfo, options.skipEnv, options.force); + await copyNodeConfigsToBuild(options); log.info("Compiling node packages ..."); shell.exec( @@ -55,11 +54,15 @@ const buildNodePckgs = async (vmInfo: Info_VM, options: Options_Cli) => { ); }; -const buildWeb = async (vmInfo: Info_VM) => { - const { baseUrl, destination } = vmInfo; - const envFile = destination === "staging" ? ".env" : ".env.prod"; +const buildWeb = async (options: Options_Cli) => { + const environment = + options.environment !== undefined + ? options.environment + : await getEnvironmentAnswer(); - const gClientId = await getClientId(destination); + const envFile = environment === "staging" ? ".env" : ".env.prod"; + const baseUrl = await getApiBaseUrl(environment); + const gClientId = await getClientId(environment); const envPath = path.join(__dirname, "..", "..", "..", "backend", envFile); dotenv.config({ path: envPath }); @@ -76,18 +79,15 @@ const buildWeb = async (vmInfo: Info_VM) => { log.tip(` Now you'll probably want to: - zip the build dir - - copy it to your ${destination} server - - unzip it and serve as the static assets + - copy it to your ${environment} environment + - unzip it to expose the static assets + - serve assets `); process.exit(0); }; -const copyNodeConfigsToBuild = async ( - vmInfo: Info_VM, - skipEnv?: boolean, - force?: boolean -) => { - const envName = vmInfo.destination === "production" ? ".prod.env" : ".env"; +const copyNodeConfigsToBuild = async (options: Options_Cli) => { + const envName = options.environment === "production" ? ".prod.env" : ".env"; const envPath = `${COMPASS_ROOT_DEV}/packages/backend/${envName}`; @@ -100,9 +100,7 @@ const copyNodeConfigsToBuild = async ( log.warning(`Env file does not exist: ${envPath}`); const keepGoing = - skipEnv === true || force === true - ? true - : await _confirm("Continue anyway?"); + options.force === true ? true : await _confirm("Continue anyway?"); if (!keepGoing) { log.error("Exiting due to missing env file"); diff --git a/packages/scripts/src/common/cli.types.ts b/packages/scripts/src/common/cli.types.ts index c08c9b89..cc9465fa 100644 --- a/packages/scripts/src/common/cli.types.ts +++ b/packages/scripts/src/common/cli.types.ts @@ -1,16 +1,13 @@ -export type Category_VM = "staging" | "production"; +import { z } from "zod"; -export interface Info_VM { - baseUrl: string; - destination: Category_VM; -} +export type Environment_Cli = "staging" | "production"; -export interface Options_Cli { - build?: boolean; - delete?: boolean; - environment?: Category_VM; - force?: boolean; - packages?: string[]; - skipEnv?: boolean; - user?: string; -} +export const Schema_Options_Cli = z.object({ + clientId: z.string().optional(), + environment: z.enum(["staging", "production"]).optional(), + force: z.boolean().optional(), + packages: z.array(z.string()).optional(), + user: z.string().optional(), +}); + +export type Options_Cli = z.infer; diff --git a/packages/scripts/src/common/cli.utils.ts b/packages/scripts/src/common/cli.utils.ts index 98ac9443..18ed41d4 100644 --- a/packages/scripts/src/common/cli.utils.ts +++ b/packages/scripts/src/common/cli.utils.ts @@ -4,18 +4,27 @@ const { prompt } = pkg; import shell from "shelljs"; import { ALL_PACKAGES, CLI_ENV } from "./cli.constants"; -import { Category_VM } from "./cli.types"; +import { Environment_Cli } from "./cli.types"; export const fileExists = (file: string) => { return shell.test("-e", file); }; -export const getClientId = async (destination: Category_VM) => { - if (destination === "staging") { +export const getApiBaseUrl = async (environment: Environment_Cli) => { + const category = environment ? environment : await getEnvironmentAnswer(); + const isStaging = category === "staging"; + const domain = await getDomainAnswer(isStaging); + const baseUrl = `https://${domain}/api`; + + return baseUrl; +}; + +export const getClientId = async (environment: Environment_Cli) => { + if (environment === "staging") { return process.env["CLIENT_ID"] as string; } - if (destination === "production") { + if (environment === "production") { const q = `Enter the googleClientId for the production environment:`; return prompt([{ type: "input", name: "answer", message: q }]) @@ -58,22 +67,17 @@ const getDomainAnswer = async (isStaging: boolean) => { process.exit(1); }); }; -export const getVmInfo = async (environment?: Category_VM) => { - const destination = environment - ? environment - : ((await getListAnswer("Select environment to use:", [ - "staging", - "production", - ])) as Category_VM); - - const isStaging = destination === "staging"; - const domain = await getDomainAnswer(isStaging); - const baseUrl = `https://${domain}/api`; - return { baseUrl, destination }; +export const getEnvironmentAnswer = async (): Promise => { + const environment = (await getListAnswer("Select environment to use:", [ + "staging", + "production", + ])) as Environment_Cli; + + return environment; }; -const getListAnswer = async (question: string, choices: string[]) => { +export const getListAnswer = async (question: string, choices: string[]) => { const q = [ { type: "list", From 84a589a04790c5a8778522e608bc484f0e81f8d5 Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Mon, 30 Dec 2024 07:17:07 -0600 Subject: [PATCH 6/7] feat(cli): add clientId as build option --- .../src/common/constants/env.constants.ts | 2 +- packages/scripts/src/cli.ts | 55 ++++++------------- packages/scripts/src/commands/build.ts | 4 +- packages/scripts/src/common/cli.utils.ts | 47 +++++++++++++++- 4 files changed, 68 insertions(+), 40 deletions(-) diff --git a/packages/backend/src/common/constants/env.constants.ts b/packages/backend/src/common/constants/env.constants.ts index 2edea214..dd1424e1 100644 --- a/packages/backend/src/common/constants/env.constants.ts +++ b/packages/backend/src/common/constants/env.constants.ts @@ -10,7 +10,7 @@ if (!Object.values(NodeEnv).includes(_nodeEnv)) { throw new Error(`Invalid NODE_ENV value: '${_nodeEnv}'`); } -const IS_DEV = isDev(_nodeEnv); +export const IS_DEV = isDev(_nodeEnv); const EnvSchema = z .object({ diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index 1ced114c..48490293 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -8,8 +8,13 @@ import { Command } from "commander"; import { runBuild } from "./commands/build"; import { ALL_PACKAGES, CATEGORY_VM } from "./common/cli.constants"; import { startDeleteFlow } from "./commands/delete"; -import { Options_Cli, Schema_Options_Cli } from "./common/cli.types"; -import { log } from "./common/cli.utils"; +import { Options_Cli } from "./common/cli.types"; +import { + log, + mergeOptions, + validateOptions, + validatePackages, +} from "./common/cli.utils"; class CompassCli { private program: Command; @@ -29,18 +34,21 @@ class CompassCli { ); program.option("-f, --force", "force operation, no cautionary prompts"); program.option( - "-u, --user [id | email]", + "--user [id | email]", "specify which user to run script for" ); program .command("build") - .description("build compass package(s)") + .description("build compass package") .argument( `[${ALL_PACKAGES.join(" | ")}]`, - "package(s) to build, separated by comma" + "package to build (only provde 1 at a time)" ) - .option("--skip-env", "skip copying env files to build"); + .option( + "-c, --clientId ", + "google client id to inject into build" + ); program .command("delete") @@ -49,37 +57,10 @@ class CompassCli { } private getCliOptions(): Options_Cli { - const _options = this.program.opts(); - const packages = this.program.args[1]?.split(","); - const options: Options_Cli = { - ..._options, - force: _options["force"] === true, - packages, - }; - - const { data, error } = Schema_Options_Cli.safeParse(options); - if (error) { - log.error(`Invalid CLI options: ${JSON.stringify(error.format())}`); - process.exit(1); - } - - return data; - } + const options = mergeOptions(this.program); + const validOptions = validateOptions(options); - private validatePackages(packages: string[] | undefined) { - if (!packages) { - log.error("Packages must be defined"); - process.exit(1); - } - const unsupportedPackages = packages.filter( - (pkg) => !ALL_PACKAGES.includes(pkg) - ); - if (unsupportedPackages.length > 0) { - log.error( - `One or more of these packages isn't supported: ${unsupportedPackages.toString()}` - ); - process.exit(1); - } + return validOptions; } public async run() { @@ -88,7 +69,7 @@ class CompassCli { switch (true) { case cmd === "build": { - this.validatePackages(packages); + validatePackages(packages); await runBuild(this.options); break; } diff --git a/packages/scripts/src/commands/build.ts b/packages/scripts/src/commands/build.ts index 3c441294..fb2fdf00 100644 --- a/packages/scripts/src/commands/build.ts +++ b/packages/scripts/src/commands/build.ts @@ -62,7 +62,9 @@ const buildWeb = async (options: Options_Cli) => { const envFile = environment === "staging" ? ".env" : ".env.prod"; const baseUrl = await getApiBaseUrl(environment); - const gClientId = await getClientId(environment); + const gClientId = options.clientId + ? options.clientId + : await getClientId(environment); const envPath = path.join(__dirname, "..", "..", "..", "backend", envFile); dotenv.config({ path: envPath }); diff --git a/packages/scripts/src/common/cli.utils.ts b/packages/scripts/src/common/cli.utils.ts index 18ed41d4..bf9e8aca 100644 --- a/packages/scripts/src/common/cli.utils.ts +++ b/packages/scripts/src/common/cli.utils.ts @@ -2,9 +2,10 @@ import pkg from "inquirer"; import chalk from "chalk"; const { prompt } = pkg; import shell from "shelljs"; +import { Command } from "commander"; import { ALL_PACKAGES, CLI_ENV } from "./cli.constants"; -import { Environment_Cli } from "./cli.types"; +import { Environment_Cli, Options_Cli, Schema_Options_Cli } from "./cli.types"; export const fileExists = (file: string) => { return shell.test("-e", file); @@ -128,6 +129,24 @@ export const log = { tip: (msg: string) => console.log(chalk.hex("#f5c150")(msg)), }; +export const mergeOptions = (program: Command): Options_Cli => { + const _options = program.opts(); + const packages = program.args[1]?.split(","); + const options: Options_Cli = { + ..._options, + force: _options["force"] === true, + packages, + }; + + const build = program.commands.find((cmd) => cmd.name() === "build"); + const clientId = build?.opts()["clientId"] as string; + if (build && clientId) { + options.clientId = clientId; + } + + return options; +}; + export const _confirm = async (question: string, _default = true) => { const q = [ { @@ -144,3 +163,29 @@ export const _confirm = async (question: string, _default = true) => { process.exit(1); }); }; + +export const validateOptions = (options: Options_Cli): Options_Cli => { + const { data, error } = Schema_Options_Cli.safeParse(options); + if (error) { + log.error(`Invalid CLI options: ${JSON.stringify(error.format())}`); + process.exit(1); + } + + return data; +}; + +export const validatePackages = (packages: string[] | undefined) => { + if (!packages) { + log.error("Packages must be defined"); + process.exit(1); + } + const unsupportedPackages = packages.filter( + (pkg) => !ALL_PACKAGES.includes(pkg) + ); + if (unsupportedPackages.length > 0) { + log.error( + `One or more of these packages isn't supported: ${unsupportedPackages.toString()}` + ); + process.exit(1); + } +}; From 1e7c7a51e206e0af0df91f9c9f4525fe9ac8b84d Mon Sep 17 00:00:00 2001 From: Tyler Dane Date: Mon, 30 Dec 2024 07:50:34 -0600 Subject: [PATCH 7/7] fix(cli): validate packages after they're provided --- packages/scripts/src/cli.ts | 3 +-- packages/scripts/src/commands/build.ts | 7 +++---- packages/scripts/src/common/cli.utils.ts | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/scripts/src/cli.ts b/packages/scripts/src/cli.ts index 48490293..c342ba88 100644 --- a/packages/scripts/src/cli.ts +++ b/packages/scripts/src/cli.ts @@ -64,12 +64,11 @@ class CompassCli { } public async run() { - const { user, force, packages } = this.options; + const { user, force } = this.options; const cmd = this.program.args[0]; switch (true) { case cmd === "build": { - validatePackages(packages); await runBuild(this.options); break; } diff --git a/packages/scripts/src/commands/build.ts b/packages/scripts/src/commands/build.ts index fb2fdf00..d3a3aaca 100644 --- a/packages/scripts/src/commands/build.ts +++ b/packages/scripts/src/commands/build.ts @@ -16,13 +16,12 @@ import { getClientId, getApiBaseUrl, getEnvironmentAnswer, + validatePackages, } from "@scripts/common/cli.utils"; export const runBuild = async (options: Options_Cli) => { - const pckgs = - options?.packages?.length === 0 - ? await getPckgsTo("build") - : (options.packages as string[]); + const pckgs = options.packages ? options.packages : await getPckgsTo("build"); + validatePackages(pckgs); if (pckgs.includes(PCKG.NODE)) { await buildNodePckgs(options); diff --git a/packages/scripts/src/common/cli.utils.ts b/packages/scripts/src/common/cli.utils.ts index bf9e8aca..297690db 100644 --- a/packages/scripts/src/common/cli.utils.ts +++ b/packages/scripts/src/common/cli.utils.ts @@ -176,7 +176,7 @@ export const validateOptions = (options: Options_Cli): Options_Cli => { export const validatePackages = (packages: string[] | undefined) => { if (!packages) { - log.error("Packages must be defined"); + log.error("Package must be defined"); process.exit(1); } const unsupportedPackages = packages.filter(