From 8ad83e86e9265e4738337f572098d555918cc2c5 Mon Sep 17 00:00:00 2001 From: avallete Date: Thu, 8 Oct 2026 11:27:52 +0200 Subject: [PATCH 1/2] fix(config): mirror new Management API postgres settings Add autovacuum_max_workers, autovacuum_work_mem, pg_net_batch_size and pg_stat_statements_max to the project-config API mirror as unmapped fields, matching their API-only siblings. Fold each workspace dependency's files into the cached types:check hash through a turbo transit task, so a sync PR that only touches packages/api re-runs dependents' type-level drift guards instead of replaying a cached pass. Co-Authored-By: Claude Opus 5.5 --- .../dependent-types-check-cache.unit.test.ts | 65 +++++++++++++++++++ .../src/project-config/api-attributes.ts | 4 ++ turbo.json | 7 +- 3 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 packages/api/scripts/dependent-types-check-cache.unit.test.ts diff --git a/packages/api/scripts/dependent-types-check-cache.unit.test.ts b/packages/api/scripts/dependent-types-check-cache.unit.test.ts new file mode 100644 index 0000000000..b512baec9f --- /dev/null +++ b/packages/api/scripts/dependent-types-check-cache.unit.test.ts @@ -0,0 +1,65 @@ +import { execFileSync } from "node:child_process"; +import { closeSync, mkdtempSync, openSync, readFileSync, rmSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { describe, expect, test } from "vitest"; + +// The scheduled API sync only rewrites this package, so a dependent's type-level drift guards +// (e.g. apps/cli's project-config mirror check) only run on the sync PR if turbo folds this +// package's files into the dependent's cached `types:check` hash, via the `transit` task chain. + +const scriptDir = path.dirname(fileURLToPath(import.meta.url)); +const packageDir = path.join(scriptDir, ".."); +const repoDir = path.join(packageDir, "..", ".."); +const turboBin = path.join(repoDir, "node_modules", ".bin", "turbo"); + +type DryRunTask = { readonly taskId: string; readonly dependencies: ReadonlyArray }; + +function dryRunTypesCheckForDependents(): ReadonlyArray { + // Redirects stdout to a file: Bun on Linux truncates a child's piped stdout at ~219 KB, and + // the dry-run JSON lists every hashed input file. + const tempDir = mkdtempSync(path.join(tmpdir(), "turbo-dry-run-")); + try { + const outputPath = path.join(tempDir, "dry-run.json"); + const fd = openSync(outputPath, "w"); + try { + execFileSync(turboBin, ["run", "types:check", "--filter=...@supabase/api", "--dry=json"], { + cwd: repoDir, + stdio: ["ignore", fd, "pipe"], + }); + } finally { + closeSync(fd); + } + return JSON.parse(readFileSync(outputPath, "utf8")).tasks; + } finally { + rmSync(tempDir, { recursive: true, force: true }); + } +} + +function reachableTaskIds(tasks: ReadonlyArray, from: string): ReadonlySet { + const byId = new Map(tasks.map((task) => [task.taskId, task])); + const seen = new Set(); + const pending = [from]; + while (pending.length > 0) { + const taskId = pending.pop(); + if (taskId === undefined || seen.has(taskId)) continue; + seen.add(taskId); + pending.push(...(byId.get(taskId)?.dependencies ?? [])); + } + return seen; +} + +describe("dependent types:check cache key", () => { + test("includes @supabase/api's files for every workspace that depends on it", () => { + const tasks = dryRunTypesCheckForDependents(); + const dependentTypesChecks = tasks + .map((task) => task.taskId) + .filter((taskId) => taskId.endsWith("#types:check") && !taskId.startsWith("@supabase/api#")); + + expect(dependentTypesChecks).toContain("supabase#types:check"); + for (const taskId of dependentTypesChecks) { + expect(reachableTaskIds(tasks, taskId), taskId).toContain("@supabase/api#transit"); + } + }, 60_000); +}); diff --git a/packages/config/src/project-config/api-attributes.ts b/packages/config/src/project-config/api-attributes.ts index 2a385e91cc..dca32a9ec1 100644 --- a/packages/config/src/project-config/api-attributes.ts +++ b/packages/config/src/project-config/api-attributes.ts @@ -12,6 +12,8 @@ import { Schema } from "effect"; const postgresSettingsAttributes = Schema.Struct({ effective_cache_size: Schema.optionalKey(Schema.String), logical_decoding_work_mem: Schema.optionalKey(Schema.String), + autovacuum_max_workers: Schema.optionalKey(Schema.Unknown), + autovacuum_work_mem: Schema.optionalKey(Schema.Unknown), log_autovacuum_min_duration: Schema.optionalKey(Schema.Unknown), log_checkpoints: Schema.optionalKey(Schema.Unknown), log_connections: Schema.optionalKey(Schema.Unknown), @@ -48,6 +50,8 @@ const postgresSettingsAttributes = Schema.Struct({ checkpoint_timeout: Schema.optionalKey(Schema.Unknown), hot_standby_feedback: Schema.optionalKey(Schema.Unknown), cron_log_statement: Schema.optionalKey(Schema.Unknown), + pg_net_batch_size: Schema.optionalKey(Schema.Unknown), + pg_stat_statements_max: Schema.optionalKey(Schema.Unknown), }); // `allowed_cidrs` is mapped (`filterCidrAddresses`, `./registry.ts`); the rest are unmapped. diff --git a/turbo.json b/turbo.json index 70cb4b17c8..b5406cad4e 100644 --- a/turbo.json +++ b/turbo.json @@ -4,8 +4,12 @@ "strictTaskEntrypointSelection": true }, "tasks": { + "transit": { + "dependsOn": ["^transit"] + }, "types:check": { - "cache": true + "cache": true, + "dependsOn": ["transit"] }, "lint:check": { "cache": false @@ -33,6 +37,7 @@ }, "@supabase/docs#types:check": { "cache": true, + "dependsOn": ["transit"], "outputs": [".source/**"] }, "test:unit:run": { From 1f5df49354e7e78249e74a1496c09865b6d47f9b Mon Sep 17 00:00:00 2001 From: avallete Date: Thu, 8 Oct 2026 11:32:34 +0200 Subject: [PATCH 2/2] test(config): cover @supabase/config in the dependent types:check cache guard Co-Authored-By: Claude Opus 5.5 --- .../dependent-types-check-cache.unit.test.ts | 36 +++++++++++-------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/packages/api/scripts/dependent-types-check-cache.unit.test.ts b/packages/api/scripts/dependent-types-check-cache.unit.test.ts index b512baec9f..f5d0b03a8e 100644 --- a/packages/api/scripts/dependent-types-check-cache.unit.test.ts +++ b/packages/api/scripts/dependent-types-check-cache.unit.test.ts @@ -5,9 +5,9 @@ import path from "node:path"; import { fileURLToPath } from "node:url"; import { describe, expect, test } from "vitest"; -// The scheduled API sync only rewrites this package, so a dependent's type-level drift guards -// (e.g. apps/cli's project-config mirror check) only run on the sync PR if turbo folds this -// package's files into the dependent's cached `types:check` hash, via the `transit` task chain. +// The scheduled API sync only rewrites this package, and the project-config mirror lives in +// @supabase/config, so apps/cli's type-level drift guards only run on a PR touching either if turbo +// folds that package's files into the dependent's cached `types:check` hash via `transit`. const scriptDir = path.dirname(fileURLToPath(import.meta.url)); const packageDir = path.join(scriptDir, ".."); @@ -16,7 +16,7 @@ const turboBin = path.join(repoDir, "node_modules", ".bin", "turbo"); type DryRunTask = { readonly taskId: string; readonly dependencies: ReadonlyArray }; -function dryRunTypesCheckForDependents(): ReadonlyArray { +function dryRunTypesCheckForDependents(packageName: string): ReadonlyArray { // Redirects stdout to a file: Bun on Linux truncates a child's piped stdout at ~219 KB, and // the dry-run JSON lists every hashed input file. const tempDir = mkdtempSync(path.join(tmpdir(), "turbo-dry-run-")); @@ -24,7 +24,7 @@ function dryRunTypesCheckForDependents(): ReadonlyArray { const outputPath = path.join(tempDir, "dry-run.json"); const fd = openSync(outputPath, "w"); try { - execFileSync(turboBin, ["run", "types:check", "--filter=...@supabase/api", "--dry=json"], { + execFileSync(turboBin, ["run", "types:check", `--filter=...${packageName}`, "--dry=json"], { cwd: repoDir, stdio: ["ignore", fd, "pipe"], }); @@ -51,15 +51,21 @@ function reachableTaskIds(tasks: ReadonlyArray, from: string): Reado } describe("dependent types:check cache key", () => { - test("includes @supabase/api's files for every workspace that depends on it", () => { - const tasks = dryRunTypesCheckForDependents(); - const dependentTypesChecks = tasks - .map((task) => task.taskId) - .filter((taskId) => taskId.endsWith("#types:check") && !taskId.startsWith("@supabase/api#")); + test.each(["@supabase/api", "@supabase/config"])( + "includes %s's files for every workspace that depends on it", + (packageName) => { + const tasks = dryRunTypesCheckForDependents(packageName); + const dependentTypesChecks = tasks + .map((task) => task.taskId) + .filter( + (taskId) => taskId.endsWith("#types:check") && !taskId.startsWith(`${packageName}#`), + ); - expect(dependentTypesChecks).toContain("supabase#types:check"); - for (const taskId of dependentTypesChecks) { - expect(reachableTaskIds(tasks, taskId), taskId).toContain("@supabase/api#transit"); - } - }, 60_000); + expect(dependentTypesChecks).toContain("supabase#types:check"); + for (const taskId of dependentTypesChecks) { + expect(reachableTaskIds(tasks, taskId), taskId).toContain(`${packageName}#transit`); + } + }, + 60_000, + ); });