feat: enhance error handling in deployment processes

- Introduced a new ExecError class to standardize error handling during command execution.
- Updated deployApplication and deployCompose functions to log detailed error messages, excluding ExecError instances.
- Improved execAsync and execAsyncRemote functions to throw ExecError with additional context for better debugging.
- Added base64 encoding for error messages to ensure sensitive information is handled appropriately.
This commit is contained in:
Mauricio Siu
2025-11-26 00:11:43 -05:00
parent e136934cbc
commit 73d87c06e1
6 changed files with 205 additions and 19 deletions
+27 -1
View File
@@ -13,6 +13,7 @@ import {
import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error";
import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success";
import {
ExecError,
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
@@ -28,6 +29,7 @@ import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab";
import { createTraefikConfig } from "@dokploy/server/utils/traefik/application";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { encodeBase64 } from "../utils/docker/utils";
import { getDokployUrl } from "./admin";
import {
createDeployment,
@@ -228,7 +230,16 @@ export const deployApplication = async ({
environmentName: application.environment.name,
});
} catch (error) {
const command = `echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};`;
let command = "";
// Only log details for non-ExecError errors
if (!(error instanceof ExecError)) {
const message = error instanceof Error ? error.message : String(error);
const encodedMessage = encodeBase64(message);
command += `echo "${encodedMessage}" | base64 -d >> "${deployment.logPath}";`;
}
command += `echo "\nError occurred ❌, check the logs for details." >> ${deployment.logPath};`;
if (application.serverId) {
await execAsyncRemote(application.serverId, command);
} else {
@@ -317,6 +328,21 @@ export const rebuildApplication = async ({
environmentName: application.environment.name,
});
} catch (error) {
let command = "";
// Only log details for non-ExecError errors
if (!(error instanceof ExecError)) {
const message = error instanceof Error ? error.message : String(error);
const encodedMessage = encodeBase64(message);
command += `echo "${encodedMessage}" | base64 -d >> "${deployment.logPath}";`;
}
command += `echo "\nError occurred ❌, check the logs for details." >> ${deployment.logPath};`;
if (application.serverId) {
await execAsyncRemote(application.serverId, command);
} else {
await execAsync(command);
}
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateApplicationStatus(applicationId, "error");
throw error;
+32
View File
@@ -18,6 +18,7 @@ import type { ComposeSpecification } from "@dokploy/server/utils/docker/types";
import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error";
import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success";
import {
ExecError,
execAsync,
execAsyncRemote,
} from "@dokploy/server/utils/process/execAsync";
@@ -32,6 +33,7 @@ import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab";
import { getCreateComposeFileCommand } from "@dokploy/server/utils/providers/raw";
import { TRPCError } from "@trpc/server";
import { eq } from "drizzle-orm";
import { encodeBase64 } from "../utils/docker/utils";
import { getDokployUrl } from "./admin";
import {
createDeploymentCompose,
@@ -270,6 +272,21 @@ export const deployCompose = async ({
environmentName: compose.environment.name,
});
} catch (error) {
let command = "";
// Only log details for non-ExecError errors
if (!(error instanceof ExecError)) {
const message = error instanceof Error ? error.message : String(error);
const encodedMessage = encodeBase64(message);
command += `echo "${encodedMessage}" | base64 -d >> "${deployment.logPath}";`;
}
command += `echo "\nError occurred ❌, check the logs for details." >> ${deployment.logPath};`;
if (compose.serverId) {
await execAsyncRemote(compose.serverId, command);
} else {
await execAsync(command);
}
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateCompose(composeId, {
composeStatus: "error",
@@ -342,6 +359,21 @@ export const rebuildCompose = async ({
composeStatus: "done",
});
} catch (error) {
let command = "";
// Only log details for non-ExecError errors
if (!(error instanceof ExecError)) {
const message = error instanceof Error ? error.message : String(error);
const encodedMessage = encodeBase64(message);
command += `echo "${encodedMessage}" | base64 -d >> "${deployment.logPath}";`;
}
command += `echo "\nError occurred ❌, check the logs for details." >> ${deployment.logPath};`;
if (compose.serverId) {
await execAsyncRemote(compose.serverId, command);
} else {
await execAsync(command);
}
await updateDeploymentStatus(deployment.deploymentId, "error");
await updateCompose(composeId, {
composeStatus: "error",
+1 -4
View File
@@ -60,10 +60,7 @@ export const getUpdateData = async (): Promise<IUpdateData> => {
try {
currentDigest = await getServiceImageDigest();
} catch (error) {
console.error(error);
// Docker service might not exist locally
// You can run the # Installation command for docker service create mentioned in the below docs to test it locally:
// https://docs.dokploy.com/docs/core/manual-installation
// TODO: Docker versions 29.0.0 change the way to get the service image digest, so we need to update this in the future we upgrade to that version.
return DEFAULT_UPDATE_DATA;
}