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

supabase / cli / 12851423183

19 Jan 2025 07:18AM UTC coverage: 58.451% (+0.008%) from 58.443%
12851423183

Pull #3046

github

web-flow
Merge 9f6c08196 into 8c03aa7c8
Pull Request #3046: feat(cli): add repeatable migrations

34 of 38 new or added lines in 3 files covered. (89.47%)

250 existing lines in 8 files now uncovered.

7622 of 13040 relevant lines covered (58.45%)

202.59 hits per line

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

85.48
/internal/functions/deploy/bundle.go
1
package deploy
2

3
import (
4
        "context"
5
        "fmt"
6
        "io"
7
        "os"
8
        "path/filepath"
9
        "strings"
10

11
        "github.com/docker/docker/api/types/container"
12
        "github.com/docker/docker/api/types/network"
13
        "github.com/go-errors/errors"
14
        "github.com/spf13/afero"
15
        "github.com/spf13/viper"
16
        "github.com/supabase/cli/internal/utils"
17
        "github.com/supabase/cli/pkg/function"
18
)
19

20
type dockerBundler struct {
21
        fsys afero.Fs
22
}
23

24
func NewDockerBundler(fsys afero.Fs) function.EszipBundler {
6✔
25
        return &dockerBundler{fsys: fsys}
6✔
26
}
6✔
27

28
func (b *dockerBundler) Bundle(ctx context.Context, entrypoint string, importMap string, staticFiles []string, output io.Writer) error {
7✔
29
        // Create temp directory to store generated eszip
7✔
30
        slug := filepath.Base(filepath.Dir(entrypoint))
7✔
31
        fmt.Fprintln(os.Stderr, "Bundling Function:", utils.Bold(slug))
7✔
32
        cwd, err := os.Getwd()
7✔
33
        if err != nil {
7✔
34
                return errors.Errorf("failed to get working directory: %w", err)
×
35
        }
×
36
        // BitBucket pipelines require docker bind mounts to be world writable
37
        hostOutputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug))
7✔
38
        if err := b.fsys.MkdirAll(hostOutputDir, 0777); err != nil {
7✔
39
                return errors.Errorf("failed to mkdir: %w", err)
×
40
        }
×
41
        defer func() {
14✔
42
                if err := b.fsys.RemoveAll(hostOutputDir); err != nil {
7✔
43
                        fmt.Fprintln(os.Stderr, err)
×
44
                }
×
45
        }()
46
        // Create bind mounts
47
        binds, err := GetBindMounts(cwd, utils.FunctionsDir, hostOutputDir, entrypoint, importMap, b.fsys)
7✔
48
        if err != nil {
7✔
49
                return err
×
50
        }
×
51
        hostOutputPath := filepath.Join(hostOutputDir, "output.eszip")
7✔
52
        // Create exec command
7✔
53
        cmd := []string{"bundle", "--entrypoint", utils.ToDockerPath(entrypoint), "--output", utils.ToDockerPath(hostOutputPath)}
7✔
54
        if len(importMap) > 0 {
8✔
55
                cmd = append(cmd, "--import-map", utils.ToDockerPath(importMap))
1✔
56
        }
1✔
57
        for _, staticFile := range staticFiles {
7✔
58
                cmd = append(cmd, "--static", utils.ToDockerPath(staticFile))
×
59
        }
×
60
        if viper.GetBool("DEBUG") {
7✔
UNCOV
61
                cmd = append(cmd, "--verbose")
×
62
        }
×
63

64
        env := []string{}
7✔
65
        if custom_registry := os.Getenv("NPM_CONFIG_REGISTRY"); custom_registry != "" {
7✔
UNCOV
66
                env = append(env, "NPM_CONFIG_REGISTRY="+custom_registry)
×
UNCOV
67
        }
×
68
        // Run bundle
69
        if err := utils.DockerRunOnceWithConfig(
7✔
70
                ctx,
7✔
71
                container.Config{
7✔
72
                        Image:      utils.Config.EdgeRuntime.Image,
7✔
73
                        Env:        env,
7✔
74
                        Cmd:        cmd,
7✔
75
                        WorkingDir: utils.ToDockerPath(cwd),
7✔
76
                },
7✔
77
                container.HostConfig{
7✔
78
                        Binds: binds,
7✔
79
                },
7✔
80
                network.NetworkingConfig{},
7✔
81
                "",
7✔
82
                os.Stdout,
7✔
83
                os.Stderr,
7✔
84
        ); err != nil {
8✔
85
                return err
1✔
86
        }
1✔
87
        // Read and compress
88
        eszipBytes, err := b.fsys.Open(hostOutputPath)
