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

supabase / cli / 11585965905

30 Oct 2024 02:38AM UTC coverage: 59.801% (-0.02%) from 59.825%
11585965905

Pull #2813

github

sweatybridge
fix: convert entrypoint path to file url
Pull Request #2813: feat(functions): allow custom functions entrypoint path

8 of 10 new or added lines in 3 files covered. (80.0%)

5 existing lines in 1 file now uncovered.

6361 of 10637 relevant lines covered (59.8%)

6.08 hits per line

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

73.1
/internal/functions/serve/serve.go
1
package serve
2

3
import (
4
        "context"
5
        _ "embed"
6
        "encoding/json"
7
        "fmt"
8
        "os"
9
        "path/filepath"
10
        "strconv"
11
        "strings"
12

13
        "github.com/docker/docker/api/types/container"
14
        "github.com/docker/docker/api/types/network"
15
        "github.com/docker/go-connections/nat"
16
        "github.com/go-errors/errors"
17
        "github.com/spf13/afero"
18
        "github.com/spf13/viper"
19
        "github.com/supabase/cli/internal/functions/deploy"
20
        "github.com/supabase/cli/internal/secrets/set"
21
        "github.com/supabase/cli/internal/utils"
22
)
23

24
type InspectMode string
25

26
const (
27
        InspectModeRun  InspectMode = "run"
28
        InspectModeBrk  InspectMode = "brk"
29
        InspectModeWait InspectMode = "wait"
30
)
31

32
func (mode InspectMode) toFlag() string {
×
33
        switch mode {
×
34
        case InspectModeBrk:
×
35
                return "inspect-brk"
×
36
        case InspectModeWait:
×
37
                return "inspect-wait"
×
38
        case InspectModeRun:
×
39
                fallthrough
×
40
        default:
×
41
                return "inspect"
×
42
        }
43
}
44

45
type RuntimeOption struct {
46
        InspectMode *InspectMode
47
        InspectMain bool
48
}
49

50
func (i *RuntimeOption) toArgs() []string {
2✔
51
        flags := []string{}
2✔
52
        if i.InspectMode != nil {
2✔
53
                flags = append(flags, fmt.Sprintf("--%s=0.0.0.0:%d", i.InspectMode.toFlag(), dockerRuntimeInspectorPort))
×
54
                if i.InspectMain {
×
55
                        flags = append(flags, "--inspect-main")
×
56
                }
×
57
        }
58
        return flags
2✔
59
}
60

61
const (
62
        dockerRuntimeServerPort    = 8081
63
        dockerRuntimeInspectorPort = 8083
64
)
65

66
var (
67
        //go:embed templates/main.ts
68
        mainFuncEmbed string
69
)
70

71
func Run(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, runtimeOption RuntimeOption, fsys afero.Fs) error {
5✔
72
        // 1. Sanity checks.
5✔
73
        if err := utils.LoadConfigFS(fsys); err != nil {
6✔
74
                return err
1✔
75
        }
1✔
76
        if err := utils.AssertSupabaseDbIsRunning(); err != nil {
5✔
77
                return err
1✔
78
        }
1✔
79
        // 2. Remove existing container.
80
        _ = utils.Docker.ContainerRemove(ctx, utils.EdgeRuntimeId, container.RemoveOptions{
3✔
81
                RemoveVolumes: true,
3✔
82
                Force:         true,
3✔
83
        })
3✔
84
        // Use network alias because Deno cannot resolve `_` in hostname
3✔
85
        dbUrl := fmt.Sprintf("postgresql://postgres:postgres@%s:5432/postgres", utils.DbAliases[0])
3✔
86
        // 3. Serve and log to console
3✔
87
        fmt.Fprintln(os.Stderr, "Setting up Edge Functions runtime...")
3✔
88
        if err := ServeFunctions(ctx, envFilePath, noVerifyJWT, importMapPath, dbUrl, runtimeOption, fsys); err != nil {
5✔
89
                return err
2✔
90
        }
2✔
91
        if err := utils.DockerStreamLogs(ctx, utils.EdgeRuntimeId, os.Stdout, os.Stderr); err != nil {
1✔
92
                return err
×
93
        }
×
94
        fmt.Println("Stopped serving " + utils.Bold(utils.FunctionsDir))
1✔
95
        return nil
1✔
96
}
97

