Adding some additional telemetry during deploy to help debug issues

This commit is contained in:
Eric Allam
2024-05-06 10:34:54 +01:00
parent 49b4d47726
commit 2156e15266
4 changed files with 152 additions and 97 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---
Adding some additional telemetry during deploy to help debug issues
+85 -59
View File
@@ -1286,7 +1286,7 @@ async function compileProject(
const javascriptProject = new JavascriptProject(config.projectDir);
const dependencies = await gatherRequiredDependencies(allImports, config, javascriptProject);
const dependencies = await resolveRequiredDependencies(allImports, config, javascriptProject);
logger.debug("gatherRequiredDependencies()", { dependencies });
@@ -1300,6 +1300,10 @@ async function compileProject(
},
};
span.setAttributes({
...flattenAttributes(packageJsonContents, "packageJson.contents"),
});
await writeJSONFile(join(tempDir, "package.json"), packageJsonContents);
const copyResult = await copyAdditionalFiles(config, tempDir);
@@ -1548,81 +1552,103 @@ async function typecheckProject(config: ResolvedConfig, options: DeployCommandOp
// Returns the dependencies that are required by the output that are found in output and the CLI package dependencies
// Returns the dependency names and the version to use (taken from the CLI deps package.json)
async function gatherRequiredDependencies(
async function resolveRequiredDependencies(
imports: Metafile["outputs"][string]["imports"],
config: ResolvedConfig,
project: JavascriptProject
) {
const dependencies: Record<string, string> = {};
const resolvablePackageNames = new Set<string>();
return await tracer.startActiveSpan("resolveRequiredDependencies", async (span) => {
const resolvablePackageNames = new Set<string>();
for (const file of imports) {
if ((file.kind !== "require-call" && file.kind !== "dynamic-import") || !file.external) {
continue;
}
const packageName = detectPackageNameFromImportPath(file.path);
if (!packageName) {
continue;
}
resolvablePackageNames.add(packageName);
}
const resolvedPackageVersions = await project.resolveAll(Array.from(resolvablePackageNames));
const missingPackages = Array.from(resolvablePackageNames).filter(
(packageName) => !resolvedPackageVersions[packageName]
);
for (const missingPackage of missingPackages) {
const internalDependencyVersion =
(packageJson.dependencies as Record<string, string>)[missingPackage] ??
detectDependencyVersion(missingPackage);
if (internalDependencyVersion) {
dependencies[missingPackage] = stripWorkspaceFromVersion(internalDependencyVersion);
}
}
for (const [packageName, version] of Object.entries(resolvedPackageVersions)) {
dependencies[packageName] = version;
}
if (config.additionalPackages) {
for (const packageName of config.additionalPackages) {
if (dependencies[packageName]) {
for (const file of imports) {
if ((file.kind !== "require-call" && file.kind !== "dynamic-import") || !file.external) {
continue;
}
const packageParts = parsePackageName(packageName);
const packageName = detectPackageNameFromImportPath(file.path);
if (packageParts.version) {
dependencies[packageParts.name] = packageParts.version;
if (!packageName) {
continue;
} else {
const externalDependencyVersion = await project.resolve(packageParts.name, {
allowDev: true,
});
}
if (externalDependencyVersion) {
dependencies[packageParts.name] = externalDependencyVersion;
resolvablePackageNames.add(packageName);
}
span.setAttribute("resolvablePackageNames", Array.from(resolvablePackageNames));
const resolvedPackageVersions = await project.resolveAll(Array.from(resolvablePackageNames));
const missingPackages = Array.from(resolvablePackageNames).filter(
(packageName) => !resolvedPackageVersions[packageName]
);
span.setAttributes({
...flattenAttributes(resolvedPackageVersions, "resolvedPackageVersions"),
});
span.setAttribute("missingPackages", missingPackages);
const dependencies: Record<string, string> = {};
for (const missingPackage of missingPackages) {
const internalDependencyVersion =
(packageJson.dependencies as Record<string, string>)[missingPackage] ??
detectDependencyVersion(missingPackage);
if (internalDependencyVersion) {
dependencies[missingPackage] = stripWorkspaceFromVersion(internalDependencyVersion);
}
}
for (const [packageName, version] of Object.entries(resolvedPackageVersions)) {
dependencies[packageName] = version;
}
if (config.additionalPackages) {
span.setAttribute("additionalPackages", config.additionalPackages);
for (const packageName of config.additionalPackages) {
if (dependencies[packageName]) {
continue;
}
const packageParts = parsePackageName(packageName);
if (packageParts.version) {
dependencies[packageParts.name] = packageParts.version;
continue;
} else {
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`
);
const externalDependencyVersion = await project.resolve(packageParts.name, {
allowDev: true,
});
if (externalDependencyVersion) {
dependencies[packageParts.name] = externalDependencyVersion;
continue;
} else {
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`
);
}
}
}
}
}
// Make sure we sort the dependencies by key to ensure consistent hashing
return Object.fromEntries(Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b)));
// Make sure we sort the dependencies by key to ensure consistent hashing
const result = Object.fromEntries(
Object.entries(dependencies).sort(([a], [b]) => a.localeCompare(b))
);
span.setAttributes({
...flattenAttributes(result, "dependencies"),
});
span.end();
return result;
});
}
type AdditionalFilesReturn =
+1 -1
View File
@@ -149,7 +149,7 @@ export function bundleDependenciesPlugin(
}
}
logger.ignore(`[${buildIdentifier}] Externalizing ${args.path}`, {
logger.debug(`[${buildIdentifier}] Externalizing ${args.path}`, {
...args,
});
@@ -6,6 +6,9 @@ import { PackageManager, getUserPackageManager } from "./getUserPackageManager";
import { PackageJson } from "type-fest";
import { assertExhaustive } from "./assertExhaustive";
import { builtinModules } from "node:module";
import { tracer } from "../cli/common";
import { recordSpanException } from "@trigger.dev/core/v3/otel";
import { flattenAttributes } from "@trigger.dev/core/v3";
export type ResolveOptions = { allowDev: boolean };
@@ -47,60 +50,81 @@ export class JavascriptProject {
packageNames: string[],
options?: ResolveOptions
): Promise<Record<string, string>> {
const externalPackages = packageNames.filter((packageName) => !isBuiltInModule(packageName));
return tracer.startActiveSpan("JavascriptProject.resolveAll", async (span) => {
const externalPackages = packageNames.filter((packageName) => !isBuiltInModule(packageName));
const opts = { allowDev: false, ...options };
const opts = { allowDev: false, ...options };
const command = await this.#getCommand();
const command = await this.#getCommand();
try {
const versions = await command.resolveDependencyVersions(externalPackages, {
cwd: this.projectPath,
span.setAttributes({
externalPackages,
packageManager: command.name,
});
if (versions) {
logger.debug(`Resolved [${externalPackages.join(", ")}] version using ${command.name}`, {
versions,
try {
const versions = await command.resolveDependencyVersions(externalPackages, {
cwd: this.projectPath,
});
}
// Merge the resolved versions with the package.json dependencies
const missingPackages = externalPackages.filter((packageName) => !versions[packageName]);
const missingPackageVersions: Record<string, string> = {};
for (const packageName of missingPackages) {
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
if (typeof packageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using package.json`, {
packageJsonVersion,
if (versions) {
logger.debug(`Resolved [${externalPackages.join(", ")}] version using ${command.name}`, {
versions,
});
missingPackageVersions[packageName] = packageJsonVersion;
span.setAttributes({
...flattenAttributes(versions, "versions"),
});
}
if (opts.allowDev) {
const devPackageJsonVersion = this.packageJson.devDependencies?.[packageName];
// Merge the resolved versions with the package.json dependencies
const missingPackages = externalPackages.filter((packageName) => !versions[packageName]);
const missingPackageVersions: Record<string, string> = {};
if (typeof devPackageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using devDependencies`, {
devPackageJsonVersion,
for (const packageName of missingPackages) {
const packageJsonVersion = this.packageJson.dependencies?.[packageName];
if (typeof packageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using package.json`, {
packageJsonVersion,
});
missingPackageVersions[packageName] = devPackageJsonVersion;
missingPackageVersions[packageName] = packageJsonVersion;
}
if (opts.allowDev) {
const devPackageJsonVersion = this.packageJson.devDependencies?.[packageName];
if (typeof devPackageJsonVersion === "string") {
logger.debug(`Resolved ${packageName} version using devDependencies`, {
devPackageJsonVersion,
});
missingPackageVersions[packageName] = devPackageJsonVersion;
}
}
}
span.setAttributes({
...flattenAttributes(missingPackageVersions, "missingPackageVersions"),
missingPackages,
});
span.end();
return { ...versions, ...missingPackageVersions };
} catch (error) {
recordSpanException(span, error);
span.end();
logger.debug(`Failed to resolve dependency versions using ${command.name}`, {
packageNames,
error,
});
return {};
}
return { ...versions, ...missingPackageVersions };
} catch (error) {
logger.debug(`Failed to resolve dependency versions using ${command.name}`, {
packageNames,
error,
});
return {};
}
});
}
async resolve(packageName: string, options?: ResolveOptions): Promise<string | undefined> {