diff --git a/.github/workflows/automerge.yml b/.github/workflows/automerge.yml index 0089bccc5e..ad1da2f19b 100644 --- a/.github/workflows/automerge.yml +++ b/.github/workflows/automerge.yml @@ -23,7 +23,6 @@ jobs: - name: Generate token id: app-token - if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || (!startsWith(steps.meta.outputs.previous-version, '0.') && steps.meta.outputs.update-type == 'version-update:semver-minor') }} uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: client-id: ${{ vars.GH_APP_CLIENT_ID }} @@ -33,16 +32,14 @@ jobs: # Here the PR gets approved. - name: Approve a PR - if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || (!startsWith(steps.meta.outputs.previous-version, '0.') && steps.meta.outputs.update-type == 'version-update:semver-minor') }} run: gh pr review --approve "${GITHUB_EVENT_PULL_REQUEST_HTML_URL}" env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} GITHUB_EVENT_PULL_REQUEST_HTML_URL: ${{ github.event.pull_request.html_url }} - # Finally, this sets the PR to allow auto-merging for patch and minor - # updates if all checks pass + # Finally, this sets the PR to allow auto-merging once all required + # checks pass. - name: Enable auto-merge for Dependabot PRs - if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || (!startsWith(steps.meta.outputs.previous-version, '0.') && steps.meta.outputs.update-type == 'version-update:semver-minor') }} run: gh pr merge --auto --squash "${GITHUB_EVENT_PULL_REQUEST_HTML_URL}" env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} diff --git a/apps/cli-e2e/src/tests/stack.e2e.test.ts b/apps/cli-e2e/src/tests/stack.e2e.test.ts index 5ba87adf1d..c07d620b23 100644 --- a/apps/cli-e2e/src/tests/stack.e2e.test.ts +++ b/apps/cli-e2e/src/tests/stack.e2e.test.ts @@ -60,8 +60,8 @@ function testParityStack(cmd: string[], opts?: { workspaceSetup?: (dir: string) // --------------------------------------------------------------------------- // services // --------------------------------------------------------------------------- -// `services` reads service image names from config (not Docker) so DOCKER_HOST -// is not needed. +// `services` prints a baked-in Go-parity service matrix, so DOCKER_HOST is not +// needed. describe("services", () => { testBehaviour("lists known service images", async ({ run }) => { diff --git a/apps/cli-go/go.mod b/apps/cli-go/go.mod index 7c9ee6dff7..643c8caf86 100644 --- a/apps/cli-go/go.mod +++ b/apps/cli-go/go.mod @@ -42,7 +42,7 @@ require ( github.com/multigres/multigres v0.0.0-20260126223308-f5a52171bbc4 github.com/oapi-codegen/nullable v1.1.0 github.com/olekukonko/tablewriter v1.1.4 - github.com/posthog/posthog-go v1.13.0 + github.com/posthog/posthog-go v1.13.1 github.com/spf13/afero v1.15.0 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 diff --git a/apps/cli-go/go.sum b/apps/cli-go/go.sum index caa2f47bdb..9707d0e595 100644 --- a/apps/cli-go/go.sum +++ b/apps/cli-go/go.sum @@ -941,8 +941,8 @@ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRI github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polyfloyd/go-errorlint v1.8.0 h1:DL4RestQqRLr8U4LygLw8g2DX6RN1eBJOpa2mzsrl1Q= github.com/polyfloyd/go-errorlint v1.8.0/go.mod h1:G2W0Q5roxbLCt0ZQbdoxQxXktTjwNyDbEaj3n7jvl4s= -github.com/posthog/posthog-go v1.13.0 h1:+i+t6txCczJcGZj7ME2ry4sLhPYvq3q7RYuUZ0z6NpQ= -github.com/posthog/posthog-go v1.13.0/go.mod h1:xsVOW9YImilUcazwPNEq4PJDqEZf2KeCS758zXjwkPg= +github.com/posthog/posthog-go v1.13.1 h1:7OtfgOEM9fC2n4Hbs4e5mK1uaLuNguoYWFEI4kEnTUY= +github.com/posthog/posthog-go v1.13.1/go.mod h1:xsVOW9YImilUcazwPNEq4PJDqEZf2KeCS758zXjwkPg= github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U= github.com/prometheus/client_golang v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= diff --git a/apps/cli-go/internal/utils/connect.go b/apps/cli-go/internal/utils/connect.go index b9e2d39df9..b3c309721f 100644 --- a/apps/cli-go/internal/utils/connect.go +++ b/apps/cli-go/internal/utils/connect.go @@ -7,6 +7,7 @@ import ( "net" "net/url" "os" + "regexp" "strings" "time" @@ -173,6 +174,24 @@ func ConnectByUrl(ctx context.Context, url string, options ...func(*pgx.ConnConf const SuggestEnvVar = "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD" +// ipv6LiteralPattern matches a bracketed IPv6 address (e.g. [2406:da18:...]) as +// it appears in a Go dial error such as +// "dial tcp [2406:da18:...]:5432: connect: network is unreachable". +var ipv6LiteralPattern = regexp.MustCompile(`\[[0-9a-fA-F:]*:[0-9a-fA-F:]*\]`) + +// isIPv6ConnectivityError reports whether the connection failure stems from the +// host resolving to an IPv6 address that the current network cannot route to. +// Supabase direct database connections (db..supabase.co:5432) are +// IPv6-only unless the IPv4 add-on is enabled, so users on IPv4-only networks +// hit "network is unreachable" / "no route to host" against an IPv6 literal. +func isIPv6ConnectivityError(msg string) bool { + if !strings.Contains(msg, "network is unreachable") && + !strings.Contains(msg, "no route to host") { + return false + } + return ipv6LiteralPattern.MatchString(msg) +} + // Sets CmdSuggestion to an actionable hint based on the given pg connection error. func SetConnectSuggestion(err error) { if err == nil { @@ -190,6 +209,11 @@ func SetConnectSuggestion(err error) { } else if strings.Contains(msg, "SCRAM exchange: Wrong password") || strings.Contains(msg, "failed SASL auth") { // password authentication failed for user / invalid SCRAM server-final-message received from server CmdSuggestion = SuggestEnvVar + } else if isIPv6ConnectivityError(msg) { + CmdSuggestion = fmt.Sprintf( + "Your network does not support IPv6, which is required for direct connections to the database.\nRun %s to connect through the IPv4 connection pooler instead.", + Aqua("supabase link"), + ) } else if strings.Contains(msg, "connect: no route to host") || strings.Contains(msg, "Tenant or user not found") { // Assumes IPv6 check has been performed before this CmdSuggestion = "Make sure your project exists on profile: " + CurrentProfile.Name diff --git a/apps/cli-go/internal/utils/connect_test.go b/apps/cli-go/internal/utils/connect_test.go index 55a81c0ca1..e5dcbcaf44 100644 --- a/apps/cli-go/internal/utils/connect_test.go +++ b/apps/cli-go/internal/utils/connect_test.go @@ -221,7 +221,17 @@ func TestSetConnectSuggestion(t *testing.T) { suggestion: "Connect to your database by setting the env var correctly: SUPABASE_DB_PASSWORD", }, { - name: "no route to host", + name: "ipv6 no route to host", + err: errors.New("dial tcp [2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432: connect: no route to host"), + suggestion: "Your network does not support IPv6", + }, + { + name: "ipv6 network is unreachable", + err: errors.New("dial tcp [2406:da18:4fd:9b0d:80ec:9812:3e65:450b]:5432: connect: network is unreachable"), + suggestion: "Your network does not support IPv6", + }, + { + name: "no route to host without ipv6 address", err: errors.New("connect: no route to host"), suggestion: "Make sure your project exists on profile: " + CurrentProfile.Name, }, diff --git a/apps/cli-go/pkg/config/templates/Dockerfile b/apps/cli-go/pkg/config/templates/Dockerfile index 753d10ee35..76ba7210ea 100644 --- a/apps/cli-go/pkg/config/templates/Dockerfile +++ b/apps/cli-go/pkg/config/templates/Dockerfile @@ -3,7 +3,7 @@ FROM supabase/postgres:17.6.1.132 AS pg # Append to ServiceImages when adding new dependencies below FROM library/kong:2.8.1 AS kong FROM axllent/mailpit:v1.22.3 AS mailpit -FROM postgrest/postgrest:v14.12 AS postgrest +FROM postgrest/postgrest:v14.13 AS postgrest FROM supabase/postgres-meta:v0.96.6 AS pgmeta FROM supabase/studio:2026.06.03-sha-0bca601 AS studio FROM darthsim/imgproxy:v3.8.0 AS imgproxy @@ -11,9 +11,9 @@ FROM supabase/edge-runtime:v1.74.0 AS edgeruntime FROM timberio/vector:0.53.0-alpine AS vector FROM supabase/supavisor:2.9.7 AS supavisor FROM supabase/gotrue:v2.189.0 AS gotrue -FROM supabase/realtime:v2.103.2 AS realtime +FROM supabase/realtime:v2.103.4 AS realtime FROM supabase/storage-api:v1.60.4 AS storage -FROM supabase/logflare:1.43.3 AS logflare +FROM supabase/logflare:1.43.4 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ FROM supabase/migra:3.0.1663481299 AS migra diff --git a/apps/cli/docs/go-cli-porting-status.md b/apps/cli/docs/go-cli-porting-status.md index ff70e62590..c202ce3684 100644 --- a/apps/cli/docs/go-cli-porting-status.md +++ b/apps/cli/docs/go-cli-porting-status.md @@ -57,9 +57,9 @@ These commands exist in the TS CLI today but have no direct top-level equivalent ## Quick Start -| Old command | TS status | TS command path or `missing` | Missing flags/params | Extra TS flags/params | Notes | -| ----------- | --------- | ---------------------------- | -------------------- | --------------------- | ------------------------------------------- | -| `bootstrap` | `missing` | `missing` | `n/a` | `n/a` | No TS command yet. Wrapped in legacy shell. | +| Old command | TS status | TS command path or `missing` | Missing flags/params | Extra TS flags/params | Notes | +| ----------- | --------- | ---------------------------- | -------------------- | --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `bootstrap` | `missing` | `missing` | `n/a` | `n/a` | No `next/` command yet. Ported to native TS in the legacy shell; the migration-push sub-step is delegated to the Go binary as a documented interim until `db push` is natively ported. | ## Project / Stack Lifecycle @@ -76,7 +76,7 @@ These commands exist in the TS CLI today but have no direct top-level equivalent -| `services` | `partial` | `supabase status` + `supabase stack update` | Go-style dedicated `services` command shape | `--stack` | The old version-reporting and linked-version drift behavior exists in TS, but it is split across `status` for per-service versions and `stack update` for refreshing pinned versions instead of a single `services` command. | +| `services` | `ported` | [`../src/next/commands/services/services.command.ts`](../src/next/commands/services/services.command.ts) | `--output` remains a global legacy-shell concern rather than a next-only command flag | `--output-format` | TS restores the dedicated `services` command, prints the bundled local service image matrix, and best-effort compares linked remote versions without proxying to Go. | ## Database @@ -265,9 +265,9 @@ Legend: | `logout` | `ported` | [`../src/legacy/commands/logout/logout.command.ts`](../src/legacy/commands/logout/logout.command.ts) | | `link` | `ported` | [`../src/legacy/commands/link/link.command.ts`](../src/legacy/commands/link/link.command.ts) | | `unlink` | `ported` | [`../src/legacy/commands/unlink/unlink.command.ts`](../src/legacy/commands/unlink/unlink.command.ts) | -| `bootstrap` | `wrapped` | [`../src/legacy/commands/bootstrap/bootstrap.command.ts`](../src/legacy/commands/bootstrap/bootstrap.command.ts) | +| `bootstrap` | `ported` | [`../src/legacy/commands/bootstrap/bootstrap.command.ts`](../src/legacy/commands/bootstrap/bootstrap.command.ts) (native; `db push` step delegated to the Go binary — interim) | | `init` | `ported` | [`../src/legacy/commands/init/init.command.ts`](../src/legacy/commands/init/init.command.ts) | -| `services` | `wrapped` | [`../src/legacy/commands/services/services.command.ts`](../src/legacy/commands/services/services.command.ts) | +| `services` | `ported` | [`../src/legacy/commands/services/services.command.ts`](../src/legacy/commands/services/services.command.ts) | | `start` | `wrapped` | [`../src/legacy/commands/start/start.command.ts`](../src/legacy/commands/start/start.command.ts) | | `stop` | `wrapped` | [`../src/legacy/commands/stop/stop.command.ts`](../src/legacy/commands/stop/stop.command.ts) | | `status` | `wrapped` | [`../src/legacy/commands/status/status.command.ts`](../src/legacy/commands/status/status.command.ts) | diff --git a/apps/cli/package.json b/apps/cli/package.json index 0bc2ba0687..4d07c5a39b 100644 --- a/apps/cli/package.json +++ b/apps/cli/package.json @@ -37,8 +37,8 @@ "fix:all": "nx run-many -t lint:fix fmt:fix knip:fix --projects=$npm_package_name" }, "devDependencies": { - "@anthropic-ai/claude-agent-sdk": "^0.3.146", - "@anthropic-ai/sdk": "^0.97.1", + "@anthropic-ai/claude-agent-sdk": "^0.3.156", + "@anthropic-ai/sdk": "^0.100.0", "@clack/prompts": "^1.4.0", "@effect/atom-react": "catalog:", "@effect/platform-bun": "catalog:", @@ -64,7 +64,7 @@ "oxfmt": "catalog:", "oxlint": "catalog:", "oxlint-tsgolint": "catalog:", - "posthog-node": "^5.35.5", + "posthog-node": "^5.35.6", "react": "^19.2.6", "react-devtools-core": "^7.0.1", "semantic-release": "^25.0.3", diff --git a/apps/cli/scripts/update-scoop.ts b/apps/cli/scripts/update-scoop.ts index 14bf9a965f..c6b7899b1a 100644 --- a/apps/cli/scripts/update-scoop.ts +++ b/apps/cli/scripts/update-scoop.ts @@ -58,35 +58,47 @@ const baseUrl = local ? `file:///${distDir.replace(/\\/g, "/")}` : `https://github.com/${repo}/releases/download/v${version}`; +// Main-bucket layout uses unversioned Windows tarballs on GitHub Releases +// (release-shared.yml copies versioned builds to supabase_windows_*.tar.gz). +// Local builds only emit versioned archives, so --local keeps those names. +const amd64Tar = local + ? `supabase_${version}_windows_amd64.tar.gz` + : "supabase_windows_amd64.tar.gz"; +const arm64Tar = local + ? `supabase_${version}_windows_arm64.tar.gz` + : "supabase_windows_arm64.tar.gz"; + const manifest = { version, description: "Supabase CLI", - homepage: "https://supabase.com", + homepage: "https://supabase.com/", license: "MIT", architecture: { "64bit": { - url: `${baseUrl}/supabase_${version}_windows_amd64.zip`, - hash: sha(`supabase_${version}_windows_amd64.zip`), - bin: [binEntry], + url: `${baseUrl}/${amd64Tar}`, + hash: sha(`supabase_${version}_windows_amd64.tar.gz`), }, arm64: { - url: `${baseUrl}/supabase_${version}_windows_arm64.zip`, - hash: sha(`supabase_${version}_windows_arm64.zip`), - bin: [binEntry], + url: `${baseUrl}/${arm64Tar}`, + hash: sha(`supabase_${version}_windows_arm64.tar.gz`), }, }, + bin: binEntry, checkver: { github: `https://github.com/${repo}`, }, autoupdate: { architecture: { "64bit": { - url: `https://github.com/${repo}/releases/download/v$version/supabase_$version_windows_amd64.zip`, + url: `https://github.com/${repo}/releases/download/v$version/supabase_windows_amd64.tar.gz`, }, arm64: { - url: `https://github.com/${repo}/releases/download/v$version/supabase_$version_windows_arm64.zip`, + url: `https://github.com/${repo}/releases/download/v$version/supabase_windows_arm64.tar.gz`, }, }, + hash: { + url: "$baseurl/supabase_$version_checksums.txt", + }, }, }; diff --git a/apps/cli/src/legacy/commands/bootstrap/SIDE_EFFECTS.md b/apps/cli/src/legacy/commands/bootstrap/SIDE_EFFECTS.md index 720d4096b6..7a4ff00269 100644 --- a/apps/cli/src/legacy/commands/bootstrap/SIDE_EFFECTS.md +++ b/apps/cli/src/legacy/commands/bootstrap/SIDE_EFFECTS.md @@ -1,71 +1,119 @@ # `supabase bootstrap [template]` +`bootstrap` is a meta-orchestrator: it chains a workdir prompt → template fetch/download → +blank `init` → ensure-login → `projects create` → `projects api-keys` → `link` services → +health poll → write `.env` → `db push` → start suggestion. Every step is native TypeScript +**except** the migration push, which is delegated to the bundled Go binary (interim — see Notes). + ## Files Read -| Path | Format | When | -| -------------------------- | ------------------------- | ---------------------------------------------------------- | -| `~/.supabase/access-token` | plain text (token string) | when `SUPABASE_ACCESS_TOKEN` unset and keyring unavailable | +| Path | Format | When | +| -------------------------------------- | ---------- | ----------------------------------------------------------- | +| `~/.supabase/access-token` | plain text | ensure-login token miss (env unset and keyring unavailable) | +| `/.env.example` | dotenv | optional; merged into the generated `.env` | +| `/supabase/.temp/project-ref` | plain text | read by the delegated `db push` subprocess (post-`chdir`) | ## Files Written -| Path | Format | When | -| -------------------------------- | ------ | ------------------------------------------------------------- | -| `/supabase/config.toml` | TOML | always on success; created from selected template | -| `/.env` | env | always on success; populated with API keys and project config | -| `/