()
| 218 | // --- commands ---------------------------------------------------------------- |
| 219 | |
| 220 | const deploy = async (): Promise<void> => { |
| 221 | const pr = prNumber(); |
| 222 | const worker = workerName(pr); |
| 223 | const teamDomain = process.env.PREVIEW_ACCESS_TEAM_DOMAIN ?? ""; |
| 224 | const allowedEmails = (process.env.PREVIEW_ACCESS_EMAILS ?? "") |
| 225 | .split(",") |
| 226 | .map((email) => email.trim()) |
| 227 | .filter((email) => email.length > 0); |
| 228 | if (teamDomain.length === 0) fail("PREVIEW_ACCESS_TEAM_DOMAIN is required"); |
| 229 | if (allowedEmails.length === 0) fail("PREVIEW_ACCESS_EMAILS is required"); |
| 230 | const adminEmails = process.env.PREVIEW_ADMIN_EMAILS ?? allowedEmails[0]!; |
| 231 | |
| 232 | const databaseId = await ensureD1(worker); |
| 233 | await ensureSharedBucket(); |
| 234 | const subdomain = await workersSubdomain(); |
| 235 | const hostname = `${worker}.${subdomain}.workers.dev`; |
| 236 | const app = await ensureAccessApp(worker, hostname, allowedEmails); |
| 237 | const configPath = writePreviewConfig(worker, worker, databaseId); |
| 238 | |
| 239 | // turbo so workspace dependencies with build steps (@executor-js/vite-plugin) |
| 240 | // are built first — a fresh checkout has no dist/ anywhere. |
| 241 | if (!process.argv.includes("--skip-build")) { |
| 242 | run("bunx", ["turbo", "build", "--filter=@executor-js/host-cloudflare"], { |
| 243 | cwd: resolve(APP_DIR, "../.."), |
| 244 | }); |
| 245 | } |
| 246 | run("bunx", [ |
| 247 | "wrangler", |
| 248 | "deploy", |
| 249 | "--config", |
| 250 | configPath, |
| 251 | "--var", |
| 252 | `ACCESS_TEAM_DOMAIN:${teamDomain}`, |
| 253 | "--var", |
| 254 | `ACCESS_AUD:${app.aud}`, |
| 255 | "--var", |
| 256 | `ADMIN_EMAILS:${adminEmails}`, |
| 257 | ]); |
| 258 | |
| 259 | // The at-rest encryption key: stable across redeploys of the same PR (a new |
| 260 | // key would orphan secrets already stored in the preview's D1), fresh per |
| 261 | // preview, set after deploy because the Worker must exist first. |
| 262 | const secretList = spawnSync("bunx", ["wrangler", "secret", "list", "--config", configPath], { |
| 263 | cwd: APP_DIR, |
| 264 | encoding: "utf8", |
| 265 | env: process.env, |
| 266 | }); |
| 267 | if (!(secretList.stdout ?? "").includes("EXECUTOR_SECRET_KEY")) { |
| 268 | run("bunx", ["wrangler", "secret", "put", "EXECUTOR_SECRET_KEY", "--config", configPath], { |
| 269 | input: randomBytes(32).toString("hex"), |
| 270 | }); |
| 271 | } |
| 272 | |
| 273 | const url = `https://${hostname}`; |
| 274 | process.stderr.write(`preview live at ${url}\n`); |
| 275 | process.stdout.write(`${JSON.stringify({ pr, worker, url, mcpUrl: `${url}/mcp` })}\n`); |
| 276 | if (process.env.GITHUB_OUTPUT) { |
| 277 | appendFileSync(process.env.GITHUB_OUTPUT, `url=${url}\nworker=${worker}\n`); |
no test coverage detected