Skip to content
Open
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
22 changes: 20 additions & 2 deletions scripts/lib/dev-share.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,13 @@ const encode = (value: string) => Stream.make(new TextEncoder().encode(value));
* Answers `tailscale status --json` with a valid tailnet name, and lets each
* test set the outcome of the `off` (pre-clear) and `serve` calls separately —
* they are the same subcommand and are told apart by the trailing `off`.
* `onServeArgs` observes each non-status, non-off invocation.
*/
const spawnerLayer = (input: { readonly off?: CallResult; readonly serve?: CallResult }) =>
const spawnerLayer = (input: {
readonly off?: CallResult;
readonly serve?: CallResult;
readonly onServeArgs?: (args: ReadonlyArray<string>) => void;
}) =>
Layer.succeed(
ChildProcessSpawner.ChildProcessSpawner,
ChildProcessSpawner.make((command) => {
Expand All @@ -36,7 +41,7 @@ const spawnerLayer = (input: { readonly off?: CallResult; readonly serve?: CallR
? { exitCode: 0 }
: args.includes("off")
? (input.off ?? { exitCode: 0 })
: (input.serve ?? { exitCode: 0 });
: (input.onServeArgs?.(args), input.serve ?? { exitCode: 0 });

return Effect.succeed(
ChildProcessSpawner.makeHandle({
Expand Down Expand Up @@ -102,6 +107,19 @@ describe("shareDevServer", () => {
}),
);

it.effect("points the serve mapping at the host Vite actually bound", () =>
Effect.gen(function* () {
const serveArgs: ReadonlyArray<string>[] = [];
yield* shareDevServer({ webPort: 5788 }).pipe(
Effect.provide(spawnerLayer({ onServeArgs: (args) => serveArgs.push([...args]) })),
);

// Vite binds `localhost`, which resolves IPv6-first on some OSes; a
// hardcoded 127.0.0.1 target would answer 502 there.
assert.deepEqual(serveArgs, [["serve", "--bg", "--https=5788", "http://localhost:5788"]]);
}),
);

// The stale-mapping clear runs before serve, so a failure here leaves the
// port serving nothing. Saying only "serve failed" would let an operator
// assume their previous mapping survived.
Expand Down
11 changes: 10 additions & 1 deletion scripts/lib/dev-share.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,16 @@ export const shareDevServer = Effect.fn("devShare.shareDevServer")(function* (in
});
}

yield* ensureTailscaleServe({ localPort: input.webPort, servePort: input.webPort }).pipe(
// "localhost", not "127.0.0.1": Vite binds `host: "localhost"` (see
// apps/web/vite.config.ts), which resolves to whichever loopback family this
// OS prefers — ::1 on modern macOS. Naming the same host here lets
// tailscaled resolve it exactly like the dev server did, so the mapping and
// the listener always agree.
yield* ensureTailscaleServe({
localPort: input.webPort,
servePort: input.webPort,
localHost: "localhost",
}).pipe(
Effect.mapError((error) => {
const explanation = explainCommandFailure(error);
return new DevServeFailedError({
Expand Down
Loading