Skip to content

Commit d0d3a64

Browse files
authored
v3: misc CLI improvements (#1173)
* prevent downgrades during update check * detect bun and use npm instead * detect missing tsconfig during init and print helpful error * add changeset * add links to dev worker started message * allow users to add custom pkg manager args during init * update changeset * fix links in unsupported terminals * deprecate terminalLink * update changeset
1 parent 568da01 commit d0d3a64

File tree

15 files changed

+357
-78
lines changed

15 files changed

+357
-78
lines changed

.changeset/spicy-frogs-remain.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
---
2+
"trigger.dev": patch
3+
---
4+
5+
- Prevent downgrades during update check and advise to upgrade CLI
6+
- Detect bun and use npm instead
7+
- During init, fail early and advise if not a TypeScript project
8+
- During init, allow specifying custom package manager args
9+
- Add links to dev worker started message
10+
- Fix links in unsupported terminals

apps/webapp/app/models/api-key.server.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,9 @@ export function createPkApiKeyForEnv(envType: RuntimeEnvironment["type"]) {
8484
return `pk_${envSlug(envType)}_${apiKeyId(20)}`;
8585
}
8686

87-
export function envSlug(environmentType: RuntimeEnvironment["type"]) {
87+
export type EnvSlug = "dev" | "stg" | "prod" | "prev";
88+
89+
export function envSlug(environmentType: RuntimeEnvironment["type"]): EnvSlug {
8890
switch (environmentType) {
8991
case "DEVELOPMENT": {
9092
return "dev";
@@ -100,3 +102,7 @@ export function envSlug(environmentType: RuntimeEnvironment["type"]) {
100102
}
101103
}
102104
}
105+
106+
export function isEnvSlug(maybeSlug: string): maybeSlug is EnvSlug {
107+
return ["dev", "stg", "prod", "prev"].includes(maybeSlug);
108+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
2+
import { z } from "zod";
3+
import { prisma } from "~/db.server";
4+
import { EnvSlug, isEnvSlug } from "~/models/api-key.server";
5+
import { requireUserId } from "~/services/session.server";
6+
7+
const ParamsSchema = z.object({
8+
projectRef: z.string(),
9+
});
10+
11+
export async function loader({ params, request }: LoaderFunctionArgs) {
12+
const userId = await requireUserId(request);
13+
14+
const { projectRef } = ParamsSchema.parse(params);
15+
16+
const project = await prisma.project.findFirst({
17+
where: {
18+
externalRef: projectRef,
19+
organization: {
20+
members: {
21+
some: {
22+
userId,
23+
},
24+
},
25+
},
26+
},
27+
include: {
28+
organization: true,
29+
},
30+
});
31+
32+
if (!project) {
33+
return new Response("Project not found", { status: 404 });
34+
}
35+
36+
const url = new URL(request.url);
37+
const envSlug = url.searchParams.get("envSlug");
38+
39+
// Get the environment from the slug
40+
if (envSlug && isEnvSlug(envSlug)) {
41+
const env = await getEnvFromSlug(project.id, userId, envSlug);
42+
43+
if (env) {
44+
url.searchParams.set("environments", env.id);
45+
}
46+
47+
url.searchParams.delete("envSlug");
48+
}
49+
50+
return redirect(
51+
`/orgs/${project.organization.slug}/projects/v3/${project.slug}/runs${url.search}`
52+
);
53+
}
54+
55+
async function getEnvFromSlug(projectId: string, userId: string, envSlug: EnvSlug) {
56+
if (envSlug === "dev") {
57+
return await prisma.runtimeEnvironment.findFirst({
58+
where: {
59+
projectId,
60+
slug: envSlug,
61+
orgMember: {
62+
userId,
63+
},
64+
},
65+
});
66+
}
67+
68+
return await prisma.runtimeEnvironment.findFirst({
69+
where: {
70+
projectId,
71+
slug: envSlug,
72+
},
73+
});
74+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import { LoaderFunctionArgs, redirect } from "@remix-run/server-runtime";
2+
import { z } from "zod";
3+
import { prisma } from "~/db.server";
4+
import { requireUserId } from "~/services/session.server";
5+
6+
const ParamsSchema = z.object({
7+
projectRef: z.string(),
8+
});
9+
10+
export async function loader({ params, request }: LoaderFunctionArgs) {
11+
const userId = await requireUserId(request);
12+
13+
const validatedParams = ParamsSchema.parse(params);
14+
15+
const project = await prisma.project.findFirst({
16+
where: {
17+
externalRef: validatedParams.projectRef,
18+
organization: {
19+
members: {
20+
some: {
21+
userId,
22+
},
23+
},
24+
},
25+
},
26+
include: {
27+
organization: true,
28+
},
29+
});
30+
31+
if (!project) {
32+
return new Response("Not found", { status: 404 });
33+
}
34+
35+
const url = new URL(request.url);
36+
37+
return redirect(
38+
`/orgs/${project.organization.slug}/projects/v3/${project.slug}/test${url.search}`
39+
);
40+
}

packages/cli-v3/e2e/handleDependencies.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
import { log } from "@clack/prompts";
44
import { Metafile } from "esbuild";
55
import { join } from "node:path";
6-
import terminalLink from "terminal-link";
76

87
import { SkipLoggingError } from "../src/cli/common.js";
98
import {
@@ -16,6 +15,7 @@ import { writeJSONFile } from "../src/utilities/fileSystem.js";
1615
import { PackageManager } from "../src/utilities/getUserPackageManager.js";
1716
import { JavascriptProject } from "../src/utilities/javascriptProject.js";
1817
import { logger } from "../src/utilities/logger.js";
18+
import { cliLink } from "../src/utilities/cliOutput.js";
1919

2020
type HandleDependenciesOptions = {
2121
entryPointMetaOutput: Metafile["outputs"]["out/stdin.js"];
@@ -81,7 +81,7 @@ export async function handleDependencies(options: HandleDependenciesOptions) {
8181
log.warn(
8282
`No additionalFiles matches for:\n\n${copyResult.noMatches
8383
.map((glob) => `- "${glob}"`)
84-
.join("\n")}\n\nIf this is unexpected you should check your ${terminalLink(
84+
.join("\n")}\n\nIf this is unexpected you should check your ${cliLink(
8585
"glob patterns",
8686
"https://github.com/isaacs/node-glob?tab=readme-ov-file#glob-primer"
8787
)} are valid.`

packages/cli-v3/src/commands/deploy.ts

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import { readFileSync } from "node:fs";
1616
import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises";
1717
import { dirname, join, posix, relative, resolve } from "node:path";
1818
import { setTimeout } from "node:timers/promises";
19-
import terminalLink from "terminal-link";
2019
import invariant from "tiny-invariant";
2120
import { z } from "zod";
2221
import * as packageJson from "../../package.json";
@@ -50,7 +49,7 @@ import {
5049
mockServerOnlyPlugin,
5150
workerSetupImportConfigPlugin,
5251
} from "../utilities/build";
53-
import { chalkError, chalkPurple, chalkWarning } from "../utilities/cliOutput";
52+
import { chalkError, chalkPurple, chalkWarning, cliLink } from "../utilities/cliOutput";
5453
import {
5554
logESMRequireError,
5655
logTaskMetadataParseError,
@@ -437,7 +436,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) {
437436
throw new SkipLoggingError(`Deployment failed to complete: ${finishedDeployment}`);
438437
}
439438

440-
const deploymentLink = terminalLink(
439+
const deploymentLink = cliLink(
441440
"View deployment",
442441
`${authorization.dashboardUrl}/projects/v3/${resolvedConfig.config.project}/deployments/${finishedDeployment.shortCode}`
443442
);
@@ -576,7 +575,7 @@ function checkLogsForWarnings(logs: string): WarningsCheckReturn {
576575
const warnings: LogParserOptions = [
577576
{
578577
regex: /prisma:warn We could not find your Prisma schema/,
579-
message: `Prisma generate failed to find the default schema. Did you include it in config.additionalFiles? ${terminalLink(
578+
message: `Prisma generate failed to find the default schema. Did you include it in config.additionalFiles? ${cliLink(
580579
"Config docs",
581580
docs.config.prisma
582581
)}\nCustom schema paths require a postinstall script like this: \`prisma generate --schema=./custom/path/to/schema.prisma\``,
@@ -626,17 +625,17 @@ function checkLogsForErrors(logs: string) {
626625
const errors: LogParserOptions = [
627626
{
628627
regex: /Error: Provided --schema at (?<schema>.*) doesn't exist/,
629-
message: `Prisma generate failed to find the specified schema at "$schema".\nDid you include it in config.additionalFiles? ${terminalLink(
628+
message: `Prisma generate failed to find the specified schema at "$schema".\nDid you include it in config.additionalFiles? ${cliLink(
630629
"Config docs",
631630
docs.config.prisma
632631
)}`,
633632
},
634633
{
635634
regex: /sh: 1: (?<packageOrBinary>.*): not found/,
636-
message: `$packageOrBinary not found\n\nIf it's a package: Include it in ${terminalLink(
635+
message: `$packageOrBinary not found\n\nIf it's a package: Include it in ${cliLink(
637636
"config.additionalPackages",
638637
docs.config.prisma
639-
)}\nIf it's a binary: Please ${terminalLink(
638+
)}\nIf it's a binary: Please ${cliLink(
640639
"get in touch",
641640
getInTouch
642641
)} and we'll see what we can do!`,
@@ -1341,7 +1340,7 @@ async function compileProject(
13411340
log.warn(
13421341
`No additionalFiles matches for:\n\n${copyResult.noMatches
13431342
.map((glob) => `- "${glob}"`)
1344-
.join("\n")}\n\nIf this is unexpected you should check your ${terminalLink(
1343+
.join("\n")}\n\nIf this is unexpected you should check your ${cliLink(
13451344
"glob patterns",
13461345
"https://github.com/isaacs/node-glob?tab=readme-ov-file#glob-primer"
13471346
)} are valid.`

packages/cli-v3/src/commands/dev.tsx

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,15 @@ import {
2929
mockServerOnlyPlugin,
3030
workerSetupImportConfigPlugin,
3131
} from "../utilities/build";
32-
import { chalkError, chalkGrey, chalkPurple, chalkTask, chalkWorker } from "../utilities/cliOutput";
32+
import {
33+
chalkError,
34+
chalkGrey,
35+
chalkLink,
36+
chalkPurple,
37+
chalkTask,
38+
chalkWorker,
39+
cliLink,
40+
} from "../utilities/cliOutput";
3341
import { readConfig } from "../utilities/configFiles";
3442
import { readJSONFile } from "../utilities/fileSystem";
3543
import { printDevBanner, printStandloneInitialBanner } from "../utilities/initialBanner.js";
@@ -624,13 +632,23 @@ function useDev({
624632
}
625633

626634
backgroundWorker.metadata = backgroundWorkerRecord.data;
635+
backgroundWorker;
636+
637+
const testUrl = `${dashboardUrl}/projects/v3/${config.project}/test?environment=dev`;
638+
const runsUrl = `${dashboardUrl}/projects/v3/${config.project}/runs?envSlug=dev`;
639+
640+
const pipe = chalkGrey("|");
641+
const bullet = chalkGrey("○");
642+
const arrow = chalkGrey("->");
643+
644+
const testLink = chalkLink(cliLink("Test tasks", testUrl));
645+
const runsLink = chalkLink(cliLink("View runs", runsUrl));
646+
647+
const workerStarted = chalkGrey("Background worker started");
648+
const workerVersion = chalkWorker(backgroundWorkerRecord.data.version);
627649

628650
logger.log(
629-
`${chalkGrey(
630-
`○ Background worker started -> ${chalkWorker(
631-
backgroundWorkerRecord.data.version
632-
)}`
633-
)}`
651+
`${bullet} ${workerStarted} ${arrow} ${workerVersion} ${pipe} ${testLink} ${pipe} ${runsLink}`
634652
);
635653

636654
firstBuild = false;

0 commit comments

Comments
 (0)