feat: initial commit

This commit is contained in:
Mauricio Siu
2024-04-28 23:57:52 -06:00
parent 8857a20344
commit be56ba046c
412 changed files with 60777 additions and 1 deletions

View File

@@ -0,0 +1,40 @@
import { docker } from "@/server/constants";
import type { WriteStream } from "node:fs";
import * as tar from "tar-fs";
import type { ApplicationNested } from ".";
import { getBuildAppDirectory } from "../filesystem/directory";
export const buildCustomDocker = async (
application: ApplicationNested,
writeStream: WriteStream,
) => {
const { appName } = application;
const dockerFilePath = getBuildAppDirectory(application);
try {
const image = `${appName}`;
const contextPath =
dockerFilePath.substring(0, dockerFilePath.lastIndexOf("/") + 1) || ".";
const tarStream = tar.pack(contextPath);
const stream = await docker.buildImage(tarStream, {
t: image,
dockerfile: dockerFilePath.substring(dockerFilePath.lastIndexOf("/") + 1),
// TODO: maybe use or not forcerm
// forcerm: true,
});
await new Promise((resolve, reject) => {
docker.modem.followProgress(
stream,
(err, res) => (err ? reject(err) : resolve(res)),
(event) => {
if (event.stream) {
writeStream.write(event.stream);
}
},
);
});
} catch (error) {
throw error;
}
};

View File

@@ -0,0 +1,40 @@
import type { ApplicationNested } from ".";
import { prepareEnvironmentVariables } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
import type { WriteStream } from "node:fs";
// TODO: integrate in the vps sudo chown -R $(whoami) ~/.docker
export const buildHeroku = async (
application: ApplicationNested,
writeStream: WriteStream,
) => {
const { env, appName } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const envVariables = prepareEnvironmentVariables(env);
try {
const args = [
"build",
appName,
"--path",
buildAppDirectory,
"--builder",
"heroku/builder:22",
];
for (const env in envVariables) {
args.push("--env", env);
}
await spawnAsync("pack", args, (data) => {
if (writeStream.writable) {
writeStream.write(data);
}
// Stream the data
console.log(data);
});
return true;
} catch (e) {
throw e;
}
};

View File

