mirror of
https://github.com/andwati/dokploy.git
synced 2026-11-05 19:52:15 +00:00
Merge remote-tracking branch 'origin/canary' into fix/env-update-issue
# Conflicts: # packages/server/src/utils/builders/railpack.ts
This commit is contained in:
@@ -67,9 +67,20 @@ Compose Type: ${composeType} ✅`;
|
||||
return bashCommand;
|
||||
};
|
||||
|
||||
// Shell control characters that must never appear in a user-provided compose
|
||||
// command: they would let it break out of the `docker ${command}` invocation
|
||||
// into arbitrary host commands. A normal docker compose CLI line never needs them.
|
||||
const UNSAFE_COMPOSE_COMMAND = /[;&|`$(){}<>\n\\]/;
|
||||
|
||||
const sanitizeCommand = (command: string) => {
|
||||
const sanitizedCommand = command.trim();
|
||||
|
||||
if (UNSAFE_COMPOSE_COMMAND.test(sanitizedCommand)) {
|
||||
throw new Error(
|
||||
"Invalid characters in compose command: shell control characters are not allowed",
|
||||
);
|
||||
}
|
||||
|
||||
const parts = sanitizedCommand.split(/\s+/);
|
||||
|
||||
const restCommand = parts.map((arg) => arg.replace(/^"(.*)"$/, "$1"));
|
||||
@@ -88,9 +99,9 @@ export const createCommand = (compose: ComposeNested) => {
|
||||
let command = "";
|
||||
|
||||
if (composeType === "docker-compose") {
|
||||
command = `compose -p ${appName} -f ${path} up -d --build --remove-orphans`;
|
||||
command = `compose -p ${quote([appName])} -f ${quote([path])} up -d --build --remove-orphans`;
|
||||
} else if (composeType === "stack") {
|
||||
command = `stack deploy -c ${path} ${appName} --prune --with-registry-auth`;
|
||||
command = `stack deploy -c ${quote([path])} ${quote([appName])} --prune --with-registry-auth`;
|
||||
}
|
||||
|
||||
return command;
|
||||
@@ -124,8 +135,8 @@ export const getCreateEnvFileCommand = (compose: ComposeNested) => {
|
||||
|
||||
const encodedContent = encodeBase64(envFileContent);
|
||||
return `
|
||||
touch ${envFilePath};
|
||||
echo "${encodedContent}" | base64 -d > "${envFilePath}";
|
||||
touch ${quote([envFilePath])};
|
||||
echo "${encodedContent}" | base64 -d > ${quote([envFilePath])};
|
||||
`;
|
||||
};
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ export const getDockerCommand = (application: ApplicationNested) => {
|
||||
}
|
||||
|
||||
command += `
|
||||
echo "Building ${appName}" ;
|
||||
cd ${dockerContextPath} || {
|
||||
echo "❌ The path ${dockerContextPath} does not exist" ;
|
||||
echo ${quote([`Building ${appName}`])} ;
|
||||
cd ${quote([dockerContextPath])} || {
|
||||
echo ${quote([`❌ The path ${dockerContextPath} does not exist`])} ;
|
||||
exit 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { resolveServiceNetworks } from "@dokploy/server/services/network";
|
||||
import { findRegistryByIdWithCredentials } from "@dokploy/server/services/registry";
|
||||
import type { InferResultType } from "@dokploy/server/types/with";
|
||||
import type { CreateServiceOptions } from "dockerode";
|
||||
import { getRegistryTag, uploadImageRemoteCommand } from "../cluster/upload";
|
||||
@@ -28,9 +30,9 @@ export type ApplicationNested = InferResultType<
|
||||
security: true;
|
||||
redirects: true;
|
||||
ports: true;
|
||||
registry: true;
|
||||
buildRegistry: true;
|
||||
rollbackRegistry: true;
|
||||
registry: { columns: { password: false } };
|
||||
buildRegistry: { columns: { password: false } };
|
||||
rollbackRegistry: { columns: { password: false } };
|
||||
deployments: true;
|
||||
environment: { with: { project: true } };
|
||||
}
|
||||
@@ -99,6 +101,8 @@ export const mechanizeDockerContainer = async (
|
||||
|
||||
const volumesMount = generateVolumeMounts(mounts);
|
||||
|
||||
const resolvedNetworks = await resolveServiceNetworks(application);
|
||||
|
||||
const {
|
||||
HealthCheck,
|
||||
RestartPolicy,
|
||||
@@ -107,7 +111,6 @@ export const mechanizeDockerContainer = async (
|
||||
Mode,
|
||||
RollbackConfig,
|
||||
UpdateConfig,
|
||||
Networks,
|
||||
StopGracePeriod,
|
||||
EndpointSpec,
|
||||
Ulimits,
|
||||
@@ -121,8 +124,8 @@ export const mechanizeDockerContainer = async (
|
||||
application.environment.env,
|
||||
);
|
||||
|
||||
const image = getImageName(application);
|
||||
const authConfig = getAuthConfig(application);
|
||||
const image = await getImageName(application);
|
||||
const authConfig = await getAuthConfig(application);
|
||||
const docker = await getRemoteDocker(application.serverId);
|
||||
|
||||
const settings: CreateServiceOptions = {
|
||||
@@ -146,7 +149,7 @@ export const mechanizeDockerContainer = async (
|
||||
...(Ulimits && { Ulimits }),
|
||||
Labels,
|
||||
},
|
||||
Networks,
|
||||
Networks: resolvedNetworks,
|
||||
RestartPolicy,
|
||||
Placement,
|
||||
Resources: {
|
||||
@@ -190,7 +193,7 @@ export const mechanizeDockerContainer = async (
|
||||
}
|
||||
};
|
||||
|
||||
const getImageName = (application: ApplicationNested) => {
|
||||
const getImageName = async (application: ApplicationNested) => {
|
||||
const { appName, sourceType, dockerImage, registry, buildRegistry } =
|
||||
application;
|
||||
const imageName = `${appName}:latest`;
|
||||
@@ -199,18 +202,18 @@ const getImageName = (application: ApplicationNested) => {
|
||||
}
|
||||
|
||||
if (registry) {
|
||||
const registryTag = getRegistryTag(registry, imageName);
|
||||
return registryTag;
|
||||
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
||||
return getRegistryTag(r, imageName);
|
||||
}
|
||||
if (buildRegistry) {
|
||||
const registryTag = getRegistryTag(buildRegistry, imageName);
|
||||
return registryTag;
|
||||
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
||||
return getRegistryTag(r, imageName);
|
||||
}
|
||||
|
||||
return imageName;
|
||||
};
|
||||
|
||||
export const getAuthConfig = (application: ApplicationNested) => {
|
||||
export const getAuthConfig = async (application: ApplicationNested) => {
|
||||
const {
|
||||
registry,
|
||||
buildRegistry,
|
||||
@@ -222,23 +225,21 @@ export const getAuthConfig = (application: ApplicationNested) => {
|
||||
|
||||
if (sourceType === "docker") {
|
||||
if (username && password) {
|
||||
return {
|
||||
password,
|
||||
username,
|
||||
serveraddress: registryUrl || "",
|
||||
};
|
||||
return { password, username, serveraddress: registryUrl || "" };
|
||||
}
|
||||
} else if (registry) {
|
||||
const r = await findRegistryByIdWithCredentials(registry.registryId);
|
||||
return {
|
||||
password: registry.password,
|
||||
username: registry.username,
|
||||
serveraddress: registry.registryUrl,
|
||||
password: r.password,
|
||||
username: r.username,
|
||||
serveraddress: r.registryUrl,
|
||||
};
|
||||
} else if (buildRegistry) {
|
||||
const r = await findRegistryByIdWithCredentials(buildRegistry.registryId);
|
||||
return {
|
||||
password: buildRegistry.password,
|
||||
username: buildRegistry.username,
|
||||
serveraddress: buildRegistry.registryUrl,
|
||||
password: r.password,
|
||||
username: r.username,
|
||||
serveraddress: r.registryUrl,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import path from "node:path";
|
||||
import { getStaticCommand } from "@dokploy/server/utils/builders/static";
|
||||
import { nanoid } from "nanoid";
|
||||
import { quote } from "shell-quote";
|
||||
import { prepareEnvironmentVariablesForShell } from "../docker/utils";
|
||||
import { getBuildAppDirectory } from "../filesystem/directory";
|
||||
import type { ApplicationNested } from ".";
|
||||
@@ -52,10 +53,10 @@ export const getNixpacksCommand = (application: ApplicationNested) => {
|
||||
|
||||
bashCommand += `
|
||||
docker create --name ${buildContainerId} ${appName}
|
||||
mkdir -p ${localPath}
|
||||
docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} || {
|
||||
mkdir -p ${quote([localPath])}
|
||||
docker cp ${quote([`${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""}`])} ${quote([localPath])} || {
|
||||
docker rm ${buildContainerId}
|
||||
echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" ;
|
||||
echo ${quote([`❌ Copying ${publishDirectory} to ${localPath} failed`])} ;
|
||||
exit 1;
|
||||
}
|
||||
docker rm ${buildContainerId}
|
||||
|
||||
@@ -44,10 +44,15 @@ export const getRailpackCommand = (application: ApplicationNested) => {
|
||||
const secretsHash = calculateSecretsHash(envVariables);
|
||||
|
||||
const cacheKey = cleanCache ? nanoid(10) : undefined;
|
||||
// Build command
|
||||
// Build command.
|
||||
// Use a unique builder name per build so concurrent deployments don't race
|
||||
// on a shared "builder-containerd" instance (create/use/rm collisions).
|
||||
const builderName = `railpack-${appName}-${nanoid(6)}`;
|
||||
const buildArgs = [
|
||||
"buildx",
|
||||
"build",
|
||||
"--builder",
|
||||
builderName,
|
||||
"--build-arg",
|
||||
`secrets-hash=${secretsHash}`,
|
||||
...(cacheKey ? ["--build-arg", `cache-key=${cacheKey}`] : []),
|
||||
@@ -79,17 +84,16 @@ export const getRailpackCommand = (application: ApplicationNested) => {
|
||||
|
||||
const bashCommand = `
|
||||
|
||||
# Ensure we have a builder with containerd
|
||||
# Ensure we have a builder with containerd (isolated per build)
|
||||
|
||||
export RAILPACK_VERSION=${application.railpackVersion}
|
||||
bash -c "$(curl -fsSL https://railpack.com/install.sh)"
|
||||
docker buildx create --use --name builder-containerd --driver docker-container || true
|
||||
docker buildx use builder-containerd
|
||||
docker buildx create --name ${builderName} --driver docker-container || true
|
||||
|
||||
echo "Preparing Railpack build plan..." ;
|
||||
railpack ${prepareArgs.join(" ")} || {
|
||||
railpack ${prepareArgs.join(" ")} || {
|
||||
echo "❌ Railpack prepare failed" ;
|
||||
docker buildx rm builder-containerd || true
|
||||
docker buildx rm ${builderName} || true
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Railpack prepare completed." ;
|
||||
@@ -97,13 +101,13 @@ echo "✅ Railpack prepare completed." ;
|
||||
echo "Building with Railpack frontend..." ;
|
||||
# Export environment variables for secrets
|
||||
${exportEnvs.join("\n")}
|
||||
docker ${buildArgs.join(" ")} || {
|
||||
docker ${buildArgs.join(" ")} || {
|
||||
echo "❌ Railpack build failed" ;
|
||||
docker buildx rm builder-containerd || true
|
||||
docker buildx rm ${builderName} || true
|
||||
exit 1;
|
||||
}
|
||||
echo "✅ Railpack build completed." ;
|
||||
docker buildx rm builder-containerd
|
||||
docker buildx rm ${builderName} || true
|
||||
`;
|
||||
|
||||
return bashCommand;
|
||||
|
||||
Reference in New Issue
Block a user