1/**
2 * Point the floci-backed suites at a locally built emulator when one exists.
3 *
4 * `pnpm floci:build` tags the `submodules/floci` checkout as `floci:dev`;
5 * that is how an emulator patch gets exercised before its release image is
6 * published to GHCR. When no such image exists the env is left unset so the
7 * floci package resolves the pinned release image — hard-defaulting to a
8 * missing ref would fail every suite's `docker run` before a test can run.
9 *
10 * Must run before any child test process is spawned and before any in-process
11 * `ensureFloci`: `ensureFloci` reads `ALCHEMY_FLOCI_IMAGE` from `process.env`
12 * (here and in every child, which inherits it), and a container running a
13 * different image than the resolved one is recreated.
14 */
15import * as Config from "effect/Config";
16import * as Console from "effect/Console";
17import * as Effect from "effect/Effect";
18import { ChildProcess } from "effect/process";
19import { ChildProcessSpawner } from "effect/process/ChildProcessSpawner";
20
21export const preferLocalFlociImage = Effect.fn(function* (label: string) {
22 const configured = yield* Config.String("ALCHEMY_FLOCI_IMAGE").pipe(Config.withDefault(""));
23 if (configured !== "") return;
24 const spawner = yield* ChildProcessSpawner;
25 const exitCode = yield* spawner
26 .exitCode(
27 ChildProcess.make("docker", ["image", "inspect", "floci:dev"], {
28 stdin: "ignore",
29 stdout: "ignore",
30 stderr: "ignore",
31 }),
32 )
33 // No docker binary behaves like a missing image.
34 .pipe(Effect.orElseSucceed(() => 1));
35 if (exitCode === 0) {
36 yield* Effect.sync(() => {
37 process.env.ALCHEMY_FLOCI_IMAGE = "floci:dev";
38 });
39 yield* Console.log(`${label}: using locally built floci:dev image`);
40 } else {
41 yield* Console.log(
42 `${label}: no local floci:dev image (pnpm floci:build) — using the pinned release image`,
43 );
44 }
45});