67 lines · 3.0 KB · TypeScript
RawDownload
// The PRODUCTION self-host artifact as a target: the Docker image from// apps/host-selfhost/Dockerfile (production Vite build, `bun src/serve.ts`,// /data volume) instead of the dev server. Same surface as the selfhost// target — same bootstrap admin, same Better Auth sign-in, same MCP consent —// so the whole scenario suite runs against what users actually deploy. Boot// lives in setup/selfhost-docker.globalsetup.ts.import { Effect } from "effect"; import { e2ePort } from "../src/ports";import type { Identity, Target } from "../src/target";import { runSelfhostContainer, stopSelfhostContainer } from "../setup/selfhost-docker.boot";import { forcedMcpConsent, SELFHOST_ADMIN, signInSession } from "./selfhost"; export const SELFHOST_DOCKER_PORT = e2ePort("E2E_SELFHOST_DOCKER_PORT", 5);export const SELFHOST_DOCKER_BASE_URL =  process.env.E2E_SELFHOST_DOCKER_URL ?? `http://localhost:${SELFHOST_DOCKER_PORT}`; export const selfhostDockerTarget = (): Target => ({  name: "selfhost-docker",  baseUrl: SELFHOST_DOCKER_BASE_URL,  mcpUrl: `${SELFHOST_DOCKER_BASE_URL}/mcp`,  capabilities: new Set(["api", "browser", "mcp-oauth"]),  newIdentity: () =>    Effect.promise(async (): Promise<Identity> => {      const { cookieHeader, cookies } = await signInSession(        SELFHOST_DOCKER_BASE_URL,        SELFHOST_ADMIN,      );      return {        label: SELFHOST_ADMIN.email,        credentials: SELFHOST_ADMIN,        headers: { cookie: cookieHeader },        cookies,      };    }),  // Same forced-consent completion as the selfhost dev target: the app forces  // `prompt=consent` on every MCP authorize, so authorize redirects to the  // relative `/mcp-consent?consent_code=...` screen instead of straight to the  // callback — mcporter's cookieConsentStrategy can't parse that redirect.  mcpConsent: (identity: Identity) =>    forcedMcpConsent(SELFHOST_DOCKER_BASE_URL, {      email: identity.credentials?.email ?? SELFHOST_ADMIN.email,      password: identity.credentials?.password || SELFHOST_ADMIN.password,    }),  // A real deployment cycle, not a warm `docker restart`: graceful stop  // (SIGTERM + docker's grace), remove the container, start a NEW one from  // the same image against the same data volume — what an upgrade, reboot,  // or `compose down && up` does to a user's instance. Only when this suite  // owns the container (attach mode can't assume the instance is docker).  ...(process.env.E2E_SELFHOST_DOCKER_URL    ? {}    : {        restart: () =>          Effect.promise(async () => {            // Published by the globalsetup after it resolved/built the image.            const image = process.env.E2E_SELFHOST_DOCKER_RESOLVED_IMAGE;            if (!image) throw new Error("selfhost-docker: no resolved image — boot ran?");            await stopSelfhostContainer(SELFHOST_DOCKER_PORT);            await runSelfhostContainer({              image,              port: SELFHOST_DOCKER_PORT,              webBaseUrl: SELFHOST_DOCKER_BASE_URL,              admin: SELFHOST_ADMIN,            });          }),      }),});