Skip to content
Closed
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
12 changes: 9 additions & 3 deletions apps/server/src/cloud/bootService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -389,13 +389,18 @@ export class BootServiceCommandError extends Schema.TaggedError<BootServiceComma
exitCode: Schema.optional(Schema.Number),
stdoutLength: Schema.optional(Schema.Number),
stderrLength: Schema.optional(Schema.Number),
outputTail: Schema.optional(Schema.String),
cause: Schema.optional(Schema.Defect()),
},
) {
override get message(): string {
return this.exitCode === undefined
? `Background setup failed while ${this.step}.`
: `Background setup failed while ${this.step} (exit code ${this.exitCode}).`;
const base =
this.exitCode === undefined
? `Background setup failed while ${this.step}.`
: `Background setup failed while ${this.step} (exit code ${this.exitCode}).`;
return this.outputTail === undefined || this.outputTail.length === 0
? base
: `${base}\n${this.outputTail}`;
}
}

Expand Down Expand Up @@ -745,6 +750,7 @@ export const make = Effect.fn("cloud.boot_service.make")(function* (input: {
exitCode: error.exitCode,
stdoutLength: error.stdoutLength,
stderrLength: error.stderrLength,
outputTail: error.outputTail,
cause: error,
})
: new BootServiceInstallError({ cause: error }),
Expand Down
147 changes: 134 additions & 13 deletions apps/server/src/cloud/pinnedRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,30 +15,149 @@ import {
PinnedRuntimeInstallError,
} from "./pinnedRuntime.ts";

const effectOverridesJson = JSON.stringify({
effect: "4.0.0-rc.112",
"@effect/platform-node": "4.0.0-rc.112",
"@effect/platform-node-shared": "4.0.0-rc.112",
"@effect/vitest>vitest": "-",
vite: "npm:@voidzero-dev/vite-plus-core@0.3.0",
});

const okResult = (stdout = "", stderr = "") => ({
stdout,
stderr,
code: ChildProcessSpawner.ExitCode(0),
timedOut: false,
stdoutTruncated: false,
stderrTruncated: false,
stdoutInvalidUtf8: false,
stderrInvalidUtf8: false,
});

const failedResult = (stderr: string, stdout = "") => ({
...okResult(stdout, stderr),
code: ChildProcessSpawner.ExitCode(1),
});

const isNpmView = (input: ProcessRunner.ProcessRunInput) =>
input.args[0] === "view" || (input.command === "pnpm" && input.args.includes("view"));

const isNpmInstall = (input: ProcessRunner.ProcessRunInput) =>
input.args[0] === "install" || (input.command === "pnpm" && input.args.includes("install"));

const stagingPrefix = (input: ProcessRunner.ProcessRunInput) => {
const prefixIndex = input.args.indexOf("--prefix");
return input.args[prefixIndex + 1];
};

const successfulRunner = (fs: FileSystem.FileSystem, path: Path.Path) =>
ProcessRunner.ProcessRunner.of({
run: (input) =>
Effect.gen(function* () {
const prefixIndex = input.args.indexOf("--prefix");
const stagingDir = input.args[prefixIndex + 1];
if (isNpmView(input)) {
return okResult(effectOverridesJson);
}
if (!isNpmInstall(input)) {
return yield* Effect.die(`unexpected command: ${input.command} ${input.args.join(" ")}`);
}
const stagingDir = stagingPrefix(input);
if (stagingDir === undefined) return yield* Effect.die("missing npm --prefix");
assert.isUndefined(
input.args.find((arg) => arg.startsWith("t3@")),
"install must use the staging manifest, not a positional t3@version",
);
const entry = path.join(stagingDir, "node_modules", "t3", "dist", "bin.mjs");
yield* fs.makeDirectory(path.dirname(entry), { recursive: true }).pipe(Effect.orDie);
yield* fs.writeFileString(entry, "export {};\n").pipe(Effect.orDie);
return {
stdout: "",
stderr: "",
code: ChildProcessSpawner.ExitCode(0),
timedOut: false,
stdoutTruncated: false,
stderrTruncated: false,
stdoutInvalidUtf8: false,
stderrInvalidUtf8: false,
};
return okResult();
}),
});

it.layer(NodeServices.layer)("ensurePinnedRuntimeInstalled", (it) => {
it.effect("writes Effect overrides into the staging manifest before install", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-pinned-manifest-" });
const commands: Array<ProcessRunner.ProcessRunInput> = [];
let manifestBeforeInstall: unknown;

yield* ensurePinnedRuntimeInstalled({
baseDir,
version: "1.2.3",
fs,
path,
runner: ProcessRunner.ProcessRunner.of({
run: (input) =>
Effect.gen(function* () {
commands.push(input);
if (isNpmView(input)) {
assert.deepEqual(input.args, ["view", "t3@1.2.3", "overrides", "--json"]);
return okResult(effectOverridesJson);
}
const stagingDir = stagingPrefix(input);
if (stagingDir === undefined) return yield* Effect.die("missing npm --prefix");
manifestBeforeInstall = JSON.parse(
yield* fs.readFileString(path.join(stagingDir, "package.json")).pipe(Effect.orDie),
);
return yield* successfulRunner(fs, path).run(input);
}),
}),
validate: () => Effect.void,
});

assert.equal(commands[0]?.args[0], "view");
assert.equal(commands[1]?.args[0], "install");
assert.deepEqual(commands[1]?.args, [
"install",
"--prefix",
stagingPrefix(commands[1]!),
"--no-fund",
"--no-audit",
]);
assert.deepEqual(manifestBeforeInstall, {
dependencies: { t3: "1.2.3" },
overrides: {
effect: "4.0.0-rc.112",
"@effect/platform-node": "4.0.0-rc.112",
"@effect/platform-node-shared": "4.0.0-rc.112",
},
});
}),
);

it.effect("surfaces a truncated npm stderr tail when install fails", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
const path = yield* Path.Path;
const baseDir = yield* fs.makeTempDirectoryScoped({ prefix: "t3-pinned-stderr-" });
// npm puts the actionable failure near the end of stderr; the bounded
// tail must keep that reason when earlier noise is large.
const stderr = `${"x".repeat(2500)}\nnpm error code ERESOLVE\nnpm error Could not resolve dependency\n`;

const error = yield* ensurePinnedRuntimeInstalled({
baseDir,
version: "1.2.3",
fs,
path,
runner: ProcessRunner.ProcessRunner.of({
run: (input) =>
isNpmView(input)
? Effect.succeed(okResult(effectOverridesJson))
: Effect.succeed(failedResult(stderr)),
}),
validate: () => Effect.die("must not validate a failed install"),
}).pipe(Effect.flip);

assert.equal(error._tag, "PinnedRuntimeInstallError");
assert.equal(error.exitCode, 1);
assert.isTrue(error.message.includes("exit code 1"));
assert.isTrue(error.outputTail !== undefined && error.outputTail.includes("ERESOLVE"));
assert.equal(error.outputTail?.length, 2048);
assert.isTrue(error.message.endsWith(error.outputTail!));
}),
);

it.effect("installs through pnpm when its Node runtime has no npm executable", () =>
Effect.gen(function* () {
const fs = yield* FileSystem.FileSystem;
Expand Down Expand Up @@ -77,9 +196,10 @@ it.layer(NodeServices.layer)("ensurePinnedRuntimeInstalled", (it) => {
});
assert.deepEqual(
commands.map((command) => command.command),
["npm", "pnpm"],
["npm", "pnpm", "npm", "pnpm"],
);
assert.deepEqual(commands[1]!.args, ["--package=npm@11", "dlx", "npm", ...commands[0]!.args]);
assert.deepEqual(commands[3]!.args, ["--package=npm@11", "dlx", "npm", ...commands[2]!.args]);
assert.equal(yield* fs.readFileString(paths.sentinelPath), "1.2.3\n");
}),
);
Expand Down Expand Up @@ -113,6 +233,7 @@ it.layer(NodeServices.layer)("ensurePinnedRuntimeInstalled", (it) => {
}),
validate: () => Effect.die("must not validate a failed install"),
}).pipe(Effect.flip);
// Fails on the first npm (view) without falling back to pnpm.
assert.deepEqual(commands, ["npm"]);
}),
);
Expand Down
Loading
Loading