mirror of
https://github.com/andwati/dokploy.git
synced 2026-10-18 03:02:56 +00:00
* feat: add builds concurrency management for servers - Introduced a new `BuildsConcurrency` component to manage the number of concurrent builds for both local and remote servers, gated by license validity. - Implemented backend logic to resolve effective builds concurrency based on server settings and organization licenses. - Added unit tests for concurrency resolution logic to ensure correct behavior under various licensing scenarios. - Updated database schema to include `buildsConcurrency` field for servers and web server settings. - Refactored deployment queue to support in-memory job processing with configurable concurrency limits. This feature enhances deployment flexibility and control for enterprise users. * refactor: enhance deployment cancellation logic and improve Railpack build isolation - Reintroduced the `initCancelDeployments` function in the server initialization sequence to ensure deployments can be canceled effectively. - Updated the Railpack build command to use a unique builder name for each build, preventing conflicts during concurrent deployments. - Enhanced the cancellation logic to reset application and compose statuses to "idle" after canceling running deployments, improving system reliability. * test: add buildsConcurrency setting to server configuration tests - Introduced a new `buildsConcurrency` property in the server configuration tests to ensure proper handling of concurrent builds in deployment scenarios. * feat: implement builds concurrency management and validation - Added `assertBuildsConcurrencyAllowed` function to validate concurrency settings based on license status. - Updated `resolveBuildsConcurrency` to reflect new concurrency limits for free and enterprise tiers. - Enhanced `BuildsConcurrency` component to manage concurrent builds for servers, with UI adjustments for better user experience. - Introduced a new settings page for managing concurrent builds across servers, ensuring proper handling of deployments. - Updated database schema to support increased maximum concurrency values for servers and web server settings.
120 lines
3.3 KiB
TypeScript
120 lines
3.3 KiB
TypeScript
import { createHash } from "node:crypto";
|
|
import { nanoid } from "nanoid";
|
|
import { quote } from "shell-quote";
|
|
import {
|
|
parseEnvironmentKeyValuePair,
|
|
prepareEnvironmentVariables,
|
|
prepareEnvironmentVariablesForShell,
|
|
} from "../docker/utils";
|
|
import { getBuildAppDirectory } from "../filesystem/directory";
|
|
import type { ApplicationNested } from ".";
|
|
|
|
const calculateSecretsHash = (envVariables: string[]): string => {
|
|
const hash = createHash("sha256");
|
|
for (const env of envVariables.sort()) {
|
|
hash.update(env);
|
|
}
|
|
return hash.digest("hex");
|
|
};
|
|
|
|
export const getRailpackCommand = (application: ApplicationNested) => {
|
|
const { env, appName, cleanCache } = application;
|
|
const buildAppDirectory = getBuildAppDirectory(application);
|
|
const envVariables = prepareEnvironmentVariablesForShell(
|
|
env,
|
|
application.environment.project.env,
|
|
application.environment.env,
|
|
);
|
|
|
|
// Prepare command
|
|
const prepareArgs = [
|
|
"prepare",
|
|
buildAppDirectory,
|
|
"--plan-out",
|
|
`${buildAppDirectory}/railpack-plan.json`,
|
|
"--info-out",
|
|
`${buildAppDirectory}/railpack-info.json`,
|
|
];
|
|
|
|
for (const env of envVariables) {
|
|
prepareArgs.push("--env", env);
|
|
}
|
|
|
|
// Calculate secrets hash for layer invalidation
|
|
const secretsHash = calculateSecretsHash(envVariables);
|
|
|
|
const cacheKey = cleanCache ? nanoid(10) : undefined;
|
|
// 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,
|
|
...(cacheKey
|
|
? [
|
|
"--build-arg",
|
|
`secrets-hash=${secretsHash}`,
|
|
"--build-arg",
|
|
`cache-key=${cacheKey}`,
|
|
]
|
|
: []),
|
|
"--build-arg",
|
|
`BUILDKIT_SYNTAX=ghcr.io/railwayapp/railpack-frontend:v${application.railpackVersion}`,
|
|
"-f",
|
|
`${buildAppDirectory}/railpack-plan.json`,
|
|
"--output",
|
|
`type=docker,name=${appName}`,
|
|
];
|
|
|
|
// Add secrets properly formatted
|
|
// Use prepareEnvironmentVariables (without ForShell) to get raw values for parsing
|
|
const rawEnvVariables = prepareEnvironmentVariables(
|
|
env,
|
|
application.environment.project.env,
|
|
application.environment.env,
|
|
);
|
|
const exportEnvs = [];
|
|
for (const pair of rawEnvVariables) {
|
|
const [key, value] = parseEnvironmentKeyValuePair(pair);
|
|
if (key && value) {
|
|
buildArgs.push("--secret", `id=${key},env=${key}`);
|
|
exportEnvs.push(`export ${key}=${quote([value])}`);
|
|
}
|
|
}
|
|
|
|
buildArgs.push(buildAppDirectory);
|
|
|
|
const bashCommand = `
|
|
|
|
# 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 --name ${builderName} --driver docker-container || true
|
|
|
|
echo "Preparing Railpack build plan..." ;
|
|
railpack ${prepareArgs.join(" ")} || {
|
|
echo "❌ Railpack prepare failed" ;
|
|
docker buildx rm ${builderName} || true
|
|
exit 1;
|
|
}
|
|
echo "✅ Railpack prepare completed." ;
|
|
|
|
echo "Building with Railpack frontend..." ;
|
|
# Export environment variables for secrets
|
|
${exportEnvs.join("\n")}
|
|
docker ${buildArgs.join(" ")} || {
|
|
echo "❌ Railpack build failed" ;
|
|
docker buildx rm ${builderName} || true
|
|
exit 1;
|
|
}
|
|
echo "✅ Railpack build completed." ;
|
|
docker buildx rm ${builderName} || true
|
|
`;
|
|
|
|
return bashCommand;
|
|
};
|