• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

supabase / cli / 17483653932

05 Sep 2025 04:25AM UTC coverage: 54.787% (-0.03%) from 54.813%
17483653932

Pull #4124

github

web-flow
Merge 572fb520c into c2dede3e8
Pull Request #4124: chore(deps): bump supabase/postgres from 17.6.1.000 to 17.6.1.002 in /pkg/config/templates

6215 of 11344 relevant lines covered (54.79%)

6.06 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

80.61
/internal/storage/rm/rm.go
1
package rm
2

3
import (
4
        "context"
5
        "fmt"
6
        "os"
7
        "strings"
8

9
        "github.com/go-errors/errors"
10
        "github.com/spf13/afero"
11
        "github.com/supabase/cli/internal/storage/client"
12
        "github.com/supabase/cli/internal/storage/cp"
13
        "github.com/supabase/cli/internal/storage/ls"
14
        "github.com/supabase/cli/internal/utils"
15
        "github.com/supabase/cli/internal/utils/flags"
16
        "github.com/supabase/cli/pkg/storage"
17
)
18

19
var (
20
        errMissingObject = errors.New("Object not found")
21
        errMissingBucket = errors.New("You must specify a bucket to delete.")
22
        errMissingFlag   = errors.New("You must specify -r flag to delete directories.")
23
)
24

25
type PrefixGroup struct {
26
        Bucket   string
27
        Prefixes []string
28
}
29

30
func Run(ctx context.Context, paths []string, recursive bool, fsys afero.Fs) error {
6✔
31
        // Group paths by buckets
6✔
32
        groups := map[string][]string{}
6✔
33
        for _, objectPath := range paths {
14✔
34
                remotePath, err := client.ParseStorageURL(objectPath)
8✔
35
                if err != nil {
9✔
36
                        return err
1✔
37
                }
1✔
38
                bucket, prefix := client.SplitBucketPrefix(remotePath)
7✔
39
                // Ignore attempts to delete all buckets
7✔
40
                if len(bucket) == 0 {
8✔
41
                        return errors.New(errMissingBucket)
1✔
42
                }
1✔
43
                if cp.IsDir(prefix) && !recursive {
7✔
44
                        return errors.New(errMissingFlag)
1✔
45
                }
1✔
46
                groups[bucket] = append(groups[bucket], prefix)
5✔
47
        }
48
        api, err := client.NewStorageAPI(ctx, flags.ProjectRef)
3✔
49
        if err != nil {
3✔
50
                return err
×
51
        }
×
52
        if len(groups) == 0 {
3✔
53
                if !recursive {
×
54
                        return errors.New(errMissingFlag)
×
55
                }
×
56
                buckets, err := api.ListBuckets(ctx)
×
57
                if err != nil {
×
58
                        return err
×
59
                }
×
60
                for _, b := range buckets {
×
61
                        groups[b.Name] = []string{""}
×
62
                }
×
63
        }
64
        console := utils.NewConsole()
3✔
65
        for bucket, prefixes := range groups {
7✔
66
                confirm := fmt.Sprintf("Confirm deleting files in bucket %v?", utils.Bold(bucket))
4✔
67
                if shouldDelete, err := console.PromptYesNo(ctx, confirm, false); err != nil {
4✔
68
                        return err
×
69
                } else if !shouldDelete {
5✔
70
                        continue
1✔
71
                }
72
                // Always try deleting first in case the paths resolve to extensionless files
73
                fmt.Fprintln(os.Stderr, "Deleting objects:", prefixes)
3✔
74
                removed, err := api.DeleteObjects(ctx, bucket, prefixes)
3✔
75
                if err != nil {
4✔
76
                        return err
1✔
77
                }
1✔
78
                set := map[string]struct{}{}
2✔
79
                for _, object := range removed {
3✔
80
                        set[object.Name] = struct{}{}
1✔
81
                }
1✔
82
                for _, prefix := range prefixes {
5✔
83
                        if _, ok := set[prefix]; ok {
4✔
84
                                continue
1✔
85
                        }
86
                        if !recursive {
3✔
87
                                fmt.Fprintln(os.Stderr, "Object not found:", prefix)
1✔
88
                                continue
1✔
89
                        }
90
                        if len(prefix) > 0 {
1✔
91
                                prefix += "/"
×
92
                        }
×
93
                        if err := RemoveStoragePathAll(ctx, api, bucket, prefix); err != nil {
1✔
94
                                return err
×
95
                        }
×
96
                }
97
        }
98
        return nil
2✔
99
}
100

101
// Expects prefix to be terminated by "/" or ""
102
func RemoveStoragePathAll(ctx context.Context, api storage.StorageAPI, bucket, prefix string) error {
6✔
103
        // We must remove one directory at a time to avoid breaking pagination result
6✔
104
        queue := make([]string, 0)
6✔
105
        queue = append(queue, prefix)
6✔
106
        for len(queue) > 0 {
13✔
107
                dirPrefix := queue[len(queue)-1]
7✔
108
                queue = queue[:len(queue)-1]
7✔
109
                paths, err := ls.ListStoragePaths(ctx, api, fmt.Sprintf("/%s/%s", bucket, dirPrefix))
7✔
110
                if err != nil {
8✔
111
                        return err
1✔
112
                }
1✔
113
                if len(paths) == 0 && len(prefix) > 0 {
7✔
114
                        return errors.Errorf("%w: %s/%s", errMissingObject, bucket, prefix)
1✔
115
                }
1✔
116
                var files []string
5✔
117
                for _, objectName := range paths {
9✔
118
                        objectPrefix := dirPrefix + objectName
4✔
119
                        if strings.HasSuffix(objectName, "/") {
5✔
120
                                queue = append(queue, objectPrefix)
1✔
121
                        } else {
4✔
122
                                files = append(files, objectPrefix)
3✔
123
                        }
3✔
124
                }
125
                if len(files) > 0 {
7✔
126
                        fmt.Fprintln(os.Stderr, "Deleting objects:", files)
2✔
127
                        if _, err := api.DeleteObjects(ctx, bucket, files); err != nil {
3✔
128
                                return err
1✔
129
                        }
1✔
130
                }
131
        }
132
        if len(prefix) == 0 {
5✔
133
                fmt.Fprintln(os.Stderr, "Deleting bucket:", bucket)
2✔
134
                if data, err := api.DeleteBucket(ctx, bucket); err == nil {
3✔
135
                        fmt.Fprintln(os.Stderr, data.Message)
1✔
136
                } else if strings.Contains(err.Error(), `"error":"Bucket not found"`) {
3✔
137
                        fmt.Fprintln(os.Stderr, "Bucket not found:", bucket)
1✔
138
                } else {
1✔
139
                        return err
×
140
                }
×
141
        }
142
        return nil
3✔
143
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc