Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,16 @@ jobs:
- name: Check code quality
run: pnpm run check:all

# Advisory only (base-vs-head diff, no acceptance artifact to gate a
# required check on) — the hard release-time gate is tracked under
# CLI-2233. `continue-on-error` flags the diff without failing the job;
# the tool's own fetch/unshallow fallback resolves a merge-base from
# this checkout's shallow clone, and skips the compare (exit 0) rather
# than failing when history still can't be resolved.
- name: config type-surface diff (advisory)
continue-on-error: true
run: pnpm run check:config-api

test-unit:
if: |
!startsWith(github.head_ref, 'release-notes/') &&
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Expected exceptions:

Use the `Cli*` prefix for the local checkout side and a bare `Project*` name for the hosted
Supabase project. Config-value helpers follow the config family regardless of their inputs (e.g.
`resolveCliConfigValue`, `MissingCliConfigValueError`). A symbol that deliberately spans both
`resolveCliConfigValue`, `CliConfigParseError`). A symbol that deliberately spans both
families takes a family-neutral name instead of a misleading prefix (see the ADR 0020 addendum for
the `EffectiveConfig` precedent).

Expand Down
1 change: 1 addition & 0 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"smol-toml": "^1.8.0",
"tldts": "catalog:",
"typescript": "catalog:",
"vite": "^6.0.0 || ^7.0.0 || ^8.0.0",
"vitest": "catalog:",
"yaml": "^2.9.0"
},
Expand Down
35 changes: 23 additions & 12 deletions apps/cli/scripts/generate-docs.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { mkdirSync, writeFileSync } from "node:fs";
import { copyFileSync, mkdirSync, writeFileSync } from "node:fs";
import path from "node:path";
import process from "node:process";
import { CLI_CONFIG_SCHEMA_URL, toCliConfigJsonSchema } from "@supabase/config";
import { CLI_CONFIG_SCHEMA_URL, PROJECT_CONFIG_SCHEMA_URL } from "@supabase/config";
import { nextRoot } from "../src/next/cli/root.ts";
import { collectCommands, getHelpDoc } from "../src/next/docs/command-docs.ts";
import { formatHelpDocAsMarkdown } from "../src/next/docs/markdown-formatter.ts";

const BINARY_NAME = "supabase";
const defaultContentDir = path.resolve(import.meta.dir, "../../../apps/docs/content/docs/commands");
const defaultDocsPublicDir = path.resolve(import.meta.dir, "../../../apps/docs/public");
const configPackageDistDir = path.resolve(import.meta.dir, "../../../packages/config/dist");
const contentDir = process.argv[2]
? path.resolve(process.cwd(), process.argv[2])
: defaultContentDir;
Expand Down Expand Up @@ -72,16 +73,26 @@ function generateCommandDocs() {
console.log(`\nGenerated ${pages.length} command page(s)`);
}

