Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prod deploy #2110

Merged
merged 30 commits into from
Apr 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
3f4b4c0
chore(deps): bump github.com/cenkalti/backoff/v4 from 4.2.1 to 4.3.0 …
dependabot[bot] Mar 26, 2024
5c8832a
fix: bump edge runtime to 1.41.2
laktek Mar 26, 2024
d310d21
Merge branch 'develop' into bump-er-1-41-2
laktek Mar 26, 2024
2b909a6
fix: bump edge runtime to 1.41.2 (#2093)
laktek Mar 26, 2024
36e2aeb
feat: implement quick start templates for cli bootstrap
sweatybridge Mar 21, 2024
e1e62a0
chore: generate password when left blank
sweatybridge Mar 22, 2024
836277b
fix: use parallel file download
sweatybridge Mar 22, 2024
35885c0
fix: prompt before overwriting current directory
sweatybridge Mar 22, 2024
4d40272
chore: emit supabase url in .env
sweatybridge Mar 22, 2024
a1f6350
fix: populate example env when available
sweatybridge Mar 25, 2024
cebf752
chore: fix linter issues
sweatybridge Mar 26, 2024
6e602c8
chore: only use go-errors import
sweatybridge Mar 26, 2024
866c29e
chore: use debug logger for all silent errors
sweatybridge Mar 26, 2024
20c9d2c
fix: track original workdir before changing
sweatybridge Mar 26, 2024
bc04f3b
fix: prompt workdir before bootstrap
sweatybridge Mar 26, 2024
9d9d228
chore: version bump Realtime image to v2.26.8 (#2092)
w3b6x9 Mar 26, 2024
d98aab5
fix(storage): resolve relative path from cwd (#2094)
sweatybridge Mar 26, 2024
90c7043
chore(deps): bump github.com/charmbracelet/glamour from 0.6.0 to 0.7.…
dependabot[bot] Mar 27, 2024
4e37b33
fix: Bump the version of studio image (#2095)
ivasilov Mar 27, 2024
723d951
chore: use pflag default value field
sweatybridge Mar 27, 2024
dbe2bcf
fix: reuse common login flows
sweatybridge Mar 27, 2024
d5c9ae1
fix: suggest npm commands after bootstrap (#2097)
sweatybridge Mar 27, 2024
41ae366
chore(deps): bump github.com/golangci/golangci-lint from 1.57.1 to 1.…
dependabot[bot] Mar 29, 2024
a1d2c87
chore(deps): bump github.com/containers/common from 0.58.0 to 0.58.1 …
dependabot[bot] Mar 29, 2024
678e9ed
chore: version bump Realtime image to v2.27.5
w3b6x9 Mar 31, 2024
5430459
chore(deps): bump github.com/go-git/go-git/v5 from 5.11.0 to 5.12.0 (…
dependabot[bot] Apr 1, 2024
e9a40c7
feat: add option to bootstrap from scratch
sweatybridge Apr 1, 2024
291a5be
feat: flag to overwrite existing config on init
sweatybridge Apr 1, 2024
75b6ab9
chore: add missing period to flag help
sweatybridge Apr 1, 2024
b04799b
fix: check project health on bootstrap (#2109)
sweatybridge Apr 1, 2024
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
4 changes: 0 additions & 4 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,3 @@ issues:
- linters:
- stylecheck
text: "ST1003:"
- linters:
- staticcheck
- stylecheck
text: "ST1005:"
88 changes: 88 additions & 0 deletions cmd/bootstrap.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cmd

import (
"context"
"fmt"
"os"
"os/signal"
"strings"

"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/spf13/viper"
"github.com/supabase/cli/internal/bootstrap"
"github.com/supabase/cli/internal/utils"
)

var (
templateUrl string

bootstrapCmd = &cobra.Command{
GroupID: groupQuickStart,
Use: "bootstrap [template]",
Short: "Bootstrap a Supabase project from a starter template",
Args: cobra.MaximumNArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
if !viper.IsSet("WORKDIR") {
title := fmt.Sprintf("Enter a directory to bootstrap your project (or leave blank to use %s): ", utils.Bold(utils.CurrentDirAbs))
workdir, err := utils.PromptText(title, os.Stdin)
if err != nil {
return err
}
viper.Set("WORKDIR", workdir)
}
ctx, _ := signal.NotifyContext(cmd.Context(), os.Interrupt)
client := bootstrap.GetGtihubClient(ctx)
templates, err := bootstrap.ListSamples(ctx, client)
if err != nil {
return err
}
if len(args) > 0 {
name := strings.ToLower(args[0])
for _, t := range templates {
if t.Name == name {
templateUrl = t.Url
}
}
}
if len(templateUrl) == 0 {
if err := promptStarterTemplate(ctx, templates); err != nil {
return err
}
}
return bootstrap.Run(ctx, templateUrl, afero.NewOsFs())
},
}
)

func init() {
bootstrapFlags := bootstrapCmd.Flags()
bootstrapFlags.StringP("password", "p", "", "Password to your remote Postgres database.")
cobra.CheckErr(viper.BindPFlag("DB_PASSWORD", bootstrapFlags.Lookup("password")))
rootCmd.AddCommand(bootstrapCmd)
}

func promptStarterTemplate(ctx context.Context, templates []bootstrap.StarterTemplate) error {
items := make([]utils.PromptItem, len(templates))
for i, t := range templates {
items[i] = utils.PromptItem{
Index: i,
Summary: t.Name,
Details: t.Description,
}
}
items = append(items, utils.PromptItem{
Index: len(items),
Summary: "scratch",
Details: "An empty project from scratch.",
})
title := "Which starter template do you want to use?"
choice, err := utils.PromptChoice(ctx, title, items)
if err != nil {
return err
}
if choice.Index < len(templates) {
templateUrl = templates[choice.Index].Url
}
return nil
}
2 changes: 1 addition & 1 deletion cmd/gen.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package cmd

import (
"errors"
"os"
"os/signal"

env "github.com/Netflix/go-env"
"github.com/go-errors/errors"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/supabase/cli/internal/gen/keys"
Expand Down
12 changes: 7 additions & 5 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ import (

var (
createVscodeSettings = new(bool)
useOrioleDB bool
createIntellijSettings = new(bool)
initCmd = &cobra.Command{
initParams = utils.InitParams{}

initCmd = &cobra.Command{
GroupID: groupLocalDev,
Use: "init",
Short: "Initialize a local project",
Expand All @@ -26,7 +27,7 @@ var (
return cmd.Root().PersistentPreRunE(cmd, args)
},
PreRun: func(cmd *cobra.Command, args []string) {
if useOrioleDB {
if initParams.UseOrioleDB {
cobra.CheckErr(cmd.MarkFlagRequired("experimental"))
}
},
Expand All @@ -40,7 +41,7 @@ var (
createIntellijSettings = nil
}

return _init.Run(fsys, createVscodeSettings, createIntellijSettings, useOrioleDB)
return _init.Run(fsys, createVscodeSettings, createIntellijSettings, initParams)
},
PostRun: func(cmd *cobra.Command, args []string) {
fmt.Println("Finished " + utils.Aqua("supabase init") + ".")
Expand All @@ -54,6 +55,7 @@ func init() {
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.BoolVar(&useOrioleDB, "use-orioledb", false, "Use OrioleDB storage engine for Postgres")
flags.BoolVar(&initParams.UseOrioleDB, "use-orioledb", false, "Use OrioleDB storage engine for Postgres.")
flags.BoolVar(&initParams.Overwrite, "force", false, "Overwrite existing "+utils.ConfigPath+".")
rootCmd.AddCommand(initCmd)
}
84 changes: 10 additions & 74 deletions cmd/login.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
package cmd

import (
"bytes"
"fmt"
"io"
"os"
"os/user"
"strings"
"time"

"github.com/go-errors/errors"
"github.com/google/uuid"
"github.com/spf13/afero"
"github.com/spf13/cobra"
"github.com/supabase/cli/internal/login"
Expand All @@ -22,94 +15,37 @@ var (
ErrMissingToken = errors.Errorf("Cannot use automatic login flow inside non-TTY environments. Please provide %s flag or set the %s environment variable.", utils.Aqua("--token"), utils.Aqua("SUPABASE_ACCESS_TOKEN"))
)

func generateTokenName() (string, error) {
user, err := user.Current()
if err != nil {
return "", errors.Errorf("cannot retrieve username: %w", err)
}

hostname, err := os.Hostname()
if err != nil {
return "", errors.Errorf("cannot retrieve hostname: %w", err)
var (
params = login.RunParams{
// Skip the browser if we are inside non-TTY environment, which is the case for any CI.
OpenBrowser: term.IsTerminal(int(os.Stdin.Fd())),
Fsys: afero.NewOsFs(),
}

return fmt.Sprintf("cli_%s@%s_%d", user.Username, hostname, time.Now().Unix()), nil
}

var (
loginCmd = &cobra.Command{
GroupID: groupLocalDev,
Use: "login",
Short: "Authenticate using an access token",
RunE: func(cmd *cobra.Command, args []string) error {
params := login.RunParams{
Fsys: afero.NewOsFs(),
}

if !term.IsTerminal(int(os.Stdin.Fd())) {
var buf bytes.Buffer
if _, err := io.Copy(&buf, os.Stdin); err == nil {
token := strings.TrimSpace(buf.String())
if len(token) > 0 {
params.Token = token
}
}
} else if cmd.Flags().Changed("token") {
token, err := cmd.Flags().GetString("token")
if err != nil {
return errors.Errorf("cannot parse 'token' flag: %w", err)
}
params.Token = token
} else if token := os.Getenv("SUPABASE_ACCESS_TOKEN"); token != "" {
params.Token = token
}

// Login encryption and Session ID are only required for end-to-end communication.
// We can skip it if token is already provided by user.
if params.Token == "" {
enc, err := login.NewLoginEncryption()
if err != nil {
return err
}
params.Encryption = enc
params.SessionId = uuid.New().String()
params.Token = login.ParseAccessToken(os.Stdin)
}

if !term.IsTerminal(int(os.Stdin.Fd())) && params.Token == "" {
if params.Token == "" && !params.OpenBrowser {
return ErrMissingToken
}

if cmd.Flags().Changed("name") {
name, err := cmd.Flags().GetString("name")
if err != nil {
return errors.Errorf("cannot parse 'name' flag: %w", err)
}
params.TokenName = name
} else {
name, err := generateTokenName()
if err != nil {
params.TokenName = fmt.Sprintf("cli_%d", time.Now().Unix())
} else {
params.TokenName = name
}
}

if cmd.Flags().Changed("no-browser") {
params.OpenBrowser = false
} else {
// Skip the browser if we are inside non-TTY environment, which is the case for any CI.
params.OpenBrowser = term.IsTerminal(int(os.Stdin.Fd()))
}

return login.Run(cmd.Context(), os.Stdout, params)
},
}
)

func init() {
loginFlags := loginCmd.Flags()
loginFlags.String("token", "", "Use provided token instead of automatic login flow")
loginFlags.String("name", "", "Name that will be used to store token in your settings, defaults to built-in token name generator")
loginFlags.StringVar(&params.Token, "token", "", "Use provided token instead of automatic login flow")
loginFlags.StringVar(&params.TokenName, "name", "", "Name that will be used to store token in your settings")
loginFlags.Lookup("name").DefValue = "built-in token name generator"
loginFlags.Bool("no-browser", false, "Do not open browser automatically")
rootCmd.AddCommand(loginCmd)
}
67 changes: 0 additions & 67 deletions cmd/projects.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
package cmd

import (
"errors"
"fmt"
"os"
"sort"
"strings"

"github.com/spf13/afero"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -58,9 +55,6 @@ var (
if len(args) > 0 {
projectName = args[0]
}
if interactive {
cobra.CheckErr(PromptCreateFlags(cmd))
}
return create.Run(cmd.Context(), api.CreateProjectBody{
Name: projectName,
OrganizationId: orgId,
Expand Down Expand Up @@ -141,64 +135,3 @@ func init() {
projectsCmd.AddCommand(projectsApiKeysCmd)
rootCmd.AddCommand(projectsCmd)
}

func PromptCreateFlags(cmd *cobra.Command) error {
ctx := cmd.Context()
if len(projectName) > 0 {
fmt.Fprintln(os.Stderr, printKeyValue("Creating project", projectName))
} else {
name, err := utils.PromptText("Enter your project name: ", os.Stdin)
if err != nil {
return err
}
if len(name) == 0 {
return errors.New("project name cannot be empty")
}
projectName = name
}
if !cmd.Flags().Changed("org-id") {
title := "Which organisation do you want to create the project for?"
resp, err := utils.GetSupabase().GetOrganizationsWithResponse(ctx)
if err != nil {
return err
}
if resp.JSON200 == nil {
return errors.New("Unexpected error retrieving organizations: " + string(resp.Body))
}
items := make([]utils.PromptItem, len(*resp.JSON200))
for i, org := range *resp.JSON200 {
items[i] = utils.PromptItem{Summary: org.Name, Details: org.Id}
}
choice, err := utils.PromptChoice(ctx, title, items)
if err != nil {
return err
}
orgId = choice.Details
}
fmt.Fprintln(os.Stderr, printKeyValue("Selected org-id", orgId))
if !cmd.Flags().Changed("region") {
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++
}
choice, err := utils.PromptChoice(ctx, title, items)
if err != nil {
return err
}
region.Value = choice.Summary
}
fmt.Fprintln(os.Stderr, printKeyValue("Selected region", region.Value))
if dbPassword == "" {
dbPassword = flags.PromptPassword(os.Stdin)
}
return nil
}

func printKeyValue(key, value string) string {
indent := 20 - len(key)
spaces := strings.Repeat(" ", indent)
return key + ":" + spaces + value
}
Loading