mirror of
https://github.com/LukeHagar/dokploy.git
synced 2025-12-10 04:19:48 +00:00
refactor: add path join to prevent concatenate double slash and update the getImageName
This commit is contained in:
@@ -4,12 +4,12 @@ import type { InferResultType } from "@dokploy/server/types/with";
|
|||||||
import type { CreateServiceOptions } from "dockerode";
|
import type { CreateServiceOptions } from "dockerode";
|
||||||
import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
|
import { uploadImage, uploadImageRemoteCommand } from "../cluster/upload";
|
||||||
import {
|
import {
|
||||||
calculateResources,
|
calculateResources,
|
||||||
generateBindMounts,
|
generateBindMounts,
|
||||||
generateConfigContainer,
|
generateConfigContainer,
|
||||||
generateFileMounts,
|
generateFileMounts,
|
||||||
generateVolumeMounts,
|
generateVolumeMounts,
|
||||||
prepareEnvironmentVariables,
|
prepareEnvironmentVariables,
|
||||||
} from "../docker/utils";
|
} from "../docker/utils";
|
||||||
import { getRemoteDocker } from "../servers/remote-docker";
|
import { getRemoteDocker } from "../servers/remote-docker";
|
||||||
import { buildCustomDocker, getDockerCommand } from "./docker-file";
|
import { buildCustomDocker, getDockerCommand } from "./docker-file";
|
||||||
@@ -24,217 +24,217 @@ import { nanoid } from "nanoid";
|
|||||||
// PAKETO codeDirectory = where is the path of the code directory
|
// PAKETO codeDirectory = where is the path of the code directory
|
||||||
// DOCKERFILE codeDirectory = where is the exact path of the (Dockerfile)
|
// DOCKERFILE codeDirectory = where is the exact path of the (Dockerfile)
|
||||||
export type ApplicationNested = InferResultType<
|
export type ApplicationNested = InferResultType<
|
||||||
"applications",
|
"applications",
|
||||||
{
|
{
|
||||||
mounts: true;
|
mounts: true;
|
||||||
security: true;
|
security: true;
|
||||||
redirects: true;
|
redirects: true;
|
||||||
ports: true;
|
ports: true;
|
||||||
registry: true;
|
registry: true;
|
||||||
project: true;
|
project: true;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
|
||||||
export const buildApplication = async (
|
export const buildApplication = async (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
logPath: string,
|
logPath: string
|
||||||
) => {
|
) => {
|
||||||
const writeStream = createWriteStream(logPath, { flags: "a" });
|
const writeStream = createWriteStream(logPath, { flags: "a" });
|
||||||
const { buildType, sourceType } = application;
|
const { buildType, sourceType } = application;
|
||||||
try {
|
try {
|
||||||
writeStream.write(
|
writeStream.write(
|
||||||
`\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n`,
|
`\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n`
|
||||||
);
|
);
|
||||||
console.log(`Build ${buildType}: ✅`);
|
console.log(`Build ${buildType}: ✅`);
|
||||||
if (buildType === "nixpacks") {
|
if (buildType === "nixpacks") {
|
||||||
await buildNixpacks(application, writeStream);
|
await buildNixpacks(application, writeStream);
|
||||||
} else if (buildType === "heroku_buildpacks") {
|
} else if (buildType === "heroku_buildpacks") {
|
||||||
await buildHeroku(application, writeStream);
|
await buildHeroku(application, writeStream);
|
||||||
} else if (buildType === "paketo_buildpacks") {
|
} else if (buildType === "paketo_buildpacks") {
|
||||||
await buildPaketo(application, writeStream);
|
await buildPaketo(application, writeStream);
|
||||||
} else if (buildType === "dockerfile") {
|
} else if (buildType === "dockerfile") {
|
||||||
await buildCustomDocker(application, writeStream);
|
await buildCustomDocker(application, writeStream);
|
||||||
} else if (buildType === "static") {
|
} else if (buildType === "static") {
|
||||||
await buildStatic(application, writeStream);
|
await buildStatic(application, writeStream);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (application.registryId) {
|
if (application.registryId) {
|
||||||
await uploadImage(application, writeStream);
|
await uploadImage(application, writeStream);
|
||||||
}
|
}
|
||||||
await mechanizeDockerContainer(application);
|
await mechanizeDockerContainer(application);
|
||||||
writeStream.write("Docker Deployed: ✅");
|
writeStream.write("Docker Deployed: ✅");
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (error instanceof Error) {
|
if (error instanceof Error) {
|
||||||
writeStream.write(`Error ❌\n${error?.message}`);
|
writeStream.write(`Error ❌\n${error?.message}`);
|
||||||
} else {
|
} else {
|
||||||
writeStream.write("Error ❌");
|
writeStream.write("Error ❌");
|
||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
writeStream.end();
|
writeStream.end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getBuildCommand = (
|
export const getBuildCommand = (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
logPath: string,
|
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, logPath);
|
||||||
break;
|
break;
|
||||||
case "heroku_buildpacks":
|
case "heroku_buildpacks":
|
||||||
command = getHerokuCommand(application, logPath);
|
command = getHerokuCommand(application, logPath);
|
||||||
break;
|
break;
|
||||||
case "paketo_buildpacks":
|
case "paketo_buildpacks":
|
||||||
command = getPaketoCommand(application, logPath);
|
command = getPaketoCommand(application, logPath);
|
||||||
break;
|
break;
|
||||||
case "static":
|
case "static":
|
||||||
command = getStaticCommand(application, logPath);
|
command = getStaticCommand(application, logPath);
|
||||||
break;
|
break;
|
||||||
case "dockerfile":
|
case "dockerfile":
|
||||||
command = getDockerCommand(application, logPath);
|
command = getDockerCommand(application, logPath);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (registry) {
|
if (registry) {
|
||||||
command += uploadImageRemoteCommand(application, logPath);
|
command += uploadImageRemoteCommand(application, logPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
return command;
|
return command;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mechanizeDockerContainer = async (
|
export const mechanizeDockerContainer = async (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
appName,
|
appName,
|
||||||
env,
|
env,
|
||||||
mounts,
|
mounts,
|
||||||
cpuLimit,
|
cpuLimit,
|
||||||
memoryLimit,
|
memoryLimit,
|
||||||
memoryReservation,
|
memoryReservation,
|
||||||
cpuReservation,
|
cpuReservation,
|
||||||
command,
|
command,
|
||||||
ports,
|
ports,
|
||||||
} = application;
|
} = application;
|
||||||
|
|
||||||
const resources = calculateResources({
|
const resources = calculateResources({
|
||||||
memoryLimit,
|
memoryLimit,
|
||||||
memoryReservation,
|
memoryReservation,
|
||||||
cpuLimit,
|
cpuLimit,
|
||||||
cpuReservation,
|
cpuReservation,
|
||||||
});
|
});
|
||||||
|
|
||||||
const volumesMount = generateVolumeMounts(mounts);
|
const volumesMount = generateVolumeMounts(mounts);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
HealthCheck,
|
HealthCheck,
|
||||||
RestartPolicy,
|
RestartPolicy,
|
||||||
Placement,
|
Placement,
|
||||||
Labels,
|
Labels,
|
||||||
Mode,
|
Mode,
|
||||||
RollbackConfig,
|
RollbackConfig,
|
||||||
UpdateConfig,
|
UpdateConfig,
|
||||||
Networks,
|
Networks,
|
||||||
} = generateConfigContainer(application);
|
} = generateConfigContainer(application);
|
||||||
|
|
||||||
const bindsMount = generateBindMounts(mounts);
|
const bindsMount = generateBindMounts(mounts);
|
||||||
const filesMount = generateFileMounts(appName, application);
|
const filesMount = generateFileMounts(appName, application);
|
||||||
const envVariables = prepareEnvironmentVariables(
|
const envVariables = prepareEnvironmentVariables(
|
||||||
env,
|
env,
|
||||||
application.project.env,
|
application.project.env
|
||||||
);
|
);
|
||||||
|
|
||||||
const image = getImageName(application);
|
const image = getImageName(application);
|
||||||
const authConfig = getAuthConfig(application);
|
const authConfig = getAuthConfig(application);
|
||||||
const docker = await getRemoteDocker(application.serverId);
|
const docker = await getRemoteDocker(application.serverId);
|
||||||
|
|
||||||
const settings: CreateServiceOptions = {
|
const settings: CreateServiceOptions = {
|
||||||
authconfig: authConfig,
|
authconfig: authConfig,
|
||||||
Name: appName,
|
Name: appName,
|
||||||
TaskTemplate: {
|
TaskTemplate: {
|
||||||
ContainerSpec: {
|
ContainerSpec: {
|
||||||
HealthCheck,
|
HealthCheck,
|
||||||
Image: image,
|
Image: image,
|
||||||
Env: envVariables,
|
Env: envVariables,
|
||||||
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
|
||||||
...(command
|
...(command
|
||||||
? {
|
? {
|
||||||
Command: ["/bin/sh"],
|
Command: ["/bin/sh"],
|
||||||
Args: ["-c", command],
|
Args: ["-c", command],
|
||||||
}
|
}
|
||||||
: {}),
|
: {}),
|
||||||
Labels,
|
Labels,
|
||||||
},
|
},
|
||||||
Networks,
|
Networks,
|
||||||
RestartPolicy,
|
RestartPolicy,
|
||||||
Placement,
|
Placement,
|
||||||
Resources: {
|
Resources: {
|
||||||
...resources,
|
...resources,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Mode,
|
Mode,
|
||||||
RollbackConfig,
|
RollbackConfig,
|
||||||
EndpointSpec: {
|
EndpointSpec: {
|
||||||
Ports: ports.map((port) => ({
|
Ports: ports.map((port) => ({
|
||||||
Protocol: port.protocol,
|
Protocol: port.protocol,
|
||||||
TargetPort: port.targetPort,
|
TargetPort: port.targetPort,
|
||||||
PublishedPort: port.publishedPort,
|
PublishedPort: port.publishedPort,
|
||||||
})),
|
})),
|
||||||
},
|
},
|
||||||
UpdateConfig,
|
UpdateConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const service = docker.getService(appName);
|
const service = docker.getService(appName);
|
||||||
const inspect = await service.inspect();
|
const inspect = await service.inspect();
|
||||||
await service.update({
|
await service.update({
|
||||||
version: Number.parseInt(inspect.Version.Index),
|
version: Number.parseInt(inspect.Version.Index),
|
||||||
...settings,
|
...settings,
|
||||||
TaskTemplate: {
|
TaskTemplate: {
|
||||||
...settings.TaskTemplate,
|
...settings.TaskTemplate,
|
||||||
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
await docker.createService(settings);
|
await docker.createService(settings);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getImageName = (application: ApplicationNested) => {
|
const getImageName = (application: ApplicationNested) => {
|
||||||
const { appName, sourceType, dockerImage, registry } = application;
|
const { appName, sourceType, dockerImage, registry } = application;
|
||||||
|
|
||||||
if (sourceType === "docker") {
|
if (sourceType === "docker") {
|
||||||
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
|
return dockerImage || "ERROR-NO-IMAGE-PROVIDED";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (registry) {
|
if (registry) {
|
||||||
return join(registry.imagePrefix || "", appName);
|
return join(registry.registryUrl, registry.imagePrefix || "", appName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return `${appName}:latest`;
|
return `${appName}:latest`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getAuthConfig = (application: ApplicationNested) => {
|
const getAuthConfig = (application: ApplicationNested) => {
|
||||||
const { registry, username, password, sourceType } = application;
|
const { registry, username, password, sourceType, registryUrl } = application;
|
||||||
|
|
||||||
if (sourceType === "docker") {
|
if (sourceType === "docker") {
|
||||||
if (username && password) {
|
if (username && password) {
|
||||||
return {
|
return {
|
||||||
password,
|
password,
|
||||||
username,
|
username,
|
||||||
serveraddress: "https://index.docker.io/v1/",
|
serveraddress: registryUrl || "",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
} else if (registry) {
|
} else if (registry) {
|
||||||
return {
|
return {
|
||||||
password: registry.password,
|
password: registry.password,
|
||||||
username: registry.username,
|
username: registry.username,
|
||||||
serveraddress: registry.registryUrl,
|
serveraddress: registry.registryUrl,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return undefined;
|
return undefined;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,81 +1,84 @@
|
|||||||
import type { WriteStream } from "node:fs";
|
import type { WriteStream } from "node:fs";
|
||||||
import { join } from "node:path";
|
import path, { join } from "node:path";
|
||||||
import type { ApplicationNested } from "../builders";
|
import type { ApplicationNested } from "../builders";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
export const uploadImage = async (
|
export const uploadImage = async (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
writeStream: WriteStream,
|
writeStream: WriteStream
|
||||||
) => {
|
) => {
|
||||||
const registry = application.registry;
|
const registry = application.registry;
|
||||||
|
|
||||||
if (!registry) {
|
if (!registry) {
|
||||||
throw new Error("Registry not found");
|
throw new Error("Registry not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { registryUrl, imagePrefix, registryType } = registry;
|
const { registryUrl, imagePrefix } = registry;
|
||||||
const { appName } = application;
|
const { appName } = application;
|
||||||
const imageName = `${appName}:latest`;
|
const imageName = `${appName}:latest`;
|
||||||
|
|
||||||
const finalURL = registryUrl;
|
const finalURL = registryUrl;
|
||||||
|
|
||||||
const registryTag =
|
const registryTag = path
|
||||||
`${registryUrl}/${join(imagePrefix || "", imageName)}`.replace(/\/+/g, "/");
|
.join(registryUrl, join(imagePrefix || "", imageName))
|
||||||
|
.replace(/\/+/g, "/");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
writeStream.write(
|
writeStream.write(
|
||||||
`📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n`,
|
`📦 [Enabled Registry] Uploading image to ${registry.registryType} | ${imageName} | ${finalURL}\n`
|
||||||
);
|
);
|
||||||
const loginCommand = spawnAsync(
|
const loginCommand = spawnAsync(
|
||||||
"docker",
|
"docker",
|
||||||
["login", finalURL, "-u", registry.username, "--password-stdin"],
|
["login", finalURL, "-u", registry.username, "--password-stdin"],
|
||||||
(data) => {
|
(data) => {
|
||||||
if (writeStream.writable) {
|
if (writeStream.writable) {
|
||||||
writeStream.write(data);
|
writeStream.write(data);
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
loginCommand.child?.stdin?.write(registry.password);
|
loginCommand.child?.stdin?.write(registry.password);
|
||||||
loginCommand.child?.stdin?.end();
|
loginCommand.child?.stdin?.end();
|
||||||
await loginCommand;
|
await loginCommand;
|
||||||
|
|
||||||
await spawnAsync("docker", ["tag", imageName, registryTag], (data) => {
|
await spawnAsync("docker", ["tag", imageName, registryTag], (data) => {
|
||||||
if (writeStream.writable) {
|
if (writeStream.writable) {
|
||||||
writeStream.write(data);
|
writeStream.write(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
await spawnAsync("docker", ["push", registryTag], (data) => {
|
await spawnAsync("docker", ["push", registryTag], (data) => {
|
||||||
if (writeStream.writable) {
|
if (writeStream.writable) {
|
||||||
writeStream.write(data);
|
writeStream.write(data);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
export const uploadImageRemoteCommand = (
|
export const uploadImageRemoteCommand = (
|
||||||
application: ApplicationNested,
|
application: ApplicationNested,
|
||||||
logPath: string,
|
logPath: string
|
||||||
) => {
|
) => {
|
||||||
const registry = application.registry;
|
const registry = application.registry;
|
||||||
|
|
||||||
if (!registry) {
|
if (!registry) {
|
||||||
throw new Error("Registry not found");
|
throw new Error("Registry not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { registryUrl, imagePrefix } = registry;
|
const { registryUrl, imagePrefix } = registry;
|
||||||
const { appName } = application;
|
const { appName } = application;
|
||||||
const imageName = `${appName}:latest`;
|
const imageName = `${appName}:latest`;
|
||||||
|
|
||||||
const finalURL = registryUrl;
|
const finalURL = registryUrl;
|
||||||
|
|
||||||
const registryTag = join(imagePrefix || "", imageName);
|
const registryTag = path
|
||||||
|
.join(registryUrl, join(imagePrefix || "", imageName))
|
||||||
|
.replace(/\/+/g, "/");
|
||||||
|
|
||||||
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}'" >> ${logPath};
|
||||||
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 >> ${logPath} 2>> ${logPath} || {
|
||||||
echo "❌ DockerHub Failed" >> ${logPath};
|
echo "❌ DockerHub Failed" >> ${logPath};
|
||||||
@@ -93,9 +96,8 @@ export const uploadImageRemoteCommand = (
|
|||||||
}
|
}
|
||||||
echo "✅ Image Pushed" >> ${logPath};
|
echo "✅ Image Pushed" >> ${logPath};
|
||||||
`;
|
`;
|
||||||
return command;
|
return command;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error);
|
throw error;
|
||||||
throw error;
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user