@@ -0,0 +1,138 @@
import { createWriteStream } from "node:fs";
import { docker } from "@/server/constants";
import type { InferResultType } from "@/server/types/with";
import type { CreateServiceOptions } from "dockerode";
import {
calculateResources,
generateBindMounts,
generateFileMounts,
generateVolumeMounts,
prepareEnvironmentVariables,
} from "../docker/utils";
import { buildCustomDocker } from "./docker-file";
import { buildHeroku } from "./heroku";
import { buildNixpacks } from "./nixpacks";
import { buildPaketo } from "./paketo";
// NIXPACKS codeDirectory = where is the path of the code directory
// HEROKU codeDirectory = where is the path of the code directory
// PAKETO codeDirectory = where is the path of the code directory
// DOKERFILE codeDirectory = where is the exact path of the (Dockerfile)
export type ApplicationNested = InferResultType<
"applications",
{ mounts: true; security: true; redirects: true; ports: true }
>;
export const buildApplication = async (
application: ApplicationNested,
logPath: string,
) => {
const writeStream = createWriteStream(logPath, { flags: "a" });
const { buildType, sourceType } = application;
try {
writeStream.write(
`\nBuild ${buildType}: ✅\nSource Type: ${sourceType}: ✅\n`,
);
console.log(`Build ${buildType}: ✅`);
if (buildType === "nixpacks") {
await buildNixpacks(application, writeStream);
} else if (buildType === "heroku_buildpacks") {
await buildHeroku(application, writeStream);
} else if (buildType === "paketo_buildpacks") {
await buildPaketo(application, writeStream);
} else if (buildType === "dockerfile") {
await buildCustomDocker(application, writeStream);
}
await mechanizeDockerContainer(application);
writeStream.write("Docker Deployed: ✅");
} catch (error) {
writeStream.write(`ERROR: ${error}: ❌`);
throw error;
} finally {
writeStream.end();
}
};
export const mechanizeDockerContainer = async (
application: ApplicationNested,
) => {
const {
appName,
env,
mounts,
sourceType,
dockerImage,
cpuLimit,
memoryLimit,
memoryReservation,
cpuReservation,
command,
ports,
} = application;
const resources = calculateResources({
memoryLimit,
memoryReservation,
cpuLimit,
cpuReservation,
});
const volumesMount = generateVolumeMounts(mounts);
const bindsMount = generateBindMounts(mounts);
const filesMount = generateFileMounts(appName, mounts);
const envVariables = prepareEnvironmentVariables(env);
const settings: CreateServiceOptions = {
Name: appName,
TaskTemplate: {
ContainerSpec: {
Image: sourceType === "docker" ? dockerImage! : `${appName}:latest`,
Env: envVariables,
Mounts: [...volumesMount, ...bindsMount, ...filesMount],
...(command
? {
Command: ["/bin/sh"],
Args: ["-c", command],
}
: {}),
},
Networks: [{ Target: "dokploy-network" }],
RestartPolicy: {
Condition: "on-failure",
},
Resources: {
...resources,
},
},
Mode: {
Replicated: {
Replicas: 1,
},
},
EndpointSpec: {
Ports: ports.map((port) => ({
Protocol: port.protocol,
TargetPort: port.targetPort,
PublishedPort: port.publishedPort,
})),
},
UpdateConfig: {
Parallelism: 1,
Order: "start-first",
},
};
try {
const service = docker.getService(appName);
const inspect = await service.inspect();
await service.update({
version: Number.parseInt(inspect.Version.Index),
...settings,
TaskTemplate: {
...settings.TaskTemplate,
ForceUpdate: inspect.Spec.TaskTemplate.ForceUpdate + 1,
},
});
} catch (error) {
await docker.createService(settings);
}
// await cleanUpUnusedImages();
};

View File

@@ -0,0 +1,30 @@
import type { ApplicationNested } from ".";
import { prepareEnvironmentVariables } from "../docker/utils";
import { getBuildAppDirectory } from "../filesystem/directory";
import { spawnAsync } from "../process/spawnAsync";
import type { WriteStream } from "node:fs";
// TODO: integrate in the vps sudo chown -R $(whoami) ~/.docker
export const buildNixpacks = async (
application: ApplicationNested,
writeStream: WriteStream,
) => {
const { env, appName } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const envVariables = prepareEnvironmentVariables(env);
try {
const args = ["build", buildAppDirectory, "--name", appName];
for (const env in envVariables) {
args.push("--env", env);
}
await spawnAsync("nixpacks", args, (data) => {
if (writeStream.writable) {
writeStream.write(data);
}
});
return true;
} catch (e) {
throw e;
}
};

View File

@@ -0,0 +1,38 @@
import type { WriteStream } from "node:fs";
import { spawnAsync } from "../process/spawnAsync";
import type { ApplicationNested } from ".";
import { getBuildAppDirectory } from "../filesystem/directory";
import { prepareEnvironmentVariables } from "../docker/utils";
// TODO: integrate in the vps sudo chown -R $(whoami) ~/.docker
export const buildPaketo = async (
application: ApplicationNested,
writeStream: WriteStream,
) => {
const { env, appName } = application;
const buildAppDirectory = getBuildAppDirectory(application);
const envVariables = prepareEnvironmentVariables(env);
try {
const args = [
"build",
appName,
"--path",
buildAppDirectory,
"--builder",
"paketobuildpacks/builder-jammy-full",
];
for (const env in envVariables) {
args.push("--env", env);
}
await spawnAsync("pack", args, (data) => {
if (writeStream.writable) {
writeStream.write(data);
}
});
return true;
} catch (e) {
throw e;
}
};