From bad1915d2a9b2574bedde4e6f23ac48592ccb7e3 Mon Sep 17 00:00:00 2001 From: omsherikar Date: Thu, 30 Oct 2025 00:10:02 +0530 Subject: [PATCH] feat(cli): add --dry-run to run --- packages/cli/src/cli/cmd/run/_types.ts | 1 + packages/cli/src/cli/cmd/run/index.ts | 14 ++++++++++++++ packages/cli/src/cli/cmd/run/setup.ts | 8 ++++++-- packages/cli/src/cli/utils/ui.ts | 15 +++++++++++++++ 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/packages/cli/src/cli/cmd/run/_types.ts b/packages/cli/src/cli/cmd/run/_types.ts index d5ea1316d..94b5b790d 100644 --- a/packages/cli/src/cli/cmd/run/_types.ts +++ b/packages/cli/src/cli/cmd/run/_types.ts @@ -43,6 +43,7 @@ export const flagsSchema = z.object({ apiKey: z.string().optional(), force: z.boolean().optional(), frozen: z.boolean().optional(), + dryRun: z.boolean().optional(), verbose: z.boolean().optional(), strict: z.boolean().optional(), interactive: z.boolean().default(false), diff --git a/packages/cli/src/cli/cmd/run/index.ts b/packages/cli/src/cli/cmd/run/index.ts index 92be8d33e..cebdd4999 100644 --- a/packages/cli/src/cli/cmd/run/index.ts +++ b/packages/cli/src/cli/cmd/run/index.ts @@ -16,6 +16,7 @@ import { renderHero, pauseIfDebug, renderSummary, + renderDryRun, } from "../../utils/ui"; import trackEvent from "../../utils/observability"; import { determineAuthId } from "./_utils"; @@ -94,6 +95,10 @@ export default new Command() "--frozen", "Validate translations are up-to-date without making changes - fails if source files, target files, or lockfile are out of sync. Ideal for CI/CD to ensure translation consistency before deployment", ) + .option( + "--dry-run", + "Preview planned changes without writing any files or checksums", + ) .option( "--api-key ", "Override API key from settings or environment variables", @@ -152,6 +157,15 @@ export default new Command() await frozen(ctx); await renderSpacer(); + if (ctx.flags.dryRun) { + await renderDryRun(ctx.tasks); + await trackEvent(authId, "cmd.run.success", { + config: ctx.config, + flags: ctx.flags, + }); + return; + } + await execute(ctx); await renderSpacer(); diff --git a/packages/cli/src/cli/cmd/run/setup.ts b/packages/cli/src/cli/cmd/run/setup.ts index ca6652109..506e71739 100644 --- a/packages/cli/src/cli/cmd/run/setup.ts +++ b/packages/cli/src/cli/cmd/run/setup.ts @@ -49,6 +49,7 @@ export default async function setup(input: CmdRunContext) { }, { title: "Selecting localization provider", + enabled: (ctx) => !ctx.flags.dryRun, task: async (ctx, task) => { ctx.localizer = createLocalizer( ctx.config?.provider, @@ -67,7 +68,8 @@ export default async function setup(input: CmdRunContext) { }, { title: "Checking authentication", - enabled: (ctx) => ctx.localizer?.id === "Lingo.dev", + enabled: (ctx) => + !ctx.flags.dryRun && ctx.localizer?.id === "Lingo.dev", task: async (ctx, task) => { const authStatus = await ctx.localizer!.checkAuth(); if (!authStatus.authenticated) { @@ -80,7 +82,8 @@ export default async function setup(input: CmdRunContext) { }, { title: "Validating configuration", - enabled: (ctx) => ctx.localizer?.id !== "Lingo.dev", + enabled: (ctx) => + !ctx.flags.dryRun && ctx.localizer?.id !== "Lingo.dev", task: async (ctx, task) => { const validationStatus = await ctx.localizer!.validateSettings!(); if (!validationStatus.valid) { @@ -93,6 +96,7 @@ export default async function setup(input: CmdRunContext) { }, { title: "Initializing localization provider", + enabled: (ctx) => !ctx.flags.dryRun, async task(ctx, task) { const isLingoDotDev = ctx.localizer!.id === "Lingo.dev"; diff --git a/packages/cli/src/cli/utils/ui.ts b/packages/cli/src/cli/utils/ui.ts index fdab3c0c0..ebba77838 100644 --- a/packages/cli/src/cli/utils/ui.ts +++ b/packages/cli/src/cli/utils/ui.ts @@ -3,6 +3,7 @@ import figlet from "figlet"; import { vice } from "gradient-string"; import readline from "readline"; import { colors } from "../constants"; +import type { CmdRunTask } from "../cmd/run/_types"; export async function renderClear() { console.log("\x1Bc"); @@ -137,3 +138,17 @@ export async function renderSummary(results: Map) { } } } + +export async function renderDryRun(tasks: CmdRunTask[]) { + console.log(chalk.hex(colors.orange)("[Dry Run]")); + if (!tasks.length) { + console.log(chalk.dim("No tasks would be executed.")); + return; + } + for (const t of tasks) { + const displayPath = t.bucketPathPattern.replace("[locale]", t.targetLocale); + console.log( + ` • ${chalk.dim(displayPath)} ${chalk.hex(colors.yellow)(`(${t.sourceLocale} → ${t.targetLocale})`)}`, + ); + } +}