diff --git a/.github/workflows/brew-test.yml b/.github/workflows/brew-test.yml index 72f62f4d..0f8c4ce2 100644 --- a/.github/workflows/brew-test.yml +++ b/.github/workflows/brew-test.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Set up Homebrew id: set-up-homebrew diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b9f11a61..0190efec 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -25,13 +25,13 @@ jobs: needs: authorize runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} #ref: "refs/pull/${{ github.event.number }}/merge" # THIS IS INSECURE - name: Setup Golang with cache - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version-file: go.mod cache: false @@ -55,7 +55,7 @@ jobs: run: go vet -vettool=${HOME}/go/bin/sqlclosecheck ./... - name: Staticcheck - uses: dominikh/staticcheck-action@v1.3.0 + uses: dominikh/staticcheck-action@v1 with: version: "2023.1.2" build-tags: "preview" diff --git a/.github/workflows/goreleaser.yml b/.github/workflows/goreleaser.yml index 6315d55a..27e1463c 100644 --- a/.github/workflows/goreleaser.yml +++ b/.github/workflows/goreleaser.yml @@ -13,11 +13,11 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: go-version: "1.21" - name: Run GoReleaser diff --git a/internal/cmd/org.go b/internal/cmd/org.go index 72f6b622..c31d2545 100644 --- a/internal/cmd/org.go +++ b/internal/cmd/org.go @@ -483,19 +483,27 @@ var membersRemoveCmd = &cobra.Command{ } var orgBillingCmd = &cobra.Command{ - Use: "billing", - Short: "Manange payment methods for the current organization.", - Args: cobra.ExactArgs(0), + Use: "billing", + Short: "Manange payment methods for the current organization.", + Hidden: true, + Args: cobra.ExactArgs(0), RunE: func(cmd *cobra.Command, args []string) error { - cmd.SilenceUsage = true + return BillingPortal() + }, +} - client, err := authedTursoClient() - if err != nil { - return err - } +func BillingPortal() error { + settings, err := settings.ReadSettings() + if err != nil { + return err + } - return billingPortal(client) - }, + org := settings.Organization() + if org == "" { + org = settings.GetUsername() + } + + return billingPortal(org) } func listOrganizations(client *turso.Client, fresh ...bool) ([]turso.Organization, error) { diff --git a/internal/cmd/plan.go b/internal/cmd/plan.go index d2869058..a44850d6 100644 --- a/internal/cmd/plan.go +++ b/internal/cmd/plan.go @@ -2,13 +2,11 @@ package cmd import ( "fmt" - "strconv" "strings" "time" "github.com/dustin/go-humanize" "github.com/fatih/color" - "github.com/manifoldco/promptui" "github.com/pkg/browser" "github.com/rodaine/table" "github.com/spf13/cobra" @@ -166,59 +164,13 @@ var planSelectCmd = &cobra.Command{ Use: "select", Short: "Change your current organization plan", Args: cobra.MaximumNArgs(1), + Hidden: true, ValidArgsFunction: planNameArg, RunE: func(cmd *cobra.Command, args []string) error { - timeline, err := flags.Timeline() - if err != nil { - return err - } - - overages := flags.Overages(cmd) - - client, err := authedTursoClient() - if err != nil { - return err - } - - plans, subscription, hasPaymentMethod, err := GetSelectPlanInfo(client) - if err != nil { - return fmt.Errorf("failed to get plans: %w", err) - } - - selected, err := selectedPlan(subscription, plans, args) - if err != nil { - return err - } - - settings, err := settings.ReadSettings() - if err != nil { - return fmt.Errorf("could not retrieve local config: %w", err) - } - - org := settings.Organization() - if org != "" && selected == "starter" { - fmt.Printf("You can't downgrade to the %s plan with a team organization\n", internal.Emph("starter")) - fmt.Printf("Instead, you can destroy it with %s\n", internal.Emph(fmt.Sprintf("turso org destroy %s", org))) - return nil - } - - return changePlan(client, plans, subscription, hasPaymentMethod, selected, timeline, overages) + return BillingPortal() }, } -func selectedPlan(subscription turso.Subscription, plans []turso.Plan, args []string) (string, error) { - if len(args) > 0 { - return args[0], nil - } - - selected, err := promptPlanSelection(plans, subscription.Plan) - if err != nil { - return "", err - } - - return selected, nil -} - func planNameArg(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { if len(args) != 0 { return []string{}, cobra.ShellCompDirectiveNoFileComp @@ -242,183 +194,35 @@ func planNameArg(cmd *cobra.Command, args []string, toComplete string) ([]string } var planUpgradeCmd = &cobra.Command{ - Use: "upgrade", - Short: "Upgrade your current organization plan", - Args: cobra.ExactArgs(0), + Use: "upgrade", + Short: "Upgrade your current organization plan", + Args: cobra.ExactArgs(0), + Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { - client, err := authedTursoClient() - if err != nil { - return err - } - - plans, current, hasPaymentMethod, err := GetSelectPlanInfo(client) - if err != nil { - return fmt.Errorf("failed to get plans: %w", err) - } - - if current.Plan == "scaler" { - fmt.Printf("You've already upgraded to %s! 🎉\n", internal.Emph(current)) - fmt.Println() - fmt.Println("If you need more resources, we're happy to help.") - fmt.Printf("You can find us at %s or at Discord (%s)\n", internal.Emph("sales@turso.tech"), internal.Emph("https://discord.com/invite/4B5D7hYwub")) - return nil - } - - return changePlan(client, plans, current, hasPaymentMethod, "scaler", "", nil) + return BillingPortal() }, } var planEnableOverages = &cobra.Command{ - Use: "enable", - Short: "Enable overages for your current organization plan", - Args: cobra.ExactArgs(0), + Use: "enable", + Short: "Enable overages for your current organization plan", + Args: cobra.ExactArgs(0), + Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { - settings, err := settings.ReadSettings() - if err != nil { - return err - } - var org string - if org = settings.Organization(); org == "" { - org = settings.GetUsername() - } - client, err := authedTursoClient() - if err != nil { - return err - } - - hasPaymentMethod, err := client.Billing.HasPaymentMethod() - if err != nil { - return err - } - if !hasPaymentMethod { - ok, err := PaymentMethodHelperOverages(client) - if err != nil { - return fmt.Errorf("failed to check payment method: %w", err) - } - if !ok { - return fmt.Errorf("failed to add payment method") - } - fmt.Println("Payment method added successfully.") - fmt.Printf("You can manage your payment methods with %s.\n\n", internal.Emph("turso org billing")) - } - if err = client.Organizations.SetOverages(org, true); err != nil { - return err - } - fmt.Println("Overages enabled successfully.") - return nil + return BillingPortal() }, } var planDisableOverages = &cobra.Command{ - Use: "disable", - Short: "Disable overages for your current organization plan", - Args: cobra.ExactArgs(0), + Use: "disable", + Short: "Disable overages for your current organization plan", + Args: cobra.ExactArgs(0), + Hidden: true, RunE: func(cmd *cobra.Command, args []string) error { - settings, err := settings.ReadSettings() - if err != nil { - return err - } - var org string - if org = settings.Organization(); org == "" { - org = settings.GetUsername() - } - client, err := authedTursoClient() - if err != nil { - return err - } - - if err = client.Organizations.SetOverages(org, false); err != nil { - return err - } - fmt.Println("Overages disabled successfully.") - return nil + return BillingPortal() }, } -func changePlan(client *turso.Client, plans []turso.Plan, subscription turso.Subscription, hasPaymentMethod bool, selected, timeline string, overages *bool) error { - current := subscription.Plan - if selected == current && (timeline == "" || subscription.Timeline == timeline) && (overages == nil || subscription.Overages == *overages) { - fmt.Println("You're all set! No changes are needed.") - return nil - } - - upgrade := planChangeType(getPlan(current, plans), getPlan(selected, plans)) - if !hasPaymentMethod && requiresPaymentMethod(getPlan(current, plans)) { - ok, err := PaymentMethodHelper(client, selected) - if err != nil { - return fmt.Errorf("failed to check payment method: %w", err) - } - if !ok { - return nil - } - fmt.Println("Payment method added successfully.") - fmt.Printf("You can manage your payment methods with %s.\n\n", internal.Emph("turso org billing")) - } - - change := "changing" - if upgrade > 0 { - change = "upgrading" - } - if upgrade < 0 { - change = "downgrading" - } - - fmt.Printf("You're %s to the %s plan.\n", change, internal.Emph(selected)) - - if requiresPaymentMethod(getPlan(selected, plans)) { - printPricingInfoDisclaimer() - } - - if ok, _ := promptConfirmation("Do you want to continue?"); !ok { - fmt.Printf("Plan change cancelled. You're still on %s.\n", internal.Emph(current)) - return nil - } - - if err := client.Subscriptions.Update(selected, timeline, overages); err != nil { - return err - } - - fmt.Println() - - change = "changed" - if upgrade > 0 { - change = "upgraded" - } - if upgrade < 0 { - change = "downgraded" - } - - fmt.Printf("You've been %s to plan %s.\n", change, internal.Emph(selected)) - fmt.Printf("To see your new quotas, use %s.\n", internal.Emph("turso plan show")) - return nil -} - -func requiresPaymentMethod(plan turso.Plan) bool { - return plan.Price != "0" -} - -func PaymentMethodHelper(client *turso.Client, selected string) (bool, error) { - fmt.Printf("You need to add a payment method before you can upgrade to the %s plan.\n", internal.Emph(selected)) - printPricingInfoDisclaimer() - - ok, _ := promptConfirmation("Want to add a payment method right now?") - if !ok { - fmt.Printf("When you're ready, you can use %s to manage your payment methods.\n", internal.Emph("turso org billing")) - return false, nil - } - - fmt.Println() - if err := billingPortal(client); err != nil { - return false, err - } - fmt.Println() - - spinner := prompt.Spinner("Waiting for you to add a payment method") - defer spinner.Stop() - - return checkPaymentMethod(client, "") -} - func hasPaymentMethodCheck(client *turso.Client, stripeId string) (bool, error) { if stripeId != "" { return client.Billing.HasPaymentMethodWithStripeId(stripeId) @@ -448,28 +252,6 @@ func checkPaymentMethod(client *turso.Client, stripeId string) (bool, error) { } } -func PaymentMethodHelperOverages(client *turso.Client) (bool, error) { - fmt.Print("You need to add a payment method before you can enable overages.\n") - printPricingInfoDisclaimer() - - ok, _ := promptConfirmation("Want to add a payment method right now?") - if !ok { - fmt.Printf("When you're ready, you can use %s to manage your payment methods.\n", internal.Emph("turso org billing")) - return false, nil - } - - fmt.Println() - if err := billingPortal(client); err != nil { - return false, err - } - fmt.Println() - - spinner := prompt.Spinner("Waiting for you to add a payment method") - defer spinner.Stop() - - return checkPaymentMethod(client, "") -} - func PaymentMethodHelperWithStripeId(client *turso.Client, stripeId, orgName string) (bool, error) { fmt.Printf("You need to add a payment method before you can create organization %s on the %s plan.\n", internal.Emph(orgName), internal.Emph("scaler")) printPricingInfoDisclaimer() @@ -522,57 +304,6 @@ func getPlans(client *turso.Client) ([]turso.Plan, error) { return plans, nil } -func promptPlanSelection(plans []turso.Plan, current string) (string, error) { - planNames := make([]string, 0, len(plans)) - cur := 0 - for _, plan := range plans { - if plan.Name == current { - cur = len(planNames) - planNames = append(planNames, fmt.Sprintf("%s (current)", internal.Emph(plan.Name))) - continue - } - planNames = append(planNames, plan.Name) - } - - settings, err := settings.ReadSettings() - if err != nil { - return "", err - } - - var org string - if org = settings.Organization(); org == "" { - org = settings.GetUsername() - } - - prompt := promptui.Select{ - CursorPos: cur, - HideHelp: true, - Label: fmt.Sprintf("Select a plan for organization %s", internal.Emph(org)), - Items: planNames, - HideSelected: true, - } - - _, result, err := prompt.Run() - if strings.HasSuffix(result, "(current)") { - result = current - } - return result, err -} - -func planChangeType(current, selected turso.Plan) int { - cp, _ := strconv.Atoi(current.Price) - sp, _ := strconv.Atoi(selected.Price) - switch { - case sp > cp: - return 1 - case sp < cp: - return -1 - default: - return 0 - } - -} - func getPlan(name string, plans []turso.Plan) turso.Plan { for _, plan := range plans { if plan.Name == name { @@ -582,18 +313,14 @@ func getPlan(name string, plans []turso.Plan) turso.Plan { return turso.Plan{} } -func billingPortal(client *turso.Client) error { - portal, err := client.Billing.Portal() - if err != nil { - return err - } - +func billingPortal(currentOrg string) error { + url := "https://app.turso.tech/" + currentOrg + "/settings/billing" msg := "Opening your browser at:" - if err := browser.OpenURL(portal.URL); err != nil { + if err := browser.OpenURL(url); err != nil { msg = "Access the following URL to manage your payment methods:" } fmt.Println(msg) - fmt.Println(portal.URL) + fmt.Println(url) return nil }