Skip to content

Commit e23d212

Browse files
committed
feat: support deploying functions without docker
1 parent 0d1f040 commit e23d212

File tree

5 files changed

+234
-42
lines changed

5 files changed

+234
-42
lines changed

cmd/functions.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ var (
5252
},
5353
}
5454

55+
useApi bool
56+
useDocker bool
5557
noVerifyJWT = new(bool)
5658
useLegacyBundle bool
5759
importMapPath string
@@ -65,7 +67,7 @@ var (
6567
if !cmd.Flags().Changed("no-verify-jwt") {
6668
noVerifyJWT = nil
6769
}
68-
return deploy.Run(cmd.Context(), args, flags.ProjectRef, noVerifyJWT, importMapPath, afero.NewOsFs())
70+
return deploy.Run(cmd.Context(), args, useDocker, noVerifyJWT, importMapPath, afero.NewOsFs())
6971
},
7072
}
7173

@@ -123,11 +125,15 @@ var (
123125
func init() {
124126
functionsListCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
125127
functionsDeleteCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
126-
functionsDeployCmd.Flags().BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.")
127-
functionsDeployCmd.Flags().StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
128-
functionsDeployCmd.Flags().BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.")
129-
functionsDeployCmd.Flags().StringVar(&importMapPath, "import-map", "", "Path to import map file.")
130-
cobra.CheckErr(functionsDeployCmd.Flags().MarkHidden("legacy-bundle"))
128+
deployFlags := functionsDeployCmd.Flags()
129+
deployFlags.BoolVar(&useApi, "use-api", true, "Use Management API to bundle functions.")
130+
deployFlags.BoolVar(&useDocker, "use-docker", false, "Use Docker to bundle functions.")
131+
functionsDeployCmd.MarkFlagsMutuallyExclusive("use-api", "use-docker")
132+
deployFlags.BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.")
133+
deployFlags.StringVar(&flags.ProjectRef, "project-ref", "", "Project ref of the Supabase project.")
134+
deployFlags.BoolVar(&useLegacyBundle, "legacy-bundle", false, "Use legacy bundling mechanism.")
135+
deployFlags.StringVar(&importMapPath, "import-map", "", "Path to import map file.")
136+
cobra.CheckErr(deployFlags.MarkHidden("legacy-bundle"))
131137
functionsServeCmd.Flags().BoolVar(noVerifyJWT, "no-verify-jwt", false, "Disable JWT verification for the Function.")
132138
functionsServeCmd.Flags().StringVar(&envFilePath, "env-file", "", "Path to an env file to be populated to the Function environment.")
133139
functionsServeCmd.Flags().StringVar(&importMapPath, "import-map", "", "Path to import map file.")

internal/functions/deploy/deploy.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/supabase/cli/pkg/function"
1717
)
1818

19-
func Run(ctx context.Context, slugs []string, projectRef string, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error {
19+
func Run(ctx context.Context, slugs []string, useDocker bool, noVerifyJWT *bool, importMapPath string, fsys afero.Fs) error {
2020
// Load function config and project id
2121
if err := flags.LoadConfig(fsys); err != nil {
2222
return err
@@ -37,12 +37,16 @@ func Run(ctx context.Context, slugs []string, projectRef string, noVerifyJWT *bo
3737
if err != nil {
3838
return err
3939
}
40-
api := function.NewEdgeRuntimeAPI(projectRef, *utils.GetSupabase(), NewDockerBundler(fsys))
41-
if err := api.UpsertFunctions(ctx, functionConfig); err != nil {
40+
if useDocker {
41+
api := function.NewEdgeRuntimeAPI(flags.ProjectRef, *utils.GetSupabase(), NewDockerBundler(fsys))
42+
if err := api.UpsertFunctions(ctx, functionConfig); err != nil {
43+
return err
44+
}
45+
} else if err := deploy(ctx, functionConfig, fsys); err != nil {
4246
return err
4347
}
44-
fmt.Printf("Deployed Functions on project %s: %s\n", utils.Aqua(projectRef), strings.Join(slugs, ", "))
45-
url := fmt.Sprintf("%s/project/%v/functions", utils.GetSupabaseDashboardURL(), projectRef)
48+
fmt.Printf("Deployed Functions on project %s: %s\n", utils.Aqua(flags.ProjectRef), strings.Join(slugs, ", "))
49+
url := fmt.Sprintf("%s/project/%v/functions", utils.GetSupabaseDashboardURL(), flags.ProjectRef)
4650
fmt.Println("You can inspect your deployment in the Dashboard: " + url)
4751
return nil
4852
}

internal/functions/deploy/deploy_test.go

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@ import (
1414
"github.com/stretchr/testify/require"
1515
"github.com/supabase/cli/internal/testing/apitest"
1616
"github.com/supabase/cli/internal/utils"
17+
"github.com/supabase/cli/internal/utils/flags"
1718
"github.com/supabase/cli/pkg/api"
1819
"github.com/supabase/cli/pkg/cast"
1920
"github.com/supabase/cli/pkg/config"
2021
)
2122

2223
func TestDeployCommand(t *testing.T) {
24+
flags.ProjectRef = apitest.RandomProjectRef()
2325
const slug = "test-func"
2426
const containerId = "test-container"
2527
imageUrl := utils.GetRegistryImageUrl(utils.Config.EdgeRuntime.Image)
@@ -29,8 +31,6 @@ func TestDeployCommand(t *testing.T) {
2931
// Setup in-memory fs
3032
fsys := afero.NewMemMapFs()
3133
require.NoError(t, utils.WriteConfig(fsys, false))
32-
// Setup valid project ref
33-
project := apitest.RandomProjectRef()
3434
// Setup valid access token
3535
token := apitest.RandomAccessToken(t)
3636
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
@@ -40,13 +40,13 @@ func TestDeployCommand(t *testing.T) {
4040
// Setup mock api
4141
defer gock.OffAll()
4242
gock.New(utils.DefaultApiHost).
43-
Get("/v1/projects/" + project + "/functions").
43+
Get("/v1/projects/" + flags.ProjectRef + "/functions").
4444
Reply(http.StatusOK).
4545
JSON([]api.FunctionResponse{})
4646
for i := range functions {
4747
// Do not match slug to avoid flakey tests
4848
gock.New(utils.DefaultApiHost).
49-
Post("/v1/projects/" + project + "/functions").
49+
Post("/v1/projects/" + flags.ProjectRef + "/functions").
5050
Reply(http.StatusCreated).
5151
JSON(api.FunctionResponse{Id: fmt.Sprintf("%d", i)})
5252
// Setup mock docker
@@ -61,7 +61,7 @@ func TestDeployCommand(t *testing.T) {
6161
}
6262
// Run test
6363
noVerifyJWT := true
64-
err = Run(context.Background(), functions, project, &noVerifyJWT, "", fsys)
64+
err = Run(context.Background(), functions, true, &noVerifyJWT, "", fsys)
6565
// Check error
6666
assert.NoError(t, err)
6767
assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -88,8 +88,6 @@ import_map = "./import_map.json"
8888
require.NoError(t, afero.WriteFile(fsys, entrypointPath, []byte{}, 0644))
8989
ignorePath := filepath.Join(utils.FunctionsDir, "_ignore", "index.ts")
9090
require.NoError(t, afero.WriteFile(fsys, ignorePath, []byte{}, 0644))
91-
// Setup valid project ref
92-
project := apitest.RandomProjectRef()
9391
// Setup valid access token
9492
token := apitest.RandomAccessToken(t)
9593
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
@@ -99,11 +97,11 @@ import_map = "./import_map.json"
9997
// Setup mock api
10098
defer gock.OffAll()
10199
gock.New(utils.DefaultApiHost).
102-
Get("/v1/projects/" + project + "/functions").
100+
Get("/v1/projects/" + flags.ProjectRef + "/functions").
103101
Reply(http.StatusOK).
104102
JSON([]api.FunctionResponse{})
105103
gock.New(utils.DefaultApiHost).
106-
Post("/v1/projects/"+project+"/functions").
104+
Post("/v1/projects/"+flags.ProjectRef+"/functions").
107105
MatchParam("slug", slug).
108106
ParamPresent("import_map_path").
109107
Reply(http.StatusCreated).
@@ -116,7 +114,7 @@ import_map = "./import_map.json"
116114
outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug))
117115
require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644))
118116
// Run test
119-
err = Run(context.Background(), nil, project, nil, "", fsys)
117+
err = Run(context.Background(), nil, true, nil, "", fsys)
120118
// Check error
121119
assert.NoError(t, err)
122120
assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -142,8 +140,6 @@ import_map = "./import_map.json"
142140
// Setup function entrypoints
143141
require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.FunctionsDir, "enabled-func", "index.ts"), []byte{}, 0644))
144142
require.NoError(t, afero.WriteFile(fsys, filepath.Join(utils.FunctionsDir, "disabled-func", "index.ts"), []byte{}, 0644))
145-
// Setup valid project ref
146-
project := apitest.RandomProjectRef()
147143
// Setup valid access token
148144
token := apitest.RandomAccessToken(t)
149145
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
@@ -153,11 +149,11 @@ import_map = "./import_map.json"
153149
// Setup mock api
154150
defer gock.OffAll()
155151
gock.New(utils.DefaultApiHost).
156-
Get("/v1/projects/" + project + "/functions").
152+
Get("/v1/projects/" + flags.ProjectRef + "/functions").
157153
Reply(http.StatusOK).
158154
JSON([]api.FunctionResponse{})
159155
gock.New(utils.DefaultApiHost).
160-
Post("/v1/projects/"+project+"/functions").
156+
Post("/v1/projects/"+flags.ProjectRef+"/functions").
161157
MatchParam("slug", "enabled-func").
162158
Reply(http.StatusCreated).
163159
JSON(api.FunctionResponse{Id: "1"})
@@ -168,7 +164,7 @@ import_map = "./import_map.json"
168164
outputDir := filepath.Join(utils.TempDir, ".output_enabled-func")
169165
require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644))
170166
// Run test
171-
err = Run(context.Background(), nil, project, nil, "", fsys)
167+
err = Run(context.Background(), nil, true, nil, "", fsys)
172168
// Check error
173169
assert.NoError(t, err)
174170
assert.Empty(t, apitest.ListUnmatchedRequests())
@@ -179,7 +175,7 @@ import_map = "./import_map.json"
179175
fsys := afero.NewMemMapFs()
180176
require.NoError(t, utils.WriteConfig(fsys, false))
181177
// Run test
182-
err := Run(context.Background(), []string{"_invalid"}, "", nil, "", fsys)
178+
err := Run(context.Background(), []string{"_invalid"}, true, nil, "", fsys)
183179
// Check error
184180
assert.ErrorContains(t, err, "Invalid Function name.")
185181
})
@@ -189,7 +185,7 @@ import_map = "./import_map.json"
189185
fsys := afero.NewMemMapFs()
190186
require.NoError(t, utils.WriteConfig(fsys, false))
191187
// Run test
192-
err := Run(context.Background(), nil, "", nil, "", fsys)
188+
err := Run(context.Background(), nil, true, nil, "", fsys)
193189
// Check error
194190
assert.ErrorContains(t, err, "No Functions specified or found in supabase/functions")
195191
})
@@ -207,8 +203,6 @@ verify_jwt = false
207203
`)
208204
require.NoError(t, err)
209205
require.NoError(t, f.Close())
210-
// Setup valid project ref
211-
project := apitest.RandomProjectRef()
212206
// Setup valid access token
213207
token := apitest.RandomAccessToken(t)
214208
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
@@ -218,11 +212,11 @@ verify_jwt = false
218212
// Setup mock api
219213
defer gock.OffAll()
220214
gock.New(utils.DefaultApiHost).
221-
Get("/v1/projects/" + project + "/functions").
215+
Get("/v1/projects/" + flags.ProjectRef + "/functions").
222216
Reply(http.StatusOK).
223217
JSON([]api.FunctionResponse{})
224218
gock.New(utils.DefaultApiHost).
225-
Post("/v1/projects/"+project+"/functions").
219+
Post("/v1/projects/"+flags.ProjectRef+"/functions").
226220
MatchParam("verify_jwt", "false").
227221
Reply(http.StatusCreated).
228222
JSON(api.FunctionResponse{Id: "1"})
@@ -234,7 +228,7 @@ verify_jwt = false
234228
outputDir := filepath.Join(utils.TempDir, fmt.Sprintf(".output_%s", slug))
235229
require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644))
236230
// Run test
237-
assert.NoError(t, Run(context.Background(), []string{slug}, project, nil, "", fsys))
231+
assert.NoError(t, Run(context.Background(), []string{slug}, true, nil, "", fsys))
238232
// Validate api
239233
assert.Empty(t, apitest.ListUnmatchedRequests())
240234
})
@@ -252,8 +246,6 @@ verify_jwt = false
252246
`)
253247
require.NoError(t, err)
254248
require.NoError(t, f.Close())
255-
// Setup valid project ref
256-
project := apitest.RandomProjectRef()
257249
// Setup valid access token
258250
token := apitest.RandomAccessToken(t)
259251
t.Setenv("SUPABASE_ACCESS_TOKEN", string(token))
@@ -263,11 +255,11 @@ verify_jwt = false
263255
// Setup mock api
264256
defer gock.OffAll()
265257
gock.New(utils.DefaultApiHost).
266-
Get("/v1/projects/" + project + "/functions").
258+
Get("/v1/projects/" + flags.ProjectRef + "/functions").
267259
Reply(http.StatusOK).
268260
JSON([]api.FunctionResponse{})
269261
gock.New(utils.DefaultApiHost).
270-
Post("/v1/projects/"+project+"/functions").
262+
Post("/v1/projects/"+flags.ProjectRef+"/functions").
271263
MatchParam("verify_jwt", "true").
272264
Reply(http.StatusCreated).
273265
JSON(api.FunctionResponse{Id: "1"})
@@ -280,7 +272,7 @@ verify_jwt = false
280272
require.NoError(t, afero.WriteFile(fsys, filepath.Join(outputDir, "output.eszip"), []byte(""), 0644))
281273
// Run test
282274
noVerifyJwt := false
283-
assert.NoError(t, Run(context.Background(), []string{slug}, project, &noVerifyJwt, "", fsys))
275+
assert.NoError(t, Run(context.Background(), []string{slug}, true, &noVerifyJwt, "", fsys))
284276
// Validate api
285277
assert.Empty(t, apitest.ListUnmatchedRequests())
286278
})

0 commit comments

Comments
 (0)