6✔
89
        if err != nil {
6✔
UNCOV
90
                return errors.Errorf("failed to open eszip: %w", err)
×
UNCOV
91
        }
×
92
        defer eszipBytes.Close()
6✔
93
        return function.Compress(eszipBytes, output)
6✔
94
}
95

96
func GetBindMounts(cwd, hostFuncDir, hostOutputDir, hostEntrypointPath, hostImportMapPath string, fsys afero.Fs) ([]string, error) {
7✔
97
        sep := string(filepath.Separator)
7✔
98
        // Docker requires all host paths to be absolute
7✔
99
        if !filepath.IsAbs(hostFuncDir) {
14✔
100
                hostFuncDir = filepath.Join(cwd, hostFuncDir)
7✔
101
        }
7✔
102
        if !strings.HasSuffix(hostFuncDir, sep) {
14✔
103
                hostFuncDir += sep
7✔
104
        }
7✔
105
        dockerFuncDir := utils.ToDockerPath(hostFuncDir)
7✔
106
        // TODO: bind ./supabase/functions:/home/deno/functions to hide PII?
7✔
107
        binds := []string{
7✔
108
                // Reuse deno cache directory, ie. DENO_DIR, between container restarts
7✔
109
                // https://denolib.gitbook.io/guide/advanced/deno_dir-code-fetch-and-cache
7✔
110
                utils.EdgeRuntimeId + ":/root/.cache/deno:rw",
7✔
111
                hostFuncDir + ":" + dockerFuncDir + ":ro",
7✔
112
        }
7✔
113
        if len(hostOutputDir) > 0 {
14✔
114
                if !filepath.IsAbs(hostOutputDir) {
14✔
115
                        hostOutputDir = filepath.Join(cwd, hostOutputDir)
7✔
116
                }
7✔
117
                if !strings.HasSuffix(hostOutputDir, sep) {
14✔
118
                        hostOutputDir += sep
7✔
119
                }
7✔
120
                if !strings.HasPrefix(hostOutputDir, hostFuncDir) {
14✔
121
                        dockerOutputDir := utils.ToDockerPath(hostOutputDir)
7✔
122
                        binds = append(binds, hostOutputDir+":"+dockerOutputDir+":rw")
7✔
123
                }
7✔
124
        }
125
        // Allow entrypoints outside the functions directory
126
        hostEntrypointDir := filepath.Dir(hostEntrypointPath)
7✔
127
        if len(hostEntrypointDir) > 0 {
14✔
128
                if !filepath.IsAbs(hostEntrypointDir) {
14✔
129
                        hostEntrypointDir = filepath.Join(cwd, hostEntrypointDir)
7✔
130
                }
7✔
131
                if !strings.HasSuffix(hostEntrypointDir, sep) {
14✔
132
                        hostEntrypointDir += sep
7✔
133
                }
7✔
134
                if !strings.HasPrefix(hostEntrypointDir, hostFuncDir) &&
7✔
135
                        !strings.HasPrefix(hostEntrypointDir, hostOutputDir) {
8✔
136
                        dockerEntrypointDir := utils.ToDockerPath(hostEntrypointDir)
1✔
137
                        binds = append(binds, hostEntrypointDir+":"+dockerEntrypointDir+":ro")
1✔
138
                }
1✔
139
        }
140
        // Imports outside of ./supabase/functions will be bound by absolute path
141
        if len(hostImportMapPath) > 0 {
8✔
142
                if !filepath.IsAbs(hostImportMapPath) {
2✔
143
                        hostImportMapPath = filepath.Join(cwd, hostImportMapPath)
1✔
144
                }
1✔
145
                importMap, err := utils.NewImportMap(hostImportMapPath, fsys)
1✔
146
                if err != nil {
1✔
UNCOV
147
                        return nil, err
×
UNCOV
148
                }
×
149
                modules := importMap.BindHostModules()
1✔
150
                dockerImportMapPath := utils.ToDockerPath(hostImportMapPath)
1✔
151
                modules = append(modules, hostImportMapPath+":"+dockerImportMapPath+":ro")
1✔
152
                // Remove any duplicate mount points
1✔
153
                for _, mod := range modules {
2✔
154
                        hostPath := strings.Split(mod, ":")[0]
1✔
155
                        if !strings.HasPrefix(hostPath, hostFuncDir) &&
1✔
156
                                (len(hostOutputDir) == 0 || !strings.HasPrefix(hostPath, hostOutputDir)) &&
1✔
157
                                (len(hostEntrypointDir) == 0 || !strings.HasPrefix(hostPath, hostEntrypointDir)) {
2✔
158
                                binds = append(binds, mod)
1✔
159
                        }
1✔
160
                }
161
        }
162
        return binds, nil
7✔
163
}
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

© 2026 Coveralls, Inc