Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ require (
github.com/mithrandie/csvq-driver v1.7.0
github.com/muesli/reflow v0.3.0
github.com/oapi-codegen/nullable v1.1.0
github.com/olekukonko/tablewriter v1.1.0
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We currently use markdown table renderer from bubbletea

func RenderTable(markdown string) error {

Shall we stick with it for consistency?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sweatybridge dug into this a bit deeper and sadly it looks like the style options for the markdown rendering using that lib are a lot more limited. For example, there's no way to configure colspan (to make the header span multiple columns) or rounded corners, so the final output will be much more basic than the above screenshot.

If we're keen on limiting dependencies, I could attempt to render the above from scratch (but we'll likely have less flexibility in the future for things like word wrap).

github.com/slack-go/slack v0.17.3
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.1
Expand Down Expand Up @@ -251,7 +252,8 @@ require (
github.com/oapi-codegen/runtime v1.1.2 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
Expand Down
9 changes: 6 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,6 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
Expand Down Expand Up @@ -770,8 +769,12 @@ github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//J
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037/go.mod h1:2bpvgLBZEtENV5scfDFEtB/5+1M4hkQhDQrccEJ/qGw=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 h1:bQx3WeLcUWy+RletIKwUIt4x3t8n2SxavmoclizMb8c=
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90/go.mod h1:y5+oSEHCPT/DGrS++Wc/479ERge0zTFxaF8PbGKcg2o=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
github.com/olekukonko/tablewriter v1.1.0 h1:N0LHrshF4T39KvI96fn6GT8HEjXRXYNDrDjKFDB7RIY=
github.com/olekukonko/tablewriter v1.1.0/go.mod h1:5c+EBPeSqvXnLLgkm9isDdzR3wjfBkHR9Nhfp3NWrzo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
Expand Down
200 changes: 168 additions & 32 deletions internal/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ import (
"net/http"
"net/url"
"os"
"reflect"
"strings"
"sync"
"time"

"github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/container"
"github.com/go-errors/errors"
"github.com/olekukonko/tablewriter"
"github.com/olekukonko/tablewriter/tw"
"github.com/spf13/afero"
"github.com/supabase/cli/internal/utils"
"github.com/supabase/cli/internal/utils/flags"
Expand All @@ -26,9 +26,11 @@ import (

type CustomName struct {
ApiURL string `env:"api.url,default=API_URL"`
RestURL string `env:"api.rest_url,default=REST_URL"`
GraphqlURL string `env:"api.graphql_url,default=GRAPHQL_URL"`
StorageS3URL string `env:"api.storage_s3_url,default=STORAGE_S3_URL"`
McpURL string `env:"api.mcp_url,default=MCP_URL"`
FunctionsURL string `env:"api.functions_url,default=FUNCTIONS_URL"`
DbURL string `env:"db.url,default=DB_URL"`
StudioURL string `env:"studio.url,default=STUDIO_URL"`
InbucketURL string `env:"inbucket.url,default=INBUCKET_URL,deprecated"`
Expand All @@ -42,6 +44,24 @@ type CustomName struct {
StorageS3SecretAccessKey string `env:"storage.s3_secret_access_key,default=S3_PROTOCOL_ACCESS_KEY_SECRET"`
StorageS3Region string `env:"storage.s3_region,default=S3_PROTOCOL_REGION"`
}
type OutputType string

const (
Text OutputType = "text"
Link OutputType = "link"
Key OutputType = "key"
)

type OutputItem struct {
Label string
Value string
Type OutputType
}

type OutputGroup struct {
Name string
Items []OutputItem
}

func (c *CustomName) toValues(exclude ...string) map[string]string {
values := map[string]string{
Expand All @@ -56,7 +76,9 @@ func (c *CustomName) toValues(exclude ...string) map[string]string {

if apiEnabled {
values[c.ApiURL] = utils.Config.Api.ExternalUrl
values[c.RestURL] = utils.GetApiUrl("/rest/v1")
values[c.GraphqlURL] = utils.GetApiUrl("/graphql/v1")
values[c.FunctionsURL] = utils.GetApiUrl("/functions/v1")
if studioEnabled {
values[c.McpURL] = utils.GetApiUrl("/mcp")
}
Expand Down Expand Up @@ -210,43 +232,157 @@ func printStatus(names CustomName, format string, w io.Writer, exclude ...string

func PrettyPrint(w io.Writer, exclude ...string) {
names := CustomName{
ApiURL: " " + utils.Aqua("API URL"),
GraphqlURL: " " + utils.Aqua("GraphQL URL"),
StorageS3URL: " " + utils.Aqua("S3 Storage URL"),
McpURL: " " + utils.Aqua("MCP URL"),
DbURL: " " + utils.Aqua("Database URL"),
StudioURL: " " + utils.Aqua("Studio URL"),
InbucketURL: " " + utils.Aqua("Inbucket URL"),
MailpitURL: " " + utils.Aqua("Mailpit URL"),
PublishableKey: " " + utils.Aqua("Publishable key"),
SecretKey: " " + utils.Aqua("Secret key"),
JWTSecret: " " + utils.Aqua("JWT secret"),
AnonKey: " " + utils.Aqua("anon key"),
ServiceRoleKey: "" + utils.Aqua("service_role key"),
StorageS3AccessKeyId: " " + utils.Aqua("S3 Access Key"),
StorageS3SecretAccessKey: " " + utils.Aqua("S3 Secret Key"),
StorageS3Region: " " + utils.Aqua("S3 Region"),
ApiURL: "API_URL",
RestURL: "REST_URL",
GraphqlURL: "GRAPHQL_URL",
FunctionsURL: "FUNCTIONS_URL",
StorageS3URL: "STORAGE_S3_URL",
McpURL: "MCP_URL",
DbURL: "DB_URL",
StudioURL: "STUDIO_URL",
InbucketURL: "INBUCKET_URL",
MailpitURL: "MAILPIT_URL",
PublishableKey: "PUBLISHABLE_KEY",
SecretKey: "SECRET_KEY",
JWTSecret: "JWT_SECRET",
AnonKey: "ANON_KEY",
ServiceRoleKey: "SERVICE_ROLE_KEY",
StorageS3AccessKeyId: "S3_PROTOCOL_ACCESS_KEY_ID",
StorageS3SecretAccessKey: "S3_PROTOCOL_SECRET_ACCESS_KEY",
StorageS3Region: "S3_PROTOCOL_REGION",
}
values := names.toValues(exclude...)
// Iterate through map in order of declared struct fields
t := reflect.TypeOf(names)
val := reflect.ValueOf(names)
for i := 0; i < val.NumField(); i++ {
k := val.Field(i).String()
if tag := t.Field(i).Tag.Get("env"); isDeprecated(tag) {
continue

groups := []OutputGroup{
{
Name: "🛠️ Development Tools",
Items: []OutputItem{
{Label: "Studio", Value: values[names.StudioURL], Type: Link},
{Label: "Mailpit", Value: values[names.MailpitURL], Type: Link},
{Label: "MCP", Value: values[names.McpURL], Type: Link},
},
},
{
Name: "🌐 APIs",
Items: []OutputItem{
{Label: "Project URL", Value: values[names.ApiURL], Type: Link},
{Label: "REST", Value: values[names.RestURL], Type: Link},
{Label: "GraphQL", Value: values[names.GraphqlURL], Type: Link},
{Label: "Edge Functions", Value: values[names.FunctionsURL], Type: Link},
},
},
{
Name: "🗄️ Database",
Items: []OutputItem{
{Label: "URL", Value: values[names.DbURL], Type: Link},
},
},
{
Name: "🔑 Authentication Keys",
Items: []OutputItem{
{Label: "Publishable", Value: values[names.PublishableKey], Type: Key},
{Label: "Secret", Value: values[names.SecretKey], Type: Key},
},
},
{
Name: "📦 Storage (S3)",
Items: []OutputItem{
{Label: "URL", Value: values[names.StorageS3URL], Type: Link},
{Label: "Access Key", Value: values[names.StorageS3AccessKeyId], Type: Key},
{Label: "Secret Key", Value: values[names.StorageS3SecretAccessKey], Type: Key},
{Label: "Region", Value: values[names.StorageS3Region], Type: Text},
},
},
}

for _, group := range groups {
// ensure at least one item in the group is non-empty
shouldPrint := false
for _, item := range group.Items {
if item.Value != "" {
shouldPrint = true
break
}
}
if v, ok := values[k]; ok {
fmt.Fprintf(w, "%s: %s\n", k, v)
if shouldPrint {
printTable(w, group.Name, group.Items)
fmt.Fprintln(w)
}
}
}

func isDeprecated(tag string) bool {
for part := range strings.SplitSeq(tag, ",") {
if strings.EqualFold(part, "deprecated") {
return true
func printTable(w io.Writer, title string, rows []OutputItem) {
table := tablewriter.NewTable(w,
// Rounded corners
tablewriter.WithSymbols(tw.NewSymbols(tw.StyleRounded)),

// Table content formatting
tablewriter.WithConfig(tablewriter.Config{
Header: tw.CellConfig{
Formatting: tw.CellFormatting{
AutoFormat: tw.Off,
MergeMode: tw.MergeHorizontal,
},
Alignment: tw.CellAlignment{
Global: tw.AlignLeft,
},
Filter: tw.CellFilter{
Global: func(s []string) []string {
for i := range s {
s[i] = utils.Bold(s[i])
}
return s
},
},
},
Row: tw.CellConfig{
Alignment: tw.CellAlignment{
Global: tw.AlignLeft,
},
ColMaxWidths: tw.CellWidth{
PerColumn: map[int]int{0: 16},
},
Filter: tw.CellFilter{
PerColumn: []func(string) string{
func(s string) string {
return utils.Green(s)
},
},
},
},
Behavior: tw.Behavior{
Compact: tw.Compact{
Merge: tw.On,
},
},
}),
)

// Set title as header (merged across all columns)
table.Header(title, title)

var appendError error

// Add data rows with values colored based on type
for _, row := range rows {
if row.Value != "" {
switch row.Type {
case Link:
appendError = table.Append(row.Label, utils.Aqua(row.Value))
case Key:
appendError = table.Append(row.Label, utils.Yellow(row.Value))
case Text:
appendError = table.Append(row.Label, row.Value)
}
}
}
return false

if appendError != nil {
fmt.Fprintln(utils.GetDebugLogger(), appendError)
}

renderError := table.Render()
if renderError != nil {
fmt.Fprintln(utils.GetDebugLogger(), renderError)
}
}
4 changes: 4 additions & 0 deletions internal/utils/colors.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ func Yellow(str string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("11")).Render(str)
}

func Green(str string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("10")).Render(str)
}

// For errors.
func Red(str string) string {
return lipgloss.NewStyle().Foreground(lipgloss.Color("9")).Render(str)
Expand Down
33 changes: 7 additions & 26 deletions pkg/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.