refactor(deployments): improve build error

This commit is contained in:
Mauricio Siu
2024-09-15 14:48:40 -06:00
parent d2c8632c4f
commit 0d3c978aad
16 changed files with 464 additions and 178 deletions

View File

@@ -9,6 +9,7 @@ import { COMPOSE_PATH } from "@/server/constants";
import type { InferResultType } from "@/server/types/with";
import boxen from "boxen";
import {
getComposePath,
writeDomainsToCompose,
writeDomainsToComposeRemote,
} from "../docker/domain";
@@ -73,12 +74,17 @@ export const getBuildComposeCommand = async (
compose: ComposeNested,
logPath: string,
) => {
const { sourceType, appName, mounts, composeType, domains } = compose;
const { sourceType, appName, mounts, composeType, domains, composePath } =
compose;
const command = createCommand(compose);
const envCommand = getCreateEnvFileCommand(compose);
const projectPath = join(COMPOSE_PATH, compose.appName, "code");
const newCompose = await writeDomainsToComposeRemote(compose, domains);
const newCompose = await writeDomainsToComposeRemote(
compose,
domains,
logPath,
);
const logContent = `
App Name: ${appName}
Build Compose 🐳
@@ -107,6 +113,11 @@ Compose Type: ${composeType} ✅`;
${envCommand}
cd "${projectPath}";
if [ ! -f "${composePath}" ]; then
echo "❌ Error: Compose file not found" >> "${logPath}";
exit 1;
fi
docker ${command.split(" ").join(" ")} >> "${logPath}" 2>&1 || { echo "Error: ❌ Docker command failed" >> "${logPath}"; exit 1; }

View File

@@ -6,7 +6,7 @@ import {
getDockerContextPath,
} from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
import { createEnvFile } from "./utils";
import { createEnvFile, createEnvFileCommand } from "./utils";
export const buildCustomDocker = async (
application: ApplicationNested,
@@ -86,11 +86,27 @@ export const getDockerCommand = (
commandArgs.push("--build-arg", arg);
}
const command = `
/*
Do not generate an environment file when publishDirectory is specified,
as it could be publicly exposed.
*/
let command = "";
if (!publishDirectory) {
command += createEnvFileCommand(dockerFilePath, env);
}
command = `
echo "Building ${appName}" >> ${logPath};
cd ${dockerContextPath} || exit 1;
docker ${commandArgs.join(" ")} >> ${logPath} 2>&1;
echo "Docker build completed." >> ${logPath};
cd ${dockerContextPath} >> ${logPath} 2>> ${logPath} || {
echo "❌ The path ${dockerContextPath} does not exist" >> ${logPath};
exit 1;
}
docker ${commandArgs.join(" ")} >> ${logPath} 2>> ${logPath} || {
echo "❌ Docker build failed" >> ${logPath};
exit 1;
}
echo "✅ Docker build completed." >> ${logPath};
`;
return command;

View File

@@ -62,8 +62,11 @@ export const getHerokuCommand = (
const command = `pack ${args.join(" ")}`;
const bashCommand = `
echo "Starting heroku build..." >> ${logPath};
${command} >> ${logPath} 2>&1;
echo "Heroku build completed." >> ${logPath};
${command} >> ${logPath} 2>> ${logPath} || {
echo "Heroku build failed" >> ${logPath};
exit 1;
}
echo "✅ Heroku build completed." >> ${logPath};
`;
return bashCommand;

View File

@@ -16,7 +16,7 @@ import { buildCustomDocker, getDockerCommand } from "./docker-file";
import { buildHeroku, getHerokuCommand } from "./heroku";
import { buildNixpacks, getNixpacksCommand } from "./nixpacks";
import { buildPaketo, getPaketoCommand } from "./paketo";
import { buildStatic } from "./static";
import { buildStatic, getStaticCommand } from "./static";
import { findServerById } from "@/server/api/services/server";
import { readSSHKey } from "../filesystem/ssh";
import { getRemoteDocker } from "../servers/remote-docker";
@@ -81,8 +81,8 @@ export const getBuildCommand = (
return getHerokuCommand(application, logPath);
case "paketo_buildpacks":
return getPaketoCommand(application, logPath);
// case "static":
// return buildStatic(application, writeStream);
case "static":
return getStaticCommand(application, logPath);
case "dockerfile":
return getDockerCommand(application, logPath);
}

View File

@@ -1,12 +1,11 @@
import type { WriteStream } from "node:fs";
import path from "node:path";
import { buildStatic } from "@/server/utils/builders/static";
import { buildStatic, getStaticCommand } from "@/server/utils/builders/static";
import { nanoid } from "nanoid";
import type { ApplicationNested } from ".";
import { prepareEnvironmentVariables } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
import { executeCommand } from "../servers/command";
export const buildNixpacks = async (
application: ApplicationNested,
@@ -83,47 +82,44 @@ export const getNixpacksCommand = (
const buildContainerId = `${appName}-${nanoid(10)}`;
const envVariables = prepareEnvironmentVariables(env);
try {
const args = ["build", buildAppDirectory, "--name", appName];
const args = ["build", buildAppDirectory, "--name", appName];
for (const env of envVariables) {
args.push("--env", env);
}
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");
}
console.log("args", args);
const command = `nixpacks ${args.join(" ")}`;
const bashCommand = `
if (publishDirectory) {
/* No need for any start command, since we'll use nginx later on */
args.push("--no-error-without-start");
}
console.log("args", args);
const command = `nixpacks ${args.join(" ")}`;
let bashCommand = `
echo "Starting nixpacks build..." >> ${logPath};
${command} >> ${logPath} 2>&1;
echo "Nixpacks build completed." >> ${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) {
}
// if (publishDirectory) {
// bashCommand += `
// docker create --name ${buildContainerId} ${appName}
// docker cp ${buildContainerId}:/app/${publishDirectory} ${path.join(buildAppDirectory, publishDirectory)}
// docker rm ${buildContainerId}
// buildStatic ${application} ${writeStream}
// `;
// }
return bashCommand;
} catch (e) {
// await spawnAsync("docker", ["rm", buildContainerId], writeToStream);
throw e;
/*
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) {
bashCommand += `
docker create --name ${buildContainerId} ${appName}
docker cp ${buildContainerId}:/app/${publishDirectory} ${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;
};

View File

@@ -61,8 +61,11 @@ export const getPaketoCommand = (
const command = `pack ${args.join(" ")}`;
const bashCommand = `
echo "Starting Paketo build..." >> ${logPath};
${command} >> ${logPath} 2>&1;
echo "Paketo build completed." >> ${logPath};
${command} >> ${logPath} 2>> ${logPath} || {
echo "Paketo build failed" >> ${logPath};
exit 1;
}
echo "✅ Paketo build completed." >> ${logPath};
`;
return bashCommand;

View File

@@ -1,7 +1,10 @@
import type { WriteStream } from "node:fs";
import { buildCustomDocker } from "@/server/utils/builders/docker-file";
import {
buildCustomDocker,
getDockerCommand,
} from "@/server/utils/builders/docker-file";
import type { ApplicationNested } from ".";
import { createFile } from "../docker/utils";
import { createFile, getCreateFileCommand } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory";
export const buildStatic = async (
@@ -36,3 +39,31 @@ export const buildStatic = async (
throw e;
}
};
export const getStaticCommand = (
application: ApplicationNested,
logPath: string,
) => {
const { publishDirectory } = application;
const buildAppDirectory = getBuildAppDirectory(application);
let command = getCreateFileCommand(
buildAppDirectory,
"Dockerfile",
[
"FROM nginx:alpine",
"WORKDIR /usr/share/nginx/html/",
`COPY ${publishDirectory || "."} .`,
].join("\n"),
);
command += getDockerCommand(
{
...application,
buildType: "dockerfile",
dockerfile: "Dockerfile",
},
logPath,
);
return command;
};

View File

@@ -13,7 +13,6 @@ export const createEnvFile = (directory: string, env: string | null) => {
export const createEnvFileCommand = (directory: string, env: string | null) => {
const envFilePath = join(dirname(directory), ".env");
// let command = ``
if (!existsSync(dirname(envFilePath))) {
mkdirSync(dirname(envFilePath), { recursive: true });
}

View File

@@ -96,7 +96,14 @@ export const loadDockerComposeRemote = async (
if (!compose.serverId) {
return null;
}
const { stdout } = await execAsyncRemote(compose.serverId, `cat ${path}`);
const { stdout, stderr } = await execAsyncRemote(
compose.serverId,
`cat ${path}`,
);
if (stderr) {
return null;
}
if (!stdout) return null;
const parsedConfig = load(stdout) as ComposeSpecification;
return parsedConfig;
@@ -135,21 +142,25 @@ export const writeDomainsToCompose = async (
export const writeDomainsToComposeRemote = async (
compose: Compose,
domains: Domain[],
logPath: string,
) => {
if (!domains.length) {
return "";
}
const composeConverted = await addDomainToCompose(compose, domains);
const path = getComposePath(compose);
try {
const composeConverted = await addDomainToCompose(compose, domains);
const path = getComposePath(compose);
if (compose.serverId) {
const composeString = dump(composeConverted, { lineWidth: 1000 });
const encodedContent = encodeBase64(composeString);
return `echo "${encodedContent}" | base64 -d > "${path}";`;
}
} catch (error) {
throw error;
return `
echo "❌ Has occured an error: ${error?.message || error}" >> ${logPath};
exit 1;
`;
}
};
// (node:59875) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 SIGTERM listeners added to [process]. Use emitter.setMaxListeners() to increase limit

View File

@@ -59,24 +59,26 @@ export const buildRemoteDocker = async (
throw new Error("Docker image not found");
}
let command = `
echo "Building ${sourceType}" >> ${logPath};
echo "Pulling ${dockerImage}" >> ${logPath};
`;
if (username && password) {
command += `
if ! docker login --username ${username} --password ${password} https://index.docker.io/v1/ >> ${logPath} 2>&1; then
echo "Error logging in to Docker Hub" >> ${logPath};
echo "❌ Login failed" >> ${logPath};
exit 1;
fi
`;
}
command += `
echo "Pulling ${dockerImage}" >> ${logPath};
docker pull ${dockerImage} >> ${logPath} 2>&1;
`;
docker pull ${dockerImage} >> ${logPath} 2>> ${logPath} || {
echo "❌ Pulling image failed" >> ${logPath};
exit 1;
}
echo "✅ Pulling image completed." >> ${logPath};
`;
return command;
} catch (error) {
throw error;

View File

@@ -29,7 +29,10 @@ export const createComposeFile = async (compose: Compose, logPath: string) => {
}
};
export const getCreateComposeFileCommand = (compose: Compose) => {
export const getCreateComposeFileCommand = (
compose: Compose,
logPath: string,
) => {
const { appName, composeFile } = compose;
const outputPath = join(COMPOSE_PATH, appName, "code");
const filePath = join(outputPath, "docker-compose.yml");
@@ -38,6 +41,7 @@ export const getCreateComposeFileCommand = (compose: Compose) => {
rm -rf ${outputPath};
mkdir -p ${outputPath};
echo "${encodedContent}" | base64 -d > "${filePath}";
echo "File 'docker-compose.yml' created: ✅" >> ${logPath};
`;
return bashCommand;
};