Files
dokploy/packages/server/src/utils/builders/nixpacks.ts

158 lines
4.3 KiB
TypeScript

import { existsSync, mkdirSync, type WriteStream } from "node:fs";
import path from "node:path";
import {
buildStatic,
getStaticCommand,
} from "@dokploy/server/utils/builders/static";
import { nanoid } from "nanoid";
import { prepareEnvironmentVariables } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
import type { ApplicationNested } from ".";
export const buildNixpacks = async (
application: ApplicationNested,
writeStream: WriteStream,
) => {
const { env, appName, publishDirectory, cleanCache } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const buildContainerId = `${appName}-${nanoid(10)}`;
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const writeToStream = (data: string) => {
if (writeStream.writable) {
writeStream.write(data);
}
};
try {
const args = ["build", buildAppDirectory, "--name", appName];
if (cleanCache) {
args.push("--no-cache");
}
for (const env of envVariables) {
args.push("--env", env);
}
if (publishDirectory) {
/* No need for any start command, since we'll use nginx later on */
args.push("--no-error-without-start");
}
await spawnAsync("nixpacks", args, writeToStream);
/*
Run the container with the image created by nixpacks,
and copy the artifacts on the host filesystem.
Then, remove the container and create a static build.
*/
if (publishDirectory) {
await spawnAsync(
"docker",
["create", "--name", buildContainerId, appName],
writeToStream,
);
const localPath = path.join(buildAppDirectory, publishDirectory);
if (!existsSync(path.dirname(localPath))) {
mkdirSync(path.dirname(localPath), { recursive: true });
}
// https://docs.docker.com/reference/cli/docker/container/cp/
const isDirectory =
publishDirectory.endsWith("/") || !path.extname(publishDirectory);
await spawnAsync(
"docker",
[
"cp",
`${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""}`,
localPath,
],
writeToStream,
);
await spawnAsync("docker", ["rm", buildContainerId], writeToStream);
await buildStatic(application, writeStream);
}
return true;
} catch (e) {
await spawnAsync("docker", ["rm", buildContainerId], writeToStream);
throw e;
}
};
export const getNixpacksCommand = (
application: ApplicationNested,
logPath: string,
) => {
const { env, appName, publishDirectory, cleanCache } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const buildContainerId = `${appName}-${nanoid(10)}`;
const envVariables = prepareEnvironmentVariables(
env,
application.environment.project.env,
application.environment.env,
);
const args = ["build", buildAppDirectory, "--name", appName];
if (cleanCache) {
args.push("--no-cache");
}
for (const env of envVariables) {
args.push("--env", `'${env}'`);
}
if (publishDirectory) {
/* No need for any start command, since we'll use nginx later on */
args.push("--no-error-without-start");
}
const command = `nixpacks ${args.join(" ")}`;
let bashCommand = `
echo "Starting nixpacks build..." >> ${logPath};
${command} >> ${logPath} 2>> ${logPath} || {
echo "❌ Nixpacks build failed" >> ${logPath};
exit 1;
}
echo "✅ Nixpacks build completed." >> ${logPath};
`;
/*
Run the container with the image created by nixpacks,
and copy the artifacts on the host filesystem.
Then, remove the container and create a static build.
*/
if (publishDirectory) {
const localPath = path.join(buildAppDirectory, publishDirectory);
const isDirectory =
publishDirectory.endsWith("/") || !path.extname(publishDirectory);
bashCommand += `
docker create --name ${buildContainerId} ${appName}
mkdir -p ${localPath}
docker cp ${buildContainerId}:/app/${publishDirectory}${isDirectory ? "/." : ""} ${path.join(buildAppDirectory, publishDirectory)} >> ${logPath} 2>> ${logPath} || {
docker rm ${buildContainerId}
echo "❌ Copying ${publishDirectory} to ${path.join(buildAppDirectory, publishDirectory)} failed" >> ${logPath};
exit 1;
}
docker rm ${buildContainerId}
${getStaticCommand(application, logPath)}
`;
}
return bashCommand;
};