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

supabase / cli / 18433970309

11 Oct 2025 07:47PM UTC coverage: 54.67% (-0.03%) from 54.701%
18433970309

Pull #4291

github

web-flow
Merge 1dae3f1b6 into 4785f7a63
Pull Request #4291: fix(functions): prevent redundant bind mounts on windows

1 of 3 new or added lines in 1 file covered. (33.33%)

5 existing lines in 1 file now uncovered.

6409 of 11723 relevant lines covered (54.67%)

6.09 hits per line

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

84.91
/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 {
7✔
25
        return &dockerBundler{fsys: fsys}
7✔
26
}
7✔
27

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

7✔
64
        env := []string{}
7✔
65
        if custom_registry := os.Getenv("NPM_CONFIG_REGISTRY"); custom_registry != "" {
7✔
66
                env = append(env, "NPM_CONFIG_REGISTRY="+custom_registry)
×
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 meta, err
1✔
86
        }
1✔
87
        // Read and compress
88
        eszipBytes, err := b.fsys.Open(hostOutputPath)
6✔
89
        if err != nil {
6✔
90
                return meta, errors.Errorf("failed to open eszip: %w", err)
×
91
        }
×
92
        defer eszipBytes.Close()
6✔
93
        return meta, function.Compress(eszipBytes, output)
6✔
94
}
95

96
func GetBindMounts(cwd, hostFuncDir, hostOutputDir, hostEntrypointPath, hostImportMapPath string, fsys afero.Fs) ([]string, error) {
10✔
97
        sep := string(filepath.Separator)
10✔
98
        // Docker requires all host paths to be absolute
10✔
99
        if !filepath.IsAbs(hostFuncDir) {
20✔
100
                hostFuncDir = filepath.Join(cwd, hostFuncDir)
10✔
101
        }
10✔
102
        if !strings.HasSuffix(hostFuncDir, sep) {
20✔
103
                hostFuncDir += sep
10✔
104
        }
10✔
105
        dockerFuncDir := utils.ToDockerPath(hostFuncDir)
10✔
106
        // TODO: bind ./supabase/functions:/home/deno/functions to hide PII?
10✔
107
        binds := []string{
10✔
108
                // Reuse deno cache directory, ie. DENO_DIR, between container restarts
10✔
109
                // https://denolib.gitbook.io/guide/advanced/deno_dir-code-fetch-and-cache
10✔
110
                utils.EdgeRuntimeId + ":/root/.cache/deno:rw",
10✔
111
                hostFuncDir + ":" + dockerFuncDir + ":ro",
10✔
112
        }
10✔
113
        if len(hostOutputDir) > 0 {
17✔
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
        // Imports outside of ./supabase/functions will be bound by walking the entrypoint
126
        modules, err := utils.BindHostModules(cwd, hostEntrypointPath, hostImportMapPath, fsys)
10✔
127
        if err != nil {
10✔
128
                return nil, err
×
129
        }
×
130
        // Remove any duplicate mount points
131
        for _, mod := range modules {
13✔
132
                hostPath := strings.Split(mod, ":")[0]
3✔
133
                if volName := filepath.VolumeName(mod); len(volName) > 0 {
3✔
NEW
134
                        hostPath = volName + strings.Split(strings.TrimPrefix(mod, volName), ":")[0]
×
NEW
135
                }
×
136
                if !strings.HasPrefix(hostPath, hostFuncDir) &&
3✔
137
                        (len(hostOutputDir) == 0 || !strings.HasPrefix(hostPath, hostOutputDir)) {
5✔
138
                        binds = append(binds, mod)
2✔
139
                }
2✔
140
        }
141
        return binds, nil
10✔
142
}
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