From 5f19d8569f25cc317f9e8a2e79f7be80a3a9b4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=83=A5=EB=83=90=EC=B1=A0?= Date: Tue, 30 Jul 2024 16:21:09 +0900 Subject: [PATCH 01/12] fix: bump edge-runtime to 1.55.2 (#2579) --- pkg/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/constants.go b/pkg/config/constants.go index 04e112d51..efe400396 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -12,7 +12,7 @@ const ( pgmetaImage = "supabase/postgres-meta:v0.83.2" studioImage = "supabase/studio:20240729-ce42139" imageProxyImage = "darthsim/imgproxy:v3.8.0" - edgeRuntimeImage = "supabase/edge-runtime:v1.55.0" + edgeRuntimeImage = "supabase/edge-runtime:v1.55.2" vectorImage = "timberio/vector:0.28.1-alpine" supavisorImage = "supabase/supavisor:1.1.56" gotrueImage = "supabase/gotrue:v2.151.0" From 88f78c02dd917612355b9fd4972be3729b3cb3a5 Mon Sep 17 00:00:00 2001 From: Joel Lee Date: Fri, 2 Aug 2024 19:31:12 +0200 Subject: [PATCH 02/12] fix: upgrade auth version to v2.157.1 and add slack oidc config (#2583) * fix: upgrade auth version to v2.157.1 * fix: add slack_oidc * fix: add comment --- pkg/config/config.go | 3 ++- pkg/config/constants.go | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/config/config.go b/pkg/config/config.go index c602f0331..12d495019 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -455,7 +455,8 @@ func NewConfig(editors ...ConfigEditor) config { "notion": {}, "twitch": {}, "twitter": {}, - "slack": {}, + "slack": {}, // TODO: remove this field in v2 + "slack_oidc": {}, "spotify": {}, "workos": {}, "zoom": {}, diff --git a/pkg/config/constants.go b/pkg/config/constants.go index efe400396..1580343b3 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -15,7 +15,7 @@ const ( edgeRuntimeImage = "supabase/edge-runtime:v1.55.2" vectorImage = "timberio/vector:0.28.1-alpine" supavisorImage = "supabase/supavisor:1.1.56" - gotrueImage = "supabase/gotrue:v2.151.0" + gotrueImage = "supabase/gotrue:v2.157.1" realtimeImage = "supabase/realtime:v2.29.15" storageImage = "supabase/storage-api:v1.0.6" logflareImage = "supabase/logflare:1.4.0" From 702b9809c140695a16153f64f29d1b694db7b833 Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 5 Aug 2024 16:00:14 +0800 Subject: [PATCH 03/12] fix: use unix diff for clearer config changes --- internal/link/diff.go | 261 ++++++++++++++++++++++++++++++++++++++++++ internal/link/link.go | 14 +-- 2 files changed, 268 insertions(+), 7 deletions(-) create mode 100644 internal/link/diff.go diff --git a/internal/link/diff.go b/internal/link/diff.go new file mode 100644 index 000000000..84f2e3494 --- /dev/null +++ b/internal/link/diff.go @@ -0,0 +1,261 @@ +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package link + +import ( + "bytes" + "fmt" + "sort" + "strings" +) + +// A pair is a pair of values tracked for both the x and y side of a diff. +// It is typically a pair of line indexes. +type pair struct{ x, y int } + +// Diff returns an anchored diff of the two texts old and new +// in the “unified diff” format. If old and new are identical, +// Diff returns a nil slice (no output). +// +// Unix diff implementations typically look for a diff with +// the smallest number of lines inserted and removed, +// which can in the worst case take time quadratic in the +// number of lines in the texts. As a result, many implementations +// either can be made to run for a long time or cut off the search +// after a predetermined amount of work. +// +// In contrast, this implementation looks for a diff with the +// smallest number of “unique” lines inserted and removed, +// where unique means a line that appears just once in both old and new. +// We call this an “anchored diff” because the unique lines anchor +// the chosen matching regions. An anchored diff is usually clearer +// than a standard diff, because the algorithm does not try to +// reuse unrelated blank lines or closing braces. +// The algorithm also guarantees to run in O(n log n) time +// instead of the standard O(n²) time. +// +// Some systems call this approach a “patience diff,” named for +// the “patience sorting” algorithm, itself named for a solitaire card game. +// We avoid that name for two reasons. First, the name has been used +// for a few different variants of the algorithm, so it is imprecise. +// Second, the name is frequently interpreted as meaning that you have +// to wait longer (to be patient) for the diff, meaning that it is a slower algorithm, +// when in fact the algorithm is faster than the standard one. +func Diff(oldName string, old []byte, newName string, new []byte) []byte { + if bytes.Equal(old, new) { + return nil + } + x := lines(old) + y := lines(new) + + // Print diff header. + var out bytes.Buffer + fmt.Fprintf(&out, "diff %s %s\n", oldName, newName) + fmt.Fprintf(&out, "--- %s\n", oldName) + fmt.Fprintf(&out, "+++ %s\n", newName) + + // Loop over matches to consider, + // expanding each match to include surrounding lines, + // and then printing diff chunks. + // To avoid setup/teardown cases outside the loop, + // tgs returns a leading {0,0} and trailing {len(x), len(y)} pair + // in the sequence of matches. + var ( + done pair // printed up to x[:done.x] and y[:done.y] + chunk pair // start lines of current chunk + count pair // number of lines from each side in current chunk + ctext []string // lines for current chunk + ) + for _, m := range tgs(x, y) { + if m.x < done.x { + // Already handled scanning forward from earlier match. + continue + } + + // Expand matching lines as far as possible, + // establishing that x[start.x:end.x] == y[start.y:end.y]. + // Note that on the first (or last) iteration we may (or definitely do) + // have an empty match: start.x==end.x and start.y==end.y. + start := m + for start.x > done.x && start.y > done.y && x[start.x-1] == y[start.y-1] { + start.x-- + start.y-- + } + end := m + for end.x < len(x) && end.y < len(y) && x[end.x] == y[end.y] { + end.x++ + end.y++ + } + + // Emit the mismatched lines before start into this chunk. + // (No effect on first sentinel iteration, when start = {0,0}.) + for _, s := range x[done.x:start.x] { + ctext = append(ctext, "-"+s) + count.x++ + } + for _, s := range y[done.y:start.y] { + ctext = append(ctext, "+"+s) + count.y++ + } + + // If we're not at EOF and have too few common lines, + // the chunk includes all the common lines and continues. + const C = 3 // number of context lines + if (end.x < len(x) || end.y < len(y)) && + (end.x-start.x < C || (len(ctext) > 0 && end.x-start.x < 2*C)) { + for _, s := range x[start.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + continue + } + + // End chunk with common lines for context. + if len(ctext) > 0 { + n := end.x - start.x + if n > C { + n = C + } + for _, s := range x[start.x : start.x+n] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = pair{start.x + n, start.y + n} + + // Format and emit chunk. + // Convert line numbers to 1-indexed. + // Special case: empty file shows up as 0,0 not 1,0. + if count.x > 0 { + chunk.x++ + } + if count.y > 0 { + chunk.y++ + } + fmt.Fprintf(&out, "@@ -%d,%d +%d,%d @@\n", chunk.x, count.x, chunk.y, count.y) + for _, s := range ctext { + out.WriteString(s) + } + count.x = 0 + count.y = 0 + ctext = ctext[:0] + } + + // If we reached EOF, we're done. + if end.x >= len(x) && end.y >= len(y) { + break + } + + // Otherwise start a new chunk. + chunk = pair{end.x - C, end.y - C} + for _, s := range x[chunk.x:end.x] { + ctext = append(ctext, " "+s) + count.x++ + count.y++ + } + done = end + } + + return out.Bytes() +} + +// lines returns the lines in the file x, including newlines. +// If the file does not end in a newline, one is supplied +// along with a warning about the missing newline. +func lines(x []byte) []string { + l := strings.SplitAfter(string(x), "\n") + if l[len(l)-1] == "" { + l = l[:len(l)-1] + } else { + // Treat last line as having a message about the missing newline attached, + // using the same text as BSD/GNU diff (including the leading backslash). + l[len(l)-1] += "\n\\ No newline at end of file\n" + } + return l +} + +// tgs returns the pairs of indexes of the longest common subsequence +// of unique lines in x and y, where a unique line is one that appears +// once in x and once in y. +// +// The longest common subsequence algorithm is as described in +// Thomas G. Szymanski, “A Special Case of the Maximal Common +// Subsequence Problem,” Princeton TR #170 (January 1975), +// available at https://research.swtch.com/tgs170.pdf. +func tgs(x, y []string) []pair { + // Count the number of times each string appears in a and b. + // We only care about 0, 1, many, counted as 0, -1, -2 + // for the x side and 0, -4, -8 for the y side. + // Using negative numbers now lets us distinguish positive line numbers later. + m := make(map[string]int) + for _, s := range x { + if c := m[s]; c > -2 { + m[s] = c - 1 + } + } + for _, s := range y { + if c := m[s]; c > -8 { + m[s] = c - 4 + } + } + + // Now unique strings can be identified by m[s] = -1+-4. + // + // Gather the indexes of those strings in x and y, building: + // xi[i] = increasing indexes of unique strings in x. + // yi[i] = increasing indexes of unique strings in y. + // inv[i] = index j such that x[xi[i]] = y[yi[j]]. + var xi, yi, inv []int + for i, s := range y { + if m[s] == -1+-4 { + m[s] = len(yi) + yi = append(yi, i) + } + } + for i, s := range x { + if j, ok := m[s]; ok && j >= 0 { + xi = append(xi, i) + inv = append(inv, j) + } + } + + // Apply Algorithm A from Szymanski's paper. + // In those terms, A = J = inv and B = [0, n). + // We add sentinel pairs {0,0}, and {len(x),len(y)} + // to the returned sequence, to help the processing loop. + J := inv + n := len(xi) + T := make([]int, n) + L := make([]int, n) + for i := range T { + T[i] = n + 1 + } + for i := 0; i < n; i++ { + k := sort.Search(n, func(k int) bool { + return T[k] >= J[i] + }) + T[k] = J[i] + L[i] = k + 1 + } + k := 0 + for _, v := range L { + if k < v { + k = v + } + } + seq := make([]pair, 2+k) + seq[1+k] = pair{len(x), len(y)} // sentinel at end + lastj := n + for i := n - 1; i >= 0; i-- { + if L[i] == k && J[i] < lastj { + seq[k] = pair{xi[i], yi[J[i]]} + k-- + } + } + seq[0] = pair{0, 0} // sentinel at start + return seq +} diff --git a/internal/link/link.go b/internal/link/link.go index 172ea09f7..9113f5434 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -11,7 +11,6 @@ import ( "github.com/BurntSushi/toml" "github.com/go-errors/errors" - "github.com/google/go-cmp/cmp" "github.com/jackc/pgconn" "github.com/jackc/pgx/v4" "github.com/spf13/afero" @@ -26,7 +25,7 @@ import ( ) func Run(ctx context.Context, projectRef string, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { - original := toTomlLines(map[string]interface{}{ + original := toTomlBytes(map[string]interface{}{ "api": utils.Config.Api, "db": utils.Config.Db, }) @@ -56,25 +55,26 @@ func Run(ctx context.Context, projectRef string, fsys afero.Fs, options ...func( fmt.Fprintln(os.Stdout, "Finished "+utils.Aqua("supabase link")+".") // 4. Suggest config update - updated := toTomlLines(map[string]interface{}{ + updated := toTomlBytes(map[string]interface{}{ "api": utils.Config.Api, "db": utils.Config.Db, }) - if lineDiff := cmp.Diff(original, updated); len(lineDiff) > 0 { + // if lineDiff := cmp.Diff(original, updated); len(lineDiff) > 0 { + if lineDiff := Diff(utils.ConfigPath, original, projectRef, updated); len(lineDiff) > 0 { fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Local config differs from linked project. Try updating", utils.Bold(utils.ConfigPath)) - fmt.Println(lineDiff) + fmt.Println(string(lineDiff)) } return nil } -func toTomlLines(config any) []string { +func toTomlBytes(config any) []byte { var buf bytes.Buffer enc := toml.NewEncoder(&buf) enc.Indent = "" if err := enc.Encode(config); err != nil { fmt.Fprintln(utils.GetDebugLogger(), "failed to marshal toml config:", err) } - return strings.Split(buf.String(), "\n") + return buf.Bytes() } func LinkServices(ctx context.Context, projectRef, anonKey string, fsys afero.Fs) { From c80962c7f38f43e01b73ad6c3b74f6b7ee6e390b Mon Sep 17 00:00:00 2001 From: Qiao Han Date: Mon, 5 Aug 2024 16:18:30 +0800 Subject: [PATCH 04/12] chore: show password reset link on missing password --- docs/main.go | 2 +- internal/utils/api.go | 2 +- internal/utils/flags/db_url.go | 2 ++ internal/utils/tenant/postgrest.go | 2 +- 4 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/main.go b/docs/main.go index b791dedcf..bfacba311 100644 --- a/docs/main.go +++ b/docs/main.go @@ -185,7 +185,7 @@ func GenYamlDoc(cmd *cobra.Command, root *SpecDoc) CmdDoc { Subcommands: subcommands, } - names := strings.Split(cmd.CommandPath(), " ") + names := strings.Fields(cmd.CommandPath()) if len(names) > 3 { base := strings.Join(names[2:], "-") names = append(names[:2], base) diff --git a/internal/utils/api.go b/internal/utils/api.go index 90e6a2610..3dc63d30d 100644 --- a/internal/utils/api.go +++ b/internal/utils/api.go @@ -264,7 +264,7 @@ func GetSupabaseDashboardURL() string { case DefaultApiHost, DeprecatedApiHost: return "https://supabase.com/dashboard" case "https://api.supabase.green": - return "https://app.supabase.green" + return "https://supabase.green/dashboard" default: return "http://127.0.0.1:8082" } diff --git a/internal/utils/flags/db_url.go b/internal/utils/flags/db_url.go index 629e0332a..c183aa1ad 100644 --- a/internal/utils/flags/db_url.go +++ b/internal/utils/flags/db_url.go @@ -107,6 +107,8 @@ func getPassword(projectRef string) string { if password, err := credentials.Get(projectRef); err == nil { return password } + resetUrl := fmt.Sprintf("%s/project/%s/settings/database", utils.GetSupabaseDashboardURL(), projectRef) + fmt.Fprintln(os.Stderr, "Forgot your password? Reset it from the Dashboard:", utils.Bold(resetUrl)) fmt.Fprint(os.Stderr, "Enter your database password: ") return credentials.PromptMasked(os.Stdin) } diff --git a/internal/utils/tenant/postgrest.go b/internal/utils/tenant/postgrest.go index 3872dffef..cb8c96d28 100644 --- a/internal/utils/tenant/postgrest.go +++ b/internal/utils/tenant/postgrest.go @@ -34,6 +34,6 @@ func (t *TenantAPI) GetPostgrestVersion(ctx context.Context) (string, error) { if len(data.Info.Version) == 0 { return "", errors.New(errPostgrestVersion) } - parts := strings.Split(data.Info.Version, " ") + parts := strings.Fields(data.Info.Version) return "v" + parts[0], nil } From af24c83ed1f698f44fb335af35036f26a76af87e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Aug 2024 16:34:18 +0800 Subject: [PATCH 05/12] chore(deps): bump golang.org/x/mod from 0.19.0 to 0.20.0 (#2585) Bumps [golang.org/x/mod](https://github.com/golang/mod) from 0.19.0 to 0.20.0. - [Commits](https://github.com/golang/mod/compare/v0.19.0...v0.20.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/go.mod b/go.mod index df5e501f0..c654a19d1 100644 --- a/go.mod +++ b/go.mod @@ -24,6 +24,7 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golangci/golangci-lint v1.59.1 + github.com/google/go-cmp v0.6.0 github.com/google/go-github/v62 v62.0.0 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.6.0 @@ -48,7 +49,7 @@ require ( github.com/stripe/pg-schema-diff v0.7.0 github.com/withfig/autocomplete-tools/packages/cobra v1.2.0 github.com/zalando/go-keyring v0.2.5 - golang.org/x/mod v0.19.0 + golang.org/x/mod v0.20.0 golang.org/x/oauth2 v0.21.0 golang.org/x/term v0.22.0 google.golang.org/grpc v1.65.0 @@ -168,7 +169,6 @@ require ( github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.5.3 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect - github.com/google/go-cmp v0.6.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gorilla/css v1.0.0 // indirect diff --git a/go.sum b/go.sum index 9dd3bac96..ca3d61989 100644 --- a/go.sum +++ b/go.sum @@ -1146,8 +1146,8 @@ golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= -golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= From b61cf4d6e947677b4d70928d508246377f5cc4cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=83=A5=EB=83=90=EC=B1=A0?= Date: Mon, 5 Aug 2024 19:48:47 +0900 Subject: [PATCH 06/12] fix: bump edge-runtime to 1.56.0 (#2580) --- pkg/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/constants.go b/pkg/config/constants.go index 1580343b3..65fff5c93 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -12,7 +12,7 @@ const ( pgmetaImage = "supabase/postgres-meta:v0.83.2" studioImage = "supabase/studio:20240729-ce42139" imageProxyImage = "darthsim/imgproxy:v3.8.0" - edgeRuntimeImage = "supabase/edge-runtime:v1.55.2" + edgeRuntimeImage = "supabase/edge-runtime:v1.56.0" vectorImage = "timberio/vector:0.28.1-alpine" supavisorImage = "supabase/supavisor:1.1.56" gotrueImage = "supabase/gotrue:v2.157.1" From f559eb7f0e7e1e6fa982c78fa196f41d55b3558c Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 7 Aug 2024 12:10:13 +0800 Subject: [PATCH 07/12] feat: support declaring objects path for seeding buckets (#2591) feat: support objects path for seeding buckets --- cmd/seed.go | 3 ++- internal/db/reset/reset.go | 12 +++++++++--- internal/seed/buckets/buckets.go | 21 +++++++++++++++++---- internal/seed/buckets/buckets_test.go | 10 ++++++++-- internal/start/start.go | 2 +- internal/storage/cp/cp.go | 19 +++++++++++-------- internal/storage/cp/cp_test.go | 2 +- pkg/config/config.go | 1 + pkg/config/templates/config.toml | 1 + pkg/config/testdata/config.toml | 1 + 10 files changed, 52 insertions(+), 20 deletions(-) diff --git a/cmd/seed.go b/cmd/seed.go index 760a9424d..c36b70741 100644 --- a/cmd/seed.go +++ b/cmd/seed.go @@ -4,6 +4,7 @@ import ( "os" "os/signal" + "github.com/spf13/afero" "github.com/spf13/cobra" "github.com/supabase/cli/internal/seed/buckets" "github.com/supabase/cli/internal/utils" @@ -27,7 +28,7 @@ var ( Short: "Seed buckets declared in [storage.buckets]", Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - return buckets.Run(cmd.Context(), flags.ProjectRef, utils.NewConsole()) + return buckets.Run(cmd.Context(), flags.ProjectRef, true, afero.NewOsFs()) }, } ) diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go index cb8568996..599365ece 100644 --- a/internal/db/reset/reset.go +++ b/internal/db/reset/reset.go @@ -8,6 +8,7 @@ import ( "os" "strconv" "strings" + "time" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" @@ -21,6 +22,7 @@ import ( "github.com/supabase/cli/internal/gen/keys" "github.com/supabase/cli/internal/migration/apply" "github.com/supabase/cli/internal/migration/repair" + "github.com/supabase/cli/internal/seed/buckets" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/pkg/migration" ) @@ -43,17 +45,21 @@ func Run(ctx context.Context, version string, config pgconn.Config, fsys afero.F } return resetRemote(ctx, version, config, fsys, options...) } - // Config file is loaded before parsing --linked or --local flags if err := utils.AssertSupabaseDbIsRunning(); err != nil { return err } - // Reset postgres database because extensions (pg_cron, pg_net) require postgres if err := resetDatabase(ctx, version, fsys, options...); err != nil { return err } - + // Seed objects from supabase/buckets directory + if err := start.WaitForHealthyService(ctx, 30*time.Second, utils.StorageId); err != nil { + return err + } + if err := buckets.Run(ctx, "", false, fsys); err != nil { + return err + } branch := keys.GetGitBranch(fsys) fmt.Fprintln(os.Stderr, "Finished "+utils.Aqua("supabase db reset")+" on branch "+utils.Aqua(branch)+".") return nil diff --git a/internal/seed/buckets/buckets.go b/internal/seed/buckets/buckets.go index 2801405af..55ec4ee65 100644 --- a/internal/seed/buckets/buckets.go +++ b/internal/seed/buckets/buckets.go @@ -3,18 +3,22 @@ package buckets import ( "context" "fmt" + "path/filepath" + "github.com/spf13/afero" "github.com/supabase/cli/internal/storage/client" + "github.com/supabase/cli/internal/storage/cp" "github.com/supabase/cli/internal/utils" ) -func Run(ctx context.Context, projectRef string, console *utils.Console) error { +func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs) error { api, err := client.NewStorageAPI(ctx, projectRef) if err != nil { return err } - if console == nil { - return api.UpsertBuckets(ctx, utils.Config.Storage.Buckets) + console := utils.NewConsole() + if !interactive { + console.IsTTY = false } filter := func(bucketId string) bool { label := fmt.Sprintf("Bucket %s already exists. Do you want to overwrite its properties?", utils.Bold(bucketId)) @@ -24,5 +28,14 @@ func Run(ctx context.Context, projectRef string, console *utils.Console) error { } return shouldOverwrite } - return api.UpsertBuckets(ctx, utils.Config.Storage.Buckets, filter) + if err := api.UpsertBuckets(ctx, utils.Config.Storage.Buckets, filter); err != nil { + return err + } + for _, bucket := range utils.Config.Storage.Buckets { + localPath := filepath.Join(utils.SupabaseDirPath, bucket.ObjectsPath) + if err := cp.UploadStorageObjectAll(ctx, api, "", localPath, 5, fsys); err != nil { + return err + } + } + return nil } diff --git a/internal/seed/buckets/buckets_test.go b/internal/seed/buckets/buckets_test.go index 1d0e5f5e9..e14a40e74 100644 --- a/internal/seed/buckets/buckets_test.go +++ b/internal/seed/buckets/buckets_test.go @@ -3,10 +3,12 @@ package buckets import ( "context" "net/http" + "path/filepath" "testing" "github.com/BurntSushi/toml" "github.com/h2non/gock" + "github.com/spf13/afero" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/supabase/cli/internal/testing/apitest" @@ -23,6 +25,10 @@ public = true [private] public = false` require.NoError(t, toml.Unmarshal([]byte(config), &utils.Config.Storage.Buckets)) + // Setup in-memory fs + fsys := afero.NewMemMapFs() + bucketPath := filepath.Join(utils.SupabaseDirPath, "images") + require.NoError(t, fsys.Mkdir(bucketPath, 0755)) // Setup mock api gock.New(utils.Config.Api.ExternalUrl). Get("/storage/v1/bucket"). @@ -40,7 +46,7 @@ public = false` Reply(http.StatusOK). JSON(storage.CreateBucketResponse{Name: "private"}) // Run test - err := Run(context.Background(), "", utils.NewConsole()) + err := Run(context.Background(), "", false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -53,7 +59,7 @@ public = false` Reply(http.StatusOK). JSON([]storage.BucketResponse{}) // Run test - err := Run(context.Background(), "", nil) + err := Run(context.Background(), "", false, afero.NewMemMapFs()) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) diff --git a/internal/start/start.go b/internal/start/start.go index ade4cc98b..7e27291ae 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -1062,7 +1062,7 @@ EOF return err } // Disable prompts when seeding - if err := buckets.Run(ctx, "", nil); err != nil { + if err := buckets.Run(ctx, "", false, fsys); err != nil { return err } } diff --git a/internal/storage/cp/cp.go b/internal/storage/cp/cp.go index 9719a7262..b41c58b38 100644 --- a/internal/storage/cp/cp.go +++ b/internal/storage/cp/cp.go @@ -94,16 +94,19 @@ func UploadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remoteP // Check if directory exists on remote dirExists := false fileExists := false - if err := ls.IterateStoragePaths(ctx, api, noSlash, func(objectName string) error { - if objectName == path.Base(noSlash) { - fileExists = true + if len(noSlash) > 0 { + callback := func(objectName string) error { + if objectName == path.Base(noSlash) { + fileExists = true + } + if objectName == path.Base(noSlash)+"/" { + dirExists = true + } + return nil } - if objectName == path.Base(noSlash)+"/" { - dirExists = true + if err := ls.IterateStoragePaths(ctx, api, noSlash, callback); err != nil { + return err } - return nil - }); err != nil { - return err } baseName := filepath.Base(localPath) jq := utils.NewJobQueue(maxJobs) diff --git a/internal/storage/cp/cp_test.go b/internal/storage/cp/cp_test.go index a310a3512..fe988c21c 100644 --- a/internal/storage/cp/cp_test.go +++ b/internal/storage/cp/cp_test.go @@ -311,7 +311,7 @@ func TestUploadAll(t *testing.T) { Get("/storage/v1/bucket"). Reply(http.StatusServiceUnavailable) // Run test - err := UploadStorageObjectAll(context.Background(), mockApi, "", ".", 1, fsys) + err := UploadStorageObjectAll(context.Background(), mockApi, "missing", ".", 1, fsys) // Check error assert.ErrorContains(t, err, "Error status 503:") assert.Empty(t, apitest.ListUnmatchedRequests()) diff --git a/pkg/config/config.go b/pkg/config/config.go index 12d495019..2af488f62 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -211,6 +211,7 @@ type ( Public *bool `toml:"public"` FileSizeLimit sizeInBytes `toml:"file_size_limit"` AllowedMimeTypes []string `toml:"allowed_mime_types"` + ObjectsPath string `toml:"objects_path"` } imageTransformation struct { diff --git a/pkg/config/templates/config.toml b/pkg/config/templates/config.toml index e9545a6da..573982798 100644 --- a/pkg/config/templates/config.toml +++ b/pkg/config/templates/config.toml @@ -78,6 +78,7 @@ enabled = true # public = false # file_size_limit = "50MiB" # allowed_mime_types = ["image/png", "image/jpeg"] +# objects_path = "./images" [auth] enabled = true diff --git a/pkg/config/testdata/config.toml b/pkg/config/testdata/config.toml index 02d8c37bd..b08879b72 100644 --- a/pkg/config/testdata/config.toml +++ b/pkg/config/testdata/config.toml @@ -78,6 +78,7 @@ enabled = false public = false file_size_limit = "50MiB" allowed_mime_types = ["image/png", "image/jpeg"] +objects_path = "./images" [auth] enabled = true From 7ac72ba9c79c909c2884b9677e241fd0b443632f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filipe=20Caba=C3=A7o?= Date: Wed, 7 Aug 2024 10:01:32 +0100 Subject: [PATCH 08/12] realtime: Bump image to 2.30.23 (#2592) --- pkg/config/constants.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/constants.go b/pkg/config/constants.go index 65fff5c93..4a408b1b2 100644 --- a/pkg/config/constants.go +++ b/pkg/config/constants.go @@ -16,7 +16,7 @@ const ( vectorImage = "timberio/vector:0.28.1-alpine" supavisorImage = "supabase/supavisor:1.1.56" gotrueImage = "supabase/gotrue:v2.157.1" - realtimeImage = "supabase/realtime:v2.29.15" + realtimeImage = "supabase/realtime:v2.30.23" storageImage = "supabase/storage-api:v1.0.6" logflareImage = "supabase/logflare:1.4.0" // Append to JobImages when adding new dependencies below From f676aa85da3265b1506530b919e1cee8846becac Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Fri, 9 Aug 2024 13:41:19 +0800 Subject: [PATCH 09/12] fix: use batch object upload api (#2598) --- internal/bootstrap/bootstrap.go | 5 ++- internal/seed/buckets/buckets.go | 13 +++--- internal/storage/cp/cp.go | 18 ++++++-- internal/utils/config.go | 16 ++++++- {internal/utils => pkg/queue}/queue.go | 2 +- pkg/storage/batch.go | 61 ++++++++++++++++++++++++++ pkg/storage/objects.go | 43 +++++++++++++----- 7 files changed, 134 insertions(+), 24 deletions(-) rename {internal/utils => pkg/queue}/queue.go (98%) diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index 10d8667cf..06b05e581 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -30,6 +30,7 @@ import ( "github.com/supabase/cli/internal/utils/tenant" "github.com/supabase/cli/pkg/api" "github.com/supabase/cli/pkg/fetcher" + "github.com/supabase/cli/pkg/queue" "golang.org/x/term" ) @@ -362,14 +363,14 @@ func downloadSample(ctx context.Context, client *github.Client, templateUrl stri type Downloader struct { api *fetcher.Fetcher - queue *utils.JobQueue + queue *queue.JobQueue fsys afero.Fs } func NewDownloader(concurrency uint, fsys afero.Fs) *Downloader { return &Downloader{ api: fetcher.NewFetcher("", fetcher.WithExpectedStatus(http.StatusOK)), - queue: utils.NewJobQueue(concurrency), + queue: queue.NewJobQueue(concurrency), fsys: fsys, } } diff --git a/internal/seed/buckets/buckets.go b/internal/seed/buckets/buckets.go index 55ec4ee65..2af80b43d 100644 --- a/internal/seed/buckets/buckets.go +++ b/internal/seed/buckets/buckets.go @@ -7,8 +7,8 @@ import ( "github.com/spf13/afero" "github.com/supabase/cli/internal/storage/client" - "github.com/supabase/cli/internal/storage/cp" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/pkg/config" ) func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs) error { @@ -31,11 +31,12 @@ func Run(ctx context.Context, projectRef string, interactive bool, fsys afero.Fs if err := api.UpsertBuckets(ctx, utils.Config.Storage.Buckets, filter); err != nil { return err } - for _, bucket := range utils.Config.Storage.Buckets { - localPath := filepath.Join(utils.SupabaseDirPath, bucket.ObjectsPath) - if err := cp.UploadStorageObjectAll(ctx, api, "", localPath, 5, fsys); err != nil { - return err + resolved := config.BucketConfig{} + for name, bucket := range utils.Config.Storage.Buckets { + if len(bucket.ObjectsPath) > 0 && !filepath.IsAbs(bucket.ObjectsPath) { + bucket.ObjectsPath = filepath.Join(utils.SupabaseDirPath, bucket.ObjectsPath) } + resolved[name] = bucket } - return nil + return api.UpsertObjects(ctx, resolved, utils.NewRootFS(fsys)) } diff --git a/internal/storage/cp/cp.go b/internal/storage/cp/cp.go index b41c58b38..3676e0348 100644 --- a/internal/storage/cp/cp.go +++ b/internal/storage/cp/cp.go @@ -16,6 +16,7 @@ import ( "github.com/supabase/cli/internal/storage/ls" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/flags" + "github.com/supabase/cli/pkg/queue" "github.com/supabase/cli/pkg/storage" ) @@ -66,7 +67,7 @@ func DownloadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remot } // No need to be atomic because it's incremented only on main thread count := 0 - jq := utils.NewJobQueue(maxJobs) + jq := queue.NewJobQueue(maxJobs) err := ls.IterateStoragePathsAll(ctx, api, remotePath, func(objectPath string) error { relPath := strings.TrimPrefix(objectPath, remotePath) dstPath := filepath.Join(localPath, filepath.FromSlash(relPath)) @@ -79,7 +80,13 @@ func DownloadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remot if err := utils.MkdirIfNotExistFS(fsys, filepath.Dir(dstPath)); err != nil { return err } - return api.DownloadObject(ctx, objectPath, dstPath, fsys) + // Overwrites existing file when using --recursive flag + f, err := fsys.OpenFile(dstPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644) + if err != nil { + return errors.Errorf("failed to create file: %w", err) + } + defer f.Close() + return api.DownloadObjectStream(ctx, objectPath, f) } return jq.Put(job) }) @@ -108,8 +115,12 @@ func UploadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remoteP return err } } + // Overwrites existing object when using --recursive flag + opts = append(opts, func(fo *storage.FileOptions) { + fo.Overwrite = true + }) baseName := filepath.Base(localPath) - jq := utils.NewJobQueue(maxJobs) + jq := queue.NewJobQueue(maxJobs) err := afero.Walk(fsys, localPath, func(filePath string, info fs.FileInfo, err error) error { if err != nil { return errors.New(err) @@ -126,6 +137,7 @@ func UploadStorageObjectAll(ctx context.Context, api storage.StorageAPI, remoteP if relPath == "." { _, prefix := client.SplitBucketPrefix(dstPath) if IsDir(prefix) || (dirExists && !fileExists) { + // Keep the file name when destination is a directory dstPath = path.Join(dstPath, info.Name()) } } else { diff --git a/internal/utils/config.go b/internal/utils/config.go index e7fb6dd56..3f89dc75c 100644 --- a/internal/utils/config.go +++ b/internal/utils/config.go @@ -3,6 +3,7 @@ package utils import ( _ "embed" "fmt" + "io/fs" "net" "net/url" "os" @@ -101,7 +102,7 @@ func GetDockerIds() []string { var Config = config.NewConfig(config.WithHostname(GetHostname())) func LoadConfigFS(fsys afero.Fs) error { - if err := Config.Load("", afero.NewIOFS(fsys)); err != nil { + if err := Config.Load("", NewRootFS(fsys)); err != nil { if errors.Is(err, os.ErrNotExist) { CmdSuggestion = fmt.Sprintf("Have you set up the project with %s?", Aqua("supabase init")) } @@ -111,6 +112,19 @@ func LoadConfigFS(fsys afero.Fs) error { return nil } +// Adapts fs.FS to support absolute paths +type rootFS struct { + fsys afero.Fs +} + +func (f *rootFS) Open(name string) (fs.File, error) { + return f.fsys.Open(name) +} + +func NewRootFS(fsys afero.Fs) fs.FS { + return &rootFS{fsys: fsys} +} + func ToRealtimeEnv(addr config.AddressFamily) string { if addr == config.AddressIPv6 { return "-proto_dist inet6_tcp" diff --git a/internal/utils/queue.go b/pkg/queue/queue.go similarity index 98% rename from internal/utils/queue.go rename to pkg/queue/queue.go index b9dffbcfe..e427e01c5 100644 --- a/internal/utils/queue.go +++ b/pkg/queue/queue.go @@ -1,4 +1,4 @@ -package utils +package queue import ( "sync" diff --git a/pkg/storage/batch.go b/pkg/storage/batch.go index 510658257..8a681242d 100644 --- a/pkg/storage/batch.go +++ b/pkg/storage/batch.go @@ -3,9 +3,14 @@ package storage import ( "context" "fmt" + "io/fs" "os" + "path" + "path/filepath" + "github.com/go-errors/errors" "github.com/supabase/cli/pkg/config" + "github.com/supabase/cli/pkg/queue" ) func (s *StorageAPI) UpsertBuckets(ctx context.Context, bucketConfig config.BucketConfig, filter ...func(string) bool) error { @@ -50,3 +55,59 @@ func (s *StorageAPI) UpsertBuckets(ctx context.Context, bucketConfig config.Buck } return nil } + +type UploadOptions struct { + MaxConcurrency uint + KeyPrefix string +} + +func (s *StorageAPI) UpsertObjects(ctx context.Context, bucketConfig config.BucketConfig, fsys fs.FS, opts ...func(*UploadOptions)) error { + uo := UploadOptions{MaxConcurrency: 5} + for _, apply := range opts { + apply(&uo) + } + jq := queue.NewJobQueue(uo.MaxConcurrency) + for name, bucket := range bucketConfig { + localPath := bucket.ObjectsPath + if len(localPath) == 0 { + continue + } + upload := func(filePath string, info fs.DirEntry, err error) error { + if err != nil { + return errors.New(err) + } + if !info.Type().IsRegular() { + return nil + } + dstPath := uo.KeyPrefix + relPath, err := filepath.Rel(localPath, filePath) + if err != nil { + return errors.Errorf("failed to resolve relative path: %w", err) + } else if relPath == "." { + // Copying single file + dstPath = path.Join(name, info.Name()) + } else { + dstPath = path.Join(name, filepath.ToSlash(relPath)) + } + fmt.Fprintln(os.Stderr, "Uploading:", filePath, "=>", dstPath) + job := func() error { + f, err := fsys.Open(filePath) + if err != nil { + return errors.Errorf("failed to open file: %w", err) + } + defer f.Close() + fo, err := ParseFileOptions(f) + if err != nil { + return err + } + fo.Overwrite = true + return s.UploadObjectStream(ctx, dstPath, f, *fo) + } + return jq.Put(job) + } + if err := fs.WalkDir(fsys, localPath, upload); err != nil { + return err + } + } + return jq.Collect() +} diff --git a/pkg/storage/objects.go b/pkg/storage/objects.go index 4a6ab64aa..f048cecf6 100644 --- a/pkg/storage/objects.go +++ b/pkg/storage/objects.go @@ -3,7 +3,9 @@ package storage import ( "context" "io" + "io/fs" "net/http" + "os" "path" "strings" @@ -56,9 +58,10 @@ func (s *StorageAPI) ListObjects(ctx context.Context, bucket, prefix string, pag type FileOptions struct { CacheControl string ContentType string + Overwrite bool } -func ParseFileOptions(f afero.File, opts ...func(*FileOptions)) (*FileOptions, error) { +func ParseFileOptions(f fs.File, opts ...func(*FileOptions)) (*FileOptions, error) { // Customise file options fo := &FileOptions{} for _, apply := range opts { @@ -76,9 +79,9 @@ func ParseFileOptions(f afero.File, opts ...func(*FileOptions)) (*FileOptions, e return nil, errors.Errorf("failed to read file: %w", err) } fo.ContentType = http.DetectContentType(buf) - // TODO: handle cases where file is not seekable - _, err = f.Seek(0, io.SeekStart) - if err != nil { + if s, ok := f.(io.Seeker); !ok { + return nil, errors.Errorf("file is not seekable") + } else if _, err = s.Seek(0, io.SeekStart); err != nil { return nil, errors.Errorf("failed to seek file: %w", err) } } @@ -95,13 +98,24 @@ func (s *StorageAPI) UploadObject(ctx context.Context, remotePath, localPath str if err != nil { return err } + return s.UploadObjectStream(ctx, remotePath, f, *fo) +} + +func (s *StorageAPI) UploadObjectStream(ctx context.Context, remotePath string, localFile io.Reader, fo FileOptions) error { headers := func(req *http.Request) { - req.Header.Add("Content-Type", fo.ContentType) - req.Header.Add("Cache-Control", fo.CacheControl) + if len(fo.ContentType) > 0 { + req.Header.Add("Content-Type", fo.ContentType) + } + if len(fo.CacheControl) > 0 { + req.Header.Add("Cache-Control", fo.CacheControl) + } + if fo.Overwrite { + req.Header.Add("x-upsert", "true") + } } // Prepare request remotePath = strings.TrimPrefix(remotePath, "/") - resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/"+remotePath, f, headers) + resp, err := s.Send(ctx, http.MethodPost, "/storage/v1/object/"+remotePath, io.NopCloser(localFile), headers) if err != nil { return err } @@ -110,16 +124,23 @@ func (s *StorageAPI) UploadObject(ctx context.Context, remotePath, localPath str } func (s *StorageAPI) DownloadObject(ctx context.Context, remotePath, localPath string, fsys afero.Fs) error { + f, err := fsys.OpenFile(localPath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0644) + if err != nil { + return errors.Errorf("failed to create file: %w", err) + } + defer f.Close() + return s.DownloadObjectStream(ctx, remotePath, f) +} + +func (s *StorageAPI) DownloadObjectStream(ctx context.Context, remotePath string, localFile io.Writer) error { remotePath = strings.TrimPrefix(remotePath, "/") resp, err := s.Send(ctx, http.MethodGet, "/storage/v1/object/"+remotePath, nil) if err != nil { return err } defer resp.Body.Close() - if err := afero.WriteReader(fsys, localPath, resp.Body); err != nil { - return errors.Errorf("failed to write file: %w", err) - } - return nil + _, err = io.Copy(localFile, resp.Body) + return err } type MoveObjectRequest struct { From d400387cb1088552f78c99355334a55b78b6c17b Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Sun, 11 Aug 2024 19:19:18 +0800 Subject: [PATCH 10/12] fix: expose supervisor websocket via Kong api (#2603) --- internal/start/start.go | 2 ++ internal/start/templates/kong.yml | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/internal/start/start.go b/internal/start/start.go index 7e27291ae..64c5e2447 100644 --- a/internal/start/start.go +++ b/internal/start/start.go @@ -105,6 +105,7 @@ type kongConfig struct { PgmetaId string EdgeRuntimeId string LogflareId string + PoolerId string ApiHost string ApiPort uint16 } @@ -350,6 +351,7 @@ EOF PgmetaId: utils.PgmetaId, EdgeRuntimeId: utils.EdgeRuntimeId, LogflareId: utils.LogflareId, + PoolerId: utils.PoolerId, ApiHost: utils.Config.Hostname, ApiPort: utils.Config.Api.Port, }); err != nil { diff --git a/internal/start/templates/kong.yml b/internal/start/templates/kong.yml index d1a81676e..6927edde1 100644 --- a/internal/start/templates/kong.yml +++ b/internal/start/templates/kong.yml @@ -140,3 +140,14 @@ services: strip_path: true paths: - /analytics/v1/ + - name: pooler-v2-ws + _comment: "Pooler: /pooler/v2/* -> ws://pooler:4000/v2/*" + url: http://{{ .PoolerId }}:4000/v2 + protocol: ws + routes: + - name: pooler-v2-ws + strip_path: true + paths: + - /pooler/v2/ + plugins: + - name: cors From 944ba6a3e19f42def1738dcff427d6ddc9a55b0f Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Mon, 12 Aug 2024 22:02:06 +0800 Subject: [PATCH 11/12] fix: add pooler to the list of services to reset (#2606) --- internal/db/reset/reset.go | 6 +++++- internal/db/reset/reset_test.go | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/internal/db/reset/reset.go b/internal/db/reset/reset.go index 599365ece..be4802fb7 100644 --- a/internal/db/reset/reset.go +++ b/internal/db/reset/reset.go @@ -205,7 +205,7 @@ func RestartDatabase(ctx context.Context, w io.Writer) error { func restartServices(ctx context.Context) error { // No need to restart PostgREST because it automatically reconnects and listens for schema changes - services := []string{utils.StorageId, utils.GotrueId, utils.RealtimeId} + services := listServicesToRestart() result := utils.WaitAll(services, func(id string) error { if err := utils.Docker.ContainerRestart(ctx, id, container.StopOptions{}); err != nil && !errdefs.IsNotFound(err) { return errors.Errorf("Failed to restart %s: %w", id, err) @@ -216,6 +216,10 @@ func restartServices(ctx context.Context) error { return errors.Join(result...) } +func listServicesToRestart() []string { + return []string{utils.StorageId, utils.GotrueId, utils.RealtimeId, utils.PoolerId} +} + func resetRemote(ctx context.Context, version string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { fmt.Fprintln(os.Stderr, "Resetting remote database"+toLogMessage(version)) conn, err := utils.ConnectByConfigStream(ctx, config, io.Discard, options...) diff --git a/internal/db/reset/reset_test.go b/internal/db/reset/reset_test.go index 3c1acd0e3..38fef4b96 100644 --- a/internal/db/reset/reset_test.go +++ b/internal/db/reset/reset_test.go @@ -225,7 +225,8 @@ func TestRestartDatabase(t *testing.T) { utils.StorageId = "test-storage" utils.GotrueId = "test-auth" utils.RealtimeId = "test-realtime" - for _, container := range []string{utils.StorageId, utils.GotrueId, utils.RealtimeId} { + utils.PoolerId = "test-pooler" + for _, container := range listServicesToRestart() { gock.New(utils.Docker.DaemonHost()). Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). Reply(http.StatusOK) @@ -259,11 +260,15 @@ func TestRestartDatabase(t *testing.T) { utils.StorageId = "test-storage" utils.GotrueId = "test-auth" utils.RealtimeId = "test-realtime" + utils.PoolerId = "test-pooler" for _, container := range []string{utils.StorageId, utils.GotrueId, utils.RealtimeId} { gock.New(utils.Docker.DaemonHost()). Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart"). Reply(http.StatusServiceUnavailable) } + gock.New(utils.Docker.DaemonHost()). + Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.PoolerId + "/restart"). + Reply(http.StatusNotFound) // Run test err := RestartDatabase(context.Background(), io.Discard) // Check error From 58f7b71c8e520fec838ce19d23faa27f633488be Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Mon, 12 Aug 2024 23:49:40 +0800 Subject: [PATCH 12/12] fix: update docker dependencies to silence warning (#2608) --- go.mod | 8 ++++---- go.sum | 8 ++++---- internal/utils/docker.go | 5 +++++ 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index c654a19d1..fe4ede743 100644 --- a/go.mod +++ b/go.mod @@ -13,8 +13,8 @@ require ( github.com/charmbracelet/lipgloss v0.12.1 github.com/containers/common v0.59.2 github.com/deepmap/oapi-codegen/v2 v2.2.0 - github.com/docker/cli v26.1.2+incompatible - github.com/docker/docker v26.1.4+incompatible + github.com/docker/cli v26.1.5+incompatible + github.com/docker/docker v26.1.5+incompatible github.com/docker/go-connections v0.5.0 github.com/docker/go-units v0.5.0 github.com/getsentry/sentry-go v0.28.1 @@ -24,7 +24,6 @@ require ( github.com/go-xmlfmt/xmlfmt v1.1.2 github.com/golang-jwt/jwt/v5 v5.2.1 github.com/golangci/golangci-lint v1.59.1 - github.com/google/go-cmp v0.6.0 github.com/google/go-github/v62 v62.0.0 github.com/google/go-querystring v1.1.0 github.com/google/uuid v1.6.0 @@ -49,6 +48,7 @@ require ( github.com/stripe/pg-schema-diff v0.7.0 github.com/withfig/autocomplete-tools/packages/cobra v1.2.0 github.com/zalando/go-keyring v0.2.5 + go.opentelemetry.io/otel v1.28.0 golang.org/x/mod v0.20.0 golang.org/x/oauth2 v0.21.0 golang.org/x/term v0.22.0 @@ -169,6 +169,7 @@ require ( github.com/golangci/plugin-module-register v0.1.1 // indirect github.com/golangci/revgrep v0.5.3 // indirect github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gordonklaus/ineffassign v0.1.0 // indirect github.com/gorilla/css v1.0.0 // indirect @@ -317,7 +318,6 @@ require ( go-simpler.org/musttag v0.12.2 // indirect go-simpler.org/sloglint v0.7.1 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect - go.opentelemetry.io/otel v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 // indirect diff --git a/go.sum b/go.sum index ca3d61989..9dcf2a2a7 100644 --- a/go.sum +++ b/go.sum @@ -234,13 +234,13 @@ github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= github.com/dnephin/pflag v1.0.7 h1:oxONGlWxhmUct0YzKTgrpQv9AUA1wtPBn7zuSjJqptk= github.com/dnephin/pflag v1.0.7/go.mod h1:uxE91IoWURlOiTUIA8Mq5ZZkAv3dPUfZNaT80Zm7OQE= -github.com/docker/cli v26.1.2+incompatible h1:/MWZpUMMlr1hCGyquL8QNbL1hbivQ1kLuT3Z9s1Tlpg= -github.com/docker/cli v26.1.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v26.1.5+incompatible h1:NxXGSdz2N+Ibdaw330TDO3d/6/f7MvHuiMbuFaIQDTk= +github.com/docker/cli v26.1.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v26.1.4+incompatible h1:vuTpXDuoga+Z38m1OZHzl7NKisKWaWlhjQk7IDPSLsU= -github.com/docker/docker v26.1.4+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v26.1.5+incompatible h1:NEAxTwEjxV6VbBMBoGG3zPqbiJosIApZjxlbrG9q3/g= +github.com/docker/docker v26.1.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.8.1 h1:j/eKUktUltBtMzKqmfLB0PAgqYyMHOp5vfsD1807oKo= github.com/docker/docker-credential-helpers v0.8.1/go.mod h1:P3ci7E3lwkZg6XiHdRKft1KckHiO9a2rNtyFbZ/ry9M= github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= diff --git a/internal/utils/docker.go b/internal/utils/docker.go index 3d80f8f59..8a44c75d3 100644 --- a/internal/utils/docker.go +++ b/internal/utils/docker.go @@ -33,6 +33,7 @@ import ( "github.com/docker/docker/pkg/stdcopy" "github.com/go-errors/errors" "github.com/spf13/viper" + "go.opentelemetry.io/otel" ) var Docker = NewDocker() @@ -43,6 +44,10 @@ func NewDocker() *client.Client { if err != nil { log.Fatalln("Failed to create Docker client:", err) } + // Silence otel errors as users don't care about docker metrics + // 2024/08/12 23:11:12 1 errors occurred detecting resource: + // * conflicting Schema URL: https://opentelemetry.io/schemas/1.21.0 + otel.SetErrorHandler(otel.ErrorHandlerFunc(func(cause error) {})) if err := cli.Initialize(&dockerFlags.ClientOptions{}); err != nil { log.Fatalln("Failed to initialize Docker client:", err) }