function generateConfigSchemaAsset() {
const schema = toCliConfigJsonSchema();
const schemaPathname = new URL(CLI_CONFIG_SCHEMA_URL).pathname.replace(/^\/docs/, "");
const filePath = path.join(defaultDocsPublicDir, schemaPathname);

mkdirSync(path.dirname(filePath), { recursive: true });
writeFileSync(filePath, `${JSON.stringify(schema, null, 2)}\n`);

console.log(`Generated: ${path.relative(path.resolve(import.meta.dir, "../../.."), filePath)}`);
/**
* Copies `@supabase/config`'s already post-processed (metadata + number-
* union-collapsed) `dist/*.json` schema artifact straight to its docs-site
* public path, rather than re-rendering `toCliConfigJsonSchema()`/
* `toProjectConfigJsonSchema()` here (CLI-2234) — re-rendering would bypass
* `json-schema-postprocess.ts` and produce a document whose `$id` doesn't
* match what actually gets published. Requires `@supabase/config#build` to
* have already run (wired via `turbo.json`).
*/
function copyConfigSchemaAsset(schemaUrl: string, distFileName: string) {
const schemaPathname = new URL(schemaUrl).pathname.replace(/^\/docs/, "");
const destPath = path.join(defaultDocsPublicDir, schemaPathname);
const sourcePath = path.join(configPackageDistDir, distFileName);

mkdirSync(path.dirname(destPath), { recursive: true });
copyFileSync(sourcePath, destPath);

console.log(`Generated: ${path.relative(path.resolve(import.meta.dir, "../../.."), destPath)}`);
}

generateCommandDocs();
generateConfigSchemaAsset();
copyConfigSchemaAsset(CLI_CONFIG_SCHEMA_URL, "schema.json");
copyConfigSchemaAsset(PROJECT_CONFIG_SCHEMA_URL, "project-schema.json");
3 changes: 2 additions & 1 deletion apps/cli/src/legacy/commands/config/push/push.handler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { dirname } from "node:path";
import { findCliProjectRoot, loadCliConfig } from "@supabase/config/effect";
import { findCliProjectRoot } from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/internal";
import { Effect, FileSystem, Path } from "effect";

import { LegacyPlatformApi } from "../../../auth/legacy-platform-api.service.ts";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadCliConfig } from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/internal";
import { defaultPublishableKey } from "@supabase/stack/effect";
import { Effect, FileSystem, Option, Path } from "effect";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { loadCliConfig, loadCliProjectEnvironment } from "@supabase/config/effect";
import { loadCliProjectEnvironment } from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/internal";
import { Effect, FileSystem, Option, Path } from "effect";
import { legacyAssertDecodableJwkAlgorithm } from "../../shared/legacy-go-jwt.ts";
import { legacyGoJsonKindName } from "../../shared/legacy-go-json.ts";
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/legacy/commands/gen/types/types.handler.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { loadCliConfig } from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/internal";
import { ChildProcessSpawner } from "effect/unstable/process";
import { Effect, FileSystem, Option, Path, Stdio, Stream } from "effect";
import {
Expand Down
3 changes: 1 addition & 2 deletions apps/cli/src/legacy/commands/secrets/set/set.handler.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import {
loadCliConfig,
loadCliProjectEnvironment,
CliConfigSchema,
resolveCliConfigSubtree,
type CliConfig,
type CliConfigParseError,
} from "@supabase/config/effect";
import { loadCliConfig, resolveCliConfigSubtree } from "@supabase/config/internal";
import { V1BulkCreateSecretsInput } from "@supabase/api/effect";
import { parse as parseDotenv } from "dotenv";
import { Effect, FileSystem, Option, Path, Redacted, Schema } from "effect";
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/legacy/commands/start/start.handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
* Native TS implementation of `start` — see `SIDE_EFFECTS.md` for the full
* behavior contract.
*/
import { inferFunctionsManifest, resolveCliConfigSubtree } from "@supabase/config/effect";
import { inferFunctionsManifest } from "@supabase/config/effect";
import { resolveCliConfigSubtree } from "@supabase/config/internal";
import { Effect, FileSystem, Option, Path, Result } from "effect";
import { FetchHttpClient } from "effect/unstable/http";
import { ChildProcessSpawner } from "effect/unstable/process";
Expand Down
10 changes: 3 additions & 7 deletions apps/cli/src/legacy/commands/storage/storage.frame.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {
loadCliConfig,
type LoadCliConfigOptions,
CliConfigSchema,
type CliConfig,
} from "@supabase/config/effect";
import { CliConfigSchema, type CliConfig } from "@supabase/config/effect";
import { loadCliConfig, type InternalLoadCliConfigOptions } from "@supabase/config/internal";
import { Effect, Schema } from "effect";
import { FetchHttpClient } from "effect/unstable/http";

Expand Down Expand Up @@ -49,7 +45,7 @@ export const legacyLoadStorageConfig = Effect.fnUntraced(function* (
workdir: string,
projectRef: string,
) {
const loadOptions: LoadCliConfigOptions =
const loadOptions: InternalLoadCliConfigOptions =
projectRef !== "" ? { projectRef, goViperCompat: true } : { goViperCompat: true };
const loaded = yield* loadCliConfig(workdir, loadOptions).pipe(
Effect.catchTag(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";

import { KONG_LOCAL_CA_CERT } from "./tls.ts";
import { KONG_LOCAL_CA_CERT } from "./kong-local-ca-cert.ts";

describe("KONG_LOCAL_CA_CERT", () => {
it("is a non-empty PEM certificate", () => {
Expand Down
3 changes: 2 additions & 1 deletion apps/cli/src/legacy/shared/legacy-local-config-values.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { readFileSync } from "node:fs";
import { basename } from "node:path";

import { ENV_CAPTURE_REGEX, type CliConfig } from "@supabase/config";
import type { CliConfig } from "@supabase/config";
import { ENV_CAPTURE_REGEX } from "@supabase/config/internal";
import { defaultJwtSecret, defaultPublishableKey, defaultSecretKey } from "@supabase/stack/effect";
import { Schema } from "effect";

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import {
loadCliConfig,
loadCliProjectEnvironment,
CliConfigSchema,
type LoadedCliConfig,
type CliConfig,
} from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/internal";
import { Effect, FileSystem, Path, Schema } from "effect";

import { LEGACY_BITBUCKET_CLONE_DIR_ENV_KEY } from "./legacy-bitbucket-pipeline.ts";
Expand Down
10 changes: 3 additions & 7 deletions apps/cli/src/legacy/shared/legacy-seed-buckets.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
import {
loadCliConfig,
type LoadCliConfigOptions,
type CliConfig,
CliConfigSchema,
} from "@supabase/config/effect";
import { type CliConfig, CliConfigSchema } from "@supabase/config/effect";
import { loadCliConfig, type InternalLoadCliConfigOptions } from "@supabase/config/internal";
import { Effect, FileSystem, Path, Schema } from "effect";
import { FetchHttpClient } from "effect/unstable/http";
import type { PlatformError } from "effect/PlatformError";
Expand Down Expand Up @@ -184,7 +180,7 @@ export const legacySeedBucketsRun = Effect.fnUntraced(function* (opts: {
// --linked. A parse failure aborts before any network call. Skipped entirely
// when the caller already supplied `resolvedConfig` — see that option's doc
// comment above.
const loadOptions: LoadCliConfigOptions =
const loadOptions: InternalLoadCliConfigOptions =
projectRef !== "" ? { projectRef, goViperCompat: true } : { goViperCompat: true };
const loaded =
opts.resolvedConfig !== undefined
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/src/legacy/shared/legacy-storage-credentials.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { KONG_LOCAL_CA_CERT } from "@supabase/config";
import { defaultJwtSecret, generateJwt } from "@supabase/stack/effect";
import { Effect, FileSystem, Path } from "effect";

Expand All @@ -7,6 +6,7 @@ import { LegacyCliSettings } from "../config/legacy-cli-settings.service.ts";
import { legacyResolveApiExternalUrl } from "./legacy-api-url.ts";
import { legacyMapTenantApiKeysError } from "./legacy-get-tenant-api-keys.ts";
import { legacyGetHostname } from "./legacy-hostname.ts";
import { KONG_LOCAL_CA_CERT } from "./kong-local-ca-cert.ts";
import { legacyExtractServiceKeys } from "./legacy-tenant-keys.ts";
import {
LegacyStorageApiKeysNetworkError,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import { V2GetProjectConfigOutput } from "@supabase/api/effect";
import { toProjectConfig, type ProjectConfigApiAttributes } from "@supabase/config";
import { toProjectConfig } from "@supabase/config";
import type { ProjectConfigApiAttributes } from "@supabase/config/internal";

/**
* Compile-time drift guards (CLI-2230 design requirement): `@supabase/config`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
projectConfigMappingRows,
unmappedSecretApiPaths,
type ProjectConfigMappingRow,
} from "@supabase/config";
} from "@supabase/config/internal";

/**
* Contract-derived auth guard (CLI-2230's residual review): closes two gaps
Expand All @@ -27,9 +27,10 @@ import {
* package must stay decoupled so it can publish to npm independently), so
* this guard lives in `apps/cli`, which can import both. It needs `@supabase/
* config`'s row data and orphan-secret list at runtime, which is why
* `projectConfigMappingRows`/`unmappedSecretApiPaths` are exported from the
* package root (`packages/config/src/index.ts`) — otherwise-internal registry
* data, exposed solely so this cross-package guard can walk it.
* `projectConfigMappingRows`/`unmappedSecretApiPaths` are exported from
* `@supabase/config/internal` (`packages/config/src/internal.ts`) —
* otherwise-internal registry data, exposed solely so this cross-package
* guard (and `apps/cli`'s own contract tests) can walk it.
*
* `V1GetAuthServiceConfigOutput` (not the v2 project-config resource) is the
* authority here: it is the generated schema whose field names are the real,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { describe, expect, it } from "vitest";
import { Schema } from "effect";
import { AUTH_HOOK_NAMES, CliConfigSchema, fromConfigDocument } from "@supabase/config";
import { CliConfigSchema, fromConfigDocument } from "@supabase/config";
import { AUTH_HOOK_NAMES } from "@supabase/config/internal";
import {
legacyPresenceIn,
type LegacyConfigPushPresence,
Expand Down
11 changes: 6 additions & 5 deletions apps/cli/src/shared/functions/functions-config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { basename } from "node:path";
import { Effect, type FileSystem, type Path } from "effect";
import { loadCliConfig, type LoadedCliConfig } from "@supabase/config/effect";
import type { LoadedCliConfig } from "@supabase/config/effect";
import { loadCliConfig } from "@supabase/config/effect";
import { normalizeProjectId } from "./functions-docker.ts";

/**
Expand Down Expand Up @@ -61,10 +62,10 @@ export const loadFunctionsCliConfig = Effect.fnUntraced(function* (input: {
readonly goConfigCompat: FunctionsGoConfigCompat | undefined;
}) {
if (input.goConfigCompat === undefined) {
const loaded = yield* loadCliConfig(input.projectRoot, {
...(input.projectRef === undefined ? {} : { projectRef: input.projectRef }),
goViperCompat: false,
});
const loaded = yield* loadCliConfig(
input.projectRoot,
input.projectRef === undefined ? {} : { projectRef: input.projectRef },
);
return {
loaded,
projectEnvValues: undefined,
Expand Down
8 changes: 5 additions & 3 deletions apps/cli/src/shared/functions/serve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@ import {
CliConfigSchema,
findCliProjectPaths,
inferFunctionsManifest,
loadCliConfig,
resolveCliConfigSubtree,
resolveCliConfigValue,
type CliConfig,
type CliProjectEnvironment,
type ResolvedCliConfigValue,
type ResolvedFunctionConfig as ManifestFunctionConfig,
} from "@supabase/config/effect";
import {
loadCliConfig,
resolveCliConfigSubtree,
resolveCliConfigValue,
} from "@supabase/config/internal";
import {
defaultJwtSecret,
defaultPublishableKey,
Expand Down
17 changes: 1 addition & 16 deletions apps/cli/src/shared/output/normalize-error.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { describe, expect, test } from "vitest";
import { Cause } from "effect";
import { CliError, Command } from "effect/unstable/cli";
import {
CliConfigParseError,
CliProjectEnvParseError,
MissingCliConfigValueError,
} from "@supabase/config";
import { CliConfigParseError, CliProjectEnvParseError } from "@supabase/config";
import { legacyBranchesCommand } from "../../legacy/commands/branches/branches.command.ts";
import { legacyNetworkRestrictionsCommand } from "../../legacy/commands/network-restrictions/network-restrictions.command.ts";
import { CliProjectHomeNotDirectoryError } from "../../next/config/cli-project-home.service.ts";
Expand Down Expand Up @@ -214,17 +210,6 @@ describe("normalizeCliError", () => {
});
});

test("MissingCliConfigValueError falls back to its bare tag as both code and message", () => {
const error = new MissingCliConfigValueError({
configPath: "project_id",
});

expect(normalizeCliError(error)).toEqual({
code: "MissingCliConfigValueError",
message: "MissingCliConfigValueError",
});
});

test("CliProjectHomeNotDirectoryError surfaces its tag as code with its own message", () => {
const error = new CliProjectHomeNotDirectoryError({
message: ".supabase could not be created: a file exists at that path",
Expand Down
1 change: 0 additions & 1 deletion apps/cli/src/shared/telemetry/error-actionability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -940,7 +940,6 @@ const externalActionabilityByTag: Record<string, ErrorActionabilityAdapter> = {
// @supabase/config
CliConfigParseError: () => actionability.invalidConfig,
CliProjectEnvParseError: () => actionability.invalidConfig,
MissingCliConfigValueError: () => actionability.invalidConfig,
DuplicateRemoteProjectIdError: () => actionability.invalidConfig,
InvalidRemoteProjectIdError: () => actionability.invalidConfig,
// A Management API project-config response that fails to map is a platform
Expand Down
21 changes: 21 additions & 0 deletions apps/cli/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,25 @@
{
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
// Lets `tsc` resolve `@supabase/config`'s exports-map `bun` condition
// straight to its `src/*.ts` sources (self-typed, no `.d.ts` needed)
// instead of `dist/*.d.ts`, which requires that package to be built
// first — see `packages/config/package.json`'s exports map and
// `packages/config/AGENTS.md`'s "Build" section (CLI-2234). This also
// affects any OTHER dependency whose own exports map declares a `bun`
// condition (e.g. `@supabase/pg-topo`) — see this package's AGENTS.md/PR
// notes for a known collision that surfaces there.
"customConditions": ["bun"],
// `@supabase/pg-topo` (external, from supabase/pg-toolbelt) also declares
// a `bun` exports condition, pointing at its UNBUILT `src/*.ts`, which
// carries type errors at 1.0.0-alpha.5 that its shipped `dist/*.d.ts`
// does not surface. `paths` wins over exports-condition resolution, so
// pin its types to the published declarations. Drop this once a fixed
// pg-topo release (>= 1.0.0-alpha.6) clears the pnpm minimumReleaseAge
// window and is bumped in this package.
"paths": {
"@supabase/pg-topo": ["./node_modules/@supabase/pg-topo/dist/index.d.ts"]
}
},
"exclude": ["supabase", "src/shared/workers/stacks"]
}
Loading
Loading