Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions apps/dokploy/__test__/server/server-components.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
parseComponentVersions,
type UpdatableComponent,
} from "@dokploy/server/setup/server-components";
import { RAILPACK_INSTALL_SCRIPT_URLS } from "@dokploy/server/setup/server-setup";
import {
buildTraefikCreateCommand,
buildTraefikCreateWithImage,
Expand Down Expand Up @@ -251,10 +252,10 @@ describe("buildComponentUpdateScript", () => {
const script = buildComponentUpdateScript(["nixpacks", "railpack"]);

expect(script).toContain(
'curl -fsSL https://nixpacks.com/install.sh -o "$nixpacks_installer"',
"for nixpacks_installer_url in https://nixpacks.com/install.sh; do",
);
expect(script).toContain(
'curl -fsSL https://railpack.com/install.sh -o "$railpack_installer"',
`for railpack_installer_url in ${RAILPACK_INSTALL_SCRIPT_URLS.join(" ")}; do`,
);
expect(script).toContain('bash -n "$nixpacks_installer"');
expect(script).not.toContain('bash -c "$(curl');
Expand Down Expand Up @@ -1590,6 +1591,46 @@ describe("buildComponentUpdateScript run order", { timeout: 60_000 }, () => {
},
SPAWN_TEST_TIMEOUT_MS,
);
it(
"installs Railpack from the GitHub copy of the install script when railpack.com fails",
() => {
const run = runComponentScript(["railpack"], {
env: { FAKE_CURL_FAILS_FOR: "railpack.com" },
});

expect(run.status).toBe(0);
expect(
run.calls.some((call) =>
call.startsWith(
"curl -fsSL https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh -o ",
),
),
).toBe(true);
expect(run.stderr).toContain(
"The Railpack install script could not be downloaded or checked from https://railpack.com/install.sh.",
);
expect(run.stdout).toContain(
`Railpack version ${RAILPACK_VERSION} installed`,
);
},
SPAWN_TEST_TIMEOUT_MS,
);

it(
"fails the update when neither copy of the Railpack install script can be downloaded",
() => {
const run = runComponentScript(["railpack"], {
env: { FAKE_CURL_FAILS_FOR: "install.sh" },
});

expect(run.status).not.toBe(0);
expect(run.stderr).toContain(
"the Railpack install script could not be downloaded or checked; Railpack was not changed.",
);
expect(run.stdout).not.toContain("Railpack version");
},
SPAWN_TEST_TIMEOUT_MS,
);
it(
"fails the update when the RClone download fails",
() => {
Expand Down
51 changes: 48 additions & 3 deletions apps/dokploy/__test__/server/server-setup.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import path from "node:path";
import {
createTraefikInstance,
defaultCommand,
RAILPACK_INSTALL_SCRIPT_URLS,
reportDockerVersion,
} from "@dokploy/server";
import {
Expand Down Expand Up @@ -129,7 +130,8 @@ const setupStep = (script: string, marker: string) => {

// Runs one step as the setup script runs it: under set -e, with a PATH that holds only
// the fake curl and the tools the step needs, so nixpacks, railpack and pack are absent.
const runSetupStep = (step: string, curlMode: "ok" | "fail") => {
// failsFor fails only the downloads whose URL contains it.
const runSetupStep = (step: string, curlMode: "ok" | "fail", failsFor = "") => {
const dir = mkdtempSync(path.join(tmpdir(), "dokploy-setup-step-"));
try {
const binDir = path.join(dir, "bin");
Expand All @@ -142,6 +144,11 @@ const runSetupStep = (step: string, curlMode: "ok" | "fail") => {
curl,
`#!/bin/sh
if [ "$FAKE_CURL_MODE" = fail ]; then exit 22; fi
if [ -n "$FAKE_CURL_FAILS_FOR" ]; then
case "$*" in
*"$FAKE_CURL_FAILS_FOR"*) exit 22 ;;
esac
fi
out=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then out="$2"; fi
Expand All @@ -165,6 +172,7 @@ printf '%s\\n' 'echo fake installer "$NIXPACKS_VERSION$RAILPACK_VERSION"' > "$ou
...process.env,
PATH: `${dir}:${binDir}`,
FAKE_CURL_MODE: curlMode,
FAKE_CURL_FAILS_FOR: failsFor,
},
});
} finally {
Expand Down Expand Up @@ -192,14 +200,20 @@ describe("install scripts in the server setup", () => {
const script = defaultCommand(isBuildServer);

expect(script).toContain(
'curl -fsSL https://nixpacks.com/install.sh -o "$nixpacks_installer"',
"for nixpacks_installer_url in https://nixpacks.com/install.sh; do",
);
expect(script).toContain(
'curl -fsSL "$nixpacks_installer_url" -o "$nixpacks_installer"',
);
expect(script).toContain('bash -n "$nixpacks_installer"');
expect(script).toContain(
`env NIXPACKS_VERSION=${NIXPACKS_VERSION} bash "$nixpacks_installer"`,
);
expect(script).toContain(
'curl -fsSL https://railpack.com/install.sh -o "$railpack_installer"',
`for railpack_installer_url in ${RAILPACK_INSTALL_SCRIPT_URLS.join(" ")}; do`,
);
expect(script).toContain(
'curl -fsSL "$railpack_installer_url" -o "$railpack_installer"',
);
expect(script).toContain('bash -n "$railpack_installer"');
expect(script).toContain(
Expand Down Expand Up @@ -248,6 +262,37 @@ describe("install scripts in the server setup", () => {
);
});

it("installs Railpack from the GitHub copy of the install script when railpack.com fails", () => {
const result = runSetupStep(
setupStep(defaultCommand(true), 'Installing Railpack"'),
"ok",
"railpack.com",
);

expect(result.status).toBe(0);
expect(result.stderr).toContain(
"The Railpack install script could not be downloaded or checked from https://railpack.com/install.sh.",
);
expect(result.stdout).toContain(`fake installer ${RAILPACK_VERSION}`);
expect(result.stdout).toContain(
`Railpack version ${RAILPACK_VERSION} installed`,
);
});

it("stops the setup when neither copy of the Railpack install script can be downloaded", () => {
const result = runSetupStep(
setupStep(defaultCommand(true), 'Installing Railpack"'),
"ok",
"install.sh",
);

expect(result.status).not.toBe(0);
expect(result.stderr).toContain(
"could not be downloaded or checked from https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh.",
);
expect(result.stdout).not.toContain("Railpack version");
});

it("stops the setup when the RClone download fails", () => {
const result = runSetupStep(
setupStep(defaultCommand(true), 'Installing RClone. "'),
Expand Down
50 changes: 48 additions & 2 deletions apps/dokploy/__test__/settings/install-agenthits-update.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1085,6 +1085,13 @@ describe("install-agenthits.sh toolchain downloads", () => {
};
const FAKE_INSTALL_CURL = `#!/bin/sh
if [ "$FAKE_CURL_MODE" = fail ]; then exit 22; fi
case "$FAKE_CURL_MODE" in
fail-for:*)
case "$*" in
*"\${FAKE_CURL_MODE#fail-for:}"*) exit 22 ;;
esac
;;
esac
out=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then out="$2"; fi
Expand All @@ -1093,7 +1100,11 @@ done
printf '%s\\n' 'echo fake nixpacks "$NIXPACKS_VERSION"' > "$out"
`;

const runHelper = (args: string[], curlMode: "ok" | "fail") => {
// "fail-for:<text>" fails only the downloads whose URL contains <text>.
const runHelper = (
args: string[],
curlMode: "ok" | "fail" | `fail-for:${string}`,
) => {
const dir = mkdtempSync(path.join(tmpdir(), "dokploy-install-script-"));
try {
const binDir = path.join(dir, "bin");
Expand Down Expand Up @@ -1131,7 +1142,7 @@ printf '%s\\n' 'echo fake nixpacks "$NIXPACKS_VERSION"' > "$out"
'run_install_script https://nixpacks.com/install.sh Nixpacks "NIXPACKS_VERSION=$NIXPACKS_VERSION"',
);
expect(installer).toContain(
'run_install_script https://railpack.com/install.sh Railpack "RAILPACK_VERSION=$RAILPACK_VERSION"',
'run_install_script https://railpack.com/install.sh Railpack "RAILPACK_VERSION=$RAILPACK_VERSION" \\\n\t\t\thttps://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh',
);
expect(installer).not.toContain('bash -c "$(curl');
});
Expand All @@ -1152,6 +1163,41 @@ printf '%s\\n' 'echo fake nixpacks "$NIXPACKS_VERSION"' > "$out"
);
});

it("downloads the install script from the fallback URL when the first one fails", () => {
const result = runHelper(
[
"https://railpack.com/install.sh",
"Railpack",
"NIXPACKS_VERSION=29.4.0",
"https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh",
],
"fail-for:railpack.com",
);

expect(result.status).toBe(0);
expect(result.stderr).toContain(
"The Railpack install script could not be downloaded or checked from https://railpack.com/install.sh.",
);
expect(result.stdout).toContain("fake nixpacks 29.4.0");
});

it("stops the install when the fallback URL fails too", () => {
const result = runHelper(
[
"https://railpack.com/install.sh",
"Railpack",
"NIXPACKS_VERSION=29.4.0",
"https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh",
],
"fail-for:install.sh",
);

expect(result.status).not.toBe(0);
expect(result.stderr).toContain(
"the Railpack install script could not be downloaded or checked; Railpack was not installed.",
);
});

it("runs the downloaded install script with the pinned version", () => {
const result = runHelper(
[
Expand Down
17 changes: 14 additions & 3 deletions install-agenthits.sh
Original file line number Diff line number Diff line change
Expand Up @@ -395,9 +395,17 @@ run_install_script() {
local url="$1"
local label="$2"
local assignment="$3"
local script=""
local fallback_url="${4:-}"
local script="" candidate="" downloaded=0
script="$(mktemp)"
if ! curl -fsSL "$url" -o "$script" || [ ! -s "$script" ] || ! bash -n "$script"; then
for candidate in "$url" ${fallback_url:+"$fallback_url"}; do
if curl -fsSL "$candidate" -o "$script" && [ -s "$script" ] && bash -n "$script"; then
downloaded=1
break
fi
echo "The $label install script could not be downloaded or checked from $candidate." >&2
done
if [ "$downloaded" != 1 ]; then
rm -f "$script"
echo "Error: the $label install script could not be downloaded or checked; $label was not installed." >&2
exit 1
Expand Down Expand Up @@ -430,7 +438,10 @@ install_toolchain() {
run_install_script https://nixpacks.com/install.sh Nixpacks "NIXPACKS_VERSION=$NIXPACKS_VERSION"
fi
if ! tool_version_matches "$RAILPACK_VERSION" railpack --version; then
run_install_script https://railpack.com/install.sh Railpack "RAILPACK_VERSION=$RAILPACK_VERSION"
# railpack.com only redirects through Cloudflare to this GitHub file, and some
# networks get a 525 from Cloudflare there.
run_install_script https://railpack.com/install.sh Railpack "RAILPACK_VERSION=$RAILPACK_VERSION" \
https://raw.githubusercontent.com/railwayapp/railpack/refs/heads/main/install.sh
fi
if ! tool_version_matches "$BUILDPACKS_VERSION" pack --version; then
curl -sSL "https://github.com/buildpacks/pack/releases/download/v${BUILDPACKS_VERSION}/pack-v${BUILDPACKS_VERSION}-linux${pack_suffix}.tgz" | tar -C /usr/local/bin/ --no-same-owner -xz pack
Expand Down
31 changes: 18 additions & 13 deletions packages/server/src/setup/server-components.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { PINNED_VERSIONS } from "./component-versions";
import { rcloneInstallCommand } from "./server-setup";
import {
installScriptDownload,
NIXPACKS_INSTALL_SCRIPT_URLS,
RAILPACK_INSTALL_SCRIPT_URLS,
rcloneInstallCommand,
} from "./server-setup";
import { runSshCommand } from "./ssh-command";
import {
buildTraefikCreateWithImage,
Expand Down Expand Up @@ -552,12 +557,12 @@ fi
case "nixpacks":
return `
echo "Updating Nixpacks to ${PINNED_VERSIONS.nixpacks}"
nixpacks_installer="$(mktemp)"
if ! curl -fsSL https://nixpacks.com/install.sh -o "$nixpacks_installer" || [ ! -s "$nixpacks_installer" ] || ! bash -n "$nixpacks_installer"; then
rm -f "$nixpacks_installer"
echo "Error: the Nixpacks install script could not be downloaded or checked; Nixpacks was not changed." >&2
exit 1
fi
${installScriptDownload({
label: "Nixpacks",
name: "nixpacks",
urls: NIXPACKS_INSTALL_SCRIPT_URLS,
outcome: "Nixpacks was not changed.",
})}
if ! $SUDO_CMD env NIXPACKS_VERSION=${PINNED_VERSIONS.nixpacks} bash "$nixpacks_installer"; then
rm -f "$nixpacks_installer"
echo "Error: the Nixpacks install script failed." >&2
Expand All @@ -569,12 +574,12 @@ echo "Nixpacks version ${PINNED_VERSIONS.nixpacks} installed ✅"
case "railpack":
return `
echo "Updating Railpack to ${PINNED_VERSIONS.railpack}"
railpack_installer="$(mktemp)"
if ! curl -fsSL https://railpack.com/install.sh -o "$railpack_installer" || [ ! -s "$railpack_installer" ] || ! bash -n "$railpack_installer"; then
rm -f "$railpack_installer"
echo "Error: the Railpack install script could not be downloaded or checked; Railpack was not changed." >&2
exit 1
fi
${installScriptDownload({
label: "Railpack",
name: "railpack",
urls: RAILPACK_INSTALL_SCRIPT_URLS,
outcome: "Railpack was not changed.",
})}
if ! $SUDO_CMD env RAILPACK_VERSION=${PINNED_VERSIONS.railpack} bash "$railpack_installer"; then
rm -f "$railpack_installer"
echo "Error: the Railpack install script failed." >&2
Expand Down
Loading