Skip to content
Merged
5 changes: 5 additions & 0 deletions .changeset/swift-eagles-float.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@trigger.dev/cli": patch
---

fix: Add an update sub-command the @trigger.dev/cli that updates all @trigger.dev/* packages
3 changes: 2 additions & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@
"trigger-cli": "./dist/index.js"
},
"devDependencies": {
"@trigger.dev/tsconfig": "workspace:*",
"@gmrchk/cli-testing-library": "^0.1.2",
"@trigger.dev/tsconfig": "workspace:*",
"@types/gradient-string": "^1.1.2",
"@types/inquirer": "^9.0.3",
"@types/jest": "^29.5.3",
Expand Down Expand Up @@ -71,6 +71,7 @@
"nanoid": "^4.0.2",
"ngrok": "5.0.0-beta.2",
"node-fetch": "^3.3.0",
"npm-check-updates": "^16.12.2",
"openai": "^3.3.0",
"ora": "^6.1.2",
"path-to-regexp": "^6.2.1",
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { initCommand } from "../commands/init";
import { CLOUD_TRIGGER_URL, COMMAND_NAME } from "../consts";
import { telemetryClient } from "../telemetry/telemetry";
import { getVersion } from "../utils/getVersion";
import { updateCommand } from "../commands/update";

export const program = new Command();

Expand Down Expand Up @@ -80,6 +81,14 @@ program
await createIntegrationCommand(path, options);
});

program
.command("update")
.description("Updates all @trigger.dev/* packages to their latest compatible versions")
.argument("[path]", "The path to the directory that contains the package.json file", ".")
.action(async (path) => {
await updateCommand(path);
});

program
.command("whoami")
.description("display the current logged in user and project details")
Expand Down
101 changes: 101 additions & 0 deletions packages/cli/src/commands/update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import path from "path";
import inquirer from "inquirer";
import { run, RunOptions } from "npm-check-updates";
import { installDependencies } from "../utils/installDependencies.js";
import { readJSONFileSync, writeJSONFile } from "../utils/fileSystem.js";

export async function updateCommand(projectPath: string) {
const triggerDevPackage = "@trigger.dev";
const packageJSONPath = path.join(projectPath, "package.json")
const packageData = readJSONFileSync(packageJSONPath);

if (!packageData) {
return;
}

const packageMaps: { [k: string]: { type: string; version: string } } = {};
const packageDependencies = packageData.dependencies || {};
const packageDevDependencies = packageData.devDependencies || {};
Object.keys(packageDependencies).forEach((i) => {
packageMaps[i] = { type: "dependencies", version: packageDependencies[i] };
});
Object.keys(packageDevDependencies).forEach((i) => {
packageMaps[i] = {
type: "devDependencies",
version: packageDevDependencies[i],
};
});

// Use npm-check-updates to get updated dependency versions
const ncuOptions: RunOptions = {
packageData,
upgrade: true,
jsonUpgraded: true,
};

// Can either give a json like package.json or just with deps and their new versions
const updatedDependencies: { [k: string]: any } | void = await run(ncuOptions);

if (!updatedDependencies) return;

const ifUpdatedDependenciesIsPackageJSON =
updatedDependencies.hasOwnProperty("dependencies") ||
updatedDependencies.hasOwnProperty("devDependencies");

const dependencies = updatedDependencies.dependencies || {};
const devDependencies = updatedDependencies.devDependencies || {};

const allDependencies = ifUpdatedDependenciesIsPackageJSON
? Object.keys({ ...dependencies, ...devDependencies })
: Object.keys(updatedDependencies);

const triggerPackages = allDependencies.filter((pkg) => pkg.startsWith(triggerDevPackage));

// If there are no @trigger.dev packages
if (triggerPackages.length === 0) {
console.log("No @trigger.dev/* packages found in package.json.");
return;
}

// Filter the packages with null and what don't match what
// they are installed with so that they can be updated
const packagesToUpdate = triggerPackages.filter((pkg: string) => updatedDependencies[pkg]);

// If no packages require any updation
if (packagesToUpdate.length === 0) {
console.log("All @trigger.dev/* packages are up to date.");
return;
}

// Inform the user of the dependencies that can be updated
console.log("\nNewer versions found for the following packages:");
console.table(
packagesToUpdate.map((i) => ({
name: i,
old: packageMaps[i]?.version,
new: updatedDependencies[i],
}))
);

// Ask the user if they want to update the dependencies
const { confirm } = await inquirer.prompt({
type: "confirm",
name: "confirm",
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);
console.log("package.json updated. Reinstalling dependencies...");
await installDependencies(projectPath);
} else {
console.log("Operation canceled. No changes were made.");
}
}
Loading