1// @effect-diagnostics nodeBuiltinImport:off - runs before `vp i`, so only Node built-ins exist.
2/**
3 * Worktree setup, run by the t3.json "Setup Worktree" action as
4 * `node scripts/setup-worktree.ts`. Plain Node keeps one command working in
5 * every shell T3 Code spawns (zsh, bash, fish, PowerShell): it installs
6 * dependencies, links the main checkout's gitignored env files into this
7 * worktree, then warms the web dependency cache.
8 */
9import * as NodeChildProcess from "node:child_process";
10import * as NodeFS from "node:fs";
11import * as NodePath from "node:path";
12
13const ENV_FILES = [".env", NodePath.join("infra", "relay", ".env")];
14
15const projectRoot = process.env.T3CODE_PROJECT_ROOT;
16if (!projectRoot) {
17 throw new Error("T3CODE_PROJECT_ROOT is not set. Run this through the t3.json setup action.");
18}
19const worktree = NodePath.dirname(import.meta.dirname);
20
21// `shell` resolves `vp` through PATH, including Windows command shims.
22// Setup has no one answering install prompts. Pass pnpm's confirmation flag
23// through vp and leave stdin closed so other prompts fail instead of hanging.
24const install = NodeChildProcess.spawnSync("vp i -- --yes", {
25 cwd: worktree,
26 shell: true,
27 stdio: ["ignore", "inherit", "inherit"],
28});
29if (install.status !== 0) process.exit(install.status ?? 1);
30
31// Env files live as real files in the main checkout; worktrees only get
32// symlinks to them. Only a symlink is ever replaced, so a real env file is
33// never deleted, including when this runs in the main checkout itself.
34for (const file of ENV_FILES) {
35 const source = NodePath.join(projectRoot, file);
36 const sourceStat = NodeFS.lstatSync(source, { throwIfNoEntry: false });
37 if (!sourceStat) continue;
38 if (!sourceStat.isFile()) {
39 process.stderr.write(`Skipping ${file}: ${source} is not a regular file.\n`);
40 continue;
41 }
42 const target = NodePath.join(worktree, file);
43 const existing = NodeFS.lstatSync(target, { throwIfNoEntry: false });
44 if (existing && !existing.isSymbolicLink()) continue;
45 if (existing) NodeFS.rmSync(target);
46 NodeFS.symlinkSync(source, target);
47}
48
49const warm = NodeChildProcess.spawnSync(
50 process.execPath,
51 [NodePath.join(worktree, "apps", "web", "scripts", "warm-dep-cache.ts")],
52 { cwd: worktree, stdio: "inherit" },
53);
54process.exit(warm.status ?? 1);