Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 73 additions & 0 deletions packages/cli-v3/src/commands/update.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { describe, expect, it } from "vitest";
import { getTriggerDependencies, getVersionMismatches, type Dependency } from "./update.js";

describe("update command dependency checks", () => {
it("skips unresolved package-manager protocol specifiers", async () => {
const deps = await getTriggerDependencies(
{
dependencies: {
"@trigger.dev/sdk": "catalog:",
"@trigger.dev/core": "workspace:4.5.0",
"@trigger.dev/build": "file:../build",
react: "^19.0.0",
},
devDependencies: {
"@trigger.dev/eslint-plugin": "link:../eslint-plugin",
},
},
"/tmp/trigger-catalog-repro/package.json"
);

expect(deps).toEqual([]);
});

it("keeps normal trigger dependencies for version mismatch checks", async () => {
const deps = await getTriggerDependencies(
{
dependencies: {
"@trigger.dev/sdk": "4.4.6",
},
},
"/tmp/trigger-version-repro/package.json"
);

expect(deps).toEqual([
{
type: "dependencies",
name: "@trigger.dev/sdk",
version: "4.4.6",
},
]);
});

it("does not throw when an invalid specifier reaches downgrade detection", () => {
const deps: Dependency[] = [
{
type: "dependencies",
name: "@trigger.dev/sdk",
version: "catalog:",
},
];

expect(() => getVersionMismatches(deps, "4.5.0")).not.toThrow();
expect(getVersionMismatches(deps, "4.5.0")).toEqual({
mismatches: deps,
isDowngrade: false,
});
});

it("still detects downgrade prompts for valid semver ranges", () => {
const deps: Dependency[] = [
{
type: "dependencies",
name: "@trigger.dev/sdk",
version: "^5.0.0",
},
];

expect(getVersionMismatches(deps, "4.5.0")).toEqual({
mismatches: deps,
isDowngrade: true,
});
});
});
110 changes: 64 additions & 46 deletions packages/cli-v3/src/commands/update.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ export function configureUpdateCommand(program: Command) {
}

const triggerPackageFilter = /^@trigger\.dev/;
const packageManagerProtocolFilter = /^(catalog|workspace|file|link|portal|patch):/;

export async function updateCommand(dir: string, options: UpdateCommandOptions) {
await updateTriggerPackages(dir, options, false);
Expand Down Expand Up @@ -111,45 +112,6 @@ export async function updateTriggerPackages(

logger.debug("Resolved trigger deps", { triggerDependencies });

function getVersionMismatches(
deps: Dependency[],
targetVersion: string
): {
mismatches: Dependency[];
isDowngrade: boolean;
} {
logger.debug("Checking for version mismatches", { deps, targetVersion });

const mismatches: Dependency[] = [];

for (const dep of deps) {
if (
dep.version === targetVersion ||
dep.version.startsWith("https://pkg.pr.new") ||
dep.version.startsWith("0.0.0")
) {
continue;
}

mismatches.push(dep);
}

const isDowngrade = mismatches.some((dep) => {
const depMinVersion = semver.minVersion(dep.version);

if (!depMinVersion) {
return false;
}

return semver.gt(depMinVersion, targetVersion);
});

return {
mismatches,
isDowngrade,
};
}

const { mismatches, isDowngrade } = getVersionMismatches(triggerDependencies, cliVersion);

logger.debug("Version mismatches", { mismatches, isDowngrade });
Expand Down Expand Up @@ -309,13 +271,59 @@ export async function updateTriggerPackages(
return hasOutput;
}

type Dependency = {
export type Dependency = {
type: "dependencies" | "devDependencies";
name: string;
version: string;
};

async function getTriggerDependencies(
export function getVersionMismatches(
deps: Dependency[],
targetVersion: string
): {
mismatches: Dependency[];
isDowngrade: boolean;
} {
logger.debug("Checking for version mismatches", { deps, targetVersion });

const mismatches: Dependency[] = [];

for (const dep of deps) {
if (
dep.version === targetVersion ||
dep.version.startsWith("https://pkg.pr.new") ||
dep.version.startsWith("0.0.0")
) {
continue;
}

mismatches.push(dep);
}

const isDowngrade = mismatches.some((dep) => {
if (!semver.validRange(dep.version)) {
logger.debug("Skipping downgrade check for non-semver dependency specifier", {
dep,
});
return false;
}

const depMinVersion = semver.minVersion(dep.version);

if (!depMinVersion) {
return false;
}

return semver.gt(depMinVersion, targetVersion);
});

return {
mismatches,
isDowngrade,
};
}

export async function getTriggerDependencies(
packageJson: PackageJson,
packageJsonPath: string
): Promise<Dependency[]> {
Expand All @@ -327,10 +335,6 @@ async function getTriggerDependencies(
continue;
}

if (version.startsWith("workspace")) {
continue;
}

if (!triggerPackageFilter.test(name)) {
continue;
}
Expand All @@ -343,7 +347,21 @@ async function getTriggerDependencies(

const $version = await tryResolveTriggerPackageVersion(name, dirname(packageJsonPath));

deps.push({ type, name, version: $version ?? version });
if ($version) {
deps.push({ type, name, version: $version });
continue;
}

if (packageManagerProtocolFilter.test(version)) {
logger.debug("Skipping unresolved package-manager dependency specifier", {
type,
name,
version,
});
continue;
}

deps.push({ type, name, version });
Comment on lines +350 to +364

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚩 Behavioral change: workspace: deps are no longer unconditionally skipped

The old code at the removed lines unconditionally skipped any dependency whose version started with "workspace". The new code at update.ts:350-364 only skips protocol-prefixed versions when tryResolveTriggerPackageVersion returns undefined. In a real monorepo where workspace-linked @trigger.dev/* packages are installed in node_modules, nodeResolve.sync will resolve them successfully, so the resolved version will be used for mismatch comparison. If a mismatch is detected and the user confirms an update, mutatePackageJsonWithUpdatedPackages at update.ts:421 would overwrite the workspace:* specifier with a pinned version (e.g., "4.5.0"), silently breaking the monorepo's workspace linking. This is mitigated by the fact that (a) it requires explicit user confirmation, (b) end users rarely have workspace: deps for trigger.dev packages, and (c) in CI (!hasTTY) the command exits before any mutation. Still, monorepo users who do use workspace protocol for trigger.dev packages could be surprised by this change.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

}
}

Expand Down