diff --git a/.github/workflows/api-sync.yml b/.github/workflows/api-sync.yml index 2cde09f1e..0248ef54c 100644 --- a/.github/workflows/api-sync.yml +++ b/.github/workflows/api-sync.yml @@ -47,7 +47,7 @@ jobs: - name: Create Pull Request if: steps.check.outputs.has_changes == 'true' id: cpr - uses: peter-evans/create-pull-request@v7 + uses: peter-evans/create-pull-request@v8 with: token: ${{ steps.app-token.outputs.token }} commit-message: "chore: sync API types from infrastructure" diff --git a/cmd/gen.go b/cmd/gen.go index 1c6566af4..f063f11a4 100644 --- a/cmd/gen.go +++ b/cmd/gen.go @@ -60,6 +60,7 @@ var ( types.LangTypescript, types.LangGo, types.LangSwift, + types.LangPython, }, Value: types.LangTypescript, } diff --git a/cmd/init.go b/cmd/init.go index a9d2d90a8..170d4dc86 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -3,18 +3,19 @@ package cmd import ( "fmt" "os" - "os/signal" "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/spf13/viper" _init "github.com/supabase/cli/internal/init" "github.com/supabase/cli/internal/utils" + "golang.org/x/term" ) var ( - createVscodeSettings = new(bool) - createIntellijSettings = new(bool) + initInteractive bool + createVscodeSettings bool + createIntellijSettings bool initParams = utils.InitParams{} initCmd = &cobra.Command{ @@ -34,16 +35,24 @@ var ( } }, RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() fsys := afero.NewOsFs() - if !cmd.Flags().Changed("with-vscode-settings") && !cmd.Flags().Changed("with-vscode-workspace") { - createVscodeSettings = nil + interactive := initInteractive && term.IsTerminal(int(os.Stdin.Fd())) + if err := _init.Run(ctx, fsys, interactive, initParams); err != nil { + return err } - - if !cmd.Flags().Changed("with-intellij-settings") { - createIntellijSettings = nil + // Handle backwards compatibility flags + if createVscodeSettings { + if err := _init.WriteVscodeConfig(fsys); err != nil { + return err + } + } + if createIntellijSettings { + if err := _init.WriteIntelliJConfig(fsys); err != nil { + return err + } } - ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt) - return _init.Run(ctx, fsys, createVscodeSettings, createIntellijSettings, initParams) + return nil }, PostRun: func(cmd *cobra.Command, args []string) { fmt.Println("Finished " + utils.Aqua("supabase init") + ".") @@ -53,11 +62,15 @@ var ( func init() { flags := initCmd.Flags() - flags.BoolVar(createVscodeSettings, "with-vscode-workspace", false, "Generate VS Code workspace.") - cobra.CheckErr(flags.MarkHidden("with-vscode-workspace")) - flags.BoolVar(createVscodeSettings, "with-vscode-settings", false, "Generate VS Code settings for Deno.") - flags.BoolVar(createIntellijSettings, "with-intellij-settings", false, "Generate IntelliJ IDEA settings for Deno.") + flags.BoolVarP(&initInteractive, "interactive", "i", false, "Enables interactive mode to configure IDE settings.") flags.BoolVar(&initParams.UseOrioleDB, "use-orioledb", false, "Use OrioleDB storage engine for Postgres.") flags.BoolVar(&initParams.Overwrite, "force", false, "Overwrite existing "+utils.ConfigPath+".") + // Backwards compatibility flags (hidden) + flags.BoolVar(&createVscodeSettings, "with-vscode-workspace", false, "Generate VS Code workspace.") + cobra.CheckErr(flags.MarkHidden("with-vscode-workspace")) + flags.BoolVar(&createVscodeSettings, "with-vscode-settings", false, "Generate VS Code settings for Deno.") + cobra.CheckErr(flags.MarkHidden("with-vscode-settings")) + flags.BoolVar(&createIntellijSettings, "with-intellij-settings", false, "Generate IntelliJ IDEA settings for Deno.") + cobra.CheckErr(flags.MarkHidden("with-intellij-settings")) rootCmd.AddCommand(initCmd) } diff --git a/cmd/projects.go b/cmd/projects.go index 297ed5853..2ab5d7d2d 100644 --- a/cmd/projects.go +++ b/cmd/projects.go @@ -155,11 +155,9 @@ func init() { } func awsRegions() []string { - result := make([]string, len(utils.RegionMap)) - i := 0 - for k := range utils.RegionMap { - result[i] = k - i++ + result := make([]string, len(utils.CurrentProfile.ProjectRegions)) + for i, region := range utils.CurrentProfile.ProjectRegions { + result[i] = string(region) } sort.Strings(result) return result diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index 6e93c0acb..d8279d135 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -60,7 +60,7 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options .. if err := downloadSample(ctx, client, starter.Url, fsys); err != nil { return err } - } else if err := initBlank.Run(ctx, fsys, nil, nil, utils.InitParams{Overwrite: true}); err != nil { + } else if err := initBlank.Run(ctx, fsys, false, utils.InitParams{Overwrite: true}); err != nil { return err } // 1. Login diff --git a/internal/db/start/start.go b/internal/db/start/start.go index f9ba14eb3..92c8fe5eb 100644 --- a/internal/db/start/start.go +++ b/internal/db/start/start.go @@ -119,7 +119,7 @@ func NewHostConfig() container.HostConfig { hostPort := strconv.FormatUint(uint64(utils.Config.Db.Port), 10) hostConfig := container.HostConfig{ PortBindings: nat.PortMap{"5432/tcp": []nat.PortBinding{{HostPort: hostPort}}}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, Binds: []string{ utils.DbId + ":/var/lib/postgresql/data", }, diff --git a/internal/functions/deploy/bundle.go b/internal/functions/deploy/bundle.go index 0b91b5377..ae807aa01 100644 --- a/internal/functions/deploy/bundle.go +++ b/internal/functions/deploy/bundle.go @@ -50,7 +50,7 @@ func (b *dockerBundler) Bundle(ctx context.Context, slug, entrypoint, importMap hostOutputPath := filepath.Join(hostOutputDir, "output.eszip") // Create exec command cmd := []string{"bundle", "--entrypoint", utils.ToDockerPath(entrypoint), "--output", utils.ToDockerPath(hostOutputPath)} - if len(importMap) > 0 { + if len(importMap) > 0 && !function.ShouldUseDenoJsonDiscovery(entrypoint, importMap) { cmd = append(cmd, "--import-map", utils.ToDockerPath(importMap)) } for _, sf := range staticFiles { diff --git a/internal/functions/new/new.go b/internal/functions/new/new.go index 5785e0783..1b0f30791 100644 --- a/internal/functions/new/new.go +++ b/internal/functions/new/new.go @@ -10,6 +10,8 @@ import ( "github.com/go-errors/errors" "github.com/spf13/afero" + "github.com/supabase/cli/internal/functions/deploy" + _init "github.com/supabase/cli/internal/init" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/flags" ) @@ -38,6 +40,13 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error { if err := utils.ValidateFunctionSlug(slug); err != nil { return err } + // Check if this is the first function being created + existingSlugs, err := deploy.GetFunctionSlugs(fsys) + if err != nil { + fmt.Fprintln(utils.GetDebugLogger(), err) + } + isFirstFunction := len(existingSlugs) == 0 + // 2. Create new function. funcDir := filepath.Join(utils.FunctionsDir, slug) if err := utils.MkdirIfNotExistFS(fsys, funcDir); err != nil { @@ -61,6 +70,12 @@ func Run(ctx context.Context, slug string, fsys afero.Fs) error { return errors.Errorf("failed to create .npmrc config: %w", err) } fmt.Println("Created new Function at " + utils.Bold(funcDir)) + + if isFirstFunction { + if err := _init.PromptForIDESettings(ctx, fsys); err != nil { + return err + } + } return nil } diff --git a/internal/functions/serve/templates/main.ts b/internal/functions/serve/templates/main.ts index 4d5430efe..f6676c163 100644 --- a/internal/functions/serve/templates/main.ts +++ b/internal/functions/serve/templates/main.ts @@ -190,6 +190,7 @@ Deno.serve({ // NOTE(Nyannyacha): Decorator type has been set to tc39 by Lakshan's request, // but in my opinion, we should probably expose this to customers at some // point, as their migration process will not be easy. + // This need to be kept for Deno 1 compatibility. const decoratorType = "tc39"; const absEntrypoint = posix.join(Deno.cwd(), functionsConfig[functionName].entrypointPath); diff --git a/internal/gen/types/types.go b/internal/gen/types/types.go index 8d616c733..fef6a9772 100644 --- a/internal/gen/types/types.go +++ b/internal/gen/types/types.go @@ -23,6 +23,7 @@ const ( LangTypescript = "typescript" LangGo = "go" LangSwift = "swift" + LangPython = "python" ) const ( diff --git a/internal/init/init.go b/internal/init/init.go index 4bb18581b..01c70bcaf 100644 --- a/internal/init/init.go +++ b/internal/init/init.go @@ -33,7 +33,7 @@ var ( intelliJDeno string ) -func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntellijSettings *bool, params utils.InitParams) error { +func Run(ctx context.Context, fsys afero.Fs, interactive bool, params utils.InitParams) error { // 1. Write `config.toml`. if err := utils.InitConfig(params, fsys); err != nil { if errors.Is(err, os.ErrExist) { @@ -49,31 +49,31 @@ func Run(ctx context.Context, fsys afero.Fs, createVscodeSettings, createIntelli } } - // 3. Generate VS Code settings. - if createVscodeSettings != nil { - if *createVscodeSettings { - return writeVscodeConfig(fsys) - } - } else if createIntellijSettings != nil { - if *createIntellijSettings { - return writeIntelliJConfig(fsys) - } - } else { - console := utils.NewConsole() - if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", false); err != nil { - return err - } else if isVscode { - return writeVscodeConfig(fsys) - } - if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ Settings for Deno?", false); err != nil { + // 3. Prompt for IDE settings in interactive mode. + if interactive { + if err := PromptForIDESettings(ctx, fsys); err != nil { return err - } else if isIntelliJ { - return writeIntelliJConfig(fsys) } } return nil } +// PromptForIDESettings prompts the user to generate IDE settings for Deno. +func PromptForIDESettings(ctx context.Context, fsys afero.Fs) error { + console := utils.NewConsole() + if isVscode, err := console.PromptYesNo(ctx, "Generate VS Code settings for Deno?", true); err != nil { + return err + } else if isVscode { + return WriteVscodeConfig(fsys) + } + if isIntelliJ, err := console.PromptYesNo(ctx, "Generate IntelliJ IDEA settings for Deno?", false); err != nil { + return err + } else if isIntelliJ { + return WriteIntelliJConfig(fsys) + } + return nil +} + func updateGitIgnore(ignorePath string, fsys afero.Fs) error { var contents []byte @@ -146,7 +146,7 @@ func updateJsonFile(path string, template string, fsys afero.Fs) error { return saveUserSettings(path, userSettings, fsys) } -func writeVscodeConfig(fsys afero.Fs) error { +func WriteVscodeConfig(fsys afero.Fs) error { // Create VS Code settings for Deno. if err := utils.MkdirIfNotExistFS(fsys, vscodeDir); err != nil { return err @@ -157,14 +157,16 @@ func writeVscodeConfig(fsys afero.Fs) error { if err := updateJsonFile(settingsPath, vscodeSettings, fsys); err != nil { return err } - fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ". Please install the recommended extension!") + fmt.Println("Generated VS Code settings in " + utils.Bold(settingsPath) + ".") + fmt.Println("Please install the Deno extension for VS Code: " + utils.Bold("https://marketplace.visualstudio.com/items?itemName=denoland.vscode-deno")) return nil } -func writeIntelliJConfig(fsys afero.Fs) error { +func WriteIntelliJConfig(fsys afero.Fs) error { if err := utils.WriteFile(denoPath, []byte(intelliJDeno), fsys); err != nil { return err } - fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ". Please install the Deno plugin!") + fmt.Println("Generated IntelliJ settings in " + utils.Bold(denoPath) + ".") + fmt.Println("Please install the Deno plugin for IntelliJ: " + utils.Bold("https://plugins.jetbrains.com/plugin/14382-deno")) return nil } diff --git a/internal/init/init_test.go b/internal/init/init_test.go index 99a96dce5..3d54feb58 100644 --- a/internal/init/init_test.go +++ b/internal/init/init_test.go @@ -11,7 +11,6 @@ import ( "github.com/stretchr/testify/require" "github.com/supabase/cli/internal/testing/fstest" "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/pkg/cast" ) func TestInitCommand(t *testing.T) { @@ -19,8 +18,8 @@ func TestInitCommand(t *testing.T) { // Setup in-memory fs fsys := &afero.MemMapFs{} require.NoError(t, fsys.Mkdir(".git", 0755)) - // Run test - assert.NoError(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{})) + // Run test (non-interactive mode) + assert.NoError(t, Run(context.Background(), fsys, false, utils.InitParams{})) // Validate generated config.toml exists, err := afero.Exists(fsys, utils.ConfigPath) assert.NoError(t, err) @@ -48,14 +47,14 @@ func TestInitCommand(t *testing.T) { _, err := fsys.Create(utils.ConfigPath) require.NoError(t, err) // Run test - assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{})) + assert.Error(t, Run(context.Background(), fsys, false, utils.InitParams{})) }) t.Run("throws error on permission denied", func(t *testing.T) { // Setup in-memory fs fsys := &fstest.OpenErrorFs{DenyPath: utils.ConfigPath} // Run test - err := Run(context.Background(), fsys, nil, nil, utils.InitParams{}) + err := Run(context.Background(), fsys, false, utils.InitParams{}) // Check error assert.ErrorIs(t, err, os.ErrPermission) }) @@ -64,57 +63,7 @@ func TestInitCommand(t *testing.T) { // Setup read-only fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) // Run test - assert.Error(t, Run(context.Background(), fsys, nil, nil, utils.InitParams{})) - }) - - t.Run("creates vscode settings file", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - // Run test - assert.NoError(t, Run(context.Background(), fsys, cast.Ptr(true), nil, utils.InitParams{})) - // Validate generated vscode settings - exists, err := afero.Exists(fsys, settingsPath) - assert.NoError(t, err) - assert.True(t, exists) - exists, err = afero.Exists(fsys, extensionsPath) - assert.NoError(t, err) - assert.True(t, exists) - }) - - t.Run("does not create vscode settings file", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - // Run test - assert.NoError(t, Run(context.Background(), fsys, cast.Ptr(false), nil, utils.InitParams{})) - // Validate vscode settings file isn't generated - exists, err := afero.Exists(fsys, settingsPath) - assert.NoError(t, err) - assert.False(t, exists) - exists, err = afero.Exists(fsys, extensionsPath) - assert.NoError(t, err) - assert.False(t, exists) - }) - - t.Run("creates intellij deno file", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - // Run test - assert.NoError(t, Run(context.Background(), fsys, nil, cast.Ptr(true), utils.InitParams{})) - // Validate generated intellij deno config - exists, err := afero.Exists(fsys, denoPath) - assert.NoError(t, err) - assert.True(t, exists) - }) - - t.Run("does not create intellij deno file", func(t *testing.T) { - // Setup in-memory fs - fsys := &afero.MemMapFs{} - // Run test - assert.NoError(t, Run(context.Background(), fsys, nil, cast.Ptr(false), utils.InitParams{})) - // Validate intellij deno config file isn't generated - exists, err := afero.Exists(fsys, denoPath) - assert.NoError(t, err) - assert.False(t, exists) + assert.Error(t, Run(context.Background(), fsys, false, utils.InitParams{})) }) } @@ -170,7 +119,7 @@ func TestWriteVSCodeConfig(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() // Run test - err := writeVscodeConfig(afero.NewReadOnlyFs(fsys)) + err := WriteVscodeConfig(afero.NewReadOnlyFs(fsys)) // Check error assert.ErrorIs(t, err, os.ErrPermission) }) @@ -179,7 +128,7 @@ func TestWriteVSCodeConfig(t *testing.T) { // Setup in-memory fs fsys := &fstest.OpenErrorFs{DenyPath: extensionsPath} // Run test - err := writeVscodeConfig(fsys) + err := WriteVscodeConfig(fsys) // Check error assert.ErrorIs(t, err, os.ErrPermission) }) @@ -188,7 +137,7 @@ func TestWriteVSCodeConfig(t *testing.T) { // Setup in-memory fs fsys := &fstest.OpenErrorFs{DenyPath: settingsPath} // Run test - err := writeVscodeConfig(fsys) + err := WriteVscodeConfig(fsys) // Check error assert.ErrorIs(t, err, os.ErrPermission) }) diff --git a/internal/projects/create/create.go b/internal/projects/create/create.go index 10bd5a460..98b042335 100644 --- a/internal/projects/create/create.go +++ b/internal/projects/create/create.go @@ -116,11 +116,12 @@ func promptOrgId(ctx context.Context) (string, error) { func promptProjectRegion(ctx context.Context) (api.V1CreateProjectBodyRegion, error) { title := "Which region do you want to host the project in?" - items := make([]utils.PromptItem, len(utils.RegionMap)) - i := 0 - for k, v := range utils.RegionMap { - items[i] = utils.PromptItem{Summary: k, Details: v} - i++ + items := make([]utils.PromptItem, len(utils.CurrentProfile.ProjectRegions)) + for i, region := range utils.CurrentProfile.ProjectRegions { + items[i] = utils.PromptItem{ + Summary: string(region), + Details: utils.FormatRegion(string(region)), + } } choice, err := utils.PromptChoice(ctx, title, items) if err != nil { diff --git a/internal/start/start.go b/internal/start/start.go index 758ac30d1..cf4bc68fd 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -318,7 +318,7 @@ EOF container.HostConfig{ Binds: bind, PortBindings: nat.PortMap{"4000/tcp": []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Analytics.Port), 10)}}}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -401,7 +401,7 @@ EOF }, container.HostConfig{ Binds: binds, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, SecurityOpt: securityOpts, }, network.NetworkingConfig{ @@ -526,7 +526,7 @@ EOF PortBindings: nat.PortMap{nat.Port(fmt.Sprintf("%d/tcp", dockerPort)): []nat.PortBinding{{ HostPort: strconv.FormatUint(uint64(utils.Config.Api.Port), 10)}, }}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -824,7 +824,7 @@ EOF }, }, container.HostConfig{ - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -868,7 +868,7 @@ EOF }, container.HostConfig{ PortBindings: inbucketPortBindings, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -924,7 +924,7 @@ EOF }, }, container.HostConfig{ - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -958,7 +958,7 @@ EOF // PostgREST does not expose a shell for health check }, container.HostConfig{ - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -1017,7 +1017,7 @@ EOF }, }, container.HostConfig{ - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, Binds: []string{utils.StorageId + ":" + dockerStoragePath}, }, network.NetworkingConfig{ @@ -1060,7 +1060,7 @@ EOF }, container.HostConfig{ VolumesFrom: []string{utils.StorageId}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -1107,7 +1107,7 @@ EOF }, }, container.HostConfig{ - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -1155,7 +1155,7 @@ EOF }, container.HostConfig{ PortBindings: nat.PortMap{"3000/tcp": []nat.PortBinding{{HostPort: strconv.FormatUint(uint64(utils.Config.Studio.Port), 10)}}}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ @@ -1232,7 +1232,7 @@ EOF PortBindings: nat.PortMap{nat.Port(fmt.Sprintf("%d/tcp", dockerPort)): []nat.PortBinding{{ HostPort: strconv.FormatUint(uint64(utils.Config.Db.Pooler.Port), 10)}, }}, - RestartPolicy: container.RestartPolicy{Name: "always"}, + RestartPolicy: container.RestartPolicy{Name: container.RestartPolicyUnlessStopped}, }, network.NetworkingConfig{ EndpointsConfig: map[string]*network.EndpointSettings{ diff --git a/internal/status/status.go b/internal/status/status.go index 62532d7ea..fcb628917 100644 --- a/internal/status/status.go +++ b/internal/status/status.go @@ -228,7 +228,7 @@ func PrettyPrint(w io.Writer, exclude ...string) { groups := []OutputGroup{ { - Name: "🛠️ Development Tools", + Name: "🔧 Development Tools", Items: []OutputItem{ {Label: "Studio", Value: values[names.StudioURL], Type: Link}, {Label: "Mailpit", Value: values[names.MailpitURL], Type: Link}, @@ -245,7 +245,7 @@ func PrettyPrint(w io.Writer, exclude ...string) { }, }, { - Name: "🗄️ Database", + Name: "⛁ Database", Items: []OutputItem{ {Label: "URL", Value: values[names.DbURL], Type: Link}, }, diff --git a/internal/utils/api.go b/internal/utils/api.go index 3cc1dd380..5b9a96fde 100644 --- a/internal/utils/api.go +++ b/internal/utils/api.go @@ -141,23 +141,6 @@ func GetSupabase() *supabase.ClientWithResponses { // Used by unit tests var DefaultApiHost = CurrentProfile.APIURL -var RegionMap = map[string]string{ - "ap-northeast-1": "Northeast Asia (Tokyo)", - "ap-northeast-2": "Northeast Asia (Seoul)", - "ap-south-1": "South Asia (Mumbai)", - "ap-southeast-1": "Southeast Asia (Singapore)", - "ap-southeast-2": "Oceania (Sydney)", - "ca-central-1": "Canada (Central)", - "eu-central-1": "Central EU (Frankfurt)", - "eu-west-1": "West EU (Ireland)", - "eu-west-2": "West EU (London)", - "eu-west-3": "West EU (Paris)", - "sa-east-1": "South America (São Paulo)", - "us-east-1": "East US (North Virginia)", - "us-west-1": "West US (North California)", - "us-west-2": "West US (Oregon)", -} - func GetSupabaseAPIHost() string { return CurrentProfile.APIURL } diff --git a/internal/utils/profile.go b/internal/utils/profile.go index fcb089e3f..1910f30b7 100644 --- a/internal/utils/profile.go +++ b/internal/utils/profile.go @@ -9,17 +9,19 @@ import ( "github.com/go-playground/validator/v10" "github.com/spf13/afero" "github.com/spf13/viper" + "github.com/supabase/cli/pkg/api" ) type Profile struct { - Name string `mapstructure:"name" validate:"required"` - APIURL string `mapstructure:"api_url" validate:"required,http_url"` - DashboardURL string `mapstructure:"dashboard_url" validate:"required,http_url"` - DocsURL string `mapstructure:"docs_url" validate:"omitempty,http_url"` - ProjectHost string `mapstructure:"project_host" validate:"required,hostname_rfc1123"` - PoolerHost string `mapstructure:"pooler_host" validate:"omitempty,hostname_rfc1123"` - AuthClientID string `mapstructure:"client_id" validate:"omitempty,uuid4"` - StudioImage string `mapstructure:"studio_image"` + Name string `mapstructure:"name" validate:"required"` + APIURL string `mapstructure:"api_url" validate:"required,http_url"` + DashboardURL string `mapstructure:"dashboard_url" validate:"required,http_url"` + DocsURL string `mapstructure:"docs_url" validate:"omitempty,http_url"` + ProjectHost string `mapstructure:"project_host" validate:"required,hostname_rfc1123"` + PoolerHost string `mapstructure:"pooler_host" validate:"omitempty,hostname_rfc1123"` + AuthClientID string `mapstructure:"client_id" validate:"omitempty,uuid4"` + StudioImage string `mapstructure:"studio_image"` + ProjectRegions []api.V1CreateProjectBodyRegion `mapstructure:"regions"` } var allProfiles = []Profile{{ @@ -29,6 +31,26 @@ var allProfiles = []Profile{{ DocsURL: "https://supabase.com/docs", ProjectHost: "supabase.co", PoolerHost: "supabase.com", + ProjectRegions: []api.V1CreateProjectBodyRegion{ + api.V1CreateProjectBodyRegionApEast1, + api.V1CreateProjectBodyRegionApNortheast1, + api.V1CreateProjectBodyRegionApNortheast2, + api.V1CreateProjectBodyRegionApSouth1, + api.V1CreateProjectBodyRegionApSoutheast1, + api.V1CreateProjectBodyRegionApSoutheast2, + api.V1CreateProjectBodyRegionCaCentral1, + api.V1CreateProjectBodyRegionEuCentral1, + api.V1CreateProjectBodyRegionEuCentral2, + api.V1CreateProjectBodyRegionEuNorth1, + api.V1CreateProjectBodyRegionEuWest1, + api.V1CreateProjectBodyRegionEuWest2, + api.V1CreateProjectBodyRegionEuWest3, + api.V1CreateProjectBodyRegionSaEast1, + api.V1CreateProjectBodyRegionUsEast1, + api.V1CreateProjectBodyRegionUsEast2, + api.V1CreateProjectBodyRegionUsWest1, + api.V1CreateProjectBodyRegionUsWest2, + }, }, { Name: "supabase-staging", APIURL: "https://api.supabase.green", @@ -36,12 +58,22 @@ var allProfiles = []Profile{{ DocsURL: "https://supabase.com/docs", ProjectHost: "supabase.red", PoolerHost: "supabase.green", + ProjectRegions: []api.V1CreateProjectBodyRegion{ + api.V1CreateProjectBodyRegionApSoutheast1, + api.V1CreateProjectBodyRegionUsEast1, + api.V1CreateProjectBodyRegionEuCentral1, + }, }, { Name: "supabase-local", APIURL: "http://localhost:8080", DashboardURL: "http://localhost:8082", DocsURL: "https://supabase.com/docs", ProjectHost: "supabase.red", + ProjectRegions: []api.V1CreateProjectBodyRegion{ + api.V1CreateProjectBodyRegionApSoutheast1, + api.V1CreateProjectBodyRegionUsEast1, + api.V1CreateProjectBodyRegionEuCentral1, + }, }, { Name: "snap", APIURL: "https://cloudapi.snap.com", @@ -50,6 +82,9 @@ var allProfiles = []Profile{{ ProjectHost: "snapcloud.dev", PoolerHost: "snapcloud.co", AuthClientID: "f7573b20-df47-48f1-b606-e8db4ec16252", + ProjectRegions: []api.V1CreateProjectBodyRegion{ + api.V1CreateProjectBodyRegionUsEast1, + }, }} var CurrentProfile Profile diff --git a/internal/utils/render.go b/internal/utils/render.go index 07d674d29..ee4a4dc6c 100644 --- a/internal/utils/render.go +++ b/internal/utils/render.go @@ -31,8 +31,29 @@ func parse(layout, value string) string { return FormatTime(t) } +var regionMap = map[string]string{ + "ap-east-1": "East Asia (Hong Kong)", + "ap-northeast-1": "Northeast Asia (Tokyo)", + "ap-northeast-2": "Northeast Asia (Seoul)", + "ap-south-1": "South Asia (Mumbai)", + "ap-southeast-1": "Southeast Asia (Singapore)", + "ap-southeast-2": "Oceania (Sydney)", + "ca-central-1": "Canada (Central)", + "eu-central-1": "Central EU (Frankfurt)", + "eu-central-2": "Central Europe (Zurich)", + "eu-north-1": "North EU (Stockholm)", + "eu-west-1": "West EU (Ireland)", + "eu-west-2": "West Europe (London)", + "eu-west-3": "West EU (Paris)", + "sa-east-1": "South America (São Paulo)", + "us-east-1": "East US (North Virginia)", + "us-east-2": "East US (Ohio)", + "us-west-1": "West US (North California)", + "us-west-2": "West US (Oregon)", +} + func FormatRegion(region string) string { - if readable, ok := RegionMap[region]; ok { + if readable, ok := regionMap[region]; ok { return readable } return region diff --git a/pkg/api/types.gen.go b/pkg/api/types.gen.go index 34029acbc..dc89e1c57 100644 --- a/pkg/api/types.gen.go +++ b/pkg/api/types.gen.go @@ -14,8 +14,9 @@ import ( ) const ( - BearerScopes = "bearer.Scopes" - Oauth2Scopes = "oauth2.Scopes" + BearerScopes = "bearer.Scopes" + Fga_permissionsScopes = "fga_permissions.Scopes" + Oauth2Scopes = "oauth2.Scopes" ) // Defines values for ActionRunResponseRunStepsName. @@ -696,6 +697,7 @@ const ( const ( OrganizationProjectClaimResponsePreviewSourceSubscriptionPlanEnterprise OrganizationProjectClaimResponsePreviewSourceSubscriptionPlan = "enterprise" OrganizationProjectClaimResponsePreviewSourceSubscriptionPlanFree OrganizationProjectClaimResponsePreviewSourceSubscriptionPlan = "free" + OrganizationProjectClaimResponsePreviewSourceSubscriptionPlanPlatform OrganizationProjectClaimResponsePreviewSourceSubscriptionPlan = "platform" OrganizationProjectClaimResponsePreviewSourceSubscriptionPlanPro OrganizationProjectClaimResponsePreviewSourceSubscriptionPlan = "pro" OrganizationProjectClaimResponsePreviewSourceSubscriptionPlanTeam OrganizationProjectClaimResponsePreviewSourceSubscriptionPlan = "team" ) @@ -704,6 +706,7 @@ const ( const ( OrganizationProjectClaimResponsePreviewTargetSubscriptionPlanEnterprise OrganizationProjectClaimResponsePreviewTargetSubscriptionPlan = "enterprise" OrganizationProjectClaimResponsePreviewTargetSubscriptionPlanFree OrganizationProjectClaimResponsePreviewTargetSubscriptionPlan = "free" + OrganizationProjectClaimResponsePreviewTargetSubscriptionPlanPlatform OrganizationProjectClaimResponsePreviewTargetSubscriptionPlan = "platform" OrganizationProjectClaimResponsePreviewTargetSubscriptionPlanPro OrganizationProjectClaimResponsePreviewTargetSubscriptionPlan = "pro" OrganizationProjectClaimResponsePreviewTargetSubscriptionPlanTeam OrganizationProjectClaimResponsePreviewTargetSubscriptionPlan = "team" ) @@ -1289,6 +1292,7 @@ const ( const ( V1OrganizationSlugResponsePlanEnterprise V1OrganizationSlugResponsePlan = "enterprise" V1OrganizationSlugResponsePlanFree V1OrganizationSlugResponsePlan = "free" + V1OrganizationSlugResponsePlanPlatform V1OrganizationSlugResponsePlan = "platform" V1OrganizationSlugResponsePlanPro V1OrganizationSlugResponsePlan = "pro" V1OrganizationSlugResponsePlanTeam V1OrganizationSlugResponsePlan = "team" ) @@ -1788,6 +1792,10 @@ type AuthConfigResponse struct { ExternalWorkosEnabled nullable.Nullable[bool] `json:"external_workos_enabled"` ExternalWorkosSecret nullable.Nullable[string] `json:"external_workos_secret"` ExternalWorkosUrl nullable.Nullable[string] `json:"external_workos_url"` + ExternalXClientId nullable.Nullable[string] `json:"external_x_client_id"` + ExternalXEmailOptional nullable.Nullable[bool] `json:"external_x_email_optional"` + ExternalXEnabled nullable.Nullable[bool] `json:"external_x_enabled"` + ExternalXSecret nullable.Nullable[string] `json:"external_x_secret"` ExternalZoomClientId nullable.Nullable[string] `json:"external_zoom_client_id"` ExternalZoomEmailOptional nullable.Nullable[bool] `json:"external_zoom_email_optional"` ExternalZoomEnabled nullable.Nullable[bool] `json:"external_zoom_enabled"` @@ -3496,6 +3504,10 @@ type UpdateAuthConfigBody struct { ExternalWorkosEnabled nullable.Nullable[bool] `json:"external_workos_enabled,omitempty"` ExternalWorkosSecret nullable.Nullable[string] `json:"external_workos_secret,omitempty"` ExternalWorkosUrl nullable.Nullable[string] `json:"external_workos_url,omitempty"` + ExternalXClientId nullable.Nullable[string] `json:"external_x_client_id,omitempty"` + ExternalXEmailOptional nullable.Nullable[bool] `json:"external_x_email_optional,omitempty"` + ExternalXEnabled nullable.Nullable[bool] `json:"external_x_enabled,omitempty"` + ExternalXSecret nullable.Nullable[string] `json:"external_x_secret,omitempty"` ExternalZoomClientId nullable.Nullable[string] `json:"external_zoom_client_id,omitempty"` ExternalZoomEmailOptional nullable.Nullable[bool] `json:"external_zoom_email_optional,omitempty"` ExternalZoomEnabled nullable.Nullable[bool] `json:"external_zoom_enabled,omitempty"` diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index 04044a246..4509e3f2a 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -1,19 +1,19 @@ # Exposed for updates by .github/dependabot.yml -FROM supabase/postgres:17.6.1.059 AS pg +FROM supabase/postgres:17.6.1.066 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.1 AS postgrest -FROM supabase/postgres-meta:v0.93.1 AS pgmeta -FROM supabase/studio:2025.12.01-sha-4ad48b7 AS studio +FROM supabase/postgres-meta:v0.95.1 AS pgmeta +FROM supabase/studio:2025.12.15-sha-e98ba37 AS studio FROM darthsim/imgproxy:v3.8.0 AS imgproxy FROM supabase/edge-runtime:v1.69.28 AS edgeruntime FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor -FROM supabase/gotrue:v2.183.0 AS gotrue -FROM supabase/realtime:v2.67.2 AS realtime -FROM supabase/storage-api:v1.32.1 AS storage -FROM supabase/logflare:1.26.20 AS logflare +FROM supabase/gotrue:v2.184.0 AS gotrue +FROM supabase/realtime:v2.68.6 AS realtime +FROM supabase/storage-api:v1.33.0 AS storage +FROM supabase/logflare:1.27.2 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/pkg/config/templates/config.toml b/pkg/config/templates/config.toml index 3fb0d0b54..eede7d83d 100644 --- a/pkg/config/templates/config.toml +++ b/pkg/config/templates/config.toml @@ -112,9 +112,9 @@ file_size_limit = "50MiB" # allowed_mime_types = ["image/png", "image/jpeg"] # objects_path = "./images" -# Uncomment to allow connections via S3 compatible clients -# [storage.s3_protocol] -# enabled = true +# Allow connections via S3 compatible clients +[storage.s3_protocol] +enabled = true # Image transformation API is available to Supabase Pro plan. # [storage.image_transformation] diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml index 0b4974321..6b92371b7 100644 --- a/pkg/config/testdata/config.toml +++ b/pkg/config/testdata/config.toml @@ -112,7 +112,7 @@ file_size_limit = "50MiB" allowed_mime_types = ["image/png", "image/jpeg"] objects_path = "./images" -# Uncomment to allow connections via S3 compatible clients +# Allow connections via S3 compatible clients [storage.s3_protocol] enabled = true diff --git a/pkg/function/bundle.go b/pkg/function/bundle.go index 04ec2c240..599006aef 100644 --- a/pkg/function/bundle.go +++ b/pkg/function/bundle.go @@ -18,15 +18,17 @@ import ( ) type nativeBundler struct { - tempDir string - fsys fs.FS - timeout time.Duration + tempDir string + fsys fs.FS + timeout time.Duration + denoVersion uint } func NewNativeBundler(tempDir string, fsys fs.FS, opts ...func(*nativeBundler)) EszipBundler { b := &nativeBundler{ - tempDir: tempDir, - fsys: fsys, + tempDir: tempDir, + fsys: fsys, + denoVersion: 2, } for _, apply := range opts { apply(b) @@ -40,12 +42,16 @@ func WithTimeout(timeout time.Duration) func(*nativeBundler) { } } +func WithDenoVersion(version uint) func(*nativeBundler) { + return func(b *nativeBundler) { + b.denoVersion = version + } +} + var ( // Use a package private variable to allow testing without gosec complaining about G204 edgeRuntimeBin = "edge-runtime" - BundleFlags = []string{ - "--decorator", "tc39", - } + BundleFlags = []string{} ) func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap string, staticFiles []string, output io.Writer) (FunctionDeployMetadata, error) { @@ -53,7 +59,7 @@ func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap outputPath := filepath.Join(b.tempDir, slug+".eszip") // TODO: make edge runtime write to stdout args := []string{"bundle", "--entrypoint", entrypoint, "--output", outputPath} - if len(importMap) > 0 { + if len(importMap) > 0 && !ShouldUseDenoJsonDiscovery(entrypoint, importMap) { args = append(args, "--import-map", importMap) } for _, staticFile := range staticFiles { @@ -84,6 +90,10 @@ func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap return meta, Compress(eszipBytes, output) } +func ShouldUseDenoJsonDiscovery(entrypoint, importMap string) bool { + return isDeno(filepath.Base(importMap)) && filepath.Dir(importMap) == filepath.Dir(entrypoint) +} + func ShouldUsePackageJsonDiscovery(entrypoint, importMap string, fsys fs.StatFS) bool { if len(importMap) > 0 { return false