| 137 | // --- up -------------------------------------------------------------------- |
| 138 | |
| 139 | const up = async (target: string, flags: ReadonlySet<string>) => { |
| 140 | if (target !== "selfhost" && target !== "cloud") { |
| 141 | throw new Error(`unknown target ${JSON.stringify(target)} — selfhost | cloud`); |
| 142 | } |
| 143 | const existing = readState(target); |
| 144 | if (existing && alive(existing.runnerPid) && existing.status !== "failed") { |
| 145 | console.log(`${target} already up (runner ${existing.runnerPid}):`); |
| 146 | printInstance(existing); |
| 147 | return; |
| 148 | } |
| 149 | rmSync(statePath(target), { force: true }); |
| 150 | |
| 151 | mkdirSync(devDir, { recursive: true }); |
| 152 | const logFile = join(devDir, `${target}.log`); |
| 153 | rmSync(logFile, { force: true }); |
| 154 | const runnerArgs = [cliPath, "__run", target, ...flags]; |
| 155 | const runner = spawn("bun", runnerArgs, { |
| 156 | cwd: e2eDir, |
| 157 | detached: true, |
| 158 | stdio: ["ignore", "ignore", "ignore"], |
| 159 | env: { ...process.env, E2E_CLI_LOG: logFile }, |
| 160 | }); |
| 161 | runner.unref(); |
| 162 | console.log(`booting ${target} (runner ${runner.pid}, log: ${logFile}) …`); |
| 163 | |
| 164 | const deadline = Date.now() + 240_000; |
| 165 | for (;;) { |
| 166 | const state = readState(target); |
| 167 | if (state?.status === "ready") { |
| 168 | printInstance(state); |
| 169 | return; |
| 170 | } |
| 171 | if (state?.status === "failed") { |
| 172 | throw new Error(`${target} boot failed: ${state.error}\n log: ${logFile}`); |
| 173 | } |
| 174 | if (runner.pid !== undefined && !alive(runner.pid)) { |
| 175 | throw new Error(`runner died during boot — log: ${logFile}`); |
| 176 | } |
| 177 | if (Date.now() > deadline) throw new Error(`timed out booting ${target} — log: ${logFile}`); |
| 178 | await new Promise((resolve) => setTimeout(resolve, 500)); |
| 179 | } |
| 180 | }; |
| 181 | |
| 182 | const printInstance = (state: InstanceState) => { |
| 183 | for (const [name, url] of Object.entries(state.urls ?? {})) { |