From 99c94d3946bae26715932cfe82648b7bc5ae5019 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:01:57 +0800 Subject: [PATCH 01/19] fix(docker): bump supabase/storage-api from v1.29.0 to v1.29.1 in /pkg/config/templates in the docker-minor group (#4431) fix(docker): bump supabase/storage-api Bumps the docker-minor group in /pkg/config/templates with 1 update: supabase/storage-api. Updates `supabase/storage-api` from v1.29.0 to v1.29.1 --- updated-dependencies: - dependency-name: supabase/storage-api dependency-version: v1.29.1 dependency-type: direct:production dependency-group: docker-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index 68322703f..9fef468d2 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -12,7 +12,7 @@ FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor FROM supabase/gotrue:v2.182.1 AS gotrue FROM supabase/realtime:v2.63.0 AS realtime -FROM supabase/storage-api:v1.29.0 AS storage +FROM supabase/storage-api:v1.29.1 AS storage FROM supabase/logflare:1.25.3 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ From d1b97868ab1e1d8f78e41cade21b93a0244636af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 11 Nov 2025 16:02:16 +0800 Subject: [PATCH 02/19] chore(deps): bump golangci/golangci-lint-action from 8 to 9 (#4425) Bumps [golangci/golangci-lint-action](https://github.com/golangci/golangci-lint-action) from 8 to 9. - [Release notes](https://github.com/golangci/golangci-lint-action/releases) - [Commits](https://github.com/golangci/golangci-lint-action/compare/v8...v9) --- updated-dependencies: - dependency-name: golangci/golangci-lint-action dependency-version: '9' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 25331b686..7f2a80f4f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: # Linter requires no cache cache: false - - uses: golangci/golangci-lint-action@v8 + - uses: golangci/golangci-lint-action@v9 with: args: --timeout 3m --verbose From 5f9e9ea7d8a62da59b14c4a23143d5767d026d01 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:21:19 +0000 Subject: [PATCH 03/19] fix(docker): bump supabase/postgres from 17.6.1.043 to 17.6.1.044 in /pkg/config/templates (#4434) fix(docker): bump supabase/postgres in /pkg/config/templates Bumps supabase/postgres from 17.6.1.043 to 17.6.1.044. --- updated-dependencies: - dependency-name: supabase/postgres dependency-version: 17.6.1.044 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index 9fef468d2..61e632bf1 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -1,5 +1,5 @@ # Exposed for updates by .github/dependabot.yml -FROM supabase/postgres:17.6.1.043 AS pg +FROM supabase/postgres:17.6.1.044 AS pg # Append to ServiceImages when adding new dependencies below FROM library/kong:2.8.1 AS kong FROM axllent/mailpit:v1.22.3 AS mailpit From 40890421247f438779ce82d45c5b951b4942cac1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 04:30:06 +0000 Subject: [PATCH 04/19] chore(deps): bump the go-minor group across 2 directories with 3 updates (#4435) Bumps the go-minor group with 2 updates in the / directory: [golang.org/x/mod](https://github.com/golang/mod) and [golang.org/x/net](https://github.com/golang/net). Bumps the go-minor group with 1 update in the /pkg directory: [golang.org/x/mod](https://github.com/golang/mod). Updates `golang.org/x/mod` from 0.29.0 to 0.30.0 - [Commits](https://github.com/golang/mod/compare/v0.29.0...v0.30.0) Updates `golang.org/x/net` from 0.46.0 to 0.47.0 - [Commits](https://github.com/golang/net/compare/v0.46.0...v0.47.0) Updates `golang.org/x/term` from 0.36.0 to 0.37.0 - [Commits](https://github.com/golang/term/compare/v0.36.0...v0.37.0) Updates `golang.org/x/mod` from 0.29.0 to 0.30.0 - [Commits](https://github.com/golang/mod/compare/v0.29.0...v0.30.0) --- updated-dependencies: - dependency-name: golang.org/x/mod dependency-version: 0.30.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor - dependency-name: golang.org/x/net dependency-version: 0.47.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor - dependency-name: golang.org/x/term dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor - dependency-name: golang.org/x/mod dependency-version: 0.30.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 16 ++++++++-------- go.sum | 32 ++++++++++++++++---------------- pkg/go.mod | 2 +- pkg/go.sum | 4 ++-- 4 files changed, 27 insertions(+), 27 deletions(-) diff --git a/go.mod b/go.mod index 62144bb51..db6adf921 100644 --- a/go.mod +++ b/go.mod @@ -48,10 +48,10 @@ require ( github.com/withfig/autocomplete-tools/packages/cobra v1.2.0 github.com/zalando/go-keyring v0.2.6 go.opentelemetry.io/otel v1.38.0 - golang.org/x/mod v0.29.0 - golang.org/x/net v0.46.0 + golang.org/x/mod v0.30.0 + golang.org/x/net v0.47.0 golang.org/x/oauth2 v0.33.0 - golang.org/x/term v0.36.0 + golang.org/x/term v0.37.0 google.golang.org/grpc v1.76.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -337,12 +337,12 @@ require ( go.uber.org/multierr v1.9.0 // indirect go.uber.org/zap v1.24.0 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.43.0 // indirect + golang.org/x/crypto v0.44.0 // indirect golang.org/x/exp/typeparams v0.0.0-20250210185358-939b2ce775ac // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect - golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.37.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/tools v0.38.0 // indirect golang.org/x/tools/go/expect v0.1.1-deprecated // indirect golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated // indirect google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b // indirect diff --git a/go.sum b/go.sum index d3992b53a..98491214a 100644 --- a/go.sum +++ b/go.sum @@ -1121,8 +1121,8 @@ golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliY golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= -golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= -golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1168,8 +1168,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= 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.13.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= 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= @@ -1217,8 +1217,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= golang.org/x/net v0.16.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= -golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= -golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1242,8 +1242,8 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1310,8 +1310,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -1322,8 +1322,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= -golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= -golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1338,8 +1338,8 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= -golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1407,8 +1407,8 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.14.0/go.mod h1:uYBEerGOWcJyEORxN+Ek8+TT266gXkNlHdJBwexUsBg= -golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= -golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools/go/expect v0.1.1-deprecated h1:jpBZDwmgPhXsKZC6WhL20P4b/wmnpsEAGHaNy0n/rJM= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated h1:1h2MnaIAIXISqTFKdENegdpAgUXz6NrPEsbIeWaBRvM= diff --git a/pkg/go.mod b/pkg/go.mod index b30ff9bfa..2c2f7814b 100644 --- a/pkg/go.mod +++ b/pkg/go.mod @@ -25,7 +25,7 @@ require ( github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.11.1 github.com/tidwall/jsonc v0.3.2 - golang.org/x/mod v0.29.0 + golang.org/x/mod v0.30.0 google.golang.org/grpc v1.76.0 ) diff --git a/pkg/go.sum b/pkg/go.sum index 5b054d35c..e75298fea 100644 --- a/pkg/go.sum +++ b/pkg/go.sum @@ -224,8 +224,8 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= +golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= From e3d22ada3db8d89fc0790d97bcbd89b9f2f4db1d Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Wed, 12 Nov 2025 14:54:37 +0800 Subject: [PATCH 05/19] fix: skip linking database if password is missing (#4429) * fix: mark database linking error as non-fatal * fix: skip pooler if db host is resolvable * chore: update unit tests * chore: unban all ip on failure to connect --- internal/bans/get/get.go | 20 ++--- internal/bootstrap/bootstrap.go | 5 +- internal/branches/get/get.go | 18 +--- internal/link/link.go | 28 ++----- internal/link/link_test.go | 58 +++++-------- internal/projects/create/create.go | 4 - internal/utils/connect.go | 17 ++++ internal/utils/flags/db_url.go | 123 ++++++++++++++++------------ internal/utils/flags/db_url_test.go | 10 ++- 9 files changed, 138 insertions(+), 145 deletions(-) diff --git a/internal/bans/get/get.go b/internal/bans/get/get.go index a9e52901d..5b747325c 100644 --- a/internal/bans/get/get.go +++ b/internal/bans/get/get.go @@ -4,23 +4,15 @@ import ( "context" "fmt" - "github.com/go-errors/errors" "github.com/spf13/afero" - "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" ) func Run(ctx context.Context, projectRef string, fsys afero.Fs) error { - // 1. Sanity checks. - // 2. get network bans - { - resp, err := utils.GetSupabase().V1ListAllNetworkBansWithResponse(ctx, projectRef) - if err != nil { - return errors.Errorf("failed to retrieve network bans: %w", err) - } - if resp.JSON201 == nil { - return errors.New("Unexpected error retrieving network bans: " + string(resp.Body)) - } - fmt.Printf("DB banned IPs: %+v\n", resp.JSON201.BannedIpv4Addresses) - return nil + ips, err := flags.ListNetworkBans(ctx, projectRef) + if err != nil { + return err } + fmt.Printf("DB banned IPs: %+v\n", ips) + return nil } diff --git a/internal/bootstrap/bootstrap.go b/internal/bootstrap/bootstrap.go index 3fd02fe65..6e93c0acb 100644 --- a/internal/bootstrap/bootstrap.go +++ b/internal/bootstrap/bootstrap.go @@ -112,7 +112,10 @@ func Run(ctx context.Context, starter StarterTemplate, fsys afero.Fs, options .. return err } // 6. Push migrations - config := flags.NewDbConfigWithPassword(ctx, flags.ProjectRef) + config, err := flags.NewDbConfigWithPassword(ctx, flags.ProjectRef) + if err != nil { + fmt.Fprintln(os.Stderr, err) + } if err := writeDotEnv(keys, config, fsys); err != nil { fmt.Fprintln(os.Stderr, "Failed to create .env file:", err) } diff --git a/internal/branches/get/get.go b/internal/branches/get/get.go index a207901cc..1f726ff77 100644 --- a/internal/branches/get/get.go +++ b/internal/branches/get/get.go @@ -27,7 +27,7 @@ func Run(ctx context.Context, branchId string, fsys afero.Fs) error { if err != nil { return err } - pooler, err := getPoolerConfig(ctx, detail.Ref) + pooler, err := utils.GetPoolerConfigPrimary(ctx, detail.Ref) if err != nil { return err } @@ -81,22 +81,6 @@ func getBranchDetail(ctx context.Context, branchId string) (api.BranchDetailResp return *resp.JSON200, nil } -func getPoolerConfig(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) { - var result api.SupavisorConfigResponse - resp, err := utils.GetSupabase().V1GetPoolerConfigWithResponse(ctx, ref) - if err != nil { - return result, errors.Errorf("failed to get pooler: %w", err) - } else if resp.JSON200 == nil { - return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body)) - } - for _, config := range *resp.JSON200 { - if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY { - return config, nil - } - } - return result, errors.Errorf("primary database not found: %s", ref) -} - func toStandardEnvs(detail api.BranchDetailResponse, pooler api.SupavisorConfigResponse, keys []api.ApiKeyResponse) map[string]string { direct := pgconn.Config{ Host: detail.DbHost, diff --git a/internal/link/link.go b/internal/link/link.go index 98e3b4814..9c7d0ce97 100644 --- a/internal/link/link.go +++ b/internal/link/link.go @@ -18,7 +18,6 @@ import ( "github.com/supabase/cli/pkg/api" "github.com/supabase/cli/pkg/cast" cliConfig "github.com/supabase/cli/pkg/config" - "github.com/supabase/cli/pkg/migration" "github.com/supabase/cli/pkg/queue" ) @@ -36,8 +35,9 @@ func Run(ctx context.Context, projectRef string, skipPooler bool, fsys afero.Fs, LinkServices(ctx, projectRef, keys.ServiceRole, skipPooler, fsys) // 2. Check database connection - config := flags.NewDbConfigWithPassword(ctx, projectRef) - if err := linkDatabase(ctx, config, fsys, options...); err != nil { + if config, err := flags.NewDbConfigWithPassword(ctx, projectRef); err != nil { + fmt.Fprintln(os.Stderr, utils.Yellow("WARN:"), err) + } else if err := linkDatabase(ctx, config, fsys, options...); err != nil { return err } @@ -186,14 +186,7 @@ func linkDatabase(ctx context.Context, config pgconn.Config, fsys afero.Fs, opti } defer conn.Close(context.Background()) updatePostgresConfig(conn) - if err := linkStorageMigration(ctx, conn, fsys); err != nil { - fmt.Fprintln(os.Stderr, err) - } - // If `schema_migrations` doesn't exist on the remote database, create it. - if err := migration.CreateMigrationTable(ctx, conn); err != nil { - return err - } - return migration.CreateSeedTable(ctx, conn) + return linkStorageMigration(ctx, conn, fsys) } func updatePostgresConfig(conn *pgx.Conn) { @@ -207,18 +200,11 @@ func updatePostgresConfig(conn *pgx.Conn) { } func linkPooler(ctx context.Context, projectRef string, fsys afero.Fs) error { - resp, err := utils.GetSupabase().V1GetPoolerConfigWithResponse(ctx, projectRef) + primary, err := utils.GetPoolerConfigPrimary(ctx, projectRef) if err != nil { - return errors.Errorf("failed to get pooler config: %w", err) - } - if resp.JSON200 == nil { - return errors.Errorf("%w: %s", tenant.ErrAuthToken, string(resp.Body)) - } - for _, config := range *resp.JSON200 { - if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY { - updatePoolerConfig(config) - } + return err } + updatePoolerConfig(primary) return utils.WriteFile(utils.PoolerUrlPath, []byte(utils.Config.Db.Pooler.ConnectionString), fsys) } diff --git a/internal/link/link_test.go b/internal/link/link_test.go index c6a7339e0..bf7c85761 100644 --- a/internal/link/link_test.go +++ b/internal/link/link_test.go @@ -15,11 +15,9 @@ import ( "github.com/stretchr/testify/assert" "github.com/supabase/cli/internal/testing/apitest" "github.com/supabase/cli/internal/testing/fstest" - "github.com/supabase/cli/internal/testing/helper" "github.com/supabase/cli/internal/utils" "github.com/supabase/cli/internal/utils/tenant" "github.com/supabase/cli/pkg/api" - "github.com/supabase/cli/pkg/migration" "github.com/supabase/cli/pkg/pgtest" "github.com/zalando/go-keyring" ) @@ -44,15 +42,6 @@ func TestLinkCommand(t *testing.T) { t.Cleanup(fstest.MockStdin(t, "\n")) // Setup in-memory fs fsys := afero.NewMemMapFs() - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.SET_SESSION_ROLE). - Reply("SET ROLE"). - Query(GET_LATEST_STORAGE_MIGRATION). - Reply("SELECT 1", []any{"custom-metadata"}) - helper.MockMigrationHistory(conn) - helper.MockSeedHistory(conn) // Flush pending mocks after test execution defer gock.OffAll() // Mock project status @@ -119,7 +108,7 @@ func TestLinkCommand(t *testing.T) { Reply(200). BodyString(storage) // Run test - err := Run(context.Background(), project, false, fsys, conn.Intercept) + err := Run(context.Background(), project, false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -198,22 +187,13 @@ func TestLinkCommand(t *testing.T) { } }) // Check error - assert.ErrorContains(t, err, "hostname resolving error") + assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) }) t.Run("throws error on write failure", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewReadOnlyFs(afero.NewMemMapFs()) - // Setup mock postgres - conn := pgtest.NewConn() - defer conn.Close(t) - conn.Query(utils.SET_SESSION_ROLE). - Reply("SET ROLE"). - Query(GET_LATEST_STORAGE_MIGRATION). - Reply("SELECT 1", []any{"custom-metadata"}) - helper.MockMigrationHistory(conn) - helper.MockSeedHistory(conn) // Flush pending mocks after test execution defer gock.OffAll() // Mock project status @@ -269,7 +249,7 @@ func TestLinkCommand(t *testing.T) { Get("/v1/projects"). ReplyError(errors.New("network error")) // Run test - err := Run(context.Background(), project, false, fsys, conn.Intercept) + err := Run(context.Background(), project, false, fsys) // Check error assert.ErrorContains(t, err, "operation not permitted") assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -419,6 +399,23 @@ func TestLinkPostgrest(t *testing.T) { } func TestLinkDatabase(t *testing.T) { + t.Run("syncs storage migration", func(t *testing.T) { + // Setup in-memory fs + fsys := afero.NewMemMapFs() + // Setup mock postgres + conn := pgtest.NewConn() + defer conn.Close(t) + conn.Query(GET_LATEST_STORAGE_MIGRATION). + Reply("SELECT 1", []any{"custom-metadata"}) + // Run test + err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept) + // Check error + assert.NoError(t, err) + storage, err := afero.ReadFile(fsys, utils.StorageMigrationPath) + assert.NoError(t, err) + assert.Equal(t, []byte("custom-metadata"), storage) + }) + t.Run("throws error on connect failure", func(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() @@ -438,8 +435,6 @@ func TestLinkDatabase(t *testing.T) { defer conn.Close(t) conn.Query(GET_LATEST_STORAGE_MIGRATION). Reply("SELECT 1", []any{"custom-metadata"}) - helper.MockMigrationHistory(conn) - helper.MockSeedHistory(conn) // Run test err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept) // Check error @@ -461,8 +456,6 @@ func TestLinkDatabase(t *testing.T) { defer conn.Close(t) conn.Query(GET_LATEST_STORAGE_MIGRATION). Reply("SELECT 1", []any{"custom-metadata"}) - helper.MockMigrationHistory(conn) - helper.MockSeedHistory(conn) // Run test err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept) // Check error @@ -481,18 +474,11 @@ func TestLinkDatabase(t *testing.T) { conn := pgtest.NewConn() defer conn.Close(t) conn.Query(GET_LATEST_STORAGE_MIGRATION). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation migrations"). - Query(migration.SET_LOCK_TIMEOUT). - Query(migration.CREATE_VERSION_SCHEMA). - Reply("CREATE SCHEMA"). - Query(migration.CREATE_VERSION_TABLE). - ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation supabase_migrations"). - Query(migration.ADD_STATEMENTS_COLUMN). - Query(migration.ADD_NAME_COLUMN) + ReplyError(pgerrcode.InsufficientPrivilege, "permission denied for relation migrations") // Run test err := linkDatabase(context.Background(), dbConfig, fsys, conn.Intercept) // Check error - assert.ErrorContains(t, err, "ERROR: permission denied for relation supabase_migrations (SQLSTATE 42501)") + assert.ErrorContains(t, err, "ERROR: permission denied for relation migrations (SQLSTATE 42501)") exists, err := afero.Exists(fsys, utils.StorageMigrationPath) assert.NoError(t, err) assert.False(t, exists) diff --git a/internal/projects/create/create.go b/internal/projects/create/create.go index 6be3fc8ab..9b8e03d1a 100644 --- a/internal/projects/create/create.go +++ b/internal/projects/create/create.go @@ -10,7 +10,6 @@ import ( "github.com/spf13/afero" "github.com/spf13/viper" "github.com/supabase/cli/internal/utils" - "github.com/supabase/cli/internal/utils/credentials" "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/api" ) @@ -30,9 +29,6 @@ func Run(ctx context.Context, params api.V1CreateProjectBody, fsys afero.Fs) err flags.ProjectRef = resp.JSON201.Id viper.Set("DB_PASSWORD", params.DbPass) - if err := credentials.StoreProvider.Set(flags.ProjectRef, params.DbPass); err != nil { - fmt.Fprintln(os.Stderr, "Failed to save database password:", err) - } projectUrl := fmt.Sprintf("%s/project/%s", utils.GetSupabaseDashboardURL(), resp.JSON201.Id) fmt.Fprintf(os.Stderr, "Created a new project at %s\n", utils.Bold(projectUrl)) diff --git a/internal/utils/connect.go b/internal/utils/connect.go index bd8d199f7..5fc4e3b9b 100644 --- a/internal/utils/connect.go +++ b/internal/utils/connect.go @@ -15,6 +15,7 @@ import ( "github.com/jackc/pgx/v4" "github.com/spf13/viper" "github.com/supabase/cli/internal/debug" + "github.com/supabase/cli/pkg/api" "github.com/supabase/cli/pkg/pgxv5" "golang.org/x/net/publicsuffix" ) @@ -43,6 +44,22 @@ func ToPostgresURL(config pgconn.Config) string { ) } +func GetPoolerConfigPrimary(ctx context.Context, ref string) (api.SupavisorConfigResponse, error) { + var result api.SupavisorConfigResponse + resp, err := GetSupabase().V1GetPoolerConfigWithResponse(ctx, ref) + if err != nil { + return result, errors.Errorf("failed to get pooler: %w", err) + } else if resp.JSON200 == nil { + return result, errors.Errorf("unexpected get pooler status %d: %s", resp.StatusCode(), string(resp.Body)) + } + for _, config := range *resp.JSON200 { + if config.DatabaseType == api.SupavisorConfigResponseDatabaseTypePRIMARY { + return config, nil + } + } + return result, errors.Errorf("primary database not found: %s", ref) +} + func GetPoolerConfig(projectRef string) *pgconn.Config { logger := GetDebugLogger() if len(Config.Db.Pooler.ConnectionString) == 0 { diff --git a/internal/utils/flags/db_url.go b/internal/utils/flags/db_url.go index 7ee2417e5..4dca1d936 100644 --- a/internal/utils/flags/db_url.go +++ b/internal/utils/flags/db_url.go @@ -6,6 +6,7 @@ import ( _ "embed" "fmt" "math/big" + "net" "net/http" "os" "strings" @@ -82,7 +83,10 @@ func ParseDatabaseConfig(ctx context.Context, flagSet *pflag.FlagSet, fsys afero if err := LoadConfig(fsys); err != nil { return err } - DbConfig = NewDbConfigWithPassword(ctx, ProjectRef) + var err error + if DbConfig, err = NewDbConfigWithPassword(ctx, ProjectRef); err != nil { + return err + } case proxy: token, err := utils.LoadAccessTokenFS(fsys) if err != nil { @@ -115,67 +119,96 @@ func RandomString(size int) (string, error) { return string(data), nil } -func NewDbConfigWithPassword(ctx context.Context, projectRef string) pgconn.Config { - config := getDbConfig(projectRef) - config.Password = viper.GetString("DB_PASSWORD") +const suggestEnvVar = "Connect to your database by setting the env var: SUPABASE_DB_PASSWORD" + +func NewDbConfigWithPassword(ctx context.Context, projectRef string) (pgconn.Config, error) { + config := pgconn.Config{ + Host: utils.GetSupabaseDbHost(projectRef), + Port: 5432, + User: "postgres", + Password: viper.GetString("DB_PASSWORD"), + Database: "postgres", + } + logger := utils.GetDebugLogger() + // Use pooler if host is not reachable directly + if _, err := net.DefaultResolver.LookupIPAddr(ctx, config.Host); err != nil { + if poolerConfig := utils.GetPoolerConfig(projectRef); poolerConfig != nil { + if len(config.Password) > 0 { + fmt.Fprintln(logger, "Using database password from env var...") + poolerConfig.Password = config.Password + } else if err := initPoolerLogin(ctx, projectRef, poolerConfig); err != nil { + utils.CmdSuggestion = suggestEnvVar + return *poolerConfig, err + } + return *poolerConfig, nil + } + utils.CmdSuggestion = fmt.Sprintf("Run %s to setup IPv4 connection.", utils.Aqua("supabase link --project-ref "+projectRef)) + return config, errors.Errorf("IPv6 is not supported on your current network: %w", err) + } + // Connect via direct connection if len(config.Password) > 0 { - return config - } - loginRole, err := initLoginRole(ctx, projectRef, config) - if err == nil { - return loginRole - } else if errors.Is(err, context.Canceled) { - return config - } - // Proceed with password prompt - fmt.Fprintln(utils.GetDebugLogger(), err) - if config.Password, err = credentials.StoreProvider.Get(projectRef); err == nil { - return config - } - 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: ") - config.Password = credentials.PromptMasked(os.Stdin) - return config + fmt.Fprintln(logger, "Using database password from env var...") + } else if err := initLoginRole(ctx, projectRef, &config); err != nil { + // Do not prompt because reading masked input is buggy on windows + utils.CmdSuggestion = suggestEnvVar + return config, err + } + return config, nil } -func initLoginRole(ctx context.Context, projectRef string, config pgconn.Config) (pgconn.Config, error) { +func initLoginRole(ctx context.Context, projectRef string, config *pgconn.Config) error { fmt.Fprintln(os.Stderr, "Initialising login role...") body := api.CreateRoleBody{ReadOnly: false} resp, err := utils.GetSupabase().V1CreateLoginRoleWithResponse(ctx, projectRef, body) if err != nil { - return pgconn.Config{}, errors.Errorf("failed to initialise login role: %w", err) + return errors.Errorf("failed to initialise login role: %w", err) } else if resp.JSON201 == nil { - return pgconn.Config{}, errors.Errorf("unexpected login role status %d: %s", resp.StatusCode(), string(resp.Body)) + return errors.Errorf("unexpected login role status %d: %s", resp.StatusCode(), string(resp.Body)) + } + config.User = resp.JSON201.Role + config.Password = resp.JSON201.Password + return nil +} + +func initPoolerLogin(ctx context.Context, projectRef string, poolerConfig *pgconn.Config) error { + poolerUser := poolerConfig.User + if err := initLoginRole(ctx, projectRef, poolerConfig); err != nil { + return err } - // Direct connection can be tried immediately suffix := "." + projectRef - if !strings.HasSuffix(config.User, suffix) { - config.User = resp.JSON201.Role - config.Password = resp.JSON201.Password - return config, nil + if strings.HasSuffix(poolerUser, suffix) { + poolerConfig.User += suffix } // Wait for pooler to refresh password - config.User = resp.JSON201.Role + suffix - config.Password = resp.JSON201.Password login := func() error { - conn, err := pgconn.ConnectConfig(ctx, &config) + conn, err := pgconn.ConnectConfig(ctx, poolerConfig) if err != nil { return errors.Errorf("failed to connect as temp role: %w", err) } return conn.Close(ctx) } - // Fallback to password prompt on error notify := utils.NewErrorCallback(func(attempt uint) error { - if attempt%3 > 0 { + if attempt < 3 { return nil } - return UnbanIP(ctx, projectRef) + if ips, err := ListNetworkBans(ctx, projectRef); err != nil { + return err + } else if len(ips) > 0 { + return UnbanIP(ctx, projectRef, ips...) + } + return nil }) - if err := backoff.RetryNotify(login, utils.NewBackoffPolicy(ctx), notify); err != nil { - return pgconn.Config{}, err + return backoff.RetryNotify(login, utils.NewBackoffPolicy(ctx), notify) +} + +func ListNetworkBans(ctx context.Context, projectRef string) ([]string, error) { + resp, err := utils.GetSupabase().V1ListAllNetworkBansWithResponse(ctx, projectRef) + if err != nil { + return nil, errors.Errorf("failed to list network bans: %w", err) + } else if resp.JSON201 == nil { + return nil, errors.Errorf("unexpected list bans status %d: %s", resp.StatusCode(), string(resp.Body)) } - return config, nil + return resp.JSON201.BannedIpv4Addresses, nil } func UnbanIP(ctx context.Context, projectRef string, addrs ...string) error { @@ -214,15 +247,3 @@ func PromptPassword(stdin *os.File) string { } return string(password) } - -func getDbConfig(projectRef string) pgconn.Config { - if poolerConfig := utils.GetPoolerConfig(projectRef); poolerConfig != nil { - return *poolerConfig - } - return pgconn.Config{ - Host: utils.GetSupabaseDbHost(projectRef), - Port: 5432, - User: "postgres", - Database: "postgres", - } -} diff --git a/internal/utils/flags/db_url_test.go b/internal/utils/flags/db_url_test.go index 1bb3fcc79..c79c7f936 100644 --- a/internal/utils/flags/db_url_test.go +++ b/internal/utils/flags/db_url_test.go @@ -2,11 +2,14 @@ package flags import ( "context" + "fmt" "os" + "strings" "testing" "github.com/spf13/afero" "github.com/spf13/pflag" + "github.com/spf13/viper" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/supabase/cli/internal/testing/apitest" @@ -70,10 +73,15 @@ func TestParseDatabaseConfig(t *testing.T) { err = afero.WriteFile(fsys, utils.ProjectRefPath, []byte(project), 0644) require.NoError(t, err) + dbURL := fmt.Sprintf("postgres://postgres:postgres@db.%s.supabase.co:6543/postgres", project) + err = afero.WriteFile(fsys, utils.PoolerUrlPath, []byte(dbURL), 0644) + require.NoError(t, err) + + viper.Set("DB_PASSWORD", "test") err = ParseDatabaseConfig(context.Background(), flagSet, fsys) assert.NoError(t, err) - assert.Equal(t, utils.GetSupabaseDbHost(project), DbConfig.Host) + assert.True(t, strings.HasPrefix(DbConfig.Host, utils.GetSupabaseDbHost(project))) }) } From a057b743046f46a390027113f15e18580643367d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 13 Nov 2025 04:19:45 +0000 Subject: [PATCH 06/19] fix(docker): bump the docker-minor group across 1 directory with 3 updates (#4441) Bumps the docker-minor group with 3 updates in the /pkg/config/templates directory: supabase/realtime, supabase/storage-api and supabase/logflare. Updates `supabase/realtime` from v2.63.0 to v2.63.3 Updates `supabase/storage-api` from v1.29.1 to v1.29.3 Updates `supabase/logflare` from 1.25.3 to 1.26.3 --- updated-dependencies: - dependency-name: supabase/realtime dependency-version: v2.63.3 dependency-type: direct:production dependency-group: docker-minor - dependency-name: supabase/storage-api dependency-version: v1.29.3 dependency-type: direct:production dependency-group: docker-minor - dependency-name: supabase/logflare dependency-version: 1.26.3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: docker-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index 61e632bf1..bdc150175 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -11,9 +11,9 @@ FROM supabase/edge-runtime:v1.69.23 AS edgeruntime FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor FROM supabase/gotrue:v2.182.1 AS gotrue -FROM supabase/realtime:v2.63.0 AS realtime -FROM supabase/storage-api:v1.29.1 AS storage -FROM supabase/logflare:1.25.3 AS logflare +FROM supabase/realtime:v2.63.3 AS realtime +FROM supabase/storage-api:v1.29.3 AS storage +FROM supabase/logflare:1.26.3 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ FROM supabase/migra:3.0.1663481299 AS migra From 9f3ad132dcb43f86bfe6cc92c5baf63eda4a7ed9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=83=A5=EB=83=90=EC=B1=A0?= Date: Thu, 13 Nov 2025 17:54:19 +0900 Subject: [PATCH 07/19] fix: toggle `DENO_NO_PACKAGE_JSON` conditionally (#4372) * fix: toggle `DENO_NO_PACKAGE_JSON` conditionally * stamp: meowmeow * stamp: meow! * chore: update `main.ts` * chore: update `main.ts` * chore: move package json check to bundler * fix: handle custom import map path * chore: respect global default --------- Co-authored-by: Qiao Han --- internal/functions/deploy/bundle.go | 3 +++ internal/functions/serve/templates/main.ts | 18 ++++++++++++++++++ pkg/function/bundle.go | 14 ++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/internal/functions/deploy/bundle.go b/internal/functions/deploy/bundle.go index 13b9a6360..0b91b5377 100644 --- a/internal/functions/deploy/bundle.go +++ b/internal/functions/deploy/bundle.go @@ -62,6 +62,9 @@ func (b *dockerBundler) Bundle(ctx context.Context, slug, entrypoint, importMap cmd = append(cmd, function.BundleFlags...) env := []string{} + if !function.ShouldUsePackageJsonDiscovery(entrypoint, importMap, afero.NewIOFS(b.fsys)) { + env = append(env, "DENO_NO_PACKAGE_JSON=1") + } if custom_registry := os.Getenv("NPM_CONFIG_REGISTRY"); custom_registry != "" { env = append(env, "NPM_CONFIG_REGISTRY="+custom_registry) } diff --git a/internal/functions/serve/templates/main.ts b/internal/functions/serve/templates/main.ts index 568c547f8..4d5430efe 100644 --- a/internal/functions/serve/templates/main.ts +++ b/internal/functions/serve/templates/main.ts @@ -116,6 +116,22 @@ async function verifyJWT(jwt: string): Promise { return true; } +// Ref: https://docs.deno.com/examples/checking_file_existence/ +async function shouldUsePackageJsonDiscovery({ entrypointPath, importMapPath }: FunctionConfig): Promise { + if (importMapPath) { + return false + } + const packageJsonPath = posix.join(posix.dirname(entrypointPath), "package.json") + try { + await Deno.lstat(packageJsonPath); + } catch (err) { + if (err instanceof Deno.errors.NotFound) { + return false + } + } + return true +} + Deno.serve({ handler: async (req: Request) => { const url = new URL(req.url); @@ -178,6 +194,7 @@ Deno.serve({ const absEntrypoint = posix.join(Deno.cwd(), functionsConfig[functionName].entrypointPath); const maybeEntrypoint = posix.toFileUrl(absEntrypoint).href; + const usePackageJson = await shouldUsePackageJsonDiscovery(functionsConfig[functionName]); const staticPatterns = functionsConfig[functionName].staticFiles; @@ -187,6 +204,7 @@ Deno.serve({ memoryLimitMb, workerTimeoutMs, noModuleCache, + noNpm: !usePackageJson, importMapPath: functionsConfig[functionName].importMapPath, envVars, forceCreate, diff --git a/pkg/function/bundle.go b/pkg/function/bundle.go index fb13ae64c..04ec2c240 100644 --- a/pkg/function/bundle.go +++ b/pkg/function/bundle.go @@ -68,6 +68,9 @@ func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap cmd := exec.CommandContext(ctx, edgeRuntimeBin, args...) cmd.Stderr = os.Stderr cmd.Stdout = os.Stdout + if fsys, ok := b.fsys.(fs.StatFS); ok && !ShouldUsePackageJsonDiscovery(entrypoint, importMap, fsys) { + cmd.Env = append(cmd.Environ(), "DENO_NO_PACKAGE_JSON=1") + } if err := cmd.Run(); err != nil { return meta, errors.Errorf("failed to bundle function: %w", err) } @@ -81,6 +84,17 @@ func (b *nativeBundler) Bundle(ctx context.Context, slug, entrypoint, importMap return meta, Compress(eszipBytes, output) } +func ShouldUsePackageJsonDiscovery(entrypoint, importMap string, fsys fs.StatFS) bool { + if len(importMap) > 0 { + return false + } + packageJsonPath := filepath.Join(filepath.Dir(entrypoint), "package.json") + if _, err := fsys.Stat(packageJsonPath); errors.Is(err, os.ErrNotExist) { + return false + } + return true +} + const compressedEszipMagicID = "EZBR" func Compress(r io.Reader, w io.Writer) error { From cbbe7eca09b00c2fbb76d05a003b4df146ca433f Mon Sep 17 00:00:00 2001 From: Matt Johnston Date: Thu, 13 Nov 2025 08:46:11 -0400 Subject: [PATCH 08/19] feat: `functions download foo --use-api` (#4381) * feat: enable serverside unbundling * chore: use a single flag for api deploy * chore: hide use docker flags * chore: remove unnecessary calls * test: ensure ""./../.." fails to joinWithinDir * wip: server-side unbundle: entrypoint-based directory structuring * test: better --use-docker coverage * refactor: improve coverage of "supabase functions download --use-api" * test: better coverage for abs paths from getBaseDir * fix: lintfix cleanup tmp files * refactor: limit changes to existing tests * test: rename TestRun and delete bad comment * docs: remove big comment * refactor: remove extraneous const * test: unify test implementations across api, docker, legacy * refactor: improve cleanup func name and comment * fix: account for deno2 entrypoint path * chore: cleanup unit tests * chore: address linter warnings --------- Co-authored-by: Qiao Han --- cmd/functions.go | 16 +- internal/functions/download/download.go | 182 +++++++++- internal/functions/download/download_test.go | 335 ++++++++++++++++++- 3 files changed, 519 insertions(+), 14 deletions(-) diff --git a/cmd/functions.go b/cmd/functions.go index 6b5684bc2..bddb114ba 100644 --- a/cmd/functions.go +++ b/cmd/functions.go @@ -49,7 +49,10 @@ var ( Long: "Download the source code for a Function from the linked Supabase project.", Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - return download.Run(cmd.Context(), args[0], flags.ProjectRef, useLegacyBundle, afero.NewOsFs()) + if useApi { + useDocker = false + } + return download.Run(cmd.Context(), args[0], flags.ProjectRef, useLegacyBundle, useDocker, afero.NewOsFs()) }, } @@ -138,6 +141,7 @@ func init() { deployFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") functionsDeployCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker", "legacy-bundle") cobra.CheckErr(deployFlags.MarkHidden("legacy-bundle")) + cobra.CheckErr(deployFlags.MarkHidden("use-docker")) deployFlags.UintVarP(&maxJobs, "jobs", "j", 1, "Maximum number of parallel jobs.") deployFlags.BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.") deployFlags.BoolVar(&prune, "prune", false, "Delete Functions that exist in Supabase project but not locally.") @@ -152,8 +156,14 @@ func init() { functionsServeCmd.MarkFlagsMutuallyExclusive("inspect", "inspect-mode") functionsServeCmd.Flags().Bool("all", true, "Serve all Functions.") cobra.CheckErr(functionsServeCmd.Flags().MarkHidden("all")) - functionsDownloadCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") - functionsDownloadCmd.Flags().BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") + downloadFlags := functionsDownloadCmd.Flags() + downloadFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") + downloadFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") + downloadFlags.BoolVar(&useApi, "use-api", false, "Use Management API to unbundle functions server-side.") + downloadFlags.BoolVar(&useDocker, "use-docker", true, "Use Docker to unbundle functions client-side.") + functionsDownloadCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker", "legacy-bundle") + cobra.CheckErr(downloadFlags.MarkHidden("legacy-bundle")) + cobra.CheckErr(downloadFlags.MarkHidden("use-docker")) functionsCmd.AddCommand(functionsListCmd) functionsCmd.AddCommand(functionsDeleteCmd) functionsCmd.AddCommand(functionsDeployCmd) diff --git a/internal/functions/download/download.go b/internal/functions/download/download.go index e75ec7d9f..cbfac7b51 100644 --- a/internal/functions/download/download.go +++ b/internal/functions/download/download.go @@ -4,9 +4,14 @@ import ( "bufio" "bytes" "context" + "encoding/json" "fmt" "io" + "mime" + "mime/multipart" "net/http" + "net/textproto" + "net/url" "os" "os/exec" "path" @@ -16,6 +21,7 @@ import ( "github.com/andybalholm/brotli" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" + "github.com/docker/go-units" "github.com/go-errors/errors" "github.com/spf13/afero" "github.com/spf13/viper" @@ -112,15 +118,30 @@ func downloadFunction(ctx context.Context, projectRef, slug, extractScriptPath s return nil } -func Run(ctx context.Context, slug string, projectRef string, useLegacyBundle bool, fsys afero.Fs) error { +func Run(ctx context.Context, slug, projectRef string, useLegacyBundle, useDocker bool, fsys afero.Fs) error { + // Sanity check + if err := flags.LoadConfig(fsys); err != nil { + return err + } + if useLegacyBundle { return RunLegacy(ctx, slug, projectRef, fsys) } - // 1. Sanity check - if err := flags.LoadConfig(fsys); err != nil { - return err + + if useDocker { + if utils.IsDockerRunning(ctx) { + // download eszip file for client-side unbundling with edge-runtime + return downloadWithDockerUnbundle(ctx, slug, projectRef, fsys) + } else { + fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), "Docker is not running") + } } - // 2. Download eszip to temp file + + // Use server-side unbundling with multipart/form-data + return downloadWithServerSideUnbundle(ctx, slug, projectRef, fsys) +} + +func downloadWithDockerUnbundle(ctx context.Context, slug string, projectRef string, fsys afero.Fs) error { eszipPath, err := downloadOne(ctx, slug, projectRef, fsys) if err != nil { return err @@ -238,3 +259,154 @@ deno_version = 2 func suggestLegacyBundle(slug string) string { return fmt.Sprintf("\nIf your function is deployed using CLI < 1.120.0, trying running %s instead.", utils.Aqua("supabase functions download --legacy-bundle "+slug)) } + +type bundleMetadata struct { + EntrypointPath string `json:"deno2_entrypoint_path,omitempty"` +} + +// New server-side unbundle implementation that mirrors Studio's entrypoint-based +// base-dir + relative path behaviour. +func downloadWithServerSideUnbundle(ctx context.Context, slug, projectRef string, fsys afero.Fs) error { + fmt.Fprintln(os.Stderr, "Downloading Function:", utils.Bold(slug)) + + form, err := readForm(ctx, projectRef, slug) + if err != nil { + return err + } + defer func() { + if err := form.RemoveAll(); err != nil { + fmt.Fprintln(os.Stderr, err) + } + }() + + // Read entrypoint path from deno2 bundles + metadata := bundleMetadata{} + if data, ok := form.Value["metadata"]; ok { + for _, part := range data { + if err := json.Unmarshal([]byte(part), &metadata); err != nil { + return errors.Errorf("failed to unmarshal metadata: %w", err) + } + } + } + + // Fallback to function metadata from upstash + if len(metadata.EntrypointPath) == 0 { + upstash, err := getFunctionMetadata(ctx, projectRef, slug) + if err != nil { + return errors.Errorf("failed to get function metadata: %w", err) + } + entrypointUrl, err := url.Parse(*upstash.EntrypointPath) + if err != nil { + return errors.Errorf("failed to parse entrypoint URL: %w", err) + } + metadata.EntrypointPath = entrypointUrl.Path + } + fmt.Fprintln(utils.GetDebugLogger(), "Using entrypoint path:", metadata.EntrypointPath) + + // Root directory on disk: supabase/functions/ + funcDir := filepath.Join(utils.FunctionsDir, slug) + for _, data := range form.File { + for _, file := range data { + if err := saveFile(file, metadata.EntrypointPath, funcDir, fsys); err != nil { + return err + } + } + } + + fmt.Println("Downloaded Function " + utils.Aqua(slug) + " from project " + utils.Aqua(projectRef) + ".") + return nil +} + +func readForm(ctx context.Context, projectRef, slug string) (*multipart.Form, error) { + // Request multipart/form-data response using RequestEditorFn + resp, err := utils.GetSupabase().V1GetAFunctionBody(ctx, projectRef, slug, func(ctx context.Context, req *http.Request) error { + req.Header.Set("Accept", "multipart/form-data") + return nil + }) + if err != nil { + return nil, errors.Errorf("failed to download function: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, errors.Errorf("Error status %d: %w", resp.StatusCode, err) + } + return nil, errors.Errorf("Error status %d: %s", resp.StatusCode, string(body)) + } + + // Parse the multipart response + mediaType, params, err := mime.ParseMediaType(resp.Header.Get("Content-Type")) + if err != nil { + return nil, errors.Errorf("failed to parse content type: %w", err) + } + if !strings.HasPrefix(mediaType, "multipart/") { + return nil, errors.Errorf("expected multipart response, got %s", mediaType) + } + + // Read entire response with caching to disk + mr := multipart.NewReader(resp.Body, params["boundary"]) + form, err := mr.ReadForm(units.MiB) + if err != nil { + return nil, errors.Errorf("failed to read form: %w", err) + } + + return form, nil +} + +func saveFile(file *multipart.FileHeader, entrypointPath, funcDir string, fsys afero.Fs) error { + part, err := file.Open() + if err != nil { + return errors.Errorf("failed to open file: %w", err) + } + defer part.Close() + + logger := utils.GetDebugLogger() + partPath, err := getPartPath(file.Header) + if len(partPath) == 0 { + fmt.Fprintln(logger, "Skipping file with empty path:", file.Filename) + return err + } + fmt.Fprintln(logger, "Resolving file path:", partPath) + + relPath, err := filepath.Rel(filepath.FromSlash(entrypointPath), filepath.FromSlash(partPath)) + if err != nil { + // Continue extracting without entrypoint + fmt.Fprintln(os.Stderr, utils.Yellow("WARNING:"), err) + relPath = filepath.FromSlash(path.Join("..", partPath)) + } + + dstPath := filepath.Join(funcDir, path.Base(entrypointPath), relPath) + fmt.Fprintln(os.Stderr, "Extracting file:", dstPath) + if err := afero.WriteReader(fsys, dstPath, part); err != nil { + return errors.Errorf("failed to save file: %w", err) + } + + return nil +} + +// getPartPath extracts the filename for a multipart part, allowing for +// relative paths via the custom Supabase-Path header. +func getPartPath(header textproto.MIMEHeader) (string, error) { + // dedicated header to specify relative path, not expected to be used + if relPath := header.Get("Supabase-Path"); relPath != "" { + return relPath, nil + } + + // part.FileName() does not allow us to handle relative paths, so we parse Content-Disposition manually + cd := header.Get("Content-Disposition") + if cd == "" { + return "", nil + } + + _, params, err := mime.ParseMediaType(cd) + if err != nil { + return "", errors.Errorf("failed to parse content disposition: %w", err) + } + + if filename := params["filename"]; filename != "" { + return filename, nil + } + return "", nil +} diff --git a/internal/functions/download/download_test.go b/internal/functions/download/download_test.go index b727f95c3..713a26243 100644 --- a/internal/functions/download/download_test.go +++ b/internal/functions/download/download_test.go @@ -1,12 +1,18 @@ package download import ( + "bytes" "context" + "encoding/json" "errors" "fmt" "log" + "mime/multipart" "net/http" + "net/textproto" "os" + "path" + "path/filepath" "testing" "github.com/h2non/gock" @@ -15,7 +21,9 @@ import ( "github.com/stretchr/testify/require" "github.com/supabase/cli/internal/testing/apitest" "github.com/supabase/cli/internal/utils" + "github.com/supabase/cli/internal/utils/flags" "github.com/supabase/cli/pkg/api" + "github.com/supabase/cli/pkg/cast" ) func TestMain(m *testing.M) { @@ -37,7 +45,46 @@ func TestMain(m *testing.M) { os.Exit(m.Run()) } -func TestDownloadCommand(t *testing.T) { +type multipartPart struct { + filename string + supabasePath string + contents string +} + +func mockMultipartBody(t *testing.T, projectRef, slug string, metadata bundleMetadata, parts []multipartPart) { + t.Helper() + var buf bytes.Buffer + writer := multipart.NewWriter(&buf) + // Write metadata + headers := textproto.MIMEHeader{} + headers.Set("Content-Disposition", `form-data; name="metadata"`) + headers.Set("Content-Type", "application/json") + pw, err := writer.CreatePart(headers) + require.NoError(t, err) + enc := json.NewEncoder(pw) + require.NoError(t, enc.Encode(metadata)) + // Write files + for _, part := range parts { + headers := textproto.MIMEHeader{} + headers.Set("Content-Disposition", fmt.Sprintf(`form-data; name="file"; filename="%s"`, part.filename)) + if part.supabasePath != "" { + headers.Set("Supabase-Path", part.supabasePath) + } + pw, err := writer.CreatePart(headers) + require.NoError(t, err) + _, err = pw.Write([]byte(part.contents)) + require.NoError(t, err) + } + require.NoError(t, writer.Close()) + + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", projectRef, slug)). + Reply(http.StatusOK). + SetHeader("Content-Type", writer.FormDataContentType()). + Body(&buf) +} + +func TestRunLegacyUnbundle(t *testing.T) { const slug = "test-func" t.Run("downloads eszip bundle", func(t *testing.T) { @@ -61,7 +108,7 @@ func TestDownloadCommand(t *testing.T) { Get("/v1/projects/" + project + "/functions/" + slug + "/body"). Reply(http.StatusOK) // Run test - err = Run(context.Background(), slug, project, true, fsys) + err = Run(context.Background(), slug, project, true, false, fsys) // Check error assert.NoError(t, err) assert.Empty(t, apitest.ListUnmatchedRequests()) @@ -73,7 +120,7 @@ func TestDownloadCommand(t *testing.T) { // Setup valid project ref project := apitest.RandomProjectRef() // Run test - err := Run(context.Background(), "@", project, true, fsys) + err := Run(context.Background(), "@", project, true, false, fsys) // Check error assert.ErrorContains(t, err, "Invalid Function name.") }) @@ -84,7 +131,7 @@ func TestDownloadCommand(t *testing.T) { // Setup valid project ref project := apitest.RandomProjectRef() // Run test - err := Run(context.Background(), slug, project, true, fsys) + err := Run(context.Background(), slug, project, true, false, fsys) // Check error assert.ErrorContains(t, err, "operation not permitted") }) @@ -98,7 +145,7 @@ func TestDownloadCommand(t *testing.T) { _, err := fsys.Create(utils.DenoPathOverride) require.NoError(t, err) // Run test - err = Run(context.Background(), slug, project, true, afero.NewReadOnlyFs(fsys)) + err = Run(context.Background(), slug, project, true, false, afero.NewReadOnlyFs(fsys)) // Check error assert.ErrorContains(t, err, "operation not permitted") }) @@ -121,12 +168,236 @@ func TestDownloadCommand(t *testing.T) { Reply(http.StatusNotFound). JSON(map[string]string{"message": "Function not found"}) // Run test - err = Run(context.Background(), slug, project, true, fsys) + err = Run(context.Background(), slug, project, true, false, fsys) // Check error assert.ErrorContains(t, err, "Function test-func does not exist on the Supabase project.") }) } +func TestRunDockerUnbundle(t *testing.T) { + t.Run("downloads bundle with docker when available", func(t *testing.T) { + const slugDocker = "demo" + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + project := apitest.RandomProjectRef() + require.NoError(t, flags.LoadConfig(fsys)) + + token := apitest.RandomAccessToken(t) + t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) + + require.NoError(t, apitest.MockDocker(utils.Docker)) + dockerHost := utils.Docker.DaemonHost() + + // Setup mock api + defer gock.OffAll() + + gock.New(dockerHost). + Head("/_ping"). + Reply(http.StatusOK) + + imageURL := utils.GetRegistryImageUrl(utils.Config.EdgeRuntime.Image) + containerID := "docker-unbundle-test" + apitest.MockDockerStart(utils.Docker, imageURL, containerID) + require.NoError(t, apitest.MockDockerLogs(utils.Docker, containerID, "unbundle ok")) + + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", project, slugDocker)). + Reply(http.StatusOK). + BodyString("fake eszip payload") + + err := Run(context.Background(), slugDocker, project, false, true, fsys) + require.NoError(t, err) + + eszipPath := filepath.Join(utils.TempDir, fmt.Sprintf("output_%s.eszip", slugDocker)) + exists, err := afero.Exists(fsys, eszipPath) + require.NoError(t, err) + assert.False(t, exists, "temporary eszip file should be removed after extraction") + + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("falls back to server-side unbundle when docker unavailable", func(t *testing.T) { + const slugDocker = "demo-fallback" + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + project := apitest.RandomProjectRef() + require.NoError(t, flags.LoadConfig(fsys)) + + token := apitest.RandomAccessToken(t) + t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) + + require.NoError(t, apitest.MockDocker(utils.Docker)) + dockerHost := utils.Docker.DaemonHost() + + // Setup mock api + defer gock.OffAll() + + gock.New(dockerHost). + Head("/_ping"). + ReplyError(errors.New("docker unavailable")) + + mockMultipartBody(t, project, slugDocker, bundleMetadata{"/source/index.ts"}, []multipartPart{ + {filename: "/source/index.ts", contents: "console.log('hello')"}, + }) + + err := Run(context.Background(), slugDocker, project, false, true, fsys) + require.NoError(t, err) + + data, err := afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slugDocker, "index.ts")) + require.NoError(t, err) + assert.Equal(t, "console.log('hello')", string(data)) + + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) +} + +func TestRunServerSideUnbundle(t *testing.T) { + const slug = "test-func" + token := apitest.RandomAccessToken(t) + t.Setenv("SUPABASE_ACCESS_TOKEN", string(token)) + project := apitest.RandomProjectRef() + + t.Run("writes files using inferred base directory", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + + defer gock.OffAll() + mockMultipartBody(t, project, slug, bundleMetadata{EntrypointPath: "source/index.ts"}, []multipartPart{ + {filename: "source/index.ts", contents: "console.log('hello')"}, + {filename: "source/utils.ts", contents: "export const value = 1;"}, + }) + + err := Run(context.Background(), slug, project, false, false, fsys) + require.NoError(t, err) + + data, err := afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slug, "index.ts")) + require.NoError(t, err) + assert.Equal(t, "console.log('hello')", string(data)) + + data, err = afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, slug, "utils.ts")) + require.NoError(t, err) + assert.Equal(t, "export const value = 1;", string(data)) + + entries, err := afero.ReadDir(fsys, utils.TempDir) + if err == nil { + assert.Len(t, entries, 0, "expected temporary directory to be cleaned up") + } else { + assert.ErrorIs(t, err, os.ErrNotExist) + } + + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("derives base directory from absolute filenames", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + + defer gock.OffAll() + indexPath := "/tmp/functions-download-abs/source/index.ts" + utilsPath := path.Join(path.Dir(indexPath), "lib", "utils.ts") + mockMultipartBody(t, project, slug, bundleMetadata{}, []multipartPart{ + {filename: indexPath, contents: "console.log('abs')"}, + {filename: utilsPath, contents: "export const util = 2;"}, + }) + + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). + Reply(http.StatusOK). + JSON(api.FunctionSlugResponse{ + Id: "1", + Name: slug, + Slug: slug, + Status: api.FunctionSlugResponseStatus("ACTIVE"), + Version: 1, + CreatedAt: 0, + UpdatedAt: 0, + EntrypointPath: cast.Ptr("file://" + indexPath), + }) + + err := Run(context.Background(), slug, project, false, false, fsys) + require.NoError(t, err) + + root := filepath.Join(utils.FunctionsDir, slug) + data, err := afero.ReadFile(fsys, filepath.Join(root, "index.ts")) + require.NoError(t, err) + assert.Equal(t, "console.log('abs')", string(data)) + + data, err = afero.ReadFile(fsys, filepath.Join(root, "lib", "utils.ts")) + require.NoError(t, err) + assert.Equal(t, "export const util = 2;", string(data)) + + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) + + t.Run("fails when response not multipart", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + + defer gock.OffAll() + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). + Reply(http.StatusOK). + JSON(api.FunctionSlugResponse{ + Id: "1", + Name: slug, + Slug: slug, + Status: api.FunctionSlugResponseStatus("ACTIVE"), + Version: 1, + CreatedAt: 0, + UpdatedAt: 0, + EntrypointPath: cast.Ptr(legacyEntrypointPath), + }) + + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s/body", project, slug)). + Reply(http.StatusOK). + SetHeader("Content-Type", "application/json"). + BodyString(`{"error":"no multipart"}`) + + err := Run(context.Background(), slug, project, false, false, fsys) + assert.ErrorContains(t, err, "expected multipart response") + }) + + t.Run("ignores unresolvable entrypoint path", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, utils.WriteConfig(fsys, false)) + + defer gock.OffAll() + mockMultipartBody(t, project, slug, bundleMetadata{}, []multipartPart{ + {filename: "source/index.ts", contents: "console.log('hello')"}, + {filename: "source/secret.env", supabasePath: "../secret.env", contents: "SECRET=1"}, + }) + + gock.New(utils.DefaultApiHost). + Get(fmt.Sprintf("/v1/projects/%s/functions/%s", project, slug)). + Reply(http.StatusOK). + JSON(api.FunctionSlugResponse{ + Id: "1", + Name: slug, + Slug: slug, + Status: api.FunctionSlugResponseStatus("ACTIVE"), + Version: 1, + CreatedAt: 0, + UpdatedAt: 0, + EntrypointPath: cast.Ptr("file:///source/index.ts"), + }) + + err := Run(context.Background(), slug, project, false, false, fsys) + assert.NoError(t, err) + + root := filepath.Join(utils.FunctionsDir, slug) + data, err := afero.ReadFile(fsys, filepath.Join(root, "source", "index.ts")) + require.NoError(t, err) + assert.Equal(t, "console.log('hello')", string(data)) + + data, err = afero.ReadFile(fsys, filepath.Join(utils.FunctionsDir, "secret.env")) + require.NoError(t, err) + assert.Equal(t, "SECRET=1", string(data)) + + assert.Empty(t, apitest.ListUnmatchedRequests()) + }) +} + func TestDownloadFunction(t *testing.T) { const slug = "test-func" // Setup valid project ref @@ -235,3 +506,55 @@ func TestGetMetadata(t *testing.T) { assert.Nil(t, meta) }) } + +func TestGetPartPath(t *testing.T) { + t.Parallel() + + t.Run("returns path from Supabase header", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Supabase-Path", "dir/file.ts") + got, err := getPartPath(header) + require.NoError(t, err) + assert.Equal(t, "dir/file.ts", got) + }) + + t.Run("returns filename from content disposition", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; name="file"; filename="test-func/index.ts"`) + got, err := getPartPath(header) + require.NoError(t, err) + assert.Equal(t, "test-func/index.ts", got) + }) + + t.Run("returns filename from editor-originated content disposition", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; name="file"; filename="source/index.ts"`) + got, err := getPartPath(header) + require.NoError(t, err) + assert.Equal(t, "source/index.ts", got) + }) + + t.Run("writes file of arbitrary depth", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; name="file"; filename="test-func/dir/subdir/file.ts"`) + got, err := getPartPath(header) + require.NoError(t, err) + assert.Equal(t, "test-func/dir/subdir/file.ts", got) + }) + + t.Run("returns empty when no filename provided", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; name="file"`) + got, err := getPartPath(header) + require.NoError(t, err) + assert.Equal(t, "", got) + }) + + t.Run("returns error on invalid content disposition", func(t *testing.T) { + header := textproto.MIMEHeader{} + header.Set("Content-Disposition", `form-data; filename="unterminated`) + got, err := getPartPath(header) + require.ErrorContains(t, err, "failed to parse content disposition") + assert.Equal(t, "", got) + }) +} From 45dc05a7a2a87bb6fa4439deb8232272e68c3ae9 Mon Sep 17 00:00:00 2001 From: Copple <10214025+kiwicopple@users.noreply.github.com> Date: Thu, 13 Nov 2025 14:02:46 +0100 Subject: [PATCH 09/19] chore: sync API types from infrastructure (#4442) Co-authored-by: avallete <8771783+avallete@users.noreply.github.com> Co-authored-by: Han Qiao --- pkg/api/types.gen.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/pkg/api/types.gen.go b/pkg/api/types.gen.go index 9c25cef2f..267dfb1cd 100644 --- a/pkg/api/types.gen.go +++ b/pkg/api/types.gen.go @@ -4135,7 +4135,8 @@ type V1ProjectResponse struct { // CreatedAt Creation timestamp CreatedAt string `json:"created_at"` - // Id Id of your project + // Id Deprecated: Use `ref` instead. + // Deprecated: Id string `json:"id"` // Name Name of your project @@ -4144,6 +4145,9 @@ type V1ProjectResponse struct { // OrganizationId Slug of your organization OrganizationId string `json:"organization_id"` + // Ref Project ref + Ref string `json:"ref"` + // Region Region of your project Region string `json:"region"` Status V1ProjectResponseStatus `json:"status"` @@ -4170,7 +4174,8 @@ type V1ProjectWithDatabaseResponse struct { Version string `json:"version"` } `json:"database"` - // Id Id of your project + // Id Deprecated: Use `ref` instead. + // Deprecated: Id string `json:"id"` // Name Name of your project @@ -4179,6 +4184,9 @@ type V1ProjectWithDatabaseResponse struct { // OrganizationId Slug of your organization OrganizationId string `json:"organization_id"` + // Ref Project ref + Ref string `json:"ref"` + // Region Region of your project Region string `json:"region"` Status V1ProjectWithDatabaseResponseStatus `json:"status"` From 05d7be175c77c7641252c82a0a4eb5c3c5af5ab0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 04:19:31 +0000 Subject: [PATCH 10/19] fix(docker): bump supabase/postgres from 17.6.1.044 to 17.6.1.049 in /pkg/config/templates (#4445) fix(docker): bump supabase/postgres in /pkg/config/templates Bumps supabase/postgres from 17.6.1.044 to 17.6.1.049. --- updated-dependencies: - dependency-name: supabase/postgres dependency-version: 17.6.1.049 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index bdc150175..d241800a1 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -1,5 +1,5 @@ # Exposed for updates by .github/dependabot.yml -FROM supabase/postgres:17.6.1.044 AS pg +FROM supabase/postgres:17.6.1.049 AS pg # Append to ServiceImages when adding new dependencies below FROM library/kong:2.8.1 AS kong FROM axllent/mailpit:v1.22.3 AS mailpit From 7be8351472b6dab688ac2855c550a25ecb35196d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 14 Nov 2025 04:24:49 +0000 Subject: [PATCH 11/19] fix(docker): bump the docker-minor group in /pkg/config/templates with 3 updates (#4444) fix(docker): bump the docker-minor group Bumps the docker-minor group in /pkg/config/templates with 3 updates: supabase/edge-runtime, supabase/storage-api and supabase/logflare. Updates `supabase/edge-runtime` from v1.69.23 to v1.69.24 Updates `supabase/storage-api` from v1.29.3 to v1.30.0 Updates `supabase/logflare` from 1.26.3 to 1.26.4 --- updated-dependencies: - dependency-name: supabase/edge-runtime dependency-version: v1.69.24 dependency-type: direct:production dependency-group: docker-minor - dependency-name: supabase/storage-api dependency-version: v1.30.0 dependency-type: direct:production dependency-group: docker-minor - dependency-name: supabase/logflare dependency-version: 1.26.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index d241800a1..addfbb0dc 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -7,13 +7,13 @@ FROM postgrest/postgrest:v13.0.7 AS postgrest FROM supabase/postgres-meta:v0.93.1 AS pgmeta FROM supabase/studio:2025.11.10-sha-5291fe3 AS studio FROM darthsim/imgproxy:v3.8.0 AS imgproxy -FROM supabase/edge-runtime:v1.69.23 AS edgeruntime +FROM supabase/edge-runtime:v1.69.24 AS edgeruntime FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor FROM supabase/gotrue:v2.182.1 AS gotrue FROM supabase/realtime:v2.63.3 AS realtime -FROM supabase/storage-api:v1.29.3 AS storage -FROM supabase/logflare:1.26.3 AS logflare +FROM supabase/storage-api:v1.30.0 AS storage +FROM supabase/logflare:1.26.4 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ FROM supabase/migra:3.0.1663481299 AS migra From 48e898bae66655d0131bdb69cbb9132503dff7a2 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Mon, 17 Nov 2025 08:46:46 +0100 Subject: [PATCH 12/19] Update storage API version in Dockerfile (#4454) --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index addfbb0dc..b5252fd39 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -12,7 +12,7 @@ FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor FROM supabase/gotrue:v2.182.1 AS gotrue FROM supabase/realtime:v2.63.3 AS realtime -FROM supabase/storage-api:v1.30.0 AS storage +FROM supabase/storage-api:v1.31.0 AS storage FROM supabase/logflare:1.26.4 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ From f5e0fa0147f79ae46b43ae5e333c4eac81c07bb0 Mon Sep 17 00:00:00 2001 From: Han Qiao Date: Mon, 17 Nov 2025 15:47:37 +0800 Subject: [PATCH 13/19] fix: use cron schedule to run on weekends (#4455) chore: use cron schedule to run on weekends --- .github/dependabot.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 0807ddecd..68c3ea150 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,7 +3,8 @@ updates: - package-ecosystem: "github-actions" directory: "/" schedule: - interval: "daily" + interval: "cron" + cronjob: "0 0 * * *" groups: action-minor: update-types: @@ -14,7 +15,8 @@ updates: - "/" - "pkg" schedule: - interval: "daily" + interval: "cron" + cronjob: "0 0 * * *" groups: go-minor: update-types: @@ -23,7 +25,8 @@ updates: - package-ecosystem: "npm" directory: "/" schedule: - interval: "daily" + interval: "cron" + cronjob: "0 0 * * *" groups: npm-minor: update-types: @@ -32,7 +35,8 @@ updates: - package-ecosystem: "docker" directory: "pkg/config/templates" schedule: - interval: "daily" + interval: "cron" + cronjob: "0 0 * * *" commit-message: prefix: "fix(docker): " groups: From 6313d19786433a20ffedc634169c008c651b5c6f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:53:48 +0800 Subject: [PATCH 14/19] fix(docker): bump supabase/postgres from 17.6.1.049 to 17.6.1.052 in /pkg/config/templates (#4457) fix(docker): bump supabase/postgres in /pkg/config/templates Bumps supabase/postgres from 17.6.1.049 to 17.6.1.052. --- updated-dependencies: - dependency-name: supabase/postgres dependency-version: 17.6.1.052 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index b5252fd39..1933c29d5 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -1,5 +1,5 @@ # Exposed for updates by .github/dependabot.yml -FROM supabase/postgres:17.6.1.049 AS pg +FROM supabase/postgres:17.6.1.052 AS pg # Append to ServiceImages when adding new dependencies below FROM library/kong:2.8.1 AS kong FROM axllent/mailpit:v1.22.3 AS mailpit From 901d87323e704f72c53c5a6e31c19eec32e6c32d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:54:01 +0800 Subject: [PATCH 15/19] fix(docker): bump supabase/studio from 2025.11.10-sha-5291fe3 to 2025.11.17-sha-6a18e49 in /pkg/config/templates in the docker-minor group (#4456) fix(docker): bump supabase/studio Bumps the docker-minor group in /pkg/config/templates with 1 update: supabase/studio. Updates `supabase/studio` from 2025.11.10-sha-5291fe3 to 2025.11.17-sha-6a18e49 --- updated-dependencies: - dependency-name: supabase/studio dependency-version: 2025.11.17-sha-6a18e49 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: docker-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index 1933c29d5..ec5563bad 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -5,7 +5,7 @@ FROM library/kong:2.8.1 AS kong FROM axllent/mailpit:v1.22.3 AS mailpit FROM postgrest/postgrest:v13.0.7 AS postgrest FROM supabase/postgres-meta:v0.93.1 AS pgmeta -FROM supabase/studio:2025.11.10-sha-5291fe3 AS studio +FROM supabase/studio:2025.11.17-sha-6a18e49 AS studio FROM darthsim/imgproxy:v3.8.0 AS imgproxy FROM supabase/edge-runtime:v1.69.24 AS edgeruntime FROM timberio/vector:0.28.1-alpine AS vector From 811472f015623eb2940259bb480831133be11ce4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:54:13 +0800 Subject: [PATCH 16/19] chore(deps): bump cycjimmy/semantic-release-action from 5 to 6 (#4458) Bumps [cycjimmy/semantic-release-action](https://github.com/cycjimmy/semantic-release-action) from 5 to 6. - [Release notes](https://github.com/cycjimmy/semantic-release-action/releases) - [Changelog](https://github.com/cycjimmy/semantic-release-action/blob/main/docs/CHANGELOG.md) - [Commits](https://github.com/cycjimmy/semantic-release-action/compare/v5...v6) --- updated-dependencies: - dependency-name: cycjimmy/semantic-release-action dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/release-beta.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-beta.yml b/.github/workflows/release-beta.yml index 02f4c3d8f..6181b2c96 100644 --- a/.github/workflows/release-beta.yml +++ b/.github/workflows/release-beta.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v5 - id: semantic-release - uses: cycjimmy/semantic-release-action@v5 + uses: cycjimmy/semantic-release-action@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From 2461318b5a2954174d3cae28c7988a4fbf70414f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 15:54:30 +0800 Subject: [PATCH 17/19] chore(deps): bump github.com/getsentry/sentry-go from 0.36.2 to 0.37.0 in the go-minor group across 1 directory (#4446) chore(deps): bump github.com/getsentry/sentry-go Bumps the go-minor group with 1 update in the / directory: [github.com/getsentry/sentry-go](https://github.com/getsentry/sentry-go). Updates `github.com/getsentry/sentry-go` from 0.36.2 to 0.37.0 - [Release notes](https://github.com/getsentry/sentry-go/releases) - [Changelog](https://github.com/getsentry/sentry-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/getsentry/sentry-go/compare/v0.36.2...v0.37.0) --- updated-dependencies: - dependency-name: github.com/getsentry/sentry-go dependency-version: 0.37.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go-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 db6adf921..0a534a28c 100644 --- a/go.mod +++ b/go.mod @@ -16,8 +16,9 @@ require ( github.com/docker/cli v28.5.2+incompatible github.com/docker/docker v28.5.2+incompatible github.com/docker/go-connections v0.6.0 + github.com/docker/go-units v0.5.0 github.com/fsnotify/fsnotify v1.9.0 - github.com/getsentry/sentry-go v0.36.2 + github.com/getsentry/sentry-go v0.37.0 github.com/go-errors/errors v1.5.1 github.com/go-git/go-git/v5 v5.16.3 github.com/go-playground/validator/v10 v10.28.0 @@ -122,7 +123,6 @@ require ( github.com/dnephin/pflag v1.0.7 // indirect github.com/docker/docker-credential-helpers v0.9.3 // indirect github.com/docker/go-metrics v0.0.1 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/dprotaso/go-yit v0.0.0-20220510233725-9ba8df137936 // indirect github.com/ecies/go/v2 v2.0.11 // indirect github.com/emirpasic/gods v1.18.1 // indirect diff --git a/go.sum b/go.sum index 98491214a..e6fb16d39 100644 --- a/go.sum +++ b/go.sum @@ -300,8 +300,8 @@ github.com/gabriel-vasile/mimetype v1.4.10 h1:zyueNbySn/z8mJZHLt6IPw0KoZsiQNszIp github.com/gabriel-vasile/mimetype v1.4.10/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s= github.com/getkin/kin-openapi v0.131.0 h1:NO2UeHnFKRYhZ8wg6Nyh5Cq7dHk4suQQr72a4pMrDxE= github.com/getkin/kin-openapi v0.131.0/go.mod h1:3OlG51PCYNsPByuiMB0t4fjnNlIDnaEDsjiKUV8nL58= -github.com/getsentry/sentry-go v0.36.2 h1:uhuxRPTrUy0dnSzTd0LrYXlBYygLkKY0hhlG5LXarzM= -github.com/getsentry/sentry-go v0.36.2/go.mod h1:p5Im24mJBeruET8Q4bbcMfCQ+F+Iadc4L48tB1apo2c= +github.com/getsentry/sentry-go v0.37.0 h1:5bavywHxVkU/9aOIF4fn3s5RTJX5Hdw6K2W6jLYtM98= +github.com/getsentry/sentry-go v0.37.0/go.mod h1:eRXCoh3uvmjQLY6qu63BjUZnaBu5L5WhMV1RwYO8W5s= github.com/ghostiam/protogetter v0.3.15 h1:1KF5sXel0HE48zh1/vn0Loiw25A9ApyseLzQuif1mLY= github.com/ghostiam/protogetter v0.3.15/go.mod h1:WZ0nw9pfzsgxuRsPOFQomgDVSWtDLJRfQJEhsGbmQMA= github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c= From 7327bbfdf6b9336c782fe0e2437bffa1a8e5bb22 Mon Sep 17 00:00:00 2001 From: Lakshan Perera Date: Mon, 17 Nov 2025 20:19:39 +1100 Subject: [PATCH 18/19] fix: update use-api help text (#4450) chore: update use-api help text --- cmd/functions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/functions.go b/cmd/functions.go index bddb114ba..6b48ad662 100644 --- a/cmd/functions.go +++ b/cmd/functions.go @@ -136,7 +136,7 @@ func init() { functionsListCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") functionsDeleteCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") deployFlags := functionsDeployCmd.Flags() - deployFlags.BoolVar(&useApi, "use-api", false, "Use Management API to bundle functions.") + deployFlags.BoolVar(&useApi, "use-api", false, "Bundle functions server-side without using Docker.") deployFlags.BoolVar(&useDocker, "use-docker", true, "Use Docker to bundle functions.") deployFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") functionsDeployCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker", "legacy-bundle") @@ -159,7 +159,7 @@ func init() { downloadFlags := functionsDownloadCmd.Flags() downloadFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.") downloadFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.") - downloadFlags.BoolVar(&useApi, "use-api", false, "Use Management API to unbundle functions server-side.") + downloadFlags.BoolVar(&useApi, "use-api", false, "Unbundle functions server-side without using Docker.") downloadFlags.BoolVar(&useDocker, "use-docker", true, "Use Docker to unbundle functions client-side.") functionsDownloadCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker", "legacy-bundle") cobra.CheckErr(downloadFlags.MarkHidden("legacy-bundle")) From 10cdd70c7eb0b9ca99624819c3482f62a8e3a695 Mon Sep 17 00:00:00 2001 From: Fabrizio Date: Mon, 17 Nov 2025 12:11:08 +0100 Subject: [PATCH 19/19] fix: storage-api version to v1.31.1 (#4459) --- pkg/config/templates/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/config/templates/Dockerfile b/pkg/config/templates/Dockerfile index ec5563bad..f730d1a65 100644 --- a/pkg/config/templates/Dockerfile +++ b/pkg/config/templates/Dockerfile @@ -12,7 +12,7 @@ FROM timberio/vector:0.28.1-alpine AS vector FROM supabase/supavisor:2.7.4 AS supavisor FROM supabase/gotrue:v2.182.1 AS gotrue FROM supabase/realtime:v2.63.3 AS realtime -FROM supabase/storage-api:v1.31.0 AS storage +FROM supabase/storage-api:v1.31.1 AS storage FROM supabase/logflare:1.26.4 AS logflare # Append to JobImages when adding new dependencies below FROM supabase/pgadmin-schema-diff:cli-0.0.5 AS differ