98
func ServeFunctions(ctx context.Context, envFilePath string, noVerifyJWT *bool, importMapPath string, dbUrl string, runtimeOption RuntimeOption, fsys afero.Fs) error {
4✔
99
        // 1. Load default values
4✔
100
        if envFilePath == "" {
6✔
101
                if f, err := fsys.Stat(utils.FallbackEnvFilePath); err == nil && !f.IsDir() {
3✔
102
                        envFilePath = utils.FallbackEnvFilePath
1✔
103
                }
1✔
104
        } else if !filepath.IsAbs(envFilePath) {
4✔
105
                envFilePath = filepath.Join(utils.CurrentDirAbs, envFilePath)
2✔
106
        }
2✔
107
        // 2. Parse user defined env
108
        env, err := parseEnvFile(envFilePath, fsys)
4✔
109
        if err != nil {
5✔
110
                return err
1✔
111
        }
1✔
112
        env = append(env,
3✔
113
                fmt.Sprintf("SUPABASE_URL=http://%s:8000", utils.KongAliases[0]),
3✔
114
                "SUPABASE_ANON_KEY="+utils.Config.Auth.AnonKey,
3✔
115
                "SUPABASE_SERVICE_ROLE_KEY="+utils.Config.Auth.ServiceRoleKey,
3✔
116
                "SUPABASE_DB_URL="+dbUrl,
3✔
117
                "SUPABASE_INTERNAL_JWT_SECRET="+utils.Config.Auth.JwtSecret,
3✔
118
                fmt.Sprintf("SUPABASE_INTERNAL_HOST_PORT=%d", utils.Config.Api.Port),
3✔
119
        )
3✔
120
        if viper.GetBool("DEBUG") {
3✔
121
                env = append(env, "SUPABASE_INTERNAL_DEBUG=true")
×
122
        }
×
123
        if runtimeOption.InspectMode != nil {
3✔
124
                env = append(env, "SUPABASE_INTERNAL_WALLCLOCK_LIMIT_SEC=0")
×
125
        }
×
126
        // 3. Parse custom import map
127
        cwd, err := os.Getwd()
3✔
128
        if err != nil {
3✔
NEW
129
                return errors.Errorf("failed to get working directory: %w", err)
×
NEW
130
        }
×
131
        binds, functionsConfigString, err := populatePerFunctionConfigs(cwd, importMapPath, noVerifyJWT, fsys)
3✔
132
        if err != nil {
4✔
133
                return err
1✔
134
        }
1✔
135
        env = append(env, "SUPABASE_INTERNAL_FUNCTIONS_CONFIG="+functionsConfigString)
2✔
136
        // 4. Parse entrypoint script
2✔
137
        cmd := append([]string{
2✔
138
                "edge-runtime",
2✔
139
                "start",
2✔
140
                "--main-service=/root",
2✔
141
                fmt.Sprintf("--port=%d", dockerRuntimeServerPort),
2✔
142
                fmt.Sprintf("--policy=%s", utils.Config.EdgeRuntime.Policy),
2✔
143
        }, runtimeOption.toArgs()...)
2✔
144
        if viper.GetBool("DEBUG") {
2✔
145
                cmd = append(cmd, "--verbose")
×
146
        }
×
147
        cmdString := strings.Join(cmd, " ")
2✔
148
        entrypoint := []string{"sh", "-c", `cat <<'EOF' > /root/index.ts && ` + cmdString + `
2✔
149
` + mainFuncEmbed + `
2✔
150
EOF
2✔
151
`}
2✔
152
        // 5. Parse exposed ports
2✔
153
        dockerRuntimePort := nat.Port(fmt.Sprintf("%d/tcp", dockerRuntimeServerPort))
2✔
154
        exposedPorts := nat.PortSet{dockerRuntimePort: struct{}{}}
2✔
155
        portBindings := nat.PortMap{}
2✔
156
        if runtimeOption.InspectMode != nil {
2✔
157
                dockerInspectorPort := nat.Port(fmt.Sprintf("%d/tcp", dockerRuntimeInspectorPort))
×
158
                exposedPorts[dockerInspectorPort] = struct{}{}
×
159
                portBindings[dockerInspectorPort] = []nat.PortBinding{{
×
160
                        HostPort: strconv.FormatUint(uint64(utils.Config.EdgeRuntime.InspectorPort), 10),
×
161
                }}
×
162
        }
×
163
        // 6. Start container
164
        _, err = utils.DockerStart(
2✔
165
                ctx,
2✔
166
                container.Config{
2✔
167
                        Image:        utils.Config.EdgeRuntime.Image,
2✔
168
                        Env:          env,
2✔
169
                        Entrypoint:   entrypoint,
2✔
170
                        ExposedPorts: exposedPorts,
2✔
171
                        WorkingDir:   utils.ToDockerPath(cwd),
2✔
172
                        // No tcp health check because edge runtime logs them as client connection error
2✔
173
                },
2✔
174
                container.HostConfig{
2✔
175
                        Binds:        binds,
2✔
176
                        PortBindings: portBindings,
2✔
177
                },
2✔
178
                network.NetworkingConfig{
2✔
179
                        EndpointsConfig: map[string]*network.EndpointSettings{
2✔
180
                                utils.NetId: {
2✔
181
                                        Aliases: utils.EdgeRuntimeAliases,
2✔
182
                                },
2✔
183
                        },
2✔
184
                },
2✔
185
                utils.EdgeRuntimeId,
2✔
186
        )
2✔
187
        return err
2✔
188
}
189

