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
79 changes: 79 additions & 0 deletions apps/server/src/cloud/pinnedRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Fiber from "effect/Fiber";
import * as Path from "effect/Path";
import * as PlatformError from "effect/PlatformError";
import * as ChildProcessSpawner from "effect/unstable/process/ChildProcessSpawner";

import * as ProcessRunner from "../processRunner.ts";
Expand Down Expand Up @@ -38,6 +39,84 @@ const successfulRunner = (fs: FileSystem.FileSystem, path: Path.Path) =>
});

it.layer(NodeServices.layer)("ensurePinnedRuntimeInstalled", (it) => {
it.effect("installs through pnpm when its Node runtime has no npm executable", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-pinned-pnpm-" });
const commands: Array<ProcessRunner.ProcessRunInput> = [];
const install = successfulRunner(fs, path);
const paths = yield* ensurePinnedRuntimeInstalled({
baseDir,
version: "1.2.3",
fs,
path,
runner: ProcessRunner.ProcessRunner.of({
run: (input) => {
commands.push(input);
return input.command === "npm"
? Effect.fail(
new ProcessRunner.ProcessSpawnError({
command: "npm",
argumentCount: input.args.length,
cause: PlatformError.systemError({
_tag: "NotFound",
module: "ChildProcess",
method: "spawn",
}),
}),
)
: install.run(input);
},
}),
validate: (staging) =>
fs.exists(staging.entryPath).pipe(
Effect.flatMap((exists) => (exists ? Effect.void : Effect.die("missing runtime"))),
Effect.orDie,
),
});
assert.deepEqual(
commands.map((command) => command.command),
["npm", "pnpm"],
);
assert.deepEqual(commands[1]!.args, ["--package=npm@11", "dlx", "npm", ...commands[0]!.args]);
assert.equal(yield* fs.readFileString(paths.sentinelPath), "1.2.3\n");
}),
);

it.effect("does not try a different installer for npm permission failures", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-pinned-permission-" });
const commands: string[] = [];
yield* ensurePinnedRuntimeInstalled({
baseDir,
version: "1.2.3",
fs,
path,
runner: ProcessRunner.ProcessRunner.of({
run: (input) => {
commands.push(input.command);
return Effect.fail(
new ProcessRunner.ProcessSpawnError({
command: input.command,
argumentCount: input.args.length,
cause: PlatformError.systemError({
_tag: "PermissionDenied",
module: "ChildProcess",
method: "spawn",
}),
}),
);
},
}),
validate: () => Effect.die("must not validate a failed install"),
}).pipe(Effect.flip);
assert.deepEqual(commands, ["npm"]);
}),
);

it.effect("validates a staging tree before atomically publishing it", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
Expand Down
24 changes: 23 additions & 1 deletion apps/server/src/cloud/pinnedRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as Duration from "effect/Duration";
import * as Effect from "effect/Effect";
import * as FileSystem from "effect/FileSystem";
import * as Path from "effect/Path";
import * as PlatformError from "effect/PlatformError";
import * as Schema from "effect/Schema";
import * as Option from "effect/Option";
import * as Semaphore from "effect/Semaphore";
Expand Down Expand Up @@ -152,14 +153,35 @@ const installPinnedRuntime = Effect.fn("cloud.pinned_runtime.ensure_installed")(

return yield* Effect.gen(function* () {
const installStep = "installing the pinned t3 runtime (this can take a few minutes)";
const installArgs = [
"install",
"--prefix",
stagingDir,
"--no-fund",
"--no-audit",
`t3@${input.version}`,
];
yield* runner
.run({
command: "npm",
args: ["install", "--prefix", stagingDir, "--no-fund", "--no-audit", `t3@${input.version}`],
args: installArgs,
// Native dependencies may compile from source on slower machines.
timeout: PINNED_RUNTIME_INSTALL_TIMEOUT,
})
.pipe(
Effect.catchTags({
ProcessSpawnError: (error) =>
error.cause instanceof PlatformError.PlatformError &&
error.cause.reason._tag === "NotFound"
? // pnpm-managed Node installations do not include npm. Keep npm
// installation semantics for the pinned runtime and native builds.
runner.run({
command: "pnpm",
args: ["--package=npm@11", "dlx", "npm", ...installArgs],
timeout: PINNED_RUNTIME_INSTALL_TIMEOUT,
})
: Effect.fail(error),
}),
Effect.mapError((cause) => new PinnedRuntimeInstallError({ step: installStep, cause })),
Effect.filterOrFail(
(result) => result.code === 0,
Expand Down
Loading