diff --git a/.changeset/cool-stingrays-invent.md b/.changeset/cool-stingrays-invent.md new file mode 100644 index 00000000000..f283a24fd80 --- /dev/null +++ b/.changeset/cool-stingrays-invent.md @@ -0,0 +1,5 @@ +--- +"@trigger.dev/cli": patch +--- + +allow users to update trigger-dev packages to a specific version diff --git a/packages/cli/src/cli/index.ts b/packages/cli/src/cli/index.ts index da8b68590da..89829eb4ff7 100644 --- a/packages/cli/src/cli/index.ts +++ b/packages/cli/src/cli/index.ts @@ -93,10 +93,13 @@ program program .command("update") - .description("Updates all @trigger.dev/* packages to their latest compatible versions") + .description( + "Updates all @trigger.dev/* packages to their latest compatible versions or the specified version" + ) .argument("[path]", "The path to the directory that contains the package.json file", ".") - .action(async (path) => { - await updateCommand(path); + .option("--to ", "The version to update to (ex: 2.1.4)", "latest") + .action(async (path, options) => { + await updateCommand(path, options); }); program diff --git a/packages/cli/src/commands/update.ts b/packages/cli/src/commands/update.ts index ff6ed559ff1..e6a81c47d54 100644 --- a/packages/cli/src/commands/update.ts +++ b/packages/cli/src/commands/update.ts @@ -4,8 +4,24 @@ import { run, RunOptions } from "npm-check-updates"; import { installDependencies } from "../utils/installDependencies.js"; import { readJSONFileSync, writeJSONFile } from "../utils/fileSystem.js"; import { logger } from "../utils/logger.js"; +import { z } from "zod"; + +export const UpdateCommandOptionsSchema = z.object({ + to: z.string().optional(), +}); + +export type UpdateCommandOptions = z.infer; + +type NcuRunOptionTarget = "latest" | `@${string}`; + +export async function updateCommand(projectPath: string, anyOptions: any) { + const parseRes = UpdateCommandOptionsSchema.safeParse(anyOptions); + if (!parseRes.success) { + logger.error(parseRes.error.message); + return; + } + const options = parseRes.data; -export async function updateCommand(projectPath: string) { const triggerDevPackage = "@trigger.dev"; const packageJSONPath = path.join(projectPath, "package.json"); const packageData = readJSONFileSync(packageJSONPath); @@ -27,12 +43,14 @@ export async function updateCommand(projectPath: string) { }; }); + const targetVersion = getTargetVersion(options.to); + // Use npm-check-updates to get updated dependency versions const ncuOptions: RunOptions = { packageData, upgrade: true, jsonUpgraded: true, - target: "latest", + target: targetVersion, }; // Can either give a json like package.json or just with deps and their new versions @@ -69,6 +87,39 @@ export async function updateCommand(projectPath: string) { return; } + let applyUpdates = targetVersion !== "latest"; + + if (targetVersion === "latest") { + applyUpdates = await hasUserConfirmed(packagesToUpdate, packageMaps, updatedDependencies); + } + + if (applyUpdates) { + const newPackageJSON = packageData; + packagesToUpdate.forEach((packageName) => { + const tmp = packageMaps[packageName]; + if (tmp) { + newPackageJSON[tmp.type][packageName] = updatedDependencies[packageName]; + } + }); + await writeJSONFile(packageJSONPath, newPackageJSON); + await installDependencies(projectPath); + } +} + +// expects a version number, or latest. +// if version number is specified, prepend it with '@' for ncu. +function getTargetVersion(toVersion?: string): NcuRunOptionTarget { + if (!toVersion) { + return "latest"; + } + return toVersion === "latest" ? "latest" : `@${toVersion}`; +} + +async function hasUserConfirmed( + packagesToUpdate: string[], + packageMaps: { [x: string]: { type: string; version: string } }, + updatedDependencies: { [x: string]: any } +): Promise { // Inform the user of the dependencies that can be updated console.log("\nNewer versions found for the following packages:"); console.table( @@ -86,15 +137,5 @@ export async function updateCommand(projectPath: string) { message: "Do you want to update these packages in package.json and re-install dependencies?", }); - if (confirm) { - const newPackageJSON = packageData; - packagesToUpdate.forEach((packageName) => { - const tmp = packageMaps[packageName]; - if (tmp) { - newPackageJSON[tmp.type][packageName] = updatedDependencies[packageName]; - } - }); - await writeJSONFile(packageJSONPath, newPackageJSON); - await installDependencies(projectPath); - } + return confirm; }