diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19ee38dc..8584fdf6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -138,11 +138,18 @@ curl -sSL https://nixpacks.com/install.sh -o install.sh \ && ./install.sh ``` +```bash +# Install Railpack +curl -sSL https://railpack.com/install.sh | sh +``` + ```bash # Install Buildpacks curl -sSL "https://github.com/buildpacks/pack/releases/download/v0.32.1/pack-v0.32.1-linux.tgz" | tar -C /usr/local/bin/ --no-same-owner -xzv pack ``` + + ## Pull Request - The `main` branch is the source of truth and should always reflect the latest stable release. diff --git a/Dockerfile b/Dockerfile index 48bbb877..a5bd7e5e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -55,6 +55,10 @@ RUN curl -sSL https://nixpacks.com/install.sh -o install.sh \ && ./install.sh \ && pnpm install -g tsx +# Install Railpack +ARG RAILPACK_VERSION=0.0.37 +RUN curl -sSL https://railpack.com/install.sh | bash + # Install buildpacks COPY --from=buildpacksio/pack:0.35.0 /usr/local/bin/pack /usr/local/bin/pack diff --git a/apps/dokploy/components/dashboard/application/build/show.tsx b/apps/dokploy/components/dashboard/application/build/show.tsx index ad83f456..5c6e044c 100644 --- a/apps/dokploy/components/dashboard/application/build/show.tsx +++ b/apps/dokploy/components/dashboard/application/build/show.tsx @@ -1,3 +1,4 @@ +import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { @@ -25,6 +26,7 @@ enum BuildType { paketo_buildpacks = "paketo_buildpacks", nixpacks = "nixpacks", static = "static", + railpack = "railpack", } const mySchema = z.discriminatedUnion("buildType", [ @@ -53,6 +55,9 @@ const mySchema = z.discriminatedUnion("buildType", [ z.object({ buildType: z.literal("static"), }), + z.object({ + buildType: z.literal("railpack"), + }), ]); type AddTemplate = z.infer; @@ -173,6 +178,15 @@ export const ShowBuildChooseForm = ({ applicationId }: Props) => { Dockerfile + + + + + + Railpack{" "} + New + + diff --git a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx index 0632b97c..a489b390 100644 --- a/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx +++ b/apps/dokploy/components/dashboard/settings/servers/validate-server.tsx @@ -139,6 +139,15 @@ export const ValidateServer = ({ serverId }: Props) => { : "Not Created" } /> + diff --git a/apps/dokploy/components/layouts/user-nav.tsx b/apps/dokploy/components/layouts/user-nav.tsx index 4a9624de..85cb96f3 100644 --- a/apps/dokploy/components/layouts/user-nav.tsx +++ b/apps/dokploy/components/layouts/user-nav.tsx @@ -72,6 +72,14 @@ export const UserNav = () => { + { + router.push("/dashboard/settings/profile"); + }} + > + Profile + { @@ -126,14 +134,6 @@ export const UserNav = () => { ) : ( <> - { - router.push("/dashboard/settings/profile"); - }} - > - Profile - {data?.role === "owner" && ( ` fi `; +const installRailpack = () => ` + if command_exists railpack; then + echo "Railpack already installed ✅" + else + export RAILPACK_VERSION=0.0.37 + bash -c "$(curl -fsSL https://railpack.com/install.sh)" + echo "Railpack version $RAILPACK_VERSION installed ✅" + fi +`; + const installBuildpacks = () => ` SUFFIX="" if [ "$SYS_ARCH" = "aarch64" ] || [ "$SYS_ARCH" = "arm64" ]; then diff --git a/packages/server/src/setup/server-validate.ts b/packages/server/src/setup/server-validate.ts index c86206b6..b190fde2 100644 --- a/packages/server/src/setup/server-validate.ts +++ b/packages/server/src/setup/server-validate.ts @@ -38,6 +38,18 @@ export const validateNixpacks = () => ` fi `; +export const validateRailpack = () => ` + if command_exists railpack; then + version=$(railpack --version | awk '{print $3}') + if [ -n "$version" ]; then + echo "$version true" + else + echo "0.0.0 false" + fi + else + echo "0.0.0 false" + fi +`; export const validateBuildpacks = () => ` if command_exists pack; then version=$(pack --version | awk '{print $1}') @@ -86,7 +98,7 @@ export const serverValidate = async (serverId: string) => { rcloneVersionEnabled=$(${validateRClone()}) nixpacksVersionEnabled=$(${validateNixpacks()}) buildpacksVersionEnabled=$(${validateBuildpacks()}) - + railpackVersionEnabled=$(${validateRailpack()}) dockerVersion=$(echo $dockerVersionEnabled | awk '{print $1}') dockerEnabled=$(echo $dockerVersionEnabled | awk '{print $2}') @@ -96,6 +108,9 @@ export const serverValidate = async (serverId: string) => { nixpacksVersion=$(echo $nixpacksVersionEnabled | awk '{print $1}') nixpacksEnabled=$(echo $nixpacksVersionEnabled | awk '{print $2}') + railpackVersion=$(echo $railpackVersionEnabled | awk '{print $1}') + railpackEnabled=$(echo $railpackVersionEnabled | awk '{print $2}') + buildpacksVersion=$(echo $buildpacksVersionEnabled | awk '{print $1}') buildpacksEnabled=$(echo $buildpacksVersionEnabled | awk '{print $2}') @@ -103,7 +118,7 @@ export const serverValidate = async (serverId: string) => { isSwarmInstalled=$(${validateSwarm()}) isMainDirectoryInstalled=$(${validateMainDirectory()}) - echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}" + echo "{\\"docker\\": {\\"version\\": \\"$dockerVersion\\", \\"enabled\\": $dockerEnabled}, \\"rclone\\": {\\"version\\": \\"$rcloneVersion\\", \\"enabled\\": $rcloneEnabled}, \\"nixpacks\\": {\\"version\\": \\"$nixpacksVersion\\", \\"enabled\\": $nixpacksEnabled}, \\"buildpacks\\": {\\"version\\": \\"$buildpacksVersion\\", \\"enabled\\": $buildpacksEnabled}, \\"railpack\\": {\\"version\\": \\"$railpackVersion\\", \\"enabled\\": $railpackEnabled}, \\"isDokployNetworkInstalled\\": $isDokployNetworkInstalled, \\"isSwarmInstalled\\": $isSwarmInstalled, \\"isMainDirectoryInstalled\\": $isMainDirectoryInstalled}" `; client.exec(bashCommand, (err, stream) => { if (err) { diff --git a/packages/server/src/utils/builders/index.ts b/packages/server/src/utils/builders/index.ts index d777b1a3..69419f0e 100644 --- a/packages/server/src/utils/builders/index.ts +++ b/packages/server/src/utils/builders/index.ts @@ -16,6 +16,7 @@ import { buildCustomDocker, getDockerCommand } from "./docker-file"; import { buildHeroku, getHerokuCommand } from "./heroku"; import { buildNixpacks, getNixpacksCommand } from "./nixpacks"; import { buildPaketo, getPaketoCommand } from "./paketo"; +import { buildRailpack, getRailpackCommand } from "./railpack"; import { buildStatic, getStaticCommand } from "./static"; // NIXPACKS codeDirectory = where is the path of the code directory @@ -55,6 +56,8 @@ export const buildApplication = async ( await buildCustomDocker(application, writeStream); } else if (buildType === "static") { await buildStatic(application, writeStream); + } else if (buildType === "railpack") { + await buildRailpack(application, writeStream); } if (application.registryId) { @@ -96,6 +99,9 @@ export const getBuildCommand = ( case "dockerfile": command = getDockerCommand(application, logPath); break; + case "railpack": + command = getRailpackCommand(application, logPath); + break; } if (registry) { command += uploadImageRemoteCommand(application, logPath); diff --git a/packages/server/src/utils/builders/railpack.ts b/packages/server/src/utils/builders/railpack.ts new file mode 100644 index 00000000..ae55a638 --- /dev/null +++ b/packages/server/src/utils/builders/railpack.ts @@ -0,0 +1,87 @@ +import type { WriteStream } from "node:fs"; +import type { ApplicationNested } from "."; +import { prepareEnvironmentVariables } from "../docker/utils"; +import { getBuildAppDirectory } from "../filesystem/directory"; +import { spawnAsync } from "../process/spawnAsync"; +import { execAsync } from "../process/execAsync"; + +export const buildRailpack = async ( + application: ApplicationNested, + writeStream: WriteStream, +) => { + const { env, appName } = application; + const buildAppDirectory = getBuildAppDirectory(application); + const envVariables = prepareEnvironmentVariables( + env, + application.project.env, + ); + + try { + // Ensure buildkit container is running, create if it doesn't exist + await execAsync( + "docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit", + ); + + // Build the application using railpack + const args = ["build", buildAppDirectory, "--name", appName]; + + // Add environment variables + for (const env of envVariables) { + args.push("--env", env); + } + + await spawnAsync( + "railpack", + args, + (data) => { + if (writeStream.writable) { + writeStream.write(data); + } + }, + { + env: { + ...process.env, + BUILDKIT_HOST: "docker-container://buildkit", + }, + }, + ); + + return true; + } catch (e) { + throw e; + } +}; + +export const getRailpackCommand = ( + application: ApplicationNested, + logPath: string, +) => { + const { env, appName } = application; + const buildAppDirectory = getBuildAppDirectory(application); + const envVariables = prepareEnvironmentVariables( + env, + application.project.env, + ); + + // Build the application using railpack + const args = ["build", buildAppDirectory, "--name", appName]; + + // Add environment variables + for (const env of envVariables) { + args.push("--env", env); + } + + const command = `railpack ${args.join(" ")}`; + const bashCommand = ` + echo "Building with Railpack..." >> "${logPath}"; + docker container inspect buildkit >/dev/null 2>&1 || docker run --rm --privileged -d --name buildkit moby/buildkit; + export BUILDKIT_HOST=docker-container://buildkit; + ${command} >> ${logPath} 2>> ${logPath} || { + echo "❌ Railpack build failed" >> ${logPath}; + exit 1; + } + echo "✅ Railpack build completed." >> ${logPath}; + `; + + return bashCommand; +}; diff --git a/packages/server/src/utils/providers/bitbucket.ts b/packages/server/src/utils/providers/bitbucket.ts index dd98a93b..11797a45 100644 --- a/packages/server/src/utils/providers/bitbucket.ts +++ b/packages/server/src/utils/providers/bitbucket.ts @@ -275,7 +275,7 @@ export const getBitbucketBranches = async ( } const bitbucketProvider = await findBitbucketById(input.bitbucketId); const { owner, repo } = input; - const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches`; + const url = `https://api.bitbucket.org/2.0/repositories/${owner}/${repo}/refs/branches?pagelen=100`; try { const response = await fetch(url, {