Repository: supabase/cli
Pull request #212 (2022-03-30 23:43:13)

chore: add supabase creation if doesn't exist


Patch:
diff --git a/cmd/functions.go b/cmd/functions.go
index 15bdc9b1..9d6f7108 100644
--- a/cmd/functions.go
+++ b/cmd/functions.go
@@ -26,9 +26,13 @@ var (
 	functionsDeployCmd = &cobra.Command{
 		Use:   "deploy <Function name>",
 		Short: "Deploy a Function to the linked Supabase project.",
-		Args:  cobra.ExactArgs(1),
+		Args:  cobra.RangeArgs(1, 2),
 		RunE: func(cmd *cobra.Command, args []string) error {
-			return deploy.Run(args[0])
+			projectRef, err := cmd.Flags().GetString("project-ref")
+			if err != nil {
+				return err
+			}
+			return deploy.Run(args[0], projectRef)
 		},
 	}
 
@@ -58,6 +62,7 @@ var (
 
 func init() {
 	functionsServeCmd.Flags().String("env-file", "", "Path to an env file to be populated to the Function environment")
+	functionsDeployCmd.Flags().String("project-ref", "", "Project ref of the Supabase project")
 	functionsCmd.AddCommand(functionsDeleteCmd)
 	functionsCmd.AddCommand(functionsDeployCmd)
 	functionsCmd.AddCommand(functionsNewCmd)
diff --git a/cmd/link.go b/cmd/link.go
index 00593d51..8edc2e37 100644
--- a/cmd/link.go
+++ b/cmd/link.go
@@ -10,7 +10,7 @@ var (
 		Use:   "link",
 		Short: "Link to a Supabase project.",
 		RunE: func(cmd *cobra.Command, args []string) error {
-			projectRef, err := cmd.Flags().GetString("ref")
+			projectRef, err := cmd.Flags().GetString("project-ref")
 			if err != nil {
 				return err
 			}
@@ -21,7 +21,7 @@ var (
 )
 
 func init() {
-	linkCmd.Flags().String("ref", "", "Project ref of the Supabase project")
-	_ = linkCmd.MarkFlagRequired("ref")
+	linkCmd.Flags().String("project-ref", "", "Project ref of the Supabase project")
+	_ = linkCmd.MarkFlagRequired("project-ref")
 	rootCmd.AddCommand(linkCmd)
 }
diff --git a/internal/functions/deploy/deploy.go b/internal/functions/deploy/deploy.go
index 07ca9654..896f8ffb 100644
--- a/internal/functions/deploy/deploy.go
+++ b/internal/functions/deploy/deploy.go
@@ -9,19 +9,30 @@ import (
 	"net/http"
 	"os"
 	"os/exec"
+	"regexp"
 
 	"github.com/adrg/xdg"
 	"github.com/supabase/cli/internal/utils"
 )
 
-func Run(slug string) error {
+func Run(slug string, projectRefArg string) error {
 	// 1. Sanity checks.
 	{
 		if err := utils.AssertSupabaseCliIsSetUp(); err != nil {
 			return err
 		}
-		if err := utils.AssertIsLinked(); err != nil {
-			return err
+		if len(projectRefArg) == 0 {
+			if err := utils.AssertIsLinked(); err != nil {
+				return err
+			}
+		} else {
+			matched, err := regexp.MatchString(`^[a-z]{20}$`, projectRefArg)
+			if err != nil {
+				return err
+			}
+			if !matched {
+				return errors.New("Invalid project ref format. Must be like `abcdefghijklmnopqrst`.")
+			}
 		}
 		if err := utils.InstallOrUpgradeDeno(); err != nil {
 			return err
@@ -55,11 +66,16 @@ func Run(slug string) error {
 	// 3. Deploy new Function.
 	var projectRef string
 	{
-		projectRefBytes, err := os.ReadFile("supabase/.temp/project-ref")
-		if err != nil {
-			return err
+		// --project-ref overrides value on disk
+		if len(projectRefArg) == 0 {
+			projectRefBytes, err := os.ReadFile("supabase/.temp/project-ref")
+			if err != nil {
+				return err
+			}
+			projectRef = string(projectRefBytes)
+		} else {
+			projectRef = projectRefArg
 		}
-		projectRef = string(projectRefBytes)
 
 		accessToken, err := utils.LoadAccessToken()
 		if err != nil {
diff --git a/internal/functions/new/new.go b/internal/functions/new/new.go
index b2f72199..1db3c7de 100644
--- a/internal/functions/new/new.go
+++ b/internal/functions/new/new.go
@@ -11,9 +11,6 @@ import (
 func Run(slug string) error {
 	// 1. Sanity checks.
 	{
-		if err := utils.AssertSupabaseCliIsSetUp(); err != nil {
-			return err
-		}
 		if err := utils.ValidateFunctionSlug(slug); err != nil {
 			return err
 		}
@@ -24,6 +21,9 @@ func Run(slug string) error {
 
 	// 2. Create new function.
 	{
+		if err := utils.MkdirIfNotExist("supabase"); err != nil {
+			return err
+		}
 		if err := utils.MkdirIfNotExist("supabase/functions"); err != nil {
 			return err
 		}
diff --git a/internal/functions/serve/serve.go b/internal/functions/serve/serve.go
index 28d0b1c6..e32543bc 100644
--- a/internal/functions/serve/serve.go
+++ b/internal/functions/serve/serve.go
@@ -38,6 +38,11 @@ func Run(envFilePath string, slug string) error {
 		if err := utils.ValidateFunctionSlug(slug); err != nil {
 			return err
 		}
+		if envFilePath != "" {
+			if _, err := os.ReadFile(envFilePath); err != nil {
+				return fmt.Errorf("Failed to read env file: %w", err)
+			}
+		}
 	}
 
 	// 2. Stop on SIGINT/SIGTERM.
@@ -116,9 +121,7 @@ func Run(envFilePath string, slug string) error {
 			// skip
 		} else {
 			envMap, err := godotenv.Read(envFilePath)
-			if errors.Is(err, os.ErrNotExist) {
-				// skip
-			} else if err != nil {
+			if err != nil {
 				return err
 			}
 			for name, value := range envMap {
diff --git a/internal/start/start.go b/internal/start/start.go
index 2f8f12d9..5fd2743a 100644
--- a/internal/start/start.go
+++ b/internal/start/start.go
@@ -32,15 +32,15 @@ func Run() error {
 		if err := utils.AssertSupabaseCliIsSetUp(); err != nil {
 			return err
 		}
-
 		if err := utils.AssertDockerIsRunning(); err != nil {
 			return err
 		}
-
 		if err := utils.LoadConfig(); err != nil {
 			return err
 		}
-
+		if err := utils.InterpolateEnvInConfig(); err != nil {
+			return err
+		}
 		if err := utils.AssertSupabaseStartIsRunning(); err == nil {
 			return errors.New(utils.Aqua("supabase start") + " is already running. Try running " + utils.Aqua("supabase stop") + " first.")
 		}
diff --git a/internal/utils/config.go b/internal/utils/config.go
index e1b69272..47626125 100644
--- a/internal/utils/config.go
+++ b/internal/utils/config.go
@@ -213,21 +213,6 @@ func LoadConfig() error {
 			Config.Auth.External = map[string]provider{}
 		}
 
-		maybeLoadEnv := func(s string) (string, error) {
-			matches := regexp.MustCompile(`^env\((.*)\)$`).FindStringSubmatch(s)
-			if len(matches) == 0 {
-				return s, nil
-			}
-
-			envName := matches[1]
-			value := os.Getenv(envName)
-			if value == "" {
-				return "", errors.New(`Error evaluating "env(` + envName + `)": environment variable ` + envName + " is unset.")
-			}
-
-			return value, nil
-		}
-
 		for _, ext := range authExternalProviders {
 			if _, ok := Config.Auth.External[ext]; !ok {
 				Config.Auth.External[ext] = provider{
@@ -235,33 +220,56 @@ func LoadConfig() error {
 					ClientId: "",
 					Secret:   "",
 				}
-			} else if Config.Auth.External[ext].Enabled {
-				if Config.Auth.External[ext].ClientId == "" {
-					return fmt.Errorf("Missing required field in config: auth.external.%s.client_id", ext)
-				} else {
-					v, err := maybeLoadEnv(Config.Auth.External[ext].ClientId)
-					if err != nil {
-						return err
-					}
-					Config.Auth.External[ext] = provider{
-						Enabled:  true,
-						ClientId: v,
-						Secret:   Config.Auth.External[ext].Secret,
-					}
+			}
+		}
+	}
+
+	return nil
+}
+
+func InterpolateEnvInConfig() error {
+	maybeLoadEnv := func(s string) (string, error) {
+		matches := regexp.MustCompile(`^env\((.*)\)$`).FindStringSubmatch(s)
+		if len(matches) == 0 {
+			return s, nil
+		}
+
+		envName := matches[1]
+		value := os.Getenv(envName)
+		if value == "" {
+			return "", errors.New(`Error evaluating "env(` + envName + `)": environment variable ` + envName + " is unset.")
+		}
+
+		return value, nil
+	}
+
+	for _, ext := range authExternalProviders {
+		if Config.Auth.External[ext].Enabled {
+			var clientId, secret string
+
+			if Config.Auth.External[ext].ClientId == "" {
+				return fmt.Errorf("Missing required field in config: auth.external.%s.client_id", ext)
+			} else {
+				v, err := maybeLoadEnv(Config.Auth.External[ext].ClientId)
+				if err != nil {
+					return err
 				}
-				if Config.Auth.External[ext].Secret == "" {
-					return fmt.Errorf("Missing required field in config: auth.external.%s.secret", ext)
-				} else {
-					v, err := maybeLoadEnv(Config.Auth.External[ext].Secret)
-					if err != nil {
-						return fmt.Errorf("failed to parse config file: %+v", err)
-					}
-					Config.Auth.External[ext] = provider{
-						Enabled:  true,
-						ClientId: Config.Auth.External[ext].ClientId,
-						Secret:   v,
-					}
+				clientId = v
+			}
+			if Config.Auth.External[ext].Secret == "" {
+				return fmt.Errorf("Missing required field in config: auth.external.%s.secret", ext)
+			} else {
+				v, err := maybeLoadEnv(Config.Auth.External[ext].Secret)
+				if err != nil {
+					return err
 				}
+				secret = v
+			}
+
+			Config.Auth.External[ext] = provider{
+				Enabled:  true,
+				ClientId: clientId,
+				Secret:   secret,
 			}
 		}
 	}


Test patch:
diff --git a/internal/utils/config_test.go b/internal/utils/config_test.go
index 90c3b08c..11fe3477 100644
--- a/internal/utils/config_test.go
+++ b/internal/utils/config_test.go
@@ -5,26 +5,26 @@ import (
 	"testing"
 )
 
-func TestConfigParsing(superTest *testing.T) {
-	superTest.Cleanup(func() {
-		if err := os.Remove("./supabase/config.toml"); err != nil {
+func TestConfigParsing(t *testing.T) {
+	t.Cleanup(func() {
+		if err := os.Remove("supabase/config.toml"); err != nil {
 			if !os.IsNotExist(err) {
-				superTest.Error(err)
+				t.Error(err)
 			}
 		}
-		if err := os.Remove("./supabase"); err != nil {
+		if err := os.Remove("supabase"); err != nil {
 			if !os.IsNotExist(err) {
-				superTest.Error(err)
+				t.Error(err)
 			}
 		}
 	})
 
-	if err := os.Mkdir("./supabase", 0777); err != nil {
-		superTest.Error(err)
-		superTest.FailNow()
+	if err := os.Mkdir("supabase", 0755); err != nil {
+		t.Error(err)
+		t.FailNow()
 	}
 
-	superTest.Run("classic config file", func(t *testing.T) {
+	t.Run("classic config file", func(t *testing.T) {
 		if err := WriteConfig(false); err != nil {
 			t.Error(err)
 			t.FailNow()
@@ -35,7 +35,7 @@ func TestConfigParsing(superTest *testing.T) {
 		}
 	})
 
-	superTest.Run("config file with environment variables", func(t *testing.T) {
+	t.Run("config file with environment variables", func(t *testing.T) {
 		if err := WriteConfig(true); err != nil {
 			t.Error(err)
 			t.FailNow()
@@ -47,6 +47,12 @@ func TestConfigParsing(superTest *testing.T) {
 			t.Error(err)
 			t.FailNow()
 		}
+		if err := InterpolateEnvInConfig(); err != nil {
+			t.Error(err)
+			t.FailNow()
+		}
+		t.Setenv("AZURE_CLIENT_ID", "hello")
+		t.Setenv("AZURE_SECRET", "this is cool")
 
 		if Config.Auth.External["azure"].ClientId != "hello" {
 			t.Errorf("unexpected value for key [ClientId]: %+v", Config.Auth.External["azure"])
@@ -59,13 +65,17 @@ func TestConfigParsing(superTest *testing.T) {
 		}
 	})
 
-	superTest.Run("config file with environment variables fails when unset", func(t *testing.T) {
+	t.Run("config file with environment variables fails when unset", func(t *testing.T) {
 		if err := WriteConfig(true); err != nil {
 			t.Error(err)
 			t.FailNow()
 		}
 
-		if err := LoadConfig(); err == nil {
+		if err := LoadConfig(); err != nil {
+			t.Error(err)
+			t.FailNow()
+		}
+		if err := InterpolateEnvInConfig(); err == nil {
 			t.Error("expected to fail")
 			t.FailNow()
 		}