mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
refactor: add .env docker stack
This commit is contained in:
@@ -211,12 +211,12 @@ const Service = (
|
|||||||
<TabsList
|
<TabsList
|
||||||
className={cn(
|
className={cn(
|
||||||
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
"md:grid md:w-fit max-md:overflow-y-scroll justify-start",
|
||||||
data?.serverId ? "md:grid-cols-6" : "md:grid-cols-7",
|
data?.serverId ? "md:grid-cols-7" : "md:grid-cols-7",
|
||||||
data?.composeType === "docker-compose"
|
data?.composeType === "docker-compose"
|
||||||
? ""
|
? ""
|
||||||
: "md:grid-cols-6",
|
: "md:grid-cols-7",
|
||||||
data?.serverId && data?.composeType === "stack"
|
data?.serverId && data?.composeType === "stack"
|
||||||
? "md:grid-cols-5"
|
? "md:grid-cols-6"
|
||||||
: "",
|
: "",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
createWriteStream,
|
createWriteStream,
|
||||||
existsSync,
|
existsSync,
|
||||||
mkdirSync,
|
mkdirSync,
|
||||||
|
readFileSync,
|
||||||
writeFileSync,
|
writeFileSync,
|
||||||
} from "node:fs";
|
} from "node:fs";
|
||||||
import { dirname, join } from "node:path";
|
import { dirname, join } from "node:path";
|
||||||
@@ -12,7 +13,11 @@ import {
|
|||||||
writeDomainsToCompose,
|
writeDomainsToCompose,
|
||||||
writeDomainsToComposeRemote,
|
writeDomainsToComposeRemote,
|
||||||
} from "../docker/domain";
|
} from "../docker/domain";
|
||||||
import { encodeBase64, prepareEnvironmentVariables } from "../docker/utils";
|
import {
|
||||||
|
encodeBase64,
|
||||||
|
getEnviromentVariablesObject,
|
||||||
|
prepareEnvironmentVariables,
|
||||||
|
} from "../docker/utils";
|
||||||
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
import { execAsync, execAsyncRemote } from "../process/execAsync";
|
||||||
import { spawnAsync } from "../process/spawnAsync";
|
import { spawnAsync } from "../process/spawnAsync";
|
||||||
|
|
||||||
@@ -28,15 +33,14 @@ export const buildCompose = async (compose: ComposeNested, logPath: string) => {
|
|||||||
const command = createCommand(compose);
|
const command = createCommand(compose);
|
||||||
await writeDomainsToCompose(compose, domains);
|
await writeDomainsToCompose(compose, domains);
|
||||||
createEnvFile(compose);
|
createEnvFile(compose);
|
||||||
await processComposeFile(compose);
|
|
||||||
|
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
Build Compose 🐳
|
Build Compose 🐳
|
||||||
Detected: ${mounts.length} mounts 📂
|
Detected: ${mounts.length} mounts 📂
|
||||||
Command: docker ${command}
|
Command: docker ${command}
|
||||||
Source Type: docker ${sourceType} ✅
|
Source Type: docker ${sourceType} ✅
|
||||||
Compose Type: ${composeType} ✅`;
|
Compose Type: ${composeType} ✅`;
|
||||||
const logBox = boxen(logContent, {
|
const logBox = boxen(logContent, {
|
||||||
padding: {
|
padding: {
|
||||||
left: 1,
|
left: 1,
|
||||||
@@ -47,7 +51,6 @@ Compose Type: ${composeType} ✅`;
|
|||||||
borderStyle: "double",
|
borderStyle: "double",
|
||||||
});
|
});
|
||||||
writeStream.write(`\n${logBox}\n`);
|
writeStream.write(`\n${logBox}\n`);
|
||||||
|
|
||||||
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
|
||||||
|
|
||||||
await spawnAsync(
|
await spawnAsync(
|
||||||
@@ -63,8 +66,11 @@ Compose Type: ${composeType} ✅`;
|
|||||||
env: {
|
env: {
|
||||||
NODE_ENV: process.env.NODE_ENV,
|
NODE_ENV: process.env.NODE_ENV,
|
||||||
PATH: process.env.PATH,
|
PATH: process.env.PATH,
|
||||||
|
...(composeType === "stack" && {
|
||||||
|
...getEnviromentVariablesObject(compose.env, compose.project.env),
|
||||||
|
}),
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
writeStream.write("Docker Compose Deployed: ✅");
|
writeStream.write("Docker Compose Deployed: ✅");
|
||||||
@@ -78,7 +84,7 @@ Compose Type: ${composeType} ✅`;
|
|||||||
|
|
||||||
export const getBuildComposeCommand = async (
|
export const getBuildComposeCommand = async (
|
||||||
compose: ComposeNested,
|
compose: ComposeNested,
|
||||||
logPath: string,
|
logPath: string
|
||||||
) => {
|
) => {
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
const { sourceType, appName, mounts, composeType, domains, composePath } =
|
||||||
@@ -86,12 +92,12 @@ export const getBuildComposeCommand = async (
|
|||||||
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 processComposeFileCommand = getProcessComposeFileCommand(compose);
|
const exportEnvCommand = getExportEnvCommand(compose);
|
||||||
|
|
||||||
const newCompose = await writeDomainsToComposeRemote(
|
const newCompose = await writeDomainsToComposeRemote(
|
||||||
compose,
|
compose,
|
||||||
domains,
|
domains,
|
||||||
logPath,
|
logPath
|
||||||
);
|
);
|
||||||
const logContent = `
|
const logContent = `
|
||||||
App Name: ${appName}
|
App Name: ${appName}
|
||||||
@@ -122,7 +128,7 @@ Compose Type: ${composeType} ✅`;
|
|||||||
|
|
||||||
cd "${projectPath}";
|
cd "${projectPath}";
|
||||||
|
|
||||||
${processComposeFileCommand}
|
${exportEnvCommand}
|
||||||
|
|
||||||
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }
|
||||||
|
|
||||||
@@ -154,20 +160,14 @@ export const createCommand = (compose: ComposeNested) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const path =
|
const path =
|
||||||
sourceType === "raw"
|
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
||||||
? composeType === "stack"
|
let command = "";
|
||||||
? "docker-compose.processed.yml"
|
|
||||||
: "docker-compose.yml"
|
|
||||||
: composeType === "stack"
|
|
||||||
? join(dirname(compose.composePath), "docker-compose.processed.yml")
|
|
||||||
: compose.composePath;
|
|
||||||
|
|
||||||
const baseCommand =
|
if (composeType === "stack") {
|
||||||
composeType === "docker-compose"
|
command = `stack deploy -c ${path} ${appName} --prune`;
|
||||||
? `compose -p ${appName} -f ${path} up -d --build --remove-orphans`
|
}
|
||||||
: `stack deploy -c ${path} ${appName} --prune`;
|
|
||||||
const customCommand = sanitizeCommand(compose.command);
|
return command;
|
||||||
return customCommand ? `${baseCommand} ${customCommand}` : baseCommand;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const createEnvFile = (compose: ComposeNested) => {
|
const createEnvFile = (compose: ComposeNested) => {
|
||||||
@@ -189,7 +189,7 @@ const createEnvFile = (compose: ComposeNested) => {
|
|||||||
|
|
||||||
const envFileContent = prepareEnvironmentVariables(
|
const envFileContent = prepareEnvironmentVariables(
|
||||||
envContent,
|
envContent,
|
||||||
compose.project.env,
|
compose.project.env
|
||||||
).join("\n");
|
).join("\n");
|
||||||
|
|
||||||
if (!existsSync(dirname(envFilePath))) {
|
if (!existsSync(dirname(envFilePath))) {
|
||||||
@@ -198,38 +198,6 @@ const createEnvFile = (compose: ComposeNested) => {
|
|||||||
writeFileSync(envFilePath, envFileContent);
|
writeFileSync(envFilePath, envFileContent);
|
||||||
};
|
};
|
||||||
|
|
||||||
export const processComposeFile = async (compose: ComposeNested) => {
|
|
||||||
const { COMPOSE_PATH } = paths();
|
|
||||||
if (compose.composeType === "stack") {
|
|
||||||
const command = getProcessComposeFileCommand(compose);
|
|
||||||
await execAsync(command, {
|
|
||||||
cwd: join(COMPOSE_PATH, compose.appName, "code"),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getProcessComposeFileCommand = (compose: ComposeNested) => {
|
|
||||||
const { composeType, sourceType } = compose;
|
|
||||||
|
|
||||||
const composePath =
|
|
||||||
sourceType === "raw" ? "docker-compose.yml" : compose.composePath;
|
|
||||||
|
|
||||||
const destinationPath =
|
|
||||||
sourceType === "raw"
|
|
||||||
? "docker-compose.processed.yml"
|
|
||||||
: join(dirname(compose.composePath), "docker-compose.processed.yml");
|
|
||||||
|
|
||||||
let command = "";
|
|
||||||
if (composeType === "stack") {
|
|
||||||
command = [
|
|
||||||
"export $(grep -v '^#' .env | xargs)",
|
|
||||||
`docker stack config -c ${composePath} > ${destinationPath}`,
|
|
||||||
].join(" && ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return command;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||||
const { COMPOSE_PATH } = paths(true);
|
const { COMPOSE_PATH } = paths(true);
|
||||||
const { env, composePath, appName } = compose;
|
const { env, composePath, appName } = compose;
|
||||||
@@ -250,7 +218,7 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
|||||||
|
|
||||||
const envFileContent = prepareEnvironmentVariables(
|
const envFileContent = prepareEnvironmentVariables(
|
||||||
envContent,
|
envContent,
|
||||||
compose.project.env,
|
compose.project.env
|
||||||
).join("\n");
|
).join("\n");
|
||||||
|
|
||||||
const encodedContent = encodeBase64(envFileContent);
|
const encodedContent = encodeBase64(envFileContent);
|
||||||
@@ -259,3 +227,14 @@ touch ${envFilePath};
|
|||||||
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getExportEnvCommand = (compose: ComposeNested) => {
|
||||||
|
if (compose.composeType !== "stack") return "";
|
||||||
|
|
||||||
|
const envVars = getEnviromentVariablesObject(compose.env, compose.project.env);
|
||||||
|
const exports = Object.entries(envVars)
|
||||||
|
.map(([key, value]) => `export ${key}=${JSON.stringify(value)}`)
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
return exports ? `\n# Export environment variables\n${exports}\n` : "";
|
||||||
|
};
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ interface RegistryAuth {
|
|||||||
export const pullImage = async (
|
export const pullImage = async (
|
||||||
dockerImage: string,
|
dockerImage: string,
|
||||||
onData?: (data: any) => void,
|
onData?: (data: any) => void,
|
||||||
authConfig?: Partial<RegistryAuth>,
|
authConfig?: Partial<RegistryAuth>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (!dockerImage) {
|
if (!dockerImage) {
|
||||||
@@ -41,7 +41,7 @@ export const pullImage = async (
|
|||||||
"-p",
|
"-p",
|
||||||
authConfig.password,
|
authConfig.password,
|
||||||
],
|
],
|
||||||
onData,
|
onData
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await spawnAsync("docker", ["pull", dockerImage], onData);
|
await spawnAsync("docker", ["pull", dockerImage], onData);
|
||||||
@@ -54,7 +54,7 @@ export const pullRemoteImage = async (
|
|||||||
dockerImage: string,
|
dockerImage: string,
|
||||||
serverId: string,
|
serverId: string,
|
||||||
onData?: (data: any) => void,
|
onData?: (data: any) => void,
|
||||||
authConfig?: Partial<RegistryAuth>,
|
authConfig?: Partial<RegistryAuth>
|
||||||
): Promise<void> => {
|
): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
if (!dockerImage) {
|
if (!dockerImage) {
|
||||||
@@ -85,9 +85,9 @@ export const pullRemoteImage = async (
|
|||||||
},
|
},
|
||||||
(event) => {
|
(event) => {
|
||||||
onData?.(event);
|
onData?.(event);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -188,7 +188,7 @@ export const cleanUpInactiveContainers = async () => {
|
|||||||
try {
|
try {
|
||||||
const containers = await docker.listContainers({ all: true });
|
const containers = await docker.listContainers({ all: true });
|
||||||
const inactiveContainers = containers.filter(
|
const inactiveContainers = containers.filter(
|
||||||
(container) => container.State !== "running",
|
(container) => container.State !== "running"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (const container of inactiveContainers) {
|
for (const container of inactiveContainers) {
|
||||||
@@ -240,7 +240,7 @@ export const startServiceRemote = async (serverId: string, appName: string) => {
|
|||||||
export const removeService = async (
|
export const removeService = async (
|
||||||
appName: string,
|
appName: string,
|
||||||
serverId?: string | null,
|
serverId?: string | null,
|
||||||
deleteVolumes = false,
|
deleteVolumes = false
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const command = `docker service rm ${appName}`;
|
const command = `docker service rm ${appName}`;
|
||||||
@@ -257,7 +257,7 @@ export const removeService = async (
|
|||||||
|
|
||||||
export const prepareEnvironmentVariables = (
|
export const prepareEnvironmentVariables = (
|
||||||
serviceEnv: string | null,
|
serviceEnv: string | null,
|
||||||
projectEnv?: string | null,
|
projectEnv?: string | null
|
||||||
) => {
|
) => {
|
||||||
const projectVars = parse(projectEnv ?? "");
|
const projectVars = parse(projectEnv ?? "");
|
||||||
const serviceVars = parse(serviceEnv ?? "");
|
const serviceVars = parse(serviceEnv ?? "");
|
||||||
@@ -278,12 +278,15 @@ export const prepareEnvironmentVariables = (
|
|||||||
return resolvedVars;
|
return resolvedVars;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const prepareBuildArgs = (input: string | null) => {
|
export const getEnviromentVariablesObject = (
|
||||||
const pairs = (input ?? "").split("\n");
|
input: string | null,
|
||||||
|
projectEnv?: string | null
|
||||||
|
) => {
|
||||||
|
const envs = prepareEnvironmentVariables(input, projectEnv);
|
||||||
|
|
||||||
const jsonObject: Record<string, string> = {};
|
const jsonObject: Record<string, string> = {};
|
||||||
|
|
||||||
for (const pair of pairs) {
|
for (const pair of envs) {
|
||||||
const [key, value] = pair.split("=");
|
const [key, value] = pair.split("=");
|
||||||
if (key && value) {
|
if (key && value) {
|
||||||
jsonObject[key] = value;
|
jsonObject[key] = value;
|
||||||
@@ -427,7 +430,7 @@ export const generateFileMounts = (
|
|||||||
| MariadbNested
|
| MariadbNested
|
||||||
| MysqlNested
|
| MysqlNested
|
||||||
| PostgresNested
|
| PostgresNested
|
||||||
| RedisNested,
|
| RedisNested
|
||||||
) => {
|
) => {
|
||||||
const { mounts } = service;
|
const { mounts } = service;
|
||||||
const { APPLICATIONS_PATH } = paths(!!service.serverId);
|
const { APPLICATIONS_PATH } = paths(!!service.serverId);
|
||||||
@@ -453,7 +456,7 @@ export const generateFileMounts = (
|
|||||||
export const createFile = async (
|
export const createFile = async (
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
content: string,
|
content: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const fullPath = path.join(outputPath, filePath);
|
const fullPath = path.join(outputPath, filePath);
|
||||||
@@ -475,7 +478,7 @@ export const encodeBase64 = (content: string) =>
|
|||||||
export const getCreateFileCommand = (
|
export const getCreateFileCommand = (
|
||||||
outputPath: string,
|
outputPath: string,
|
||||||
filePath: string,
|
filePath: string,
|
||||||
content: string,
|
content: string
|
||||||
) => {
|
) => {
|
||||||
const fullPath = path.join(outputPath, filePath);
|
const fullPath = path.join(outputPath, filePath);
|
||||||
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
if (fullPath.endsWith(path.sep) || filePath.endsWith("/")) {
|
||||||
@@ -515,7 +518,7 @@ export const getServiceContainer = async (appName: string) => {
|
|||||||
|
|
||||||
export const getRemoteServiceContainer = async (
|
export const getRemoteServiceContainer = async (
|
||||||
serverId: string,
|
serverId: string,
|
||||||
appName: string,
|
appName: string
|
||||||
) => {
|
) => {
|
||||||
try {
|
try {
|
||||||
const filter = {
|
const filter = {
|
||||||
|
|||||||
Reference in New Issue
Block a user