mirror of
https://github.com/webstudio-is/webstudio.git
synced 2026-10-27 23:27:09 +09:00
## Outcome Builder development, backend, E2E, and CI tooling now share one ownership model: Builder defines named commands and backend modes, while CI only schedules those commands. ## Technical choices - Group shared Compose files, PostgreSQL initialization, and backend helpers under `apps/builder/backend`. - Resolve `compose.<mode>.yaml` by convention instead of maintaining Compose filename lists in scripts and workflows. - Keep persistent development and disposable test Compose projects separate by default, while allowing the standard `COMPOSE_PROJECT_NAME` override. - Make schema snapshots invalidate from discovered migration sources and backend files. - Move E2E behavior recipes into named Builder commands; retain concise root proxies for developer entry points. - Let Vitest allocate available browser ports instead of maintaining a cross-package port registry. - Keep test-suite scheduling explicit in `main.yml`, but move executable test commands to root package scripts. - Move fixture-size enforcement from inline GitHub JavaScript into a locally runnable repository script. ## Todo - [x] Consolidate Builder backend and Compose ownership. - [x] Add named development, E2E, schema-cache, Supabase, and CI-shard commands. - [x] Remove Compose topology and behavior flags from CI. - [x] Remove the static Vitest browser-port registry. - [x] Extract fixture-size checking from the workflow. - [x] Verify migration bootstrap, snapshot-backed E2E, tests, builds, types, formatting, lint, generated contracts, and workflow syntax. - [x] Review documentation impact: updated the E2E developer guide; no user-facing `docs/` content is affected. ## Verification - Full package, CLI, Builder core, and Builder app test suites - Full workspace typecheck - Builder production build - Schema-cache generation through the complete migration history - Supabase migration compatibility - Snapshot-backed Playwright animation workflow - Backend regression tests, including demonstrated fail/pass coverage - All four Compose mode configurations - `oxfmt`, `oxlint`, package boundaries, generated API, and generated docs checks - `actionlint` and Bash syntax checks The fixture-size checker preserves the existing Linux `du` thresholds. Its three inputs build locally; macOS allocation sizes are not directly comparable to the Linux CI thresholds, so CI remains the authoritative size result.
126 lines
2.9 KiB
TypeScript
126 lines
2.9 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { promisify } from "node:util";
|
|
import { fileURLToPath } from "node:url";
|
|
import {
|
|
createClient,
|
|
type Database,
|
|
} from "@webstudio-is/postgrest/index.server";
|
|
import env from "../app/env/env.server";
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
|
|
const backendScript = fileURLToPath(
|
|
new URL("../backend/run.sh", import.meta.url)
|
|
);
|
|
|
|
const postgrest = createClient(env.POSTGREST_URL, env.POSTGREST_API_KEY);
|
|
|
|
const throwIfError = <Data>({
|
|
error,
|
|
data,
|
|
}: {
|
|
error: { message: string } | null;
|
|
data: Data;
|
|
}): NonNullable<Data> => {
|
|
if (error) {
|
|
throw new Error(error.message);
|
|
}
|
|
if (data === null || data === undefined) {
|
|
throw new Error("Expected PostgREST response data");
|
|
}
|
|
return data;
|
|
};
|
|
|
|
export const resetDatabase = async () => {
|
|
const sql = `
|
|
DO $$
|
|
DECLARE
|
|
table_names text;
|
|
BEGIN
|
|
SELECT string_agg(format('%I.%I', schemaname, tablename), ', ')
|
|
INTO table_names
|
|
FROM pg_tables
|
|
WHERE schemaname = 'public'
|
|
AND tablename <> '_prisma_migrations';
|
|
|
|
IF table_names IS NOT NULL THEN
|
|
EXECUTE 'TRUNCATE TABLE ' || table_names || ' RESTART IDENTITY CASCADE';
|
|
END IF;
|
|
END $$;
|
|
`;
|
|
|
|
await execFileAsync(
|
|
"bash",
|
|
[
|
|
backendScript,
|
|
process.env.E2E_BACKEND_MODE ?? "test",
|
|
"exec",
|
|
"-T",
|
|
"db",
|
|
"sh",
|
|
"-c",
|
|
'PGPASSWORD="$POSTGRES_PASSWORD" psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -v ON_ERROR_STOP=1 -c "$1"',
|
|
"sh",
|
|
sql,
|
|
],
|
|
{ maxBuffer: 1024 * 1024 }
|
|
);
|
|
};
|
|
|
|
type Tables = Database["public"]["Tables"];
|
|
|
|
export const updateBuild = async (
|
|
id: string,
|
|
row: Tables["Build"]["Update"]
|
|
) => {
|
|
return throwIfError(
|
|
await postgrest.from("Build").update(row).eq("id", id).select().single()
|
|
);
|
|
};
|
|
|
|
export const updateProject = async (
|
|
id: string,
|
|
row: Tables["Project"]["Update"]
|
|
) => {
|
|
return throwIfError(
|
|
await postgrest.from("Project").update(row).eq("id", id).select().single()
|
|
);
|
|
};
|
|
|
|
export const insertAuthorizationToken = async (
|
|
row: Tables["AuthorizationToken"]["Insert"]
|
|
) => {
|
|
return throwIfError(
|
|
await postgrest.from("AuthorizationToken").insert(row).select().single()
|
|
);
|
|
};
|
|
|
|
export const insertFile = async (row: Tables["File"]["Insert"]) => {
|
|
return throwIfError(
|
|
await postgrest.from("File").insert(row).select().single()
|
|
);
|
|
};
|
|
|
|
export const insertAsset = async (row: Tables["Asset"]["Insert"]) => {
|
|
return throwIfError(
|
|
await postgrest.from("Asset").insert(row).select().single()
|
|
);
|
|
};
|
|
|
|
export const loadDevBuild = async ({
|
|
projectId,
|
|
}: {
|
|
projectId: string;
|
|
}): Promise<Database["public"]["Tables"]["Build"]["Row"]> => {
|
|
return throwIfError(
|
|
await postgrest
|
|
.from("Build")
|
|
.select("*")
|
|
.eq("projectId", projectId)
|
|
.is("deployment", null)
|
|
.order("createdAt", { ascending: false })
|
|
.limit(1)
|
|
.single()
|
|
);
|
|
};
|