mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-13 07:25:17 +00:00
Refactor compose and deployment services: streamline cloning and execution commands, remove redundant remote functions, and enhance error handling. Update database schema to include application build server ID for better tracking of deployments.
This commit is contained in:
@@ -3,13 +3,14 @@ import {
|
|||||||
addNewService,
|
addNewService,
|
||||||
checkServiceAccess,
|
checkServiceAccess,
|
||||||
cloneCompose,
|
cloneCompose,
|
||||||
cloneComposeRemote,
|
|
||||||
createCommand,
|
createCommand,
|
||||||
createCompose,
|
createCompose,
|
||||||
createComposeByTemplate,
|
createComposeByTemplate,
|
||||||
createDomain,
|
createDomain,
|
||||||
createMount,
|
createMount,
|
||||||
deleteMount,
|
deleteMount,
|
||||||
|
execAsync,
|
||||||
|
execAsyncRemote,
|
||||||
findComposeById,
|
findComposeById,
|
||||||
findDomainsByComposeId,
|
findDomainsByComposeId,
|
||||||
findEnvironmentById,
|
findEnvironmentById,
|
||||||
@@ -302,10 +303,12 @@ export const composeRouter = createTRPCRouter({
|
|||||||
message: "You are not authorized to fetch this compose",
|
message: "You are not authorized to fetch this compose",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const command = await cloneCompose(compose);
|
||||||
if (compose.serverId) {
|
if (compose.serverId) {
|
||||||
await cloneComposeRemote(compose);
|
await execAsyncRemote(compose.serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await cloneCompose(compose);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
return compose.sourceType;
|
return compose.sourceType;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|||||||
@@ -2,13 +2,9 @@ import {
|
|||||||
deployApplication,
|
deployApplication,
|
||||||
deployCompose,
|
deployCompose,
|
||||||
deployPreviewApplication,
|
deployPreviewApplication,
|
||||||
deployRemoteApplication,
|
|
||||||
deployRemoteCompose,
|
|
||||||
deployRemotePreviewApplication,
|
deployRemotePreviewApplication,
|
||||||
rebuildApplication,
|
rebuildApplication,
|
||||||
rebuildCompose,
|
rebuildCompose,
|
||||||
rebuildRemoteApplication,
|
|
||||||
rebuildRemoteCompose,
|
|
||||||
updateApplicationStatus,
|
updateApplicationStatus,
|
||||||
updateCompose,
|
updateCompose,
|
||||||
updatePreviewDeployment,
|
updatePreviewDeployment,
|
||||||
@@ -24,68 +20,35 @@ export const deploymentWorker = new Worker(
|
|||||||
if (job.data.applicationType === "application") {
|
if (job.data.applicationType === "application") {
|
||||||
await updateApplicationStatus(job.data.applicationId, "running");
|
await updateApplicationStatus(job.data.applicationId, "running");
|
||||||
|
|
||||||
if (job.data.server) {
|
if (job.data.type === "redeploy") {
|
||||||
if (job.data.type === "redeploy") {
|
await rebuildApplication({
|
||||||
await rebuildRemoteApplication({
|
applicationId: job.data.applicationId,
|
||||||
applicationId: job.data.applicationId,
|
titleLog: job.data.titleLog,
|
||||||
titleLog: job.data.titleLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
descriptionLog: job.data.descriptionLog,
|
});
|
||||||
});
|
} else if (job.data.type === "deploy") {
|
||||||
} else if (job.data.type === "deploy") {
|
await deployApplication({
|
||||||
await deployRemoteApplication({
|
applicationId: job.data.applicationId,
|
||||||
applicationId: job.data.applicationId,
|
titleLog: job.data.titleLog,
|
||||||
titleLog: job.data.titleLog,
|
descriptionLog: job.data.descriptionLog,
|
||||||
descriptionLog: job.data.descriptionLog,
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (job.data.type === "redeploy") {
|
|
||||||
await rebuildApplication({
|
|
||||||
applicationId: job.data.applicationId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
} else if (job.data.type === "deploy") {
|
|
||||||
await deployApplication({
|
|
||||||
applicationId: job.data.applicationId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (job.data.applicationType === "compose") {
|
} else if (job.data.applicationType === "compose") {
|
||||||
await updateCompose(job.data.composeId, {
|
await updateCompose(job.data.composeId, {
|
||||||
composeStatus: "running",
|
composeStatus: "running",
|
||||||
});
|
});
|
||||||
|
if (job.data.type === "deploy") {
|
||||||
if (job.data.server) {
|
await deployCompose({
|
||||||
if (job.data.type === "redeploy") {
|
composeId: job.data.composeId,
|
||||||
await rebuildRemoteCompose({
|
titleLog: job.data.titleLog,
|
||||||
composeId: job.data.composeId,
|
descriptionLog: job.data.descriptionLog,
|
||||||
titleLog: job.data.titleLog,
|
});
|
||||||
descriptionLog: job.data.descriptionLog,
|
} else if (job.data.type === "redeploy") {
|
||||||
});
|
await rebuildCompose({
|
||||||
} else if (job.data.type === "deploy") {
|
composeId: job.data.composeId,
|
||||||
await deployRemoteCompose({
|
titleLog: job.data.titleLog,
|
||||||
composeId: job.data.composeId,
|
descriptionLog: job.data.descriptionLog,
|
||||||
titleLog: job.data.titleLog,
|
});
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (job.data.type === "deploy") {
|
|
||||||
await deployCompose({
|
|
||||||
composeId: job.data.composeId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
} else if (job.data.type === "redeploy") {
|
|
||||||
await rebuildCompose({
|
|
||||||
composeId: job.data.composeId,
|
|
||||||
titleLog: job.data.titleLog,
|
|
||||||
descriptionLog: job.data.descriptionLog,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (job.data.applicationType === "application-preview") {
|
} else if (job.data.applicationType === "application-preview") {
|
||||||
await updatePreviewDeployment(job.data.previewDeploymentId, {
|
await updatePreviewDeployment(job.data.previewDeploymentId, {
|
||||||
|
|||||||
@@ -34,6 +34,12 @@ export const deployments = pgTable("deployment", {
|
|||||||
status: deploymentStatus("status").default("running"),
|
status: deploymentStatus("status").default("running"),
|
||||||
logPath: text("logPath").notNull(),
|
logPath: text("logPath").notNull(),
|
||||||
pid: text("pid"),
|
pid: text("pid"),
|
||||||
|
applicationBuildServerId: text("applicationBuildServerId").references(
|
||||||
|
() => server.serverId,
|
||||||
|
{
|
||||||
|
onDelete: "cascade",
|
||||||
|
},
|
||||||
|
),
|
||||||
applicationId: text("applicationId").references(
|
applicationId: text("applicationId").references(
|
||||||
() => applications.applicationId,
|
() => applications.applicationId,
|
||||||
{ onDelete: "cascade" },
|
{ onDelete: "cascade" },
|
||||||
|
|||||||
@@ -13,35 +13,23 @@ import {
|
|||||||
} from "@dokploy/server/utils/builders";
|
} from "@dokploy/server/utils/builders";
|
||||||
import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error";
|
import { sendBuildErrorNotifications } from "@dokploy/server/utils/notifications/build-error";
|
||||||
import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success";
|
import { sendBuildSuccessNotifications } from "@dokploy/server/utils/notifications/build-success";
|
||||||
import { execAsyncRemote } from "@dokploy/server/utils/process/execAsync";
|
|
||||||
import {
|
import {
|
||||||
cloneBitbucketRepository,
|
execAsync,
|
||||||
getBitbucketCloneCommand,
|
execAsyncRemote,
|
||||||
} from "@dokploy/server/utils/providers/bitbucket";
|
} from "@dokploy/server/utils/process/execAsync";
|
||||||
|
import { cloneBitbucketRepository } from "@dokploy/server/utils/providers/bitbucket";
|
||||||
import {
|
import {
|
||||||
buildDocker,
|
buildDocker,
|
||||||
buildRemoteDocker,
|
buildRemoteDocker,
|
||||||
} from "@dokploy/server/utils/providers/docker";
|
} from "@dokploy/server/utils/providers/docker";
|
||||||
import {
|
import { cloneGitRepository } from "@dokploy/server/utils/providers/git";
|
||||||
cloneGitRepository,
|
import { cloneGiteaRepository } from "@dokploy/server/utils/providers/gitea";
|
||||||
getCustomGitCloneCommand,
|
import { cloneGithubRepository } from "@dokploy/server/utils/providers/github";
|
||||||
} from "@dokploy/server/utils/providers/git";
|
import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab";
|
||||||
import {
|
|
||||||
cloneGiteaRepository,
|
|
||||||
getGiteaCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/gitea";
|
|
||||||
import {
|
|
||||||
cloneGithubRepository,
|
|
||||||
getGithubCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/github";
|
|
||||||
import {
|
|
||||||
cloneGitlabRepository,
|
|
||||||
getGitlabCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/gitlab";
|
|
||||||
import { createTraefikConfig } from "@dokploy/server/utils/traefik/application";
|
import { createTraefikConfig } from "@dokploy/server/utils/traefik/application";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { encodeBase64 } from "../utils/docker/utils";
|
import { cleanUpSystemPrune, encodeBase64 } from "../utils/docker/utils";
|
||||||
import { getDokployUrl } from "./admin";
|
import { getDokployUrl } from "./admin";
|
||||||
import {
|
import {
|
||||||
createDeployment,
|
createDeployment,
|
||||||
@@ -192,43 +180,43 @@ export const deployApplication = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let command = "set -e;";
|
||||||
if (application.sourceType === "github") {
|
if (application.sourceType === "github") {
|
||||||
await cloneGithubRepository({
|
command += await cloneGithubRepository(application);
|
||||||
...application,
|
|
||||||
logPath: deployment.logPath,
|
|
||||||
});
|
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "gitlab") {
|
} else if (application.sourceType === "gitlab") {
|
||||||
await cloneGitlabRepository(application, deployment.logPath);
|
command += await cloneGitlabRepository(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "gitea") {
|
} else if (application.sourceType === "gitea") {
|
||||||
await cloneGiteaRepository(application, deployment.logPath);
|
command += await cloneGiteaRepository(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "bitbucket") {
|
} else if (application.sourceType === "bitbucket") {
|
||||||
await cloneBitbucketRepository(application, deployment.logPath);
|
command += await cloneBitbucketRepository(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "docker") {
|
|
||||||
await buildDocker(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "git") {
|
} else if (application.sourceType === "git") {
|
||||||
await cloneGitRepository(application, deployment.logPath);
|
command += await cloneGitRepository(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "drop") {
|
} else if (application.sourceType === "drop") {
|
||||||
await buildApplication(application, deployment.logPath);
|
await buildApplication(application, deployment.logPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
command += getBuildCommand(application);
|
||||||
|
const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||||
|
if (application.serverId) {
|
||||||
|
await execAsyncRemote(application.serverId, commandWithLog);
|
||||||
|
} else {
|
||||||
|
await execAsync(commandWithLog);
|
||||||
|
}
|
||||||
|
|
||||||
|
await mechanizeDockerContainer(application);
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateApplicationStatus(applicationId, "done");
|
await updateApplicationStatus(applicationId, "done");
|
||||||
|
|
||||||
if (application.rollbackActive) {
|
// if (application.rollbackActive) {
|
||||||
const tagImage =
|
// const tagImage =
|
||||||
application.sourceType === "docker"
|
// application.sourceType === "docker"
|
||||||
? application.dockerImage
|
// ? application.dockerImage
|
||||||
: application.appName;
|
// : application.appName;
|
||||||
await createRollback({
|
// await createRollback({
|
||||||
appName: tagImage || "",
|
// appName: tagImage || "",
|
||||||
deploymentId: deployment.deploymentId,
|
// deploymentId: deployment.deploymentId,
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
|
|
||||||
await sendBuildSuccessNotifications({
|
await sendBuildSuccessNotifications({
|
||||||
projectName: application.environment.project.name,
|
projectName: application.environment.project.name,
|
||||||
@@ -239,6 +227,12 @@ export const deployApplication = async ({
|
|||||||
domains: application.domains,
|
domains: application.domains,
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
const command = `echo "Error 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 updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updateApplicationStatus(applicationId, "error");
|
await updateApplicationStatus(applicationId, "error");
|
||||||
|
|
||||||
@@ -254,7 +248,6 @@ export const deployApplication = async ({
|
|||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -276,129 +269,21 @@ export const rebuildApplication = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (application.sourceType === "github") {
|
let command = "set -e;";
|
||||||
await buildApplication(application, deployment.logPath);
|
// Check case for docker only
|
||||||
} else if (application.sourceType === "gitlab") {
|
command += getBuildCommand(application);
|
||||||
await buildApplication(application, deployment.logPath);
|
const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||||
} else if (application.sourceType === "bitbucket") {
|
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "docker") {
|
|
||||||
await buildDocker(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "git") {
|
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "drop") {
|
|
||||||
await buildApplication(application, deployment.logPath);
|
|
||||||
}
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
|
||||||
await updateApplicationStatus(applicationId, "done");
|
|
||||||
} catch (error) {
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
|
||||||
await updateApplicationStatus(applicationId, "error");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deployRemoteApplication = async ({
|
|
||||||
applicationId,
|
|
||||||
titleLog = "Manual deployment",
|
|
||||||
descriptionLog = "",
|
|
||||||
}: {
|
|
||||||
applicationId: string;
|
|
||||||
titleLog: string;
|
|
||||||
descriptionLog: string;
|
|
||||||
}) => {
|
|
||||||
const application = await findApplicationById(applicationId);
|
|
||||||
|
|
||||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${application.environment.projectId}/environment/${application.environmentId}/services/application/${application.applicationId}?tab=deployments`;
|
|
||||||
const deployment = await createDeployment({
|
|
||||||
applicationId: applicationId,
|
|
||||||
title: titleLog,
|
|
||||||
description: descriptionLog,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
let command = "set -e;";
|
await execAsyncRemote(application.serverId, commandWithLog);
|
||||||
if (application.sourceType === "github") {
|
} else {
|
||||||
command += await getGithubCloneCommand({
|
await execAsync(commandWithLog);
|
||||||
...application,
|
|
||||||
serverId: application.serverId,
|
|
||||||
logPath: deployment.logPath,
|
|
||||||
});
|
|
||||||
} else if (application.sourceType === "gitlab") {
|
|
||||||
command += await getGitlabCloneCommand(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "bitbucket") {
|
|
||||||
command += await getBitbucketCloneCommand(
|
|
||||||
application,
|
|
||||||
deployment.logPath,
|
|
||||||
);
|
|
||||||
} else if (application.sourceType === "gitea") {
|
|
||||||
command += await getGiteaCloneCommand(application, deployment.logPath);
|
|
||||||
} else if (application.sourceType === "git") {
|
|
||||||
command += await getCustomGitCloneCommand(
|
|
||||||
application,
|
|
||||||
deployment.logPath,
|
|
||||||
);
|
|
||||||
} else if (application.sourceType === "docker") {
|
|
||||||
command += await buildRemoteDocker(application, deployment.logPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (application.sourceType !== "docker") {
|
|
||||||
command += getBuildCommand(application, deployment.logPath);
|
|
||||||
}
|
|
||||||
await execAsyncRemote(application.serverId, command);
|
|
||||||
await mechanizeDockerContainer(application);
|
|
||||||
}
|
}
|
||||||
|
await mechanizeDockerContainer(application);
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateApplicationStatus(applicationId, "done");
|
await updateApplicationStatus(applicationId, "done");
|
||||||
|
|
||||||
if (application.rollbackActive) {
|
|
||||||
const tagImage =
|
|
||||||
application.sourceType === "docker"
|
|
||||||
? application.dockerImage
|
|
||||||
: application.appName;
|
|
||||||
await createRollback({
|
|
||||||
appName: tagImage || "",
|
|
||||||
deploymentId: deployment.deploymentId,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await sendBuildSuccessNotifications({
|
|
||||||
projectName: application.environment.project.name,
|
|
||||||
applicationName: application.name,
|
|
||||||
applicationType: "application",
|
|
||||||
buildLink,
|
|
||||||
organizationId: application.environment.project.organizationId,
|
|
||||||
domains: application.domains,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
||||||
|
|
||||||
const encodedContent = encodeBase64(errorMessage);
|
|
||||||
|
|
||||||
await execAsyncRemote(
|
|
||||||
application.serverId,
|
|
||||||
`
|
|
||||||
echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath};
|
|
||||||
echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};
|
|
||||||
echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updateApplicationStatus(applicationId, "error");
|
await updateApplicationStatus(applicationId, "error");
|
||||||
|
|
||||||
await sendBuildErrorNotifications({
|
|
||||||
projectName: application.environment.project.name,
|
|
||||||
applicationName: application.name,
|
|
||||||
applicationType: "application",
|
|
||||||
errorMessage: `Please check the logs for details: ${errorMessage}`,
|
|
||||||
buildLink,
|
|
||||||
organizationId: application.environment.project.organizationId,
|
|
||||||
});
|
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +471,7 @@ export const deployRemotePreviewApplication = async ({
|
|||||||
if (application.serverId) {
|
if (application.serverId) {
|
||||||
let command = "set -e;";
|
let command = "set -e;";
|
||||||
if (application.sourceType === "github") {
|
if (application.sourceType === "github") {
|
||||||
command += await getGithubCloneCommand({
|
command += await cloneGithubRepository({
|
||||||
...application,
|
...application,
|
||||||
appName: previewDeployment.appName,
|
appName: previewDeployment.appName,
|
||||||
branch: previewDeployment.branch,
|
branch: previewDeployment.branch,
|
||||||
@@ -629,53 +514,6 @@ export const deployRemotePreviewApplication = async ({
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const rebuildRemoteApplication = async ({
|
|
||||||
applicationId,
|
|
||||||
titleLog = "Rebuild deployment",
|
|
||||||
descriptionLog = "",
|
|
||||||
}: {
|
|
||||||
applicationId: string;
|
|
||||||
titleLog: string;
|
|
||||||
descriptionLog: string;
|
|
||||||
}) => {
|
|
||||||
const application = await findApplicationById(applicationId);
|
|
||||||
|
|
||||||
const deployment = await createDeployment({
|
|
||||||
applicationId: applicationId,
|
|
||||||
title: titleLog,
|
|
||||||
description: descriptionLog,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (application.serverId) {
|
|
||||||
if (application.sourceType !== "docker") {
|
|
||||||
let command = "set -e;";
|
|
||||||
command += getBuildCommand(application, deployment.logPath);
|
|
||||||
await execAsyncRemote(application.serverId, command);
|
|
||||||
}
|
|
||||||
await mechanizeDockerContainer(application);
|
|
||||||
}
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
|
||||||
await updateApplicationStatus(applicationId, "done");
|
|
||||||
} catch (error) {
|
|
||||||
// @ts-ignore
|
|
||||||
const encodedContent = encodeBase64(error?.message);
|
|
||||||
|
|
||||||
await execAsyncRemote(
|
|
||||||
application.serverId,
|
|
||||||
`
|
|
||||||
echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath};
|
|
||||||
echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};
|
|
||||||
echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`,
|
|
||||||
);
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
|
||||||
await updateApplicationStatus(applicationId, "error");
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getApplicationStats = async (appName: string) => {
|
export const getApplicationStats = async (appName: string) => {
|
||||||
const filter = {
|
const filter = {
|
||||||
status: ["running"],
|
status: ["running"],
|
||||||
|
|||||||
@@ -7,14 +7,10 @@ import {
|
|||||||
cleanAppName,
|
cleanAppName,
|
||||||
compose,
|
compose,
|
||||||
} from "@dokploy/server/db/schema";
|
} from "@dokploy/server/db/schema";
|
||||||
import {
|
import { getBuildComposeCommand } from "@dokploy/server/utils/builders/compose";
|
||||||
buildCompose,
|
|
||||||
getBuildComposeCommand,
|
|
||||||
} from "@dokploy/server/utils/builders/compose";
|
|
||||||
import { randomizeSpecificationFile } from "@dokploy/server/utils/docker/compose";
|
import { randomizeSpecificationFile } from "@dokploy/server/utils/docker/compose";
|
||||||
import {
|
import {
|
||||||
cloneCompose,
|
cloneCompose,
|
||||||
cloneComposeRemote,
|
|
||||||
loadDockerCompose,
|
loadDockerCompose,
|
||||||
loadDockerComposeRemote,
|
loadDockerComposeRemote,
|
||||||
} from "@dokploy/server/utils/docker/domain";
|
} from "@dokploy/server/utils/docker/domain";
|
||||||
@@ -25,33 +21,14 @@ import {
|
|||||||
execAsync,
|
execAsync,
|
||||||
execAsyncRemote,
|
execAsyncRemote,
|
||||||
} from "@dokploy/server/utils/process/execAsync";
|
} from "@dokploy/server/utils/process/execAsync";
|
||||||
import {
|
import { cloneBitbucketRepository } from "@dokploy/server/utils/providers/bitbucket";
|
||||||
cloneBitbucketRepository,
|
import { cloneGitRepository } from "@dokploy/server/utils/providers/git";
|
||||||
getBitbucketCloneCommand,
|
import { cloneGiteaRepository } from "@dokploy/server/utils/providers/gitea";
|
||||||
} from "@dokploy/server/utils/providers/bitbucket";
|
import { cloneGithubRepository } from "@dokploy/server/utils/providers/github";
|
||||||
import {
|
import { cloneGitlabRepository } from "@dokploy/server/utils/providers/gitlab";
|
||||||
cloneGitRepository,
|
import { getCreateComposeFileCommand } from "@dokploy/server/utils/providers/raw";
|
||||||
getCustomGitCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/git";
|
|
||||||
import {
|
|
||||||
cloneGiteaRepository,
|
|
||||||
getGiteaCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/gitea";
|
|
||||||
import {
|
|
||||||
cloneGithubRepository,
|
|
||||||
getGithubCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/github";
|
|
||||||
import {
|
|
||||||
cloneGitlabRepository,
|
|
||||||
getGitlabCloneCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/gitlab";
|
|
||||||
import {
|
|
||||||
createComposeFile,
|
|
||||||
getCreateComposeFileCommand,
|
|
||||||
} from "@dokploy/server/utils/providers/raw";
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { eq } from "drizzle-orm";
|
import { eq } from "drizzle-orm";
|
||||||
import { encodeBase64 } from "../utils/docker/utils";
|
|
||||||
import { getDokployUrl } from "./admin";
|
import { getDokployUrl } from "./admin";
|
||||||
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
|
import { createDeploymentCompose, updateDeploymentStatus } from "./deployment";
|
||||||
import { validUniqueServerAppName } from "./project";
|
import { validUniqueServerAppName } from "./project";
|
||||||
@@ -163,10 +140,11 @@ export const loadServices = async (
|
|||||||
const compose = await findComposeById(composeId);
|
const compose = await findComposeById(composeId);
|
||||||
|
|
||||||
if (type === "fetch") {
|
if (type === "fetch") {
|
||||||
|
const command = await cloneCompose(compose);
|
||||||
if (compose.serverId) {
|
if (compose.serverId) {
|
||||||
await cloneComposeRemote(compose);
|
await execAsyncRemote(compose.serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await cloneCompose(compose);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,24 +213,33 @@ export const deployCompose = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const entity = {
|
||||||
|
...compose,
|
||||||
|
type: "compose" as const,
|
||||||
|
};
|
||||||
|
let command = "set -e;";
|
||||||
if (compose.sourceType === "github") {
|
if (compose.sourceType === "github") {
|
||||||
await cloneGithubRepository({
|
command += await cloneGithubRepository(entity);
|
||||||
...compose,
|
|
||||||
logPath: deployment.logPath,
|
|
||||||
type: "compose",
|
|
||||||
});
|
|
||||||
} else if (compose.sourceType === "gitlab") {
|
} else if (compose.sourceType === "gitlab") {
|
||||||
await cloneGitlabRepository(compose, deployment.logPath, true);
|
command += await cloneGitlabRepository(entity);
|
||||||
} else if (compose.sourceType === "bitbucket") {
|
} else if (compose.sourceType === "bitbucket") {
|
||||||
await cloneBitbucketRepository(compose, deployment.logPath, true);
|
command += await cloneBitbucketRepository(entity);
|
||||||
} else if (compose.sourceType === "git") {
|
} else if (compose.sourceType === "git") {
|
||||||
await cloneGitRepository(compose, deployment.logPath, true);
|
command += await cloneGitRepository(entity);
|
||||||
} else if (compose.sourceType === "gitea") {
|
} else if (compose.sourceType === "gitea") {
|
||||||
await cloneGiteaRepository(compose, deployment.logPath, true);
|
command += await cloneGiteaRepository(entity);
|
||||||
} else if (compose.sourceType === "raw") {
|
} else if (compose.sourceType === "raw") {
|
||||||
await createComposeFile(compose, deployment.logPath);
|
command += getCreateComposeFileCommand(entity);
|
||||||
}
|
}
|
||||||
await buildCompose(compose, deployment.logPath);
|
|
||||||
|
command += await getBuildComposeCommand(entity);
|
||||||
|
const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||||
|
if (compose.serverId) {
|
||||||
|
await execAsyncRemote(compose.serverId, commandWithLog);
|
||||||
|
} else {
|
||||||
|
await execAsync(commandWithLog);
|
||||||
|
}
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
await updateCompose(composeId, {
|
await updateCompose(composeId, {
|
||||||
composeStatus: "done",
|
composeStatus: "done",
|
||||||
@@ -302,154 +289,16 @@ export const rebuildCompose = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let command = "set -e;";
|
||||||
if (compose.sourceType === "raw") {
|
if (compose.sourceType === "raw") {
|
||||||
await createComposeFile(compose, deployment.logPath);
|
command += getCreateComposeFileCommand(compose);
|
||||||
}
|
}
|
||||||
await buildCompose(compose, deployment.logPath);
|
command += await getBuildComposeCommand(compose);
|
||||||
|
const commandWithLog = `(${command}) >> ${deployment.logPath} 2>&1`;
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
|
||||||
await updateCompose(composeId, {
|
|
||||||
composeStatus: "done",
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
|
||||||
await updateCompose(composeId, {
|
|
||||||
composeStatus: "error",
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const deployRemoteCompose = async ({
|
|
||||||
composeId,
|
|
||||||
titleLog = "Manual deployment",
|
|
||||||
descriptionLog = "",
|
|
||||||
}: {
|
|
||||||
composeId: string;
|
|
||||||
titleLog: string;
|
|
||||||
descriptionLog: string;
|
|
||||||
}) => {
|
|
||||||
const compose = await findComposeById(composeId);
|
|
||||||
|
|
||||||
const buildLink = `${await getDokployUrl()}/dashboard/project/${
|
|
||||||
compose.environment.projectId
|
|
||||||
}/environment/${compose.environmentId}/services/compose/${compose.composeId}?tab=deployments`;
|
|
||||||
const deployment = await createDeploymentCompose({
|
|
||||||
composeId: composeId,
|
|
||||||
title: titleLog,
|
|
||||||
description: descriptionLog,
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
if (compose.serverId) {
|
if (compose.serverId) {
|
||||||
let command = "set -e;";
|
await execAsyncRemote(compose.serverId, commandWithLog);
|
||||||
|
} else {
|
||||||
if (compose.sourceType === "github") {
|
await execAsync(commandWithLog);
|
||||||
command += await getGithubCloneCommand({
|
|
||||||
...compose,
|
|
||||||
logPath: deployment.logPath,
|
|
||||||
type: "compose",
|
|
||||||
serverId: compose.serverId,
|
|
||||||
});
|
|
||||||
} else if (compose.sourceType === "gitlab") {
|
|
||||||
command += await getGitlabCloneCommand(
|
|
||||||
compose,
|
|
||||||
deployment.logPath,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else if (compose.sourceType === "bitbucket") {
|
|
||||||
command += await getBitbucketCloneCommand(
|
|
||||||
compose,
|
|
||||||
deployment.logPath,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
} else if (compose.sourceType === "git") {
|
|
||||||
command += await getCustomGitCloneCommand(
|
|
||||||
compose,
|
|
||||||
deployment.logPath,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
console.log(command);
|
|
||||||
} else if (compose.sourceType === "raw") {
|
|
||||||
command += getCreateComposeFileCommand(compose, deployment.logPath);
|
|
||||||
} else if (compose.sourceType === "gitea") {
|
|
||||||
command += await getGiteaCloneCommand(
|
|
||||||
compose,
|
|
||||||
deployment.logPath,
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await execAsyncRemote(compose.serverId, command);
|
|
||||||
await getBuildComposeCommand(compose, deployment.logPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
|
||||||
await updateCompose(composeId, {
|
|
||||||
composeStatus: "done",
|
|
||||||
});
|
|
||||||
|
|
||||||
await sendBuildSuccessNotifications({
|
|
||||||
projectName: compose.environment.project.name,
|
|
||||||
applicationName: compose.name,
|
|
||||||
applicationType: "compose",
|
|
||||||
buildLink,
|
|
||||||
organizationId: compose.environment.project.organizationId,
|
|
||||||
domains: compose.domains,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
// @ts-ignore
|
|
||||||
const encodedContent = encodeBase64(error?.message);
|
|
||||||
|
|
||||||
await execAsyncRemote(
|
|
||||||
compose.serverId,
|
|
||||||
`
|
|
||||||
echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath};
|
|
||||||
echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};
|
|
||||||
echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`,
|
|
||||||
);
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
|
||||||
await updateCompose(composeId, {
|
|
||||||
composeStatus: "error",
|
|
||||||
});
|
|
||||||
await sendBuildErrorNotifications({
|
|
||||||
projectName: compose.environment.project.name,
|
|
||||||
applicationName: compose.name,
|
|
||||||
applicationType: "compose",
|
|
||||||
// @ts-ignore
|
|
||||||
errorMessage: error?.message || "Error building",
|
|
||||||
buildLink,
|
|
||||||
organizationId: compose.environment.project.organizationId,
|
|
||||||
});
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rebuildRemoteCompose = async ({
|
|
||||||
composeId,
|
|
||||||
titleLog = "Rebuild deployment",
|
|
||||||
descriptionLog = "",
|
|
||||||
}: {
|
|
||||||
composeId: string;
|
|
||||||
titleLog: string;
|
|
||||||
descriptionLog: string;
|
|
||||||
}) => {
|
|
||||||
const compose = await findComposeById(composeId);
|
|
||||||
|
|
||||||
const deployment = await createDeploymentCompose({
|
|
||||||
composeId: composeId,
|
|
||||||
title: titleLog,
|
|
||||||
description: descriptionLog,
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (compose.sourceType === "raw") {
|
|
||||||
const command = getCreateComposeFileCommand(compose, deployment.logPath);
|
|
||||||
await execAsyncRemote(compose.serverId, command);
|
|
||||||
}
|
|
||||||
if (compose.serverId) {
|
|
||||||
await getBuildComposeCommand(compose, deployment.logPath);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "done");
|
await updateDeploymentStatus(deployment.deploymentId, "done");
|
||||||
@@ -457,16 +306,6 @@ export const rebuildRemoteCompose = async ({
|
|||||||
composeStatus: "done",
|
composeStatus: "done",
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// @ts-ignore
|
|
||||||
const encodedContent = encodeBase64(error?.message);
|
|
||||||
|
|
||||||
await execAsyncRemote(
|
|
||||||
compose.serverId,
|
|
||||||
`
|
|
||||||
echo "\n\n===================================EXTRA LOGS============================================" >> ${deployment.logPath};
|
|
||||||
echo "Error occurred ❌, check the logs for details." >> ${deployment.logPath};
|
|
||||||
echo "${encodedContent}" | base64 -d >> "${deployment.logPath}";`,
|
|
||||||
);
|
|
||||||
await updateDeploymentStatus(deployment.deploymentId, "error");
|
await updateDeploymentStatus(deployment.deploymentId, "error");
|
||||||
await updateCompose(composeId, {
|
await updateCompose(composeId, {
|
||||||
composeStatus: "error",
|
composeStatus: "error",
|
||||||
|
|||||||
@@ -74,20 +74,21 @@ export const createDeployment = async (
|
|||||||
>,
|
>,
|
||||||
) => {
|
) => {
|
||||||
const application = await findApplicationById(deployment.applicationId);
|
const application = await findApplicationById(deployment.applicationId);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await removeLastTenDeployments(
|
await removeLastTenDeployments(
|
||||||
deployment.applicationId,
|
deployment.applicationId,
|
||||||
"application",
|
"application",
|
||||||
application.serverId,
|
application.serverId,
|
||||||
);
|
);
|
||||||
const { LOGS_PATH } = paths(!!application.serverId);
|
const serverId = application.serverId;
|
||||||
|
|
||||||
|
const { LOGS_PATH } = paths(!!serverId);
|
||||||
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
const formattedDateTime = format(new Date(), "yyyy-MM-dd:HH:mm:ss");
|
||||||
const fileName = `${application.appName}-${formattedDateTime}.log`;
|
const fileName = `${application.appName}-${formattedDateTime}.log`;
|
||||||
const logFilePath = path.join(LOGS_PATH, application.appName, fileName);
|
const logFilePath = path.join(LOGS_PATH, application.appName, fileName);
|
||||||
|
|
||||||
if (application.serverId) {
|
if (serverId) {
|
||||||
const server = await findServerById(application.serverId);
|
const server = await findServerById(serverId);
|
||||||
|
|
||||||
const command = `
|
const command = `
|
||||||
mkdir -p ${LOGS_PATH}/${application.appName};
|
mkdir -p ${LOGS_PATH}/${application.appName};
|
||||||
@@ -99,7 +100,7 @@ export const createDeployment = async (
|
|||||||
await fsPromises.mkdir(path.join(LOGS_PATH, application.appName), {
|
await fsPromises.mkdir(path.join(LOGS_PATH, application.appName), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
await fsPromises.writeFile(logFilePath, "Initializing deployment");
|
await fsPromises.writeFile(logFilePath, "Initializing deployment\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const deploymentCreate = await db
|
const deploymentCreate = await db
|
||||||
@@ -111,6 +112,7 @@ export const createDeployment = async (
|
|||||||
logPath: logFilePath,
|
logPath: logFilePath,
|
||||||
description: deployment.description || "",
|
description: deployment.description || "",
|
||||||
startedAt: new Date().toISOString(),
|
startedAt: new Date().toISOString(),
|
||||||
|
// applicationBuildServerId: application.serverBuildId,
|
||||||
})
|
})
|
||||||
.returning();
|
.returning();
|
||||||
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
|
if (deploymentCreate.length === 0 || !deploymentCreate[0]) {
|
||||||
@@ -249,7 +251,7 @@ export const createDeploymentCompose = async (
|
|||||||
|
|
||||||
const command = `
|
const command = `
|
||||||
mkdir -p ${LOGS_PATH}/${compose.appName};
|
mkdir -p ${LOGS_PATH}/${compose.appName};
|
||||||
echo "Initializing deployment" >> ${logFilePath};
|
echo "Initializing deployment\n" >> ${logFilePath};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
await execAsyncRemote(server.serverId, command);
|
await execAsyncRemote(server.serverId, command);
|
||||||
@@ -257,7 +259,7 @@ echo "Initializing deployment" >> ${logFilePath};
|
|||||||
await fsPromises.mkdir(path.join(LOGS_PATH, compose.appName), {
|
await fsPromises.mkdir(path.join(LOGS_PATH, compose.appName), {
|
||||||
recursive: true,
|
recursive: true,
|
||||||
});
|
});
|
||||||
await fsPromises.writeFile(logFilePath, "Initializing deployment");
|
await fsPromises.writeFile(logFilePath, "Initializing deployment\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
const deploymentCreate = await db
|
const deploymentCreate = await db
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import {
|
|||||||
getEnviromentVariablesObject,
|
getEnviromentVariablesObject,
|
||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
export type ComposeNested = InferResultType<
|
export type ComposeNested = InferResultType<
|
||||||
@@ -96,22 +96,15 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBuildComposeCommand = async (
|
export const getBuildComposeCommand = async (compose: ComposeNested) => {
|
||||||
compose: ComposeNested,
|
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
const { sourceType, appName, mounts, composeType, domains } = compose;
|
const { sourceType, appName, mounts, composeType, domains } = compose;
|
||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
const envCommand = getCreateEnvFileCommand(compose);
|
const envCommand = getCreateEnvFileCommand(compose);
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
const exportEnvCommand = getExportEnvCommand(compose);
|
const exportEnvCommand = getExportEnvCommand(compose);
|
||||||
|
|
||||||
const newCompose = await writeDomainsToComposeRemote(
|
const newCompose = await writeDomainsToComposeRemote(compose, domains);
|
||||||
compose,
|
|
||||||
domains,
|
|
||||||
logPath,
|
|
||||||
);
|
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
Build Compose 🐳
|
Build Compose 🐳
|
||||||
@@ -133,7 +126,7 @@ Compose Type: ${composeType} ✅`;
|
|||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
set -e
|
set -e
|
||||||
{
|
{
|
||||||
echo "${logBox}" >> "${logPath}"
|
echo "${logBox}";
|
||||||
|
|
||||||
${newCompose}
|
${newCompose}
|
||||||
|
|
||||||
@@ -143,17 +136,18 @@ Compose Type: ${composeType} ✅`;
|
|||||||
|
|
||||||
${exportEnvCommand}
|
${exportEnvCommand}
|
||||||
${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""}
|
${compose.isolatedDeployment ? `docker network inspect ${compose.appName} >/dev/null 2>&1 || docker network create --attachable ${compose.appName}` : ""}
|
||||||
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
docker ${command.split(" ").join(" ")} 2>&1 || { echo "Error: ❌ Docker command failed"; exit 1; }
|
||||||
${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
|
${compose.isolatedDeployment ? `docker network connect ${compose.appName} $(docker ps --filter "name=dokploy-traefik" -q) >/dev/null 2>&1` : ""}
|
||||||
|
|
||||||
echo "Docker Compose Deployed: ✅" >> "${logPath}"
|
echo "Docker Compose Deployed: ✅";
|
||||||
} || {
|
} || {
|
||||||
echo "Error: ❌ Script execution failed" >> "${logPath}"
|
echo "Error: ❌ Script execution failed";
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return await execAsyncRemote(compose.serverId, bashCommand);
|
return bashCommand;
|
||||||
|
// return await execAsyncRemote(compose.serverId, bashCommand);
|
||||||
};
|
};
|
||||||
|
|
||||||
const sanitizeCommand = (command: string) => {
|
const sanitizeCommand = (command: string) => {
|
||||||
@@ -216,7 +210,7 @@ const createEnvFile = (compose: ComposeNested) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(!!compose.serverId);
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
const composeFilePath =
|
const composeFilePath =
|
||||||
join(COMPOSE_PATH, appName, "code", composePath) ||
|
join(COMPOSE_PATH, appName, "code", composePath) ||
|
||||||
|
|||||||
@@ -100,10 +100,7 @@ export const buildCustomDocker = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getDockerCommand = (
|
export const getDockerCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
@@ -176,17 +173,17 @@ export const getDockerCommand = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
command += `
|
command += `
|
||||||
echo "Building ${appName}" >> ${logPath};
|
echo "Building ${appName}" ;
|
||||||
cd ${dockerContextPath} >> ${logPath} 2>> ${logPath} || {
|
cd ${dockerContextPath} || {
|
||||||
echo "❌ The path ${dockerContextPath} does not exist" >> ${logPath};
|
echo "❌ The path ${dockerContextPath} does not exist" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
${joinedSecrets} docker ${commandArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
|
${joinedSecrets} docker ${commandArgs.join(" ")} || {
|
||||||
echo "❌ Docker build failed" >> ${logPath};
|
echo "❌ Docker build failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Docker build completed." >> ${logPath};
|
echo "✅ Docker build completed." ;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
|
|||||||
@@ -45,10 +45,7 @@ export const buildHeroku = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getHerokuCommand = (
|
export const getHerokuCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { env, appName, cleanCache } = application;
|
const { env, appName, cleanCache } = application;
|
||||||
|
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
@@ -77,12 +74,12 @@ export const getHerokuCommand = (
|
|||||||
|
|
||||||
const command = `pack ${args.join(" ")}`;
|
const command = `pack ${args.join(" ")}`;
|
||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
echo "Starting heroku build..." >> ${logPath};
|
echo "Starting heroku build..." ;
|
||||||
${command} >> ${logPath} 2>> ${logPath} || {
|
${command} || {
|
||||||
echo "❌ Heroku build failed" >> ${logPath};
|
echo "❌ Heroku build failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Heroku build completed." >> ${logPath};
|
echo "✅ Heroku build completed." ;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return bashCommand;
|
return bashCommand;
|
||||||
|
|||||||
@@ -76,34 +76,31 @@ export const buildApplication = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBuildCommand = (
|
export const getBuildCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
let command = "";
|
let command = "";
|
||||||
const { buildType, registry } = application;
|
const { buildType, registry } = application;
|
||||||
switch (buildType) {
|
switch (buildType) {
|
||||||
case "nixpacks":
|
case "nixpacks":
|
||||||
command = getNixpacksCommand(application, logPath);
|
command = getNixpacksCommand(application);
|
||||||
break;
|
break;
|
||||||
case "heroku_buildpacks":
|
case "heroku_buildpacks":
|
||||||
command = getHerokuCommand(application, logPath);
|
command = getHerokuCommand(application);
|
||||||
break;
|
break;
|
||||||
case "paketo_buildpacks":
|
case "paketo_buildpacks":
|
||||||
command = getPaketoCommand(application, logPath);
|
command = getPaketoCommand(application);
|
||||||
break;
|
break;
|
||||||
case "static":
|
case "static":
|
||||||
command = getStaticCommand(application, logPath);
|
command = getStaticCommand(application);
|
||||||
break;
|
break;
|
||||||
case "dockerfile":
|
case "dockerfile":
|
||||||
command = getDockerCommand(application, logPath);
|
command = getDockerCommand(application);
|
||||||
break;
|
break;
|
||||||
case "railpack":
|
case "railpack":
|
||||||
command = getRailpackCommand(application, logPath);
|
command = getRailpackCommand(application);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (registry) {
|
if (registry) {
|
||||||
command += uploadImageRemoteCommand(application, logPath);
|
command += uploadImageRemoteCommand(application);
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
|
|||||||
@@ -92,10 +92,7 @@ export const buildNixpacks = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getNixpacksCommand = (
|
export const getNixpacksCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { env, appName, publishDirectory, cleanCache } = application;
|
const { env, appName, publishDirectory, cleanCache } = application;
|
||||||
|
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
@@ -121,13 +118,13 @@ export const getNixpacksCommand = (
|
|||||||
args.push("--no-error-without-start");
|
args.push("--no-error-without-start");
|
||||||
}
|
}
|
||||||
const command = `nixpacks ${args.join(" ")}`;
|
const command = `nixpacks ${args.join(" ")}`;
|
||||||
let bashCommand = `
|
const bashCommand = `
|
||||||
echo "Starting nixpacks build..." >> ${logPath};
|
echo "Starting nixpacks build..." ;
|
||||||
${command} >> ${logPath} 2>> ${logPath} || {
|
${command} || {
|
||||||
echo "❌ Nixpacks build failed" >> ${logPath};
|
echo "❌ Nixpacks build failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Nixpacks build completed." >> ${logPath};
|
echo "✅ Nixpacks build completed." ;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -135,23 +132,23 @@ echo "✅ Nixpacks build completed." >> ${logPath};
|
|||||||
and copy the artifacts on the host filesystem.
|
and copy the artifacts on the host filesystem.
|
||||||
Then, remove the container and create a static build.
|
Then, remove the container and create a static build.
|
||||||
*/
|
*/
|
||||||
if (publishDirectory) {
|
// if (publishDirectory) {
|
||||||
const localPath = path.join(buildAppDirectory, publishDirectory);
|
// const localPath = path.join(buildAppDirectory, publishDirectory);
|
||||||
const isDirectory =
|
// const isDirectory =
|
||||||
publishDirectory.endsWith("/") || !path.extname(publishDirectory);
|
// publishDirectory.endsWith("/") || !path.extname(publishDirectory);
|
||||||
|
|
||||||
bashCommand += `
|
// bashCommand += `
|
||||||
docker create --name ${buildContainerId} ${appName}
|
// docker create --name ${buildContainerId} ${appName}
|
||||||
mkdir -p ${localPath}
|
// mkdir -p ${localPath}
|
||||||
docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || {
|
// docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} || {
|
||||||
docker rm ${buildContainerId}
|
// docker rm ${buildContainerId}
|
||||||
echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" >> ${logPath};
|
// echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" ;
|
||||||
exit 1;
|
// exit 1;
|
||||||
}
|
// }
|
||||||
docker rm ${buildContainerId}
|
// docker rm ${buildContainerId}
|
||||||
${getStaticCommand(application, logPath)}
|
// ${getStaticCommand(application)}
|
||||||
`;
|
// `;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return bashCommand;
|
return bashCommand;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,10 +44,7 @@ export const buildPaketo = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPaketoCommand = (
|
export const getPaketoCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { env, appName, cleanCache } = application;
|
const { env, appName, cleanCache } = application;
|
||||||
|
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
@@ -76,12 +73,12 @@ export const getPaketoCommand = (
|
|||||||
|
|
||||||
const command = `pack ${args.join(" ")}`;
|
const command = `pack ${args.join(" ")}`;
|
||||||
const bashCommand = `
|
const bashCommand = `
|
||||||
echo "Starting Paketo build..." >> ${logPath};
|
echo "Starting Paketo build..." ;
|
||||||
${command} >> ${logPath} 2>> ${logPath} || {
|
${command} || {
|
||||||
echo "❌ Paketo build failed" >> ${logPath};
|
echo "❌ Paketo build failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Paketo build completed." >> ${logPath};
|
echo "✅ Paketo build completed." ;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return bashCommand;
|
return bashCommand;
|
||||||
|
|||||||
@@ -116,10 +116,7 @@ export const buildRailpack = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getRailpackCommand = (
|
export const getRailpackCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { env, appName, cleanCache } = application;
|
const { env, appName, cleanCache } = application;
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
const envVariables = prepareEnvironmentVariables(
|
const envVariables = prepareEnvironmentVariables(
|
||||||
@@ -183,21 +180,21 @@ export const getRailpackCommand = (
|
|||||||
docker buildx create --use --name builder-containerd --driver docker-container || true
|
docker buildx create --use --name builder-containerd --driver docker-container || true
|
||||||
docker buildx use builder-containerd
|
docker buildx use builder-containerd
|
||||||
|
|
||||||
echo "Preparing Railpack build plan..." >> "${logPath}";
|
echo "Preparing Railpack build plan..." ;
|
||||||
railpack ${prepareArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
|
railpack ${prepareArgs.join(" ")} || {
|
||||||
echo "❌ Railpack prepare failed" >> ${logPath};
|
echo "❌ Railpack prepare failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Railpack prepare completed." >> ${logPath};
|
echo "✅ Railpack prepare completed." ;
|
||||||
|
|
||||||
echo "Building with Railpack frontend..." >> "${logPath}";
|
echo "Building with Railpack frontend..." ;
|
||||||
# Export environment variables for secrets
|
# Export environment variables for secrets
|
||||||
${exportEnvs.join("\n")}
|
${exportEnvs.join("\n")}
|
||||||
docker ${buildArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
|
docker ${buildArgs.join(" ")} || {
|
||||||
echo "❌ Railpack build failed" >> ${logPath};
|
echo "❌ Railpack build failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Railpack build completed." >> ${logPath};
|
echo "✅ Railpack build completed." ;
|
||||||
docker buildx rm builder-containerd
|
docker buildx rm builder-containerd
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -83,10 +83,7 @@ export const buildStatic = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getStaticCommand = (
|
export const getStaticCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { publishDirectory } = application;
|
const { publishDirectory } = application;
|
||||||
const buildAppDirectory = getBuildAppDirectory(application);
|
const buildAppDirectory = getBuildAppDirectory(application);
|
||||||
|
|
||||||
@@ -100,13 +97,10 @@ export const getStaticCommand = (
|
|||||||
].join("\n"),
|
].join("\n"),
|
||||||
);
|
);
|
||||||
|
|
||||||
command += getDockerCommand(
|
command += getDockerCommand({
|
||||||
{
|
...application,
|
||||||
...application,
|
buildType: "dockerfile",
|
||||||
buildType: "dockerfile",
|
dockerfile: "Dockerfile",
|
||||||
dockerfile: "Dockerfile",
|
});
|
||||||
},
|
|
||||||
logPath,
|
|
||||||
);
|
|
||||||
return command;
|
return command;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -59,10 +59,7 @@ export const uploadImage = async (
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadImageRemoteCommand = (
|
export const uploadImageRemoteCommand = (application: ApplicationNested) => {
|
||||||
application: ApplicationNested,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const registry = application.registry;
|
const registry = application.registry;
|
||||||
|
|
||||||
if (!registry) {
|
if (!registry) {
|
||||||
@@ -82,22 +79,22 @@ export const uploadImageRemoteCommand = (
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const command = `
|
const command = `
|
||||||
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" >> ${logPath};
|
echo "📦 [Enabled Registry] Uploading image to '${registry.registryType}' | '${registryTag}'" ;
|
||||||
echo "${registry.password}" | docker login ${finalURL} -u ${registry.username} --password-stdin >> ${logPath} 2>> ${logPath} || {
|
echo "${registry.password}" | docker login ${finalURL} -u ${registry.username} --password-stdin || {
|
||||||
echo "❌ DockerHub Failed" >> ${logPath};
|
echo "❌ DockerHub Failed" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Registry Login Success" >> ${logPath};
|
echo "✅ Registry Login Success" ;
|
||||||
docker tag ${imageName} ${registryTag} >> ${logPath} 2>> ${logPath} || {
|
docker tag ${imageName} ${registryTag} || {
|
||||||
echo "❌ Error tagging image" >> ${logPath};
|
echo "❌ Error tagging image" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Image Tagged" >> ${logPath};
|
echo "✅ Image Tagged" ;
|
||||||
docker push ${registryTag} 2>> ${logPath} || {
|
docker push ${registryTag} || {
|
||||||
echo "❌ Error pushing image" >> ${logPath};
|
echo "❌ Error pushing image" ;
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
echo "✅ Image Pushed" >> ${logPath};
|
echo "✅ Image Pushed" ;
|
||||||
`;
|
`;
|
||||||
return command;
|
return command;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { findComposeById } from "@dokploy/server/services/compose";
|
import { findComposeById } from "@dokploy/server/services/compose";
|
||||||
import { stringify } from "yaml";
|
import { stringify } from "yaml";
|
||||||
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { addAppNameToAllServiceNames } from "./collision/root-network";
|
import { addAppNameToAllServiceNames } from "./collision/root-network";
|
||||||
import { generateRandomHash } from "./compose";
|
import { generateRandomHash } from "./compose";
|
||||||
import { addSuffixToAllVolumes } from "./compose/volume";
|
import { addSuffixToAllVolumes } from "./compose/volume";
|
||||||
import {
|
import {
|
||||||
cloneCompose,
|
cloneCompose,
|
||||||
cloneComposeRemote,
|
|
||||||
loadDockerCompose,
|
loadDockerCompose,
|
||||||
loadDockerComposeRemote,
|
loadDockerComposeRemote,
|
||||||
} from "./domain";
|
} from "./domain";
|
||||||
@@ -31,10 +31,11 @@ export const randomizeIsolatedDeploymentComposeFile = async (
|
|||||||
) => {
|
) => {
|
||||||
const compose = await findComposeById(composeId);
|
const compose = await findComposeById(composeId);
|
||||||
|
|
||||||
|
const command = await cloneCompose(compose);
|
||||||
if (compose.serverId) {
|
if (compose.serverId) {
|
||||||
await cloneComposeRemote(compose);
|
await execAsyncRemote(compose.serverId, command);
|
||||||
} else {
|
} else {
|
||||||
await cloneCompose(compose);
|
await execAsync(command);
|
||||||
}
|
}
|
||||||
|
|
||||||
let composeData: ComposeSpecification | null;
|
let composeData: ComposeSpecification | null;
|
||||||
|
|||||||
@@ -6,30 +6,12 @@ import type { Compose } from "@dokploy/server/services/compose";
|
|||||||
import type { Domain } from "@dokploy/server/services/domain";
|
import type { Domain } from "@dokploy/server/services/domain";
|
||||||
import { parse, stringify } from "yaml";
|
import { parse, stringify } from "yaml";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
import {
|
import { cloneBitbucketRepository } from "../providers/bitbucket";
|
||||||
cloneRawBitbucketRepository,
|
import { cloneGitRepository } from "../providers/git";
|
||||||
cloneRawBitbucketRepositoryRemote,
|
import { cloneGiteaRepository } from "../providers/gitea";
|
||||||
} from "../providers/bitbucket";
|
import { cloneGithubRepository } from "../providers/github";
|
||||||
import {
|
import { cloneGitlabRepository } from "../providers/gitlab";
|
||||||
cloneGitRawRepository,
|
import { getCreateComposeFileCommand } from "../providers/raw";
|
||||||
cloneRawGitRepositoryRemote,
|
|
||||||
} from "../providers/git";
|
|
||||||
import {
|
|
||||||
cloneRawGiteaRepository,
|
|
||||||
cloneRawGiteaRepositoryRemote,
|
|
||||||
} from "../providers/gitea";
|
|
||||||
import {
|
|
||||||
cloneRawGithubRepository,
|
|
||||||
cloneRawGithubRepositoryRemote,
|
|
||||||
} from "../providers/github";
|
|
||||||
import {
|
|
||||||
cloneRawGitlabRepository,
|
|
||||||
cloneRawGitlabRepositoryRemote,
|
|
||||||
} from "../providers/gitlab";
|
|
||||||
import {
|
|
||||||
createComposeFileRaw,
|
|
||||||
createComposeFileRawRemote,
|
|
||||||
} from "../providers/raw";
|
|
||||||
import { randomizeDeployableSpecificationFile } from "./collision";
|
import { randomizeDeployableSpecificationFile } from "./collision";
|
||||||
import { randomizeSpecificationFile } from "./compose";
|
import { randomizeSpecificationFile } from "./compose";
|
||||||
import type {
|
import type {
|
||||||
@@ -40,35 +22,25 @@ import type {
|
|||||||
import { encodeBase64 } from "./utils";
|
import { encodeBase64 } from "./utils";
|
||||||
|
|
||||||
export const cloneCompose = async (compose: Compose) => {
|
export const cloneCompose = async (compose: Compose) => {
|
||||||
|
let command = "set -e;";
|
||||||
|
const entity = {
|
||||||
|
...compose,
|
||||||
|
type: "compose" as const,
|
||||||
|
};
|
||||||
if (compose.sourceType === "github") {
|
if (compose.sourceType === "github") {
|
||||||
await cloneRawGithubRepository(compose);
|
command += await cloneGithubRepository(entity);
|
||||||
} else if (compose.sourceType === "gitlab") {
|
} else if (compose.sourceType === "gitlab") {
|
||||||
await cloneRawGitlabRepository(compose);
|
command += await cloneGitlabRepository(entity);
|
||||||
} else if (compose.sourceType === "bitbucket") {
|
} else if (compose.sourceType === "bitbucket") {
|
||||||
await cloneRawBitbucketRepository(compose);
|
command += await cloneBitbucketRepository(entity);
|
||||||
} else if (compose.sourceType === "git") {
|
} else if (compose.sourceType === "git") {
|
||||||
await cloneGitRawRepository(compose);
|
command += await cloneGitRepository(entity);
|
||||||
} else if (compose.sourceType === "gitea") {
|
} else if (compose.sourceType === "gitea") {
|
||||||
await cloneRawGiteaRepository(compose);
|
command += await cloneGiteaRepository(entity);
|
||||||
} else if (compose.sourceType === "raw") {
|
} else if (compose.sourceType === "raw") {
|
||||||
await createComposeFileRaw(compose);
|
command += getCreateComposeFileCommand(compose);
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneComposeRemote = async (compose: Compose) => {
|
|
||||||
if (compose.sourceType === "github") {
|
|
||||||
await cloneRawGithubRepositoryRemote(compose);
|
|
||||||
} else if (compose.sourceType === "gitlab") {
|
|
||||||
await cloneRawGitlabRepositoryRemote(compose);
|
|
||||||
} else if (compose.sourceType === "bitbucket") {
|
|
||||||
await cloneRawBitbucketRepositoryRemote(compose);
|
|
||||||
} else if (compose.sourceType === "git") {
|
|
||||||
await cloneRawGitRepositoryRemote(compose);
|
|
||||||
} else if (compose.sourceType === "gitea") {
|
|
||||||
await cloneRawGiteaRepositoryRemote(compose);
|
|
||||||
} else if (compose.sourceType === "raw") {
|
|
||||||
await createComposeFileRawRemote(compose);
|
|
||||||
}
|
}
|
||||||
|
return command;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getComposePath = (compose: Compose) => {
|
export const getComposePath = (compose: Compose) => {
|
||||||
@@ -152,7 +124,6 @@ export const writeDomainsToCompose = async (
|
|||||||
export const writeDomainsToComposeRemote = async (
|
export const writeDomainsToComposeRemote = async (
|
||||||
compose: Compose,
|
compose: Compose,
|
||||||
domains: Domain[],
|
domains: Domain[],
|
||||||
logPath: string,
|
|
||||||
) => {
|
) => {
|
||||||
if (!domains.length) {
|
if (!domains.length) {
|
||||||
return "";
|
return "";
|
||||||
@@ -164,7 +135,7 @@ export const writeDomainsToComposeRemote = async (
|
|||||||
|
|
||||||
if (!composeConverted) {
|
if (!composeConverted) {
|
||||||
return `
|
return `
|
||||||
echo "❌ Error: Compose file not found" >> ${logPath};
|
echo "❌ Error: Compose file not found";
|
||||||
exit 1;
|
exit 1;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
@@ -175,7 +146,7 @@ exit 1;
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
return `echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
|
return `echo "❌ Has occured an error: ${error?.message || error}";
|
||||||
exit 1;
|
exit 1;
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,11 +116,7 @@ export const execAsyncRemote = async (
|
|||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve({ stdout, stderr });
|
resolve({ stdout, stderr });
|
||||||
} else {
|
} else {
|
||||||
reject(
|
reject(new Error(`Error occurred ❌: ${stderr}`));
|
||||||
new Error(
|
|
||||||
`Command exited with code ${code}. Stderr: ${stderr}, command: ${command}`,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.on("data", (data: string) => {
|
.on("data", (data: string) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type {
|
import type {
|
||||||
@@ -9,12 +8,8 @@ import {
|
|||||||
type Bitbucket,
|
type Bitbucket,
|
||||||
findBitbucketById,
|
findBitbucketById,
|
||||||
} from "@dokploy/server/services/bitbucket";
|
} from "@dokploy/server/services/bitbucket";
|
||||||
import type { Compose } from "@dokploy/server/services/compose";
|
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export type ApplicationWithBitbucket = InferResultType<
|
export type ApplicationWithBitbucket = InferResultType<
|
||||||
"applications",
|
"applications",
|
||||||
@@ -81,13 +76,16 @@ export const getBitbucketHeaders = (bitbucketProvider: Bitbucket) => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneBitbucketRepository = async (
|
type BitbucketClone = (ApplicationWithBitbucket | ComposeWithBitbucket) & {
|
||||||
entity: ApplicationWithBitbucket | ComposeWithBitbucket,
|
serverId: string | null;
|
||||||
logPath: string,
|
type?: "application" | "compose";
|
||||||
isCompose = false,
|
};
|
||||||
) => {
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
export const cloneBitbucketRepository = async ({
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
type = "application",
|
||||||
|
...entity
|
||||||
|
}: BitbucketClone) => {
|
||||||
|
let command = "set -e;";
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
bitbucketRepository,
|
bitbucketRepository,
|
||||||
@@ -96,187 +94,24 @@ export const cloneBitbucketRepository = async (
|
|||||||
bitbucketId,
|
bitbucketId,
|
||||||
bitbucket,
|
bitbucket,
|
||||||
enableSubmodules,
|
enableSubmodules,
|
||||||
|
serverId,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId);
|
||||||
|
|
||||||
if (!bitbucketId) {
|
if (!bitbucketId) {
|
||||||
throw new TRPCError({
|
command += `echo "Error: ❌ Bitbucket Provider not found"; exit 1;`;
|
||||||
code: "NOT_FOUND",
|
return command;
|
||||||
message: "Bitbucket Provider not found",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
command += `rm -rf ${outputPath};`;
|
||||||
|
command += `mkdir -p ${outputPath};`;
|
||||||
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
||||||
const cloneUrl = getBitbucketCloneUrl(bitbucket, repoclone);
|
const cloneUrl = getBitbucketCloneUrl(bitbucket, repoclone);
|
||||||
try {
|
command += `echo "Cloning Repo ${repoclone} to ${outputPath}: ✅";`;
|
||||||
writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`);
|
command += `git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`;
|
||||||
const cloneArgs = [
|
return command;
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
bitbucketBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync("git", cloneArgs, (data) => {
|
|
||||||
if (writeStream.writable) {
|
|
||||||
writeStream.write(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
writeStream.write(`\nCloned ${repoclone} to ${outputPath}: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`ERROR Cloning: ${error}: ❌`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawBitbucketRepository = async (entity: Compose) => {
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
bitbucketRepository,
|
|
||||||
bitbucketOwner,
|
|
||||||
bitbucketBranch,
|
|
||||||
bitbucketId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!bitbucketId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Bitbucket Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bitbucketProvider = await findBitbucketById(bitbucketId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
|
||||||
const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
bitbucketBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync("git", cloneArgs);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawBitbucketRepositoryRemote = async (compose: Compose) => {
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
bitbucketRepository,
|
|
||||||
bitbucketOwner,
|
|
||||||
bitbucketBranch,
|
|
||||||
bitbucketId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = compose;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!bitbucketId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Bitbucket Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bitbucketProvider = await findBitbucketById(bitbucketId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
|
||||||
const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const cloneCommand = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath}
|
|
||||||
`;
|
|
||||||
await execAsyncRemote(serverId, cloneCommand);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getBitbucketCloneCommand = async (
|
|
||||||
entity: ApplicationWithBitbucket | ComposeWithBitbucket,
|
|
||||||
logPath: string,
|
|
||||||
isCompose = false,
|
|
||||||
) => {
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
bitbucketRepository,
|
|
||||||
bitbucketOwner,
|
|
||||||
bitbucketBranch,
|
|
||||||
bitbucketId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!bitbucketId) {
|
|
||||||
const command = `
|
|
||||||
echo "Error: ❌ Bitbucket Provider not found" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`;
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Bitbucket Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const bitbucketProvider = await findBitbucketById(bitbucketId);
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
const repoclone = `bitbucket.org/${bitbucketOwner}/${bitbucketRepository}.git`;
|
|
||||||
const cloneUrl = getBitbucketCloneUrl(bitbucketProvider, repoclone);
|
|
||||||
|
|
||||||
const cloneCommand = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
mkdir -p ${outputPath};
|
|
||||||
if ! git clone --branch ${bitbucketBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
|
||||||
echo "❌ [ERROR] Fail to clone the repository ${repoclone}" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return cloneCommand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBitbucketRepositories = async (bitbucketId?: string) => {
|
export const getBitbucketRepositories = async (bitbucketId?: string) => {
|
||||||
|
|||||||
@@ -1,159 +1,64 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import path, { join } from "node:path";
|
import path, { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { Compose } from "@dokploy/server/services/compose";
|
|
||||||
import {
|
import {
|
||||||
findSSHKeyById,
|
findSSHKeyById,
|
||||||
updateSSHKeyById,
|
updateSSHKeyById,
|
||||||
} from "@dokploy/server/services/ssh-key";
|
} from "@dokploy/server/services/ssh-key";
|
||||||
import { TRPCError } from "@trpc/server";
|
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export const cloneGitRepository = async (
|
interface CloneGitRepository {
|
||||||
entity: {
|
appName: string;
|
||||||
appName: string;
|
customGitUrl?: string | null;
|
||||||
customGitUrl?: string | null;
|
customGitBranch?: string | null;
|
||||||
customGitBranch?: string | null;
|
customGitSSHKeyId?: string | null;
|
||||||
customGitSSHKeyId?: string | null;
|
enableSubmodules?: boolean;
|
||||||
enableSubmodules?: boolean;
|
serverId: string | null;
|
||||||
},
|
type?: "application" | "compose";
|
||||||
logPath: string,
|
}
|
||||||
isCompose = false,
|
|
||||||
) => {
|
export const cloneGitRepository = async ({
|
||||||
const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
type = "application",
|
||||||
|
...entity
|
||||||
|
}: CloneGitRepository) => {
|
||||||
|
let command = "set -e;";
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
customGitUrl,
|
customGitUrl,
|
||||||
customGitBranch,
|
customGitBranch,
|
||||||
customGitSSHKeyId,
|
customGitSSHKeyId,
|
||||||
enableSubmodules,
|
enableSubmodules,
|
||||||
|
serverId,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId);
|
||||||
|
|
||||||
if (!customGitUrl || !customGitBranch) {
|
if (!customGitUrl || !customGitBranch) {
|
||||||
throw new TRPCError({
|
command += `echo "Error: ❌ Repository not found"; exit 1;`;
|
||||||
code: "BAD_REQUEST",
|
return command;
|
||||||
message: "Error: Repository not found",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
|
||||||
const temporalKeyPath = path.join("/tmp", "id_rsa");
|
const temporalKeyPath = path.join("/tmp", "id_rsa");
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
if (customGitSSHKeyId) {
|
||||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
||||||
|
|
||||||
await execAsync(`
|
command += `
|
||||||
echo "${sshKey.privateKey}" > ${temporalKeyPath}
|
echo "${sshKey.privateKey}" > ${temporalKeyPath}
|
||||||
chmod 600 ${temporalKeyPath}
|
chmod 600 ${temporalKeyPath};
|
||||||
`);
|
`;
|
||||||
}
|
}
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
||||||
|
|
||||||
try {
|
if (!isHttpOrHttps(customGitUrl)) {
|
||||||
if (!isHttpOrHttps(customGitUrl)) {
|
if (!customGitSSHKeyId) {
|
||||||
if (!customGitSSHKeyId) {
|
command += `echo "Error: ❌ You are trying to clone a ssh repository without a ssh key, please set a ssh key"; exit 1;`;
|
||||||
throw new TRPCError({
|
return command;
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message:
|
|
||||||
"Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await addHostToKnownHosts(customGitUrl);
|
|
||||||
}
|
}
|
||||||
await recreateDirectory(outputPath);
|
command += addHostToKnownHostsCommand(customGitUrl);
|
||||||
writeStream.write(
|
|
||||||
`\nCloning Repo Custom ${customGitUrl} to ${outputPath}: ✅\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
await updateSSHKeyById({
|
|
||||||
sshKeyId: customGitSSHKeyId,
|
|
||||||
lastUsedAt: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
customGitBranch,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
customGitUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync(
|
|
||||||
"git",
|
|
||||||
cloneArgs,
|
|
||||||
(data) => {
|
|
||||||
if (writeStream.writable) {
|
|
||||||
writeStream.write(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
...(customGitSSHKeyId && {
|
|
||||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
writeStream.write(`\nCloned Custom Git ${customGitUrl}: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`\nERROR Cloning Custom Git: ${error}: ❌\n`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
}
|
||||||
};
|
command += `rm -rf ${outputPath};`;
|
||||||
|
command += `mkdir -p ${outputPath};`;
|
||||||
export const getCustomGitCloneCommand = async (
|
command += `echo "Cloning Repo Custom ${customGitUrl} to ${outputPath}: ✅";`;
|
||||||
entity: {
|
|
||||||
appName: string;
|
|
||||||
customGitUrl?: string | null;
|
|
||||||
customGitBranch?: string | null;
|
|
||||||
customGitSSHKeyId?: string | null;
|
|
||||||
serverId: string | null;
|
|
||||||
enableSubmodules: boolean;
|
|
||||||
},
|
|
||||||
logPath: string,
|
|
||||||
isCompose = false,
|
|
||||||
) => {
|
|
||||||
const { SSH_PATH, COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
customGitUrl,
|
|
||||||
customGitBranch,
|
|
||||||
customGitSSHKeyId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!customGitUrl || !customGitBranch) {
|
|
||||||
const command = `
|
|
||||||
echo "Error: ❌ Repository not found" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error: Repository not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
if (customGitSSHKeyId) {
|
||||||
await updateSSHKeyById({
|
await updateSSHKeyById({
|
||||||
@@ -161,48 +66,22 @@ export const getCustomGitCloneCommand = async (
|
|||||||
lastUsedAt: new Date().toISOString(),
|
lastUsedAt: new Date().toISOString(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
const command = [];
|
|
||||||
if (!isHttpOrHttps(customGitUrl)) {
|
|
||||||
if (!customGitSSHKeyId) {
|
|
||||||
command.push(
|
|
||||||
`echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
command.push(addHostToKnownHostsCommand(customGitUrl));
|
|
||||||
}
|
|
||||||
command.push(`rm -rf ${outputPath};`);
|
|
||||||
command.push(`mkdir -p ${outputPath};`);
|
|
||||||
command.push(
|
|
||||||
`echo "Cloning Custom Git ${customGitUrl}" to ${outputPath}: ✅ >> ${logPath};`,
|
|
||||||
);
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
|
||||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
|
||||||
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
|
|
||||||
command.push(
|
|
||||||
`
|
|
||||||
echo "${sshKey.privateKey}" > /tmp/id_rsa
|
|
||||||
chmod 600 /tmp/id_rsa
|
|
||||||
export GIT_SSH_COMMAND="${gitSshCommand}"
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
command.push(
|
if (customGitSSHKeyId) {
|
||||||
`if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath} >> ${logPath} 2>&1; then
|
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
||||||
echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}" >> ${logPath};
|
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
||||||
|
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
|
||||||
|
command += `echo "${sshKey.privateKey}" > /tmp/id_rsa;`;
|
||||||
|
command += "chmod 600 /tmp/id_rsa;";
|
||||||
|
command += `export GIT_SSH_COMMAND="${gitSshCommand}";`;
|
||||||
|
}
|
||||||
|
command += `if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath}; then
|
||||||
|
echo "❌ [ERROR] Fail to clone the repository ${customGitUrl}";
|
||||||
exit 1;
|
exit 1;
|
||||||
fi
|
fi
|
||||||
`,
|
`;
|
||||||
);
|
|
||||||
command.push(`echo "Cloned Custom Git ${customGitUrl}: ✅" >> ${logPath};`);
|
return command;
|
||||||
return command.join("\n");
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const isHttpOrHttps = (url: string): boolean => {
|
const isHttpOrHttps = (url: string): boolean => {
|
||||||
@@ -210,19 +89,19 @@ const isHttpOrHttps = (url: string): boolean => {
|
|||||||
return regex.test(url);
|
return regex.test(url);
|
||||||
};
|
};
|
||||||
|
|
||||||
const addHostToKnownHosts = async (repositoryURL: string) => {
|
// const addHostToKnownHosts = async (repositoryURL: string) => {
|
||||||
const { SSH_PATH } = paths();
|
// const { SSH_PATH } = paths();
|
||||||
const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
// const { domain, port } = sanitizeRepoPathSSH(repositoryURL);
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
// const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
||||||
|
|
||||||
const command = `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath}`;
|
// const command = `ssh-keyscan -p ${port} ${domain} >> ${knownHostsPath}`;
|
||||||
try {
|
// try {
|
||||||
await execAsync(command);
|
// await execAsync(command);
|
||||||
} catch (error) {
|
// } catch (error) {
|
||||||
console.error(`Error adding host to known_hosts: ${error}`);
|
// console.error(`Error adding host to known_hosts: ${error}`);
|
||||||
throw error;
|
// throw error;
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const addHostToKnownHostsCommand = (repositoryURL: string) => {
|
const addHostToKnownHostsCommand = (repositoryURL: string) => {
|
||||||
const { SSH_PATH } = paths(true);
|
const { SSH_PATH } = paths(true);
|
||||||
@@ -266,161 +145,3 @@ const sanitizeRepoPathSSH = (input: string) => {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneGitRawRepository = async (entity: {
|
|
||||||
appName: string;
|
|
||||||
customGitUrl?: string | null;
|
|
||||||
customGitBranch?: string | null;
|
|
||||||
customGitSSHKeyId?: string | null;
|
|
||||||
enableSubmodules?: boolean;
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
customGitUrl,
|
|
||||||
customGitBranch,
|
|
||||||
customGitSSHKeyId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!customGitUrl || !customGitBranch) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error: Repository not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { SSH_PATH, COMPOSE_PATH } = paths();
|
|
||||||
const temporalKeyPath = path.join("/tmp", "id_rsa");
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
|
||||||
|
|
||||||
await execAsync(`
|
|
||||||
echo "${sshKey.privateKey}" > ${temporalKeyPath}
|
|
||||||
chmod 600 ${temporalKeyPath}
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (!isHttpOrHttps(customGitUrl)) {
|
|
||||||
if (!customGitSSHKeyId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message:
|
|
||||||
"Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await addHostToKnownHosts(customGitUrl);
|
|
||||||
}
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
await updateSSHKeyById({
|
|
||||||
sshKeyId: customGitSSHKeyId,
|
|
||||||
lastUsedAt: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
customGitBranch,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
customGitUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync("git", cloneArgs, (_data) => {}, {
|
|
||||||
env: {
|
|
||||||
...process.env,
|
|
||||||
...(customGitSSHKeyId && {
|
|
||||||
GIT_SSH_COMMAND: `ssh -i ${temporalKeyPath}${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`,
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGitRepositoryRemote = async (compose: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
customGitBranch,
|
|
||||||
customGitUrl,
|
|
||||||
customGitSSHKeyId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = compose;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!customGitUrl) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Git Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { SSH_PATH, COMPOSE_PATH } = paths(true);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const knownHostsPath = path.join(SSH_PATH, "known_hosts");
|
|
||||||
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
await updateSSHKeyById({
|
|
||||||
sshKeyId: customGitSSHKeyId,
|
|
||||||
lastUsedAt: new Date().toISOString(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const command = [];
|
|
||||||
if (!isHttpOrHttps(customGitUrl)) {
|
|
||||||
if (!customGitSSHKeyId) {
|
|
||||||
command.push(
|
|
||||||
`echo "Error: you are trying to clone a ssh repository without a ssh key, please set a ssh key ❌" ;
|
|
||||||
exit 1;
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
command.push(addHostToKnownHostsCommand(customGitUrl));
|
|
||||||
}
|
|
||||||
command.push(`rm -rf ${outputPath};`);
|
|
||||||
command.push(`mkdir -p ${outputPath};`);
|
|
||||||
if (customGitSSHKeyId) {
|
|
||||||
const sshKey = await findSSHKeyById(customGitSSHKeyId);
|
|
||||||
const { port } = sanitizeRepoPathSSH(customGitUrl);
|
|
||||||
const gitSshCommand = `ssh -i /tmp/id_rsa${port ? ` -p ${port}` : ""} -o UserKnownHostsFile=${knownHostsPath}`;
|
|
||||||
command.push(
|
|
||||||
`
|
|
||||||
echo "${sshKey.privateKey}" > /tmp/id_rsa
|
|
||||||
chmod 600 /tmp/id_rsa
|
|
||||||
export GIT_SSH_COMMAND="${gitSshCommand}"
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
command.push(
|
|
||||||
`if ! git clone --branch ${customGitBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${customGitUrl} ${outputPath} ; then
|
|
||||||
echo "[ERROR] Fail to clone the repository ";
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, command.join("\n"));
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { Compose } from "@dokploy/server/services/compose";
|
|
||||||
import {
|
import {
|
||||||
findGiteaById,
|
findGiteaById,
|
||||||
type Gitea,
|
type Gitea,
|
||||||
@@ -9,9 +7,6 @@ import {
|
|||||||
} from "@dokploy/server/services/gitea";
|
} from "@dokploy/server/services/gitea";
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export const getErrorCloneRequirements = (entity: {
|
export const getErrorCloneRequirements = (entity: {
|
||||||
giteaRepository?: string | null;
|
giteaRepository?: string | null;
|
||||||
@@ -119,79 +114,16 @@ export type ApplicationWithGitea = InferResultType<
|
|||||||
|
|
||||||
export type ComposeWithGitea = InferResultType<"compose", { gitea: true }>;
|
export type ComposeWithGitea = InferResultType<"compose", { gitea: true }>;
|
||||||
|
|
||||||
export const getGiteaCloneCommand = async (
|
type GiteaClone = (ApplicationWithGitea | ComposeWithGitea) & {
|
||||||
entity: ApplicationWithGitea | ComposeWithGitea,
|
serverId: string | null;
|
||||||
logPath: string,
|
type?: "application" | "compose";
|
||||||
isCompose = false,
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
giteaBranch,
|
|
||||||
giteaId,
|
|
||||||
giteaOwner,
|
|
||||||
giteaRepository,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!giteaId) {
|
|
||||||
const command = `
|
|
||||||
echo "Error: ❌ Gitlab Provider not found" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitea Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use paths(true) for remote operations
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
|
||||||
await refreshGiteaToken(giteaId);
|
|
||||||
const gitea = await findGiteaById(giteaId);
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
|
|
||||||
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
|
||||||
const cloneUrl = buildGiteaCloneUrl(
|
|
||||||
gitea?.giteaUrl!,
|
|
||||||
gitea?.accessToken!,
|
|
||||||
giteaOwner!,
|
|
||||||
giteaRepository!,
|
|
||||||
);
|
|
||||||
|
|
||||||
const cloneCommand = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
mkdir -p ${outputPath};
|
|
||||||
|
|
||||||
if ! git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
|
||||||
echo "❌ [ERROR] Failed to clone the repository ${repoClone}" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return cloneCommand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneGiteaRepository = async (
|
export const cloneGiteaRepository = async ({
|
||||||
entity: ApplicationWithGitea | ComposeWithGitea,
|
type = "application",
|
||||||
logPath: string,
|
...entity
|
||||||
isCompose = false,
|
}: GiteaClone) => {
|
||||||
) => {
|
let command = "set -e;";
|
||||||
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths();
|
|
||||||
|
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
giteaBranch,
|
giteaBranch,
|
||||||
@@ -199,27 +131,27 @@ export const cloneGiteaRepository = async (
|
|||||||
giteaOwner,
|
giteaOwner,
|
||||||
giteaRepository,
|
giteaRepository,
|
||||||
enableSubmodules,
|
enableSubmodules,
|
||||||
|
serverId,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(!!serverId);
|
||||||
|
|
||||||
if (!giteaId) {
|
if (!giteaId) {
|
||||||
throw new TRPCError({
|
command += `echo "Error: ❌ Gitea Provider not found"; exit 1;`;
|
||||||
code: "NOT_FOUND",
|
return command;
|
||||||
message: "Gitea Provider not found",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await refreshGiteaToken(giteaId);
|
await refreshGiteaToken(giteaId);
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
const giteaProvider = await findGiteaById(giteaId);
|
||||||
|
|
||||||
if (!giteaProvider) {
|
if (!giteaProvider) {
|
||||||
throw new TRPCError({
|
command += `echo "❌ [ERROR] Gitea provider not found in the database"; exit 1;`;
|
||||||
code: "NOT_FOUND",
|
return command;
|
||||||
message: "Gitea provider not found in the database",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
command += `rm -rf ${outputPath};`;
|
||||||
|
command += `mkdir -p ${outputPath};`;
|
||||||
|
|
||||||
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
const repoClone = `${giteaOwner}/${giteaRepository}.git`;
|
||||||
const cloneUrl = buildGiteaCloneUrl(
|
const cloneUrl = buildGiteaCloneUrl(
|
||||||
@@ -229,134 +161,9 @@ export const cloneGiteaRepository = async (
|
|||||||
giteaRepository!,
|
giteaRepository!,
|
||||||
);
|
);
|
||||||
|
|
||||||
writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}...\n`);
|
command += `echo "Cloning Repo ${repoClone} to ${outputPath}: ✅";`;
|
||||||
|
command += `git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`;
|
||||||
try {
|
return command;
|
||||||
await spawnAsync(
|
|
||||||
"git",
|
|
||||||
[
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
giteaBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
],
|
|
||||||
(data) => {
|
|
||||||
if (writeStream.writable) {
|
|
||||||
writeStream.write(data);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
writeStream.write(`\nCloned ${repoClone}: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`ERROR Cloning: ${error}: ❌`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGiteaRepository = async (entity: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
giteaRepository,
|
|
||||||
giteaOwner,
|
|
||||||
giteaBranch,
|
|
||||||
giteaId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
|
|
||||||
if (!giteaId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitea Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
await refreshGiteaToken(giteaId);
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
|
||||||
if (!giteaProvider) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitea provider not found in the database",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
|
|
||||||
const cloneUrl = buildGiteaCloneUrl(
|
|
||||||
giteaProvider.giteaUrl,
|
|
||||||
giteaProvider.accessToken!,
|
|
||||||
giteaOwner!,
|
|
||||||
giteaRepository!,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
await spawnAsync("git", [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
giteaBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
]);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGiteaRepositoryRemote = async (compose: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
giteaRepository,
|
|
||||||
giteaOwner,
|
|
||||||
giteaBranch,
|
|
||||||
giteaId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = compose;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!giteaId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitea Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
const giteaProvider = await findGiteaById(giteaId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const cloneUrl = buildGiteaCloneUrl(
|
|
||||||
giteaProvider.giteaUrl,
|
|
||||||
giteaProvider.accessToken!,
|
|
||||||
giteaOwner!,
|
|
||||||
giteaRepository!,
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const command = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
git clone --branch ${giteaBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath}
|
|
||||||
`;
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const haveGiteaRequirements = (giteaProvider: Gitea) => {
|
export const haveGiteaRequirements = (giteaProvider: Gitea) => {
|
||||||
|
|||||||
@@ -1,16 +1,11 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { apiFindGithubBranches } from "@dokploy/server/db/schema";
|
import type { apiFindGithubBranches } from "@dokploy/server/db/schema";
|
||||||
import type { Compose } from "@dokploy/server/services/compose";
|
|
||||||
import { findGithubById, type Github } from "@dokploy/server/services/github";
|
import { findGithubById, type Github } from "@dokploy/server/services/github";
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import { createAppAuth } from "@octokit/auth-app";
|
import { createAppAuth } from "@octokit/auth-app";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { Octokit } from "octokit";
|
import { Octokit } from "octokit";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export const authGithub = (githubProvider: Github): Octokit => {
|
export const authGithub = (githubProvider: Github): Octokit => {
|
||||||
if (!haveGithubRequirements(githubProvider)) {
|
if (!haveGithubRequirements(githubProvider)) {
|
||||||
@@ -123,42 +118,39 @@ interface CloneGithubRepository {
|
|||||||
branch: string | null;
|
branch: string | null;
|
||||||
githubId: string | null;
|
githubId: string | null;
|
||||||
repository: string | null;
|
repository: string | null;
|
||||||
logPath: string;
|
|
||||||
type?: "application" | "compose";
|
type?: "application" | "compose";
|
||||||
enableSubmodules: boolean;
|
enableSubmodules: boolean;
|
||||||
|
serverId: string | null;
|
||||||
}
|
}
|
||||||
export const cloneGithubRepository = async ({
|
export const cloneGithubRepository = async ({
|
||||||
logPath,
|
|
||||||
type = "application",
|
type = "application",
|
||||||
...entity
|
...entity
|
||||||
}: CloneGithubRepository) => {
|
}: CloneGithubRepository) => {
|
||||||
|
let command = "set -e;";
|
||||||
const isCompose = type === "compose";
|
const isCompose = type === "compose";
|
||||||
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths();
|
const {
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
appName,
|
||||||
const { appName, repository, owner, branch, githubId, enableSubmodules } =
|
repository,
|
||||||
entity;
|
owner,
|
||||||
|
branch,
|
||||||
|
githubId,
|
||||||
|
enableSubmodules,
|
||||||
|
serverId,
|
||||||
|
} = entity;
|
||||||
|
const { APPLICATIONS_PATH, COMPOSE_PATH } = paths(!!serverId);
|
||||||
|
|
||||||
if (!githubId) {
|
if (!githubId) {
|
||||||
throw new TRPCError({
|
command += `echo "Error: ❌ Github Provider not found"; exit 1;`;
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "GitHub Provider not found",
|
return command;
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const requirements = getErrorCloneRequirements(entity);
|
const requirements = getErrorCloneRequirements(entity);
|
||||||
|
|
||||||
// Check if requirements are met
|
// Check if requirements are met
|
||||||
if (requirements.length > 0) {
|
if (requirements.length > 0) {
|
||||||
writeStream.write(
|
command += `echo "GitHub Repository configuration failed for application: ${appName}"; echo "Reasons:"; echo "${requirements.join("\n")}"; exit 1;`;
|
||||||
`\nGitHub Repository configuration failed for application: ${appName}\n`,
|
return command;
|
||||||
);
|
|
||||||
writeStream.write("Reasons:\n");
|
|
||||||
writeStream.write(requirements.join("\n"));
|
|
||||||
writeStream.end();
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error: GitHub repository information is incomplete.",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const githubProvider = await findGithubById(githubId);
|
const githubProvider = await findGithubById(githubId);
|
||||||
@@ -167,193 +159,15 @@ export const cloneGithubRepository = async ({
|
|||||||
const octokit = authGithub(githubProvider);
|
const octokit = authGithub(githubProvider);
|
||||||
const token = await getGithubToken(octokit);
|
const token = await getGithubToken(octokit);
|
||||||
const repoclone = `github.com/${owner}/${repository}.git`;
|
const repoclone = `github.com/${owner}/${repository}.git`;
|
||||||
await recreateDirectory(outputPath);
|
// await recreateDirectory(outputPath);
|
||||||
|
command += `rm -rf ${outputPath};`;
|
||||||
|
command += `mkdir -p ${outputPath};`;
|
||||||
const cloneUrl = `https://oauth2:${token}@${repoclone}`;
|
const cloneUrl = `https://oauth2:${token}@${repoclone}`;
|
||||||
|
|
||||||
try {
|
command += `echo "Cloning Repo ${repoclone} to ${outputPath}: ✅";`;
|
||||||
writeStream.write(`\nCloning Repo ${repoclone} to ${outputPath}: ✅\n`);
|
command += `git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`;
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
branch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync("git", cloneArgs, (data) => {
|
return command;
|
||||||
if (writeStream.writable) {
|
|
||||||
writeStream.write(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
writeStream.write(`\nCloned ${repoclone}: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`ERROR Cloning: ${error}: ❌`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getGithubCloneCommand = async ({
|
|
||||||
logPath,
|
|
||||||
type = "application",
|
|
||||||
...entity
|
|
||||||
}: CloneGithubRepository & { serverId: string }) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
repository,
|
|
||||||
owner,
|
|
||||||
branch,
|
|
||||||
githubId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
const isCompose = type === "compose";
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!githubId) {
|
|
||||||
const command = `
|
|
||||||
echo "Error: ❌ Github Provider not found" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "GitHub Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const requirements = getErrorCloneRequirements(entity);
|
|
||||||
|
|
||||||
// Build log messages
|
|
||||||
let logMessages = "";
|
|
||||||
if (requirements.length > 0) {
|
|
||||||
logMessages += `\nGitHub Repository configuration failed for application: ${appName}\n`;
|
|
||||||
logMessages += "Reasons:\n";
|
|
||||||
logMessages += requirements.join("\n");
|
|
||||||
const escapedLogMessages = logMessages
|
|
||||||
.replace(/\\/g, "\\\\")
|
|
||||||
.replace(/"/g, '\\"')
|
|
||||||
.replace(/\n/g, "\\n");
|
|
||||||
|
|
||||||
const bashCommand = `
|
|
||||||
echo "${escapedLogMessages}" >> ${logPath};
|
|
||||||
exit 1; # Exit with error code
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, bashCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
|
||||||
const githubProvider = await findGithubById(githubId);
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const octokit = authGithub(githubProvider);
|
|
||||||
const token = await getGithubToken(octokit);
|
|
||||||
const repoclone = `github.com/${owner}/${repository}.git`;
|
|
||||||
const cloneUrl = `https://oauth2:${token}@${repoclone}`;
|
|
||||||
|
|
||||||
const cloneCommand = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
mkdir -p ${outputPath};
|
|
||||||
if ! git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
|
||||||
echo "❌ [ERROR] Fail to clone repository ${repoclone}" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
echo "Cloned ${repoclone} to ${outputPath}: ✅" >> ${logPath};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return cloneCommand;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGithubRepository = async (entity: Compose) => {
|
|
||||||
const { appName, repository, owner, branch, githubId, enableSubmodules } =
|
|
||||||
entity;
|
|
||||||
|
|
||||||
if (!githubId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "GitHub Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
const githubProvider = await findGithubById(githubId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const octokit = authGithub(githubProvider);
|
|
||||||
const token = await getGithubToken(octokit);
|
|
||||||
const repoclone = `github.com/${owner}/${repository}.git`;
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
const cloneUrl = `https://oauth2:${token}@${repoclone}`;
|
|
||||||
try {
|
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
branch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
await spawnAsync("git", cloneArgs);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGithubRepositoryRemote = async (compose: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
repository,
|
|
||||||
owner,
|
|
||||||
branch,
|
|
||||||
githubId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = compose;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!githubId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "GitHub Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
const githubProvider = await findGithubById(githubId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const octokit = authGithub(githubProvider);
|
|
||||||
const token = await getGithubToken(octokit);
|
|
||||||
const repoclone = `github.com/${owner}/${repository}.git`;
|
|
||||||
const cloneUrl = `https://oauth2:${token}@${repoclone}`;
|
|
||||||
try {
|
|
||||||
const command = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
git clone --branch ${branch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath}
|
|
||||||
`;
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGithubRepositories = async (githubId?: string) => {
|
export const getGithubRepositories = async (githubId?: string) => {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
import type { apiGitlabTestConnection } from "@dokploy/server/db/schema";
|
import type { apiGitlabTestConnection } from "@dokploy/server/db/schema";
|
||||||
import type { Compose } from "@dokploy/server/services/compose";
|
|
||||||
import {
|
import {
|
||||||
findGitlabById,
|
findGitlabById,
|
||||||
type Gitlab,
|
type Gitlab,
|
||||||
@@ -10,9 +8,6 @@ import {
|
|||||||
} from "@dokploy/server/services/gitlab";
|
} from "@dokploy/server/services/gitlab";
|
||||||
import type { InferResultType } from "@dokploy/server/types/with";
|
import type { InferResultType } from "@dokploy/server/types/with";
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
import { recreateDirectory } from "../filesystem/directory";
|
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
|
||||||
|
|
||||||
export const refreshGitlabToken = async (gitlabProviderId: string) => {
|
export const refreshGitlabToken = async (gitlabProviderId: string) => {
|
||||||
const gitlabProvider = await findGitlabById(gitlabProviderId);
|
const gitlabProvider = await findGitlabById(gitlabProviderId);
|
||||||
@@ -102,25 +97,29 @@ const getGitlabCloneUrl = (gitlab: GitlabInfo, repoClone: string) => {
|
|||||||
return cloneUrl;
|
return cloneUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneGitlabRepository = async (
|
type GitlabClone = (ApplicationWithGitlab | ComposeWithGitlab) & {
|
||||||
entity: ApplicationWithGitlab | ComposeWithGitlab,
|
serverId: string | null;
|
||||||
logPath: string,
|
type?: "application" | "compose";
|
||||||
isCompose = false,
|
};
|
||||||
) => {
|
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
export const cloneGitlabRepository = async ({
|
||||||
|
type = "application",
|
||||||
|
...entity
|
||||||
|
}: GitlabClone) => {
|
||||||
|
let command = "set -e;";
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
gitlabBranch,
|
gitlabBranch,
|
||||||
gitlabId,
|
gitlabId,
|
||||||
gitlabPathNamespace,
|
gitlabPathNamespace,
|
||||||
enableSubmodules,
|
enableSubmodules,
|
||||||
|
serverId,
|
||||||
} = entity;
|
} = entity;
|
||||||
|
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(!!serverId);
|
||||||
|
|
||||||
if (!gitlabId) {
|
if (!gitlabId) {
|
||||||
throw new TRPCError({
|
command += `echo "Error: ❌ Gitlab Provider not found"; exit 1;`;
|
||||||
code: "NOT_FOUND",
|
return command;
|
||||||
message: "Gitlab Provider not found",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await refreshGitlabToken(gitlabId);
|
await refreshGitlabToken(gitlabId);
|
||||||
@@ -130,127 +129,19 @@ export const cloneGitlabRepository = async (
|
|||||||
|
|
||||||
// Check if requirements are met
|
// Check if requirements are met
|
||||||
if (requirements.length > 0) {
|
if (requirements.length > 0) {
|
||||||
writeStream.write(
|
command += `echo "❌ [ERROR] GitLab Repository configuration failed for application: ${appName}"; echo "Reasons:"; echo "${requirements.join("\n")}"; exit 1;`;
|
||||||
`\nGitLab Repository configuration failed for application: ${appName}\n`,
|
return command;
|
||||||
);
|
|
||||||
writeStream.write("Reasons:\n");
|
|
||||||
writeStream.write(requirements.join("\n"));
|
|
||||||
writeStream.end();
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "BAD_REQUEST",
|
|
||||||
message: "Error: GitLab repository information is incomplete.",
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths();
|
const basePath = type === "compose" ? COMPOSE_PATH : APPLICATIONS_PATH;
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
const outputPath = join(basePath, appName, "code");
|
||||||
await recreateDirectory(outputPath);
|
command += `rm -rf ${outputPath};`;
|
||||||
|
command += `mkdir -p ${outputPath};`;
|
||||||
const repoClone = getGitlabRepoClone(gitlab, gitlabPathNamespace);
|
const repoClone = getGitlabRepoClone(gitlab, gitlabPathNamespace);
|
||||||
const cloneUrl = getGitlabCloneUrl(gitlab, repoClone);
|
const cloneUrl = getGitlabCloneUrl(gitlab, repoClone);
|
||||||
try {
|
command += `echo "Cloning Repo ${repoClone} to ${outputPath}: ✅";`;
|
||||||
writeStream.write(`\nCloning Repo ${repoClone} to ${outputPath}: ✅\n`);
|
command += `git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath} --progress;`;
|
||||||
const cloneArgs = [
|
return command;
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
gitlabBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
|
|
||||||
await spawnAsync("git", cloneArgs, (data) => {
|
|
||||||
if (writeStream.writable) {
|
|
||||||
writeStream.write(data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
writeStream.write(`\nCloned ${repoClone}: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`ERROR Cloning: ${error}: ❌`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getGitlabCloneCommand = async (
|
|
||||||
entity: ApplicationWithGitlab | ComposeWithGitlab,
|
|
||||||
logPath: string,
|
|
||||||
isCompose = false,
|
|
||||||
) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
gitlabPathNamespace,
|
|
||||||
gitlabBranch,
|
|
||||||
gitlabId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!gitlabId) {
|
|
||||||
const command = `
|
|
||||||
echo "Error: ❌ Gitlab Provider not found" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitlab Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const requirements = getErrorCloneRequirements(entity);
|
|
||||||
|
|
||||||
// Build log messages
|
|
||||||
let logMessages = "";
|
|
||||||
if (requirements.length > 0) {
|
|
||||||
logMessages += `\nGitLab Repository configuration failed for application: ${appName}\n`;
|
|
||||||
logMessages += "Reasons:\n";
|
|
||||||
logMessages += requirements.join("\n");
|
|
||||||
const escapedLogMessages = logMessages
|
|
||||||
.replace(/\\/g, "\\\\")
|
|
||||||
.replace(/"/g, '\\"')
|
|
||||||
.replace(/\n/g, "\\n");
|
|
||||||
|
|
||||||
const bashCommand = `
|
|
||||||
echo "${escapedLogMessages}" >> ${logPath};
|
|
||||||
exit 1; # Exit with error code
|
|
||||||
`;
|
|
||||||
|
|
||||||
await execAsyncRemote(serverId, bashCommand);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { COMPOSE_PATH, APPLICATIONS_PATH } = paths(true);
|
|
||||||
await refreshGitlabToken(gitlabId);
|
|
||||||
const gitlab = await findGitlabById(gitlabId);
|
|
||||||
const basePath = isCompose ? COMPOSE_PATH : APPLICATIONS_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
const repoClone = getGitlabRepoClone(gitlab, gitlabPathNamespace);
|
|
||||||
const cloneUrl = getGitlabCloneUrl(gitlab, repoClone);
|
|
||||||
const cloneCommand = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
mkdir -p ${outputPath};
|
|
||||||
if ! git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} --progress ${cloneUrl} ${outputPath} >> ${logPath} 2>&1; then
|
|
||||||
echo "❌ [ERROR] Fail to clone the repository ${repoClone}" >> ${logPath};
|
|
||||||
exit 1;
|
|
||||||
fi
|
|
||||||
echo "Cloned ${repoClone} to ${outputPath}: ✅" >> ${logPath};
|
|
||||||
`;
|
|
||||||
|
|
||||||
return cloneCommand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getGitlabRepositories = async (gitlabId?: string) => {
|
export const getGitlabRepositories = async (gitlabId?: string) => {
|
||||||
@@ -355,88 +246,6 @@ export const getGitlabBranches = async (input: {
|
|||||||
}[];
|
}[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const cloneRawGitlabRepository = async (entity: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
gitlabBranch,
|
|
||||||
gitlabId,
|
|
||||||
gitlabPathNamespace,
|
|
||||||
enableSubmodules,
|
|
||||||
} = entity;
|
|
||||||
|
|
||||||
if (!gitlabId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitlab Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
await refreshGitlabToken(gitlabId);
|
|
||||||
const gitlabProvider = await findGitlabById(gitlabId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace);
|
|
||||||
const cloneUrl = getGitlabCloneUrl(gitlabProvider, repoClone);
|
|
||||||
try {
|
|
||||||
const cloneArgs = [
|
|
||||||
"clone",
|
|
||||||
"--branch",
|
|
||||||
gitlabBranch!,
|
|
||||||
"--depth",
|
|
||||||
"1",
|
|
||||||
...(enableSubmodules ? ["--recurse-submodules"] : []),
|
|
||||||
cloneUrl,
|
|
||||||
outputPath,
|
|
||||||
"--progress",
|
|
||||||
];
|
|
||||||
await spawnAsync("git", cloneArgs);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cloneRawGitlabRepositoryRemote = async (compose: Compose) => {
|
|
||||||
const {
|
|
||||||
appName,
|
|
||||||
gitlabPathNamespace,
|
|
||||||
gitlabBranch,
|
|
||||||
gitlabId,
|
|
||||||
serverId,
|
|
||||||
enableSubmodules,
|
|
||||||
} = compose;
|
|
||||||
|
|
||||||
if (!serverId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Server not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (!gitlabId) {
|
|
||||||
throw new TRPCError({
|
|
||||||
code: "NOT_FOUND",
|
|
||||||
message: "Gitlab Provider not found",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
|
||||||
await refreshGitlabToken(gitlabId);
|
|
||||||
const gitlabProvider = await findGitlabById(gitlabId);
|
|
||||||
const basePath = COMPOSE_PATH;
|
|
||||||
const outputPath = join(basePath, appName, "code");
|
|
||||||
const repoClone = getGitlabRepoClone(gitlabProvider, gitlabPathNamespace);
|
|
||||||
const cloneUrl = getGitlabCloneUrl(gitlabProvider, repoClone);
|
|
||||||
try {
|
|
||||||
const command = `
|
|
||||||
rm -rf ${outputPath};
|
|
||||||
git clone --branch ${gitlabBranch} --depth 1 ${enableSubmodules ? "--recurse-submodules" : ""} ${cloneUrl} ${outputPath}
|
|
||||||
`;
|
|
||||||
await execAsyncRemote(serverId, command);
|
|
||||||
} catch (error) {
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const testGitlabConnection = async (
|
export const testGitlabConnection = async (
|
||||||
input: typeof apiGitlabTestConnection._type,
|
input: typeof apiGitlabTestConnection._type,
|
||||||
) => {
|
) => {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { createWriteStream } from "node:fs";
|
|
||||||
import { writeFile } from "node:fs/promises";
|
import { writeFile } from "node:fs/promises";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { paths } from "@dokploy/server/constants";
|
import { paths } from "@dokploy/server/constants";
|
||||||
@@ -7,33 +6,7 @@ import { encodeBase64 } from "../docker/utils";
|
|||||||
import { recreateDirectory } from "../filesystem/directory";
|
import { recreateDirectory } from "../filesystem/directory";
|
||||||
import { execAsyncRemote } from "../process/execAsync";
|
import { execAsyncRemote } from "../process/execAsync";
|
||||||
|
|
||||||
export const createComposeFile = async (compose: Compose, logPath: string) => {
|
export const getCreateComposeFileCommand = (compose: Compose) => {
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
const { appName, composeFile } = compose;
|
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
|
||||||
|
|
||||||
try {
|
|
||||||
await recreateDirectory(outputPath);
|
|
||||||
writeStream.write(
|
|
||||||
`\nCreating File 'docker-compose.yml' to ${outputPath}: ✅\n`,
|
|
||||||
);
|
|
||||||
|
|
||||||
await writeFile(join(outputPath, "docker-compose.yml"), composeFile);
|
|
||||||
|
|
||||||
writeStream.write(`\nFile 'docker-compose.yml' created: ✅\n`);
|
|
||||||
} catch (error) {
|
|
||||||
writeStream.write(`\nERROR Creating Compose File: ${error}: ❌\n`);
|
|
||||||
throw error;
|
|
||||||
} finally {
|
|
||||||
writeStream.end();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCreateComposeFileCommand = (
|
|
||||||
compose: Compose,
|
|
||||||
logPath: string,
|
|
||||||
) => {
|
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { appName, composeFile } = compose;
|
const { appName, composeFile } = compose;
|
||||||
const outputPath = join(COMPOSE_PATH, appName, "code");
|
const outputPath = join(COMPOSE_PATH, appName, "code");
|
||||||
@@ -43,7 +16,7 @@ export const getCreateComposeFileCommand = (
|
|||||||
rm -rf ${outputPath};
|
rm -rf ${outputPath};
|
||||||
mkdir -p ${outputPath};
|
mkdir -p ${outputPath};
|
||||||
echo "${encodedContent}" | base64 -d > "${filePath}";
|
echo "${encodedContent}" | base64 -d > "${filePath}";
|
||||||
echo "File 'docker-compose.yml' created: ✅" >> ${logPath};
|
echo "File 'docker-compose.yml' created: ✅";
|
||||||
`;
|
`;
|
||||||
return bashCommand;
|
return bashCommand;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user