190
func parseEnvFile(envFilePath string, fsys afero.Fs) ([]string, error) {
4✔
191
        env := []string{}
4✔
192
        if len(envFilePath) == 0 {
5✔
193
                return env, nil
1✔
194
        }
1✔
195
        envMap, err := set.ParseEnvFile(envFilePath, fsys)
3✔
196
        if err != nil {
4✔
197
                return env, err
1✔
198
        }
1✔
199
        for name, value := range envMap {
2✔
200
                if strings.HasPrefix(name, "SUPABASE_") {
×
201
                        fmt.Fprintln(os.Stderr, "Env name cannot start with SUPABASE_, skipping: "+name)
×
202
                        continue
×
203
                }
204
                env = append(env, name+"="+value)
×
205
        }
206
        return env, nil
2✔
207
}
208

209
func populatePerFunctionConfigs(cwd, importMapPath string, noVerifyJWT *bool, fsys afero.Fs) ([]string, string, error) {
3✔
210
        slugs, err := deploy.GetFunctionSlugs(fsys)
3✔
211
        if err != nil {
3✔
212
                return nil, "", err
×
213
        }
×
214
        functionsConfig, err := deploy.GetFunctionConfig(slugs, importMapPath, noVerifyJWT, fsys)
3✔
215
        if err != nil {
3✔
216
                return nil, "", err
×
217
        }
×
218
        binds := []string{}
3✔
219
        for slug, fc := range functionsConfig {
4✔
220
                if !fc.IsEnabled() {
1✔
221
                        fmt.Fprintln(os.Stderr, "Skipped serving Function:", slug)
×
222
                        continue
×
223
                }
224
                modules, err := deploy.GetBindMounts(cwd, utils.FunctionsDir, "", fc.Entrypoint, fc.ImportMap, fsys)
1✔
225
                if err != nil {
2✔
226
                        return nil, "", err
1✔
227
                }
1✔
228
                binds = append(binds, modules...)
×
229
                fc.ImportMap = utils.ToDockerPath(fc.ImportMap)
×
230
                fc.Entrypoint = utils.ToDockerPath(fc.Entrypoint)
×
231
                functionsConfig[slug] = fc
×
232
        }
233
        functionsConfigBytes, err := json.Marshal(functionsConfig)
2✔
234
        if err != nil {
2✔
235
                return nil, "", errors.Errorf("failed to marshal config json: %w", err)
×
236
        }
×
237
        return utils.RemoveDuplicates(binds), string(functionsConfigBytes), nil
2✔
238
}
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