From b625e97f4b007e7cec68cf37d556462319c1d231 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 29 Mar 2024 10:15:20 +0000 Subject: [PATCH 1/4] Better error output when an ESM only package is required --- packages/cli-v3/src/commands/deploy.ts | 53 +++++++++++++++++-- packages/cli-v3/src/utilities/deployErrors.ts | 28 ++++++++++ 2 files changed, 76 insertions(+), 5 deletions(-) create mode 100644 packages/cli-v3/src/utilities/deployErrors.ts diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index 908ec737e03..b7a0ce7c281 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -15,7 +15,7 @@ import { resolve as importResolve } from "import-meta-resolve"; import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, join, relative } from "node:path"; +import { dirname, join, parse, relative } from "node:path"; import { setTimeout } from "node:timers/promises"; import terminalLink from "terminal-link"; import invariant from "tiny-invariant"; @@ -46,6 +46,8 @@ import { login } from "./login"; import type { SetOptional } from "type-fest"; import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build"; import { Glob } from "glob"; +import { chalkError, chalkGreen, chalkGrey, chalkPurple } from "../utilities/cliOutput"; +import { parseDeployErrorStack } from "../utilities/deployErrors"; const DeployCommandOptions = CommonCommandOptions.extend({ skipTypecheck: z.boolean().default(false), @@ -379,10 +381,51 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { } case "FAILED": { if (finishedDeployment.errorData) { - deploymentSpinner.stop( - `Deployment encountered an error: ${finishedDeployment.errorData.name}. ${deploymentLink}` - ); - logger.error(finishedDeployment.errorData.stack); + const parsedError = finishedDeployment.errorData.stack + ? parseDeployErrorStack(finishedDeployment.errorData.stack) + : finishedDeployment.errorData.message; + + if (typeof parsedError === "string") { + deploymentSpinner.stop(`Deployment encountered an error. ${deploymentLink}`); + + logger.log(`${chalkError("X Error:")} ${parsedError}`); + } else { + deploymentSpinner.stop(`Deployment encountered an error. ${deploymentLink}`); + + logger.log( + `\n${chalkError("X Error:")} The ${chalkPurple( + parsedError.moduleName + )} module is being required even though it's ESM only, and builds only support CommonJS. There are two ways to fix this:` + ); + logger.log( + `\n${chalkGrey("○")} Dynamically import the module in your code: ${chalkGrey( + `const myModule = await import("${parsedError.moduleName}");` + )}` + ); + + if (resolvedConfig.status === "file") { + const relativePath = relative( + resolvedConfig.config.projectDir, + resolvedConfig.path + ).replace(/\\/g, "/"); + + logger.log( + `${chalkGrey("○")} Add ${chalkPurple(parsedError.moduleName)} to the ${chalkGreen( + "dependenciesToBundle" + )} array in your config file ${chalkGrey( + `(${relativePath})` + )}. This will bundle the module with your code.\n` + ); + } else { + logger.log( + `${chalkGrey("○")} Add ${chalkPurple(parsedError.moduleName)} to the ${chalkGreen( + "dependenciesToBundle" + )} array in your config file ${chalkGrey( + "(you'll need to create one)" + )}. This will bundle the module with your code.\n` + ); + } + } throw new SkipLoggingError( `Deployment encountered an error: ${finishedDeployment.errorData.name}` diff --git a/packages/cli-v3/src/utilities/deployErrors.ts b/packages/cli-v3/src/utilities/deployErrors.ts new file mode 100644 index 00000000000..698f96b7ebc --- /dev/null +++ b/packages/cli-v3/src/utilities/deployErrors.ts @@ -0,0 +1,28 @@ +export type ESMRequireError = { + type: "esm-require-error"; + moduleName: string; +}; + +export type DeployError = ESMRequireError | string; + +export function parseDeployErrorStack(error: string): DeployError { + const isErrRequireEsm = error.includes("ERR_REQUIRE_ESM"); + + let moduleName = null; + + if (isErrRequireEsm) { + // Regular expression to match the module path + const moduleRegex = /\/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/; + const match = moduleRegex.exec(error); + if (match) { + moduleName = match[1] as string; // Capture the module name + + return { + type: "esm-require-error", + moduleName, + }; + } + } + + return error; +} From c49553b03ea632e7ef927f17ce5041fb738d7895 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 29 Mar 2024 11:30:45 +0000 Subject: [PATCH 2/4] Add better errors for npm package errors, and bring deploy errors to the dev CLI --- packages/cli-v3/src/commands/deploy.ts | 94 +++++------ packages/cli-v3/src/commands/dev.tsx | 54 ++++++- packages/cli-v3/src/utilities/deployErrors.ts | 148 ++++++++++++++++-- .../cli-v3/src/utilities/installPackages.ts | 13 +- 4 files changed, 229 insertions(+), 80 deletions(-) diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index b7a0ce7c281..f07f10cc2ff 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -15,7 +15,7 @@ import { resolve as importResolve } from "import-meta-resolve"; import { createHash } from "node:crypto"; import { readFileSync } from "node:fs"; import { copyFile, mkdir, readFile, writeFile } from "node:fs/promises"; -import { dirname, join, parse, relative } from "node:path"; +import { dirname, join, relative } from "node:path"; import { setTimeout } from "node:timers/promises"; import terminalLink from "terminal-link"; import invariant from "tiny-invariant"; @@ -43,11 +43,15 @@ import { logger } from "../utilities/logger.js"; import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles"; import { login } from "./login"; +import { Glob } from "glob"; import type { SetOptional } from "type-fest"; import { bundleDependenciesPlugin, workerSetupImportConfigPlugin } from "../utilities/build"; -import { Glob } from "glob"; -import { chalkError, chalkGreen, chalkGrey, chalkPurple } from "../utilities/cliOutput"; -import { parseDeployErrorStack } from "../utilities/deployErrors"; +import { chalkError, chalkPurple, chalkWarning } from "../utilities/cliOutput"; +import { + logESMRequireError, + parseBuildErrorStack, + parseNpmInstallError, +} from "../utilities/deployErrors"; const DeployCommandOptions = CommonCommandOptions.extend({ skipTypecheck: z.boolean().default(false), @@ -382,7 +386,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { case "FAILED": { if (finishedDeployment.errorData) { const parsedError = finishedDeployment.errorData.stack - ? parseDeployErrorStack(finishedDeployment.errorData.stack) + ? parseBuildErrorStack(finishedDeployment.errorData) : finishedDeployment.errorData.message; if (typeof parsedError === "string") { @@ -392,39 +396,7 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { } else { deploymentSpinner.stop(`Deployment encountered an error. ${deploymentLink}`); - logger.log( - `\n${chalkError("X Error:")} The ${chalkPurple( - parsedError.moduleName - )} module is being required even though it's ESM only, and builds only support CommonJS. There are two ways to fix this:` - ); - logger.log( - `\n${chalkGrey("○")} Dynamically import the module in your code: ${chalkGrey( - `const myModule = await import("${parsedError.moduleName}");` - )}` - ); - - if (resolvedConfig.status === "file") { - const relativePath = relative( - resolvedConfig.config.projectDir, - resolvedConfig.path - ).replace(/\\/g, "/"); - - logger.log( - `${chalkGrey("○")} Add ${chalkPurple(parsedError.moduleName)} to the ${chalkGreen( - "dependenciesToBundle" - )} array in your config file ${chalkGrey( - `(${relativePath})` - )}. This will bundle the module with your code.\n` - ); - } else { - logger.log( - `${chalkGrey("○")} Add ${chalkPurple(parsedError.moduleName)} to the ${chalkGreen( - "dependenciesToBundle" - )} array in your config file ${chalkGrey( - "(you'll need to create one)" - )}. This will bundle the module with your code.\n` - ); - } + logESMRequireError(parsedError, resolvedConfig); } throw new SkipLoggingError( @@ -1100,7 +1072,7 @@ async function compileProject( ); if (!resolvingDependenciesResult) { - throw new Error("Failed to resolve dependencies"); + throw new SkipLoggingError("Failed to resolve dependencies"); } // Write the Containerfile to /tmp/dir/Containerfile @@ -1231,15 +1203,39 @@ async function resolveDependencies( return true; } catch (installError) { - logger.debug(`Failed to resolve dependencies: ${JSON.stringify(installError)}`); - recordSpanException(span, installError); - span.end(); - resolvingDepsSpinner.stop( - "Failed to resolve dependencies. Rerun with --log-level=debug for more information" - ); + const parsedError = parseNpmInstallError(installError); + + if (typeof parsedError === "string") { + resolvingDepsSpinner.stop(`Failed to resolve dependencies: ${parsedError}`); + } else { + switch (parsedError.type) { + case "package-not-found-error": { + resolvingDepsSpinner.stop(`Failed to resolve dependencies`); + + logger.log( + `\n${chalkError("X Error:")} The package ${chalkPurple( + parsedError.packageName + )} could not be found in the npm registry.` + ); + + break; + } + case "no-matching-version-error": { + resolvingDepsSpinner.stop(`Failed to resolve dependencies`); + + logger.log( + `\n${chalkError("X Error:")} The package ${chalkPurple( + parsedError.packageName + )} could not resolve because the version doesn't exist` + ); + + break; + } + } + } return false; } @@ -1355,8 +1351,12 @@ async function gatherRequiredDependencies( dependencies[packageParts.name] = externalDependencyVersion; continue; } else { - logger.warn( - `Could not find version for package ${packageName}, add a version specifier to the package name (e.g. ${packageParts.name}@latest) or add it to your project's package.json` + logger.log( + `${chalkWarning("X Warning:")} Could not find version for package ${chalkPurple( + packageName + )}, add a version specifier to the package name (e.g. ${ + packageParts.name + }@latest) or add it to your project's package.json` ); } } diff --git a/packages/cli-v3/src/commands/dev.tsx b/packages/cli-v3/src/commands/dev.tsx index 53cfabaa445..d5b1babd896 100644 --- a/packages/cli-v3/src/commands/dev.tsx +++ b/packages/cli-v3/src/commands/dev.tsx @@ -41,6 +41,11 @@ import { createTaskFileImports, gatherTaskFiles } from "../utilities/taskFiles"; import { UncaughtExceptionError } from "../workers/common/errors"; import { BackgroundWorker, BackgroundWorkerCoordinator } from "../workers/dev/backgroundWorker.js"; import { runtimeCheck } from "../utilities/runtimeCheck"; +import { + logESMRequireError, + parseBuildErrorStack, + parseNpmInstallError, +} from "../utilities/deployErrors"; let apiClient: CliApiClient | undefined; @@ -544,20 +549,55 @@ function useDev({ ); } catch (e) { if (e instanceof UncaughtExceptionError) { + const parsedBuildError = parseBuildErrorStack(e.originalError); + + if (typeof parsedBuildError !== "string") { + logESMRequireError( + parsedBuildError, + configPath + ? { status: "file", path: configPath, config } + : { status: "in-memory", config } + ); + return; + } else { + } + if (e.originalError.stack) { - logger.error("Background worker failed to start", e.originalError.stack); + logger.log( + `${chalkError("X Error:")} Worker failed to start`, + e.originalError.stack + ); } return; } - if (e instanceof Error) { - logger.error(`Background worker failed to start`, e.stack); - - return; + const parsedError = parseNpmInstallError(e); + + if (typeof parsedError === "string") { + logger.log(`${chalkError("X Error:")} ${parsedError}`); + } else { + switch (parsedError.type) { + case "package-not-found-error": { + logger.log( + `\n${chalkError("X Error:")} The package ${chalkPurple( + parsedError.packageName + )} could not be found in the npm registry.` + ); + + break; + } + case "no-matching-version-error": { + logger.log( + `\n${chalkError("X Error:")} The package ${chalkPurple( + parsedError.packageName + )} could not resolve because the version doesn't exist` + ); + + break; + } + } } - - logger.error(`Background worker failed to start: ${e}`); } }); }, diff --git a/packages/cli-v3/src/utilities/deployErrors.ts b/packages/cli-v3/src/utilities/deployErrors.ts index 698f96b7ebc..03b1f908eb4 100644 --- a/packages/cli-v3/src/utilities/deployErrors.ts +++ b/packages/cli-v3/src/utilities/deployErrors.ts @@ -1,28 +1,146 @@ +import chalk from "chalk"; +import { relative } from "node:path"; +import { chalkError, chalkPurple, chalkGrey, chalkGreen } from "./cliOutput"; +import { logger } from "./logger"; +import { ReadConfigResult } from "./configFiles"; + export type ESMRequireError = { type: "esm-require-error"; moduleName: string; }; -export type DeployError = ESMRequireError | string; +export type BuildError = ESMRequireError | string; + +function errorIsErrorLike(error: unknown): error is Error { + return ( + error instanceof Error || (typeof error === "object" && error !== null && "message" in error) + ); +} + +export function parseBuildErrorStack(error: unknown): BuildError { + if (typeof error === "string") { + return error; + } + + if (errorIsErrorLike(error)) { + if (typeof error.stack === "string") { + const isErrRequireEsm = error.stack.includes("ERR_REQUIRE_ESM"); + + let moduleName = null; + + if (isErrRequireEsm) { + // Regular expression to match the module path + const moduleRegex = /node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)\/[^\/]+\s/; + const match = moduleRegex.exec(error.stack); + if (match) { + moduleName = match[1] as string; // Capture the module name + + return { + type: "esm-require-error", + moduleName, + }; + } + } + } else { + return error.message; + } + } + + return "Unknown error"; +} + +export function logESMRequireError(parsedError: ESMRequireError, resolvedConfig: ReadConfigResult) { + logger.log( + `\n${chalkError("X Error:")} The ${chalkPurple( + parsedError.moduleName + )} module is being required even though it's ESM only, and builds only support CommonJS. There are two ${chalk.underline( + "possible" + )} ways to fix this:` + ); + logger.log( + `\n${chalkGrey("○")} Dynamically import the module in your code: ${chalkGrey( + `const myModule = await import("${parsedError.moduleName}");` + )}` + ); + + if (resolvedConfig.status === "file") { + const relativePath = relative(resolvedConfig.config.projectDir, resolvedConfig.path).replace( + /\\/g, + "/" + ); + + logger.log( + `${chalkGrey("○")} ${chalk.underline("Or")} add ${chalkPurple( + parsedError.moduleName + )} to the ${chalkGreen("dependenciesToBundle")} array in your config file ${chalkGrey( + `(${relativePath})` + )}. This will bundle the module with your code.\n` + ); + } else { + logger.log( + `${chalkGrey("○")} ${chalk.underline("Or")} add ${chalkPurple( + parsedError.moduleName + )} to the ${chalkGreen("dependenciesToBundle")} array in your config file ${chalkGrey( + "(you'll need to create one)" + )}. This will bundle the module with your code.\n` + ); + } +} + +export type PackageNotFoundError = { + type: "package-not-found-error"; + packageName: string; +}; + +export type NoMatchingVersionError = { + type: "no-matching-version-error"; + packageName: string; +}; + +export type NpmInstallError = PackageNotFoundError | NoMatchingVersionError | string; + +export function parseNpmInstallError(error: unknown): NpmInstallError { + if (typeof error === "string") { + return error; + } + + if (error instanceof Error) { + if (typeof error.stack === "string") { + const isPackageNotFoundError = + error.stack.includes("ERR! 404 Not Found") && + error.stack.includes("is not in this registry"); + let packageName = null; -export function parseDeployErrorStack(error: string): DeployError { - const isErrRequireEsm = error.includes("ERR_REQUIRE_ESM"); + if (isPackageNotFoundError) { + // Regular expression to match the package name + const packageNameRegex = /'([^']+)' is not in this registry/; + const match = packageNameRegex.exec(error.stack); + if (match) { + packageName = match[1] as string; // Capture the package name + } + } - let moduleName = null; + if (packageName) { + return { + type: "package-not-found-error", + packageName, + }; + } - if (isErrRequireEsm) { - // Regular expression to match the module path - const moduleRegex = /\/node_modules\/(@[^\/]+\/[^\/]+|[^\/]+)/; - const match = moduleRegex.exec(error); - if (match) { - moduleName = match[1] as string; // Capture the module name + const noMatchingVersionRegex = /No matching version found for ([^\s]+)\s/; + const noMatchingVersionMatch = noMatchingVersionRegex.exec(error.stack); + if (noMatchingVersionMatch) { + return { + type: "no-matching-version-error", + packageName: (noMatchingVersionMatch[1] as string).replace(/.$/, ""), + }; + } - return { - type: "esm-require-error", - moduleName, - }; + return error.message; + } else { + return error.message; } } - return error; + return "Unknown error"; } diff --git a/packages/cli-v3/src/utilities/installPackages.ts b/packages/cli-v3/src/utilities/installPackages.ts index b7d716c322e..aee7da50663 100644 --- a/packages/cli-v3/src/utilities/installPackages.ts +++ b/packages/cli-v3/src/utilities/installPackages.ts @@ -15,23 +15,14 @@ export async function installPackages( await setPackageJsonDeps(join(cwd, "package.json"), packages); - const childProcess = execa( + await execa( "npm", ["install", "--install-strategy", "nested", "--ignore-scripts", "--no-audit", "--no-fund"], { cwd, - stderr: "inherit", + stderr: "pipe", } ); - - await new Promise((res, rej) => { - childProcess.on("error", (e) => rej(e)); - childProcess.on("close", () => res()); - }); - - await childProcess; - - return; } async function getPackageVersion(path: string) { From 5c5af2f721eb87a4b71863b85c16513565af9300 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 29 Mar 2024 12:08:45 +0000 Subject: [PATCH 3/4] Handle depot build errors and write out a temporary file to view the build logs --- .vscode/launch.json | 2 +- packages/cli-v3/src/cli/common.ts | 4 +- packages/cli-v3/src/commands/deploy.ts | 81 +++++++++++++++++++------- 3 files changed, 65 insertions(+), 22 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 01f730f6196..0aab3dea0eb 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -41,7 +41,7 @@ "type": "node-terminal", "request": "launch", "name": "Debug V3 Deploy CLI", - "command": "pnpm exec trigger.dev deploy", + "command": "pnpm exec trigger.dev deploy --skip-deploy", "cwd": "${workspaceFolder}/references/v3-catalog", "sourceMaps": true }, diff --git a/packages/cli-v3/src/cli/common.ts b/packages/cli-v3/src/cli/common.ts index eac342ee84b..96202dfb15a 100644 --- a/packages/cli-v3/src/cli/common.ts +++ b/packages/cli-v3/src/cli/common.ts @@ -5,6 +5,7 @@ import { getTracer, provider } from "../telemetry/tracing"; import { fromZodError } from "zod-validation-error"; import { logger } from "../utilities/logger"; import { outro } from "@clack/prompts"; +import { chalkError } from "../utilities/cliOutput"; export const CommonCommandOptions = z.object({ apiUrl: z.string().optional(), @@ -84,7 +85,8 @@ export async function wrapCommandAction( // do nothing } else { recordSpanException(span, e); - logger.error(e instanceof Error ? e.message : String(e)); + + logger.log(`${chalkError("X Error:")} ${e instanceof Error ? e.message : String(e)}`); } span.end(); diff --git a/packages/cli-v3/src/commands/deploy.ts b/packages/cli-v3/src/commands/deploy.ts index f07f10cc2ff..dc5c247d6ad 100644 --- a/packages/cli-v3/src/commands/deploy.ts +++ b/packages/cli-v3/src/commands/deploy.ts @@ -278,28 +278,44 @@ async function _deployCommand(dir: string, options: DeployCommandOptions) { ); } - return buildAndPushImage({ - registryHost, - auth: authorization.auth.accessToken, - imageTag: deploymentResponse.data.imageTag, - buildId: deploymentResponse.data.externalBuildData.buildId, - buildToken: deploymentResponse.data.externalBuildData.buildToken, - buildProjectId: deploymentResponse.data.externalBuildData.projectId, - cwd: compilation.path, - projectId: resolvedConfig.config.project, - deploymentId: deploymentResponse.data.id, - deploymentVersion: deploymentResponse.data.version, - contentHash: deploymentResponse.data.contentHash, - projectRef: resolvedConfig.config.project, - loadImage: options.loadImage, - buildPlatform: options.buildPlatform, - }); + return buildAndPushImage( + { + registryHost, + auth: authorization.auth.accessToken, + imageTag: deploymentResponse.data.imageTag, + buildId: deploymentResponse.data.externalBuildData.buildId, + buildToken: deploymentResponse.data.externalBuildData.buildToken, + buildProjectId: deploymentResponse.data.externalBuildData.projectId, + cwd: compilation.path, + projectId: resolvedConfig.config.project, + deploymentId: deploymentResponse.data.id, + deploymentVersion: deploymentResponse.data.version, + contentHash: deploymentResponse.data.contentHash, + projectRef: resolvedConfig.config.project, + loadImage: options.loadImage, + buildPlatform: options.buildPlatform, + }, + deploymentSpinner + ); }; const image = await buildImage(); if (!image.ok) { - deploymentSpinner.stop(`Failed to build project image: ${image.error}`); + deploymentSpinner.stop(`Failed to build project.`); + + // If there are logs, let's write it out to a temporary file and include the path in the error message + if (image.logs.trim() !== "") { + const logPath = join(await createTempDir(), `build-${deploymentResponse.data.shortCode}.log`); + + await writeFile(logPath, image.logs); + + logger.log( + `${chalkError("X Error:")} ${image.error}. Full build logs have been saved to ${logPath})` + ); + } else { + logger.log(`${chalkError("X Error:")} ${image.error}.`); + } throw new SkipLoggingError(`Failed to build project image: ${image.error}`); } @@ -566,15 +582,18 @@ type BuildAndPushImageResults = | { ok: true; image: string; + logs: string; digest?: string; } | { ok: false; error: string; + logs: string; }; async function buildAndPushImage( - options: BuildAndPushImageOptions + options: BuildAndPushImageOptions, + updater: ReturnType ): Promise { return tracer.startActiveSpan("buildAndPushImage", async (span) => { span.setAttributes({ @@ -641,7 +660,7 @@ async function buildAndPushImage( const errors: string[] = []; try { - await new Promise((res, rej) => { + const processCode = await new Promise((res, rej) => { // For some reason everything is output on stderr, not stdout childProcess.stderr?.on("data", (data: Buffer) => { const text = data.toString(); @@ -651,9 +670,19 @@ async function buildAndPushImage( }); childProcess.on("error", (e) => rej(e)); - childProcess.on("close", () => res()); + childProcess.on("close", (code) => res(code)); }); + const logs = extractLogs(errors); + + if (processCode !== 0) { + return { + ok: false as const, + error: `Error building image`, + logs, + }; + } + const digest = extractImageDigest(errors); span.setAttributes({ @@ -665,6 +694,7 @@ async function buildAndPushImage( return { ok: true as const, image: options.imageTag, + logs, digest, }; } catch (e) { @@ -674,6 +704,7 @@ async function buildAndPushImage( return { ok: false as const, error: e instanceof Error ? e.message : JSON.stringify(e), + logs: extractLogs(errors), }; } }); @@ -766,6 +797,7 @@ async function buildAndPushSelfHostedImage( return { ok: false as const, error: e instanceof Error ? e.message : JSON.stringify(e), + logs: extractLogs(errors), }; } @@ -808,6 +840,7 @@ async function buildAndPushSelfHostedImage( return { ok: false as const, error: e instanceof Error ? e.message : JSON.stringify(e), + logs: extractLogs(errors), }; } } @@ -818,6 +851,7 @@ async function buildAndPushSelfHostedImage( ok: true as const, image: options.imageTag, digest, + logs: extractLogs(errors), }; }); } @@ -835,6 +869,13 @@ function extractImageDigest(outputs: string[]) { } } +function extractLogs(outputs: string[]) { + // Remove empty lines + const cleanedOutputs = outputs.map((line) => line.trim()).filter((line) => line !== ""); + + return cleanedOutputs.map((line) => line.trim()).join("\n"); +} + async function compileProject( config: ResolvedConfig, options: DeployCommandOptions, From ee6f89bb4c6ac3fb3d1c4799ac4a1b668993ed31 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Fri, 29 Mar 2024 12:09:55 +0000 Subject: [PATCH 4/4] Add changeset --- .changeset/mighty-camels-joke.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/mighty-camels-joke.md diff --git a/.changeset/mighty-camels-joke.md b/.changeset/mighty-camels-joke.md new file mode 100644 index 00000000000..de97b3d9c10 --- /dev/null +++ b/.changeset/mighty-camels-joke.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Improve error messages during dev/deploy and handle deploy image build issues