Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
5e2539c
fix(cli): start secrets selinux (#6000)
7ttp Jul 31, 2026
08be342
fix(cli): reload kong after db reset (#6017)
7ttp Jul 31, 2026
c0cb3ff
chore(deps): bump github.com/docker/go-connections from 0.7.0 to 0.8.…
dependabot[bot] Aug 1, 2026
7f51f28
chore(ci): bump the actions-major group with 3 updates (#6033)
dependabot[bot] Aug 3, 2026
267ebbe
chore: sync API types from infrastructure (#6039)
supabase-cli-releaser[bot] Aug 3, 2026
9e4d713
fix(cli): use cobra mutual-exclusivity template in sso add (#5974)
Coly010 Aug 3, 2026
47fdba9
fix(cli): edge and cosmetic parity sweep from the 2026-07-24 audit (C…
Coly010 Aug 3, 2026
06715fd
fix(cli): match Go bundler env and deploy path anchoring (CLI-1985) (…
Coly010 Aug 3, 2026
b4d52dc
docs(cli): record intentional start --ignore-health-check divergence …
Coly010 Aug 3, 2026
3eaee31
chore(cli): apply CLI-1989 parity ruling for db push pipeline-incompa…
Coly010 Aug 3, 2026
07b3d04
fix(stack): stage binary downloads and extract via atomic rename to a…
Coly010 Aug 3, 2026
cfb979d
fix(cli): reuse existing volumes (#6037)
7ttp Aug 3, 2026
2d47ed1
test(stack): verify postgres data survives native to docker mode tran…
Coly010 Aug 3, 2026
c2ec9f5
fix(cli): order migrations by version (#6038)
7ttp Aug 3, 2026
b6ca6c9
test(cli): deflake e2e image pulls (#6030)
7ttp Aug 3, 2026
fdc8951
fix(cli): restore Go gen types flag guards, bless pg-meta permissiven…
Coly010 Aug 3, 2026
56296d9
fix(cli): match Go machine-format encoder output for -o toml/yaml/jso…
Coly010 Aug 3, 2026
8df4167
fix(cli): migrate remaining StringSlice flags onto legacyStringSliceF…
Coly010 Aug 3, 2026
97d6a66
refactor(cli): hoist sso pflag/profile reconciliation into shared lay…
Coly010 Aug 3, 2026
b17f8e7
chore: gitignore `.DS_Store` (#6011)
kanadgupta Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/cli-go-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ jobs:
- name: Move coverage report
run: cp apps/cli-go/coverage.out coverage.out
working-directory: .
- uses: coverallsapp/github-action@5cbfd81b66ca5d10c19b062c04de0199c215fb6e # v2.3.7
- uses: coverallsapp/github-action@8d6379e14d29928660c4ba802d8e85393440b329 # v2.3.8
with:
file: coverage.out
format: golang
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cli-go-mirror-image.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,10 @@ jobs:
with:
role-to-assume: ${{ secrets.PROD_AWS_ROLE }}
aws-region: us-east-1
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
registry: public.ecr.aws
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cli-go-pg-prove.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
with:
endpoint: builders
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
Expand All @@ -67,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/cli-go-publish-migra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ jobs:
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
with:
endpoint: builders
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
Expand All @@ -67,7 +67,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
- uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/mirror-template-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ jobs:
dependency-firewall-token: ${{ secrets.DF_FIREWALL_TOKEN }}

- name: Log in to ghcr.io
uses: docker/login-action@abd2ef45e78c5afb21d64d4ca52ee8550d9572c7 # v4.5.1
uses: docker/login-action@371161bbe7024a29a25c5e19bfcbc0804fe9ad2c # v4.5.2
with:
registry: ghcr.io
username: ${{ github.actor }}
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release-shared.yml
Original file line number Diff line number Diff line change
Expand Up @@ -426,7 +426,7 @@ jobs:

- name: Sync stable release to Linear
if: ${{ inputs.channel == 'stable' && env.LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY != '' }}
uses: linear/linear-release-action@3858a5d7892435dc63302ac76b0cdb587435caa9 # v0
uses: linear/linear-release-action@af56a9a388625921f3757a2f988e4d7aca958377 # v0
with:
access_key: ${{ env.LINEAR_CLI_STABLE_RELEASE_ACCESS_KEY }}
version: v${{ inputs.version }}
Expand All @@ -437,7 +437,7 @@ jobs:

- name: Sync beta release to Linear
if: ${{ inputs.channel == 'beta' && env.LINEAR_CLI_BETA_RELEASE_ACCESS_KEY != '' }}
uses: linear/linear-release-action@3858a5d7892435dc63302ac76b0cdb587435caa9 # v0
uses: linear/linear-release-action@af56a9a388625921f3757a2f988e4d7aca958377 # v0
with:
access_key: ${{ env.LINEAR_CLI_BETA_RELEASE_ACCESS_KEY }}
version: v${{ inputs.version }}
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
node_modules
dist
coverage/
.DS_Store
.env
.env.*
!.env.example
Expand All @@ -22,4 +23,4 @@ packages/cli-*/bin/

# Nx
.nx/cache
.nx/workspace-data
.nx/workspace-data
2 changes: 1 addition & 1 deletion apps/cli-go/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ require (
github.com/docker/cli v28.5.2+incompatible
github.com/docker/compose/v2 v2.40.3
github.com/docker/docker v28.5.2+incompatible
github.com/docker/go-connections v0.7.0
github.com/docker/go-connections v0.8.0
github.com/docker/go-units v0.5.0
github.com/fsnotify/fsnotify v1.10.1
github.com/getsentry/sentry-go v0.48.0
Expand Down
4 changes: 2 additions & 2 deletions apps/cli-go/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -319,8 +319,8 @@ github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
github.com/docker/go-connections v0.7.0 h1:6SsRfJddP22WMrCkj19x9WKjEDTB+ahsdiGYf0mN39c=
github.com/docker/go-connections v0.7.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-connections v0.8.0 h1:T9UlP76qPLA/HaLrcC+s4Doqqv5XsWMMUGPF5Aih/k0=
github.com/docker/go-connections v0.8.0/go.mod h1:no1qkHdjq7kLMGUXYAduOhYPSJxxvgWBh7ogVvptn3Q=
github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8=
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
Expand Down
46 changes: 45 additions & 1 deletion apps/cli-go/internal/db/reset/reset.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package reset

import (
"bytes"
"context"
_ "embed"
"fmt"
Expand Down Expand Up @@ -265,13 +266,56 @@ func restartServices(ctx context.Context) error {
return nil
})
// Do not wait for service healthy as those services may be excluded from starting
return errors.Join(result...)
if err := errors.Join(result...); err != nil {
return err
}
return reloadKong(ctx)
}

func listServicesToRestart() []string {
return []string{utils.StorageId, utils.GotrueId, utils.RealtimeId, utils.PoolerId}
}

// reloadKong reloads Kong after the restarts above so its nginx re-resolves each
// upstream container's address. Kong caches resolved addresses for the life of a
// worker process, and a restarted container can come back on a different one,
// leaving the gateway returning 502 for that route until Kong restarts. An
// in-place reload (the `functions serve` pattern) keeps the gateway serving
// throughout. https://github.com/supabase/cli/issues/6016
func reloadKong(ctx context.Context) error {
resp, err := utils.Docker.ContainerInspect(ctx, utils.KongId)
if errdefs.IsNotFound(err) {
// Kong may be excluded from the stack.
return nil
} else if err != nil {
return suggestKongRecovery(errors.Errorf("failed to inspect kong: %w", err))
}
if !resp.State.Running {
// A stopped gateway has no stale cache to flush.
return nil
}
var out bytes.Buffer
if err := utils.DockerExecOnceWithStream(ctx, utils.KongId, "", nil, []string{"kong", "reload"}, &out, &out); err != nil {
if msg := strings.TrimSpace(out.String()); len(msg) > 0 {
return suggestKongRecovery(errors.Errorf("failed to reload kong: %w:\n%s", err, msg))
}
return suggestKongRecovery(errors.Errorf("failed to reload kong: %w", err))
}
return nil
}

// suggestKongRecovery decorates a gateway-left-unconfirmed failure with the
// advisory next step; the caller-neutral wording also covers branch switch,
// which shares RestartDatabase.
func suggestKongRecovery(err error) error {
utils.CmdSuggestion = fmt.Sprintf(
"Local services restarted, but API routes may return 502 until the gateway reloads.\nTry restarting it with %s, and check %s if the failure persists.",
utils.Aqua("docker restart "+utils.KongId),
utils.Aqua("docker logs "+utils.KongId),
)
return err
}

func resetRemote(ctx context.Context, version string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error {
msg := "Do you want to reset the remote database?"
if shouldReset, err := utils.NewConsole().PromptYesNo(ctx, msg, false); err != nil {
Expand Down
148 changes: 148 additions & 0 deletions apps/cli-go/internal/db/reset/reset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,19 @@ func TestResetCommand(t *testing.T) {
utils.GotrueId = "test-auth"
utils.RealtimeId = "test-realtime"
utils.PoolerId = "test-pooler"
utils.KongId = "test-kong"
for _, container := range listServicesToRestart() {
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
Reply(http.StatusOK)
}
// Kong is not running so the reload is skipped (a successful exec attach is not gock-mockable, see TestExecOnce)
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{Running: false},
}})
// Seeds storage
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.StorageId + "/json").
Expand Down Expand Up @@ -308,11 +316,19 @@ func TestRestartDatabase(t *testing.T) {
utils.GotrueId = "test-auth"
utils.RealtimeId = "test-realtime"
utils.PoolerId = "test-pooler"
utils.KongId = "test-kong"
for _, container := range listServicesToRestart() {
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
Reply(http.StatusOK)
}
// Kong is not running so the reload is skipped (a successful exec attach is not gock-mockable, see TestExecOnce)
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{Running: false},
}})
// Run test
err := RestartDatabase(context.Background(), io.Discard)
// Check error
Expand Down Expand Up @@ -360,6 +376,138 @@ func TestRestartDatabase(t *testing.T) {
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("skips kong reload when kong is not running", func(t *testing.T) {
utils.DbId = "test-reset"
// Setup mock docker
require.NoError(t, apitest.MockDocker(utils.Docker))
defer gock.OffAll()
// Restarts postgres
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart").
Reply(http.StatusOK)
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{
Running: true,
Health: &container.Health{Status: types.Healthy},
},
}})
// Restarts services
utils.StorageId = "test-storage"
utils.GotrueId = "test-auth"
utils.RealtimeId = "test-realtime"
utils.PoolerId = "test-pooler"
utils.KongId = "test-kong"
for _, container := range listServicesToRestart() {
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
Reply(http.StatusOK)
}
// Kong is excluded from the stack: no exec follows
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json").
Reply(http.StatusNotFound)
// Run test
err := RestartDatabase(context.Background(), io.Discard)
// Check error
assert.NoError(t, err)
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on kong inspect failure", func(t *testing.T) {
utils.DbId = "test-reset"
// Setup mock docker
require.NoError(t, apitest.MockDocker(utils.Docker))
defer gock.OffAll()
// Restarts postgres
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart").
Reply(http.StatusOK)
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{
Running: true,
Health: &container.Health{Status: types.Healthy},
},
}})
// Restarts services
utils.StorageId = "test-storage"
utils.GotrueId = "test-auth"
utils.RealtimeId = "test-realtime"
utils.PoolerId = "test-pooler"
utils.KongId = "test-kong"
for _, container := range listServicesToRestart() {
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
Reply(http.StatusOK)
}
// A daemon error is not the excluded-kong skip case
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json").
Reply(http.StatusServiceUnavailable)
// Run test
err := RestartDatabase(context.Background(), io.Discard)
// Check error
assert.ErrorContains(t, err, "failed to inspect kong")
assert.Contains(t, utils.CmdSuggestion, "API routes may return 502")
assert.Contains(t, utils.CmdSuggestion, "docker restart test-kong")
t.Cleanup(func() { utils.CmdSuggestion = "" })
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on kong reload failure", func(t *testing.T) {
utils.DbId = "test-reset"
// Setup mock docker
require.NoError(t, apitest.MockDocker(utils.Docker))
defer gock.OffAll()
// Restarts postgres
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/restart").
Reply(http.StatusOK)
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.DbId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{
Running: true,
Health: &container.Health{Status: types.Healthy},
},
}})
// Restarts services
utils.StorageId = "test-storage"
utils.GotrueId = "test-auth"
utils.RealtimeId = "test-realtime"
utils.PoolerId = "test-pooler"
utils.KongId = "test-kong"
for _, container := range listServicesToRestart() {
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + container + "/restart").
Reply(http.StatusOK)
}
// Kong is up but the reload exec fails
gock.New(utils.Docker.DaemonHost()).
Get("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/json").
Reply(http.StatusOK).
JSON(container.InspectResponse{ContainerJSONBase: &container.ContainerJSONBase{
State: &container.State{Running: true},
}})
gock.New(utils.Docker.DaemonHost()).
Post("/v" + utils.Docker.ClientVersion() + "/containers/" + utils.KongId + "/exec").
Reply(http.StatusServiceUnavailable)
// Run test
err := RestartDatabase(context.Background(), io.Discard)
// Check error
assert.ErrorContains(t, err, "failed to reload kong")
assert.Contains(t, utils.CmdSuggestion, "API routes may return 502")
assert.Contains(t, utils.CmdSuggestion, "docker restart test-kong")
t.Cleanup(func() { utils.CmdSuggestion = "" })
assert.Empty(t, apitest.ListUnmatchedRequests())
})

t.Run("throws error on db restart failure", func(t *testing.T) {
utils.DbId = "test-reset"
// Setup mock docker
Expand Down
6 changes: 0 additions & 6 deletions apps/cli-go/pkg/api/types.gen.go

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

Loading
Loading