Skip to content

Commit 64313a6

Browse files
authored
feat(verify): Add new command to verify queries and migrations (sqlc-dev#2986)
We've renamed the upload sub-command to push. Using upload is supported for now, but please switch over to using push. We changed the data sent along in a push request. Upload used to include the configuration file, migrations, queries, and all generated code. Push drops the generated code in favor of including the [plugin.GenerateRequest], which is the protobuf message we pass to codegen plugins. We now pass along annotations for each push. By default, we include these environment variables if they are present: GITHUB_REPOSITORY GITHUB_REF GITHUB_REF_NAME GITHUB_REF_TYPE GITHUB_SHA Like upload, push should be run when you tag a release of your application. We run it on every push to main, as we continuously deploy those commits. verify The verify command, building on top of the push command, ensures migrations are safe to deploy. Verify sends your current schema and queries to sqlc cloud. There, we run the queries for your latest push against your new schema changes. This check catches backwards incompatible schema changes for existing queries.
1 parent a7d50c5 commit 64313a6

14 files changed

+1165
-507
lines changed

buf.gen.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
version: v1
22
managed:
33
enabled: true
4+
go_package_prefix:
5+
default: "github.com/sqlc-dev/sqlc/internal"
46
plugins:
57
- plugin: buf.build/protocolbuffers/go:v1.30.0
68
out: internal

internal/bundler/multipart.go

+21-36
Original file line numberDiff line numberDiff line change
@@ -4,52 +4,37 @@ import (
44
"os"
55
"path/filepath"
66

7-
"github.com/sqlc-dev/sqlc/internal/config"
87
pb "github.com/sqlc-dev/sqlc/internal/quickdb/v1"
98
"github.com/sqlc-dev/sqlc/internal/sql/sqlpath"
109
)
1110

12-
func readInputs(file string, conf *config.Config) ([]*pb.File, error) {
13-
refs := map[string]struct{}{}
14-
refs[filepath.Base(file)] = struct{}{}
15-
16-
for _, pkg := range conf.SQL {
17-
for _, paths := range []config.Paths{pkg.Schema, pkg.Queries} {
18-
files, err := sqlpath.Glob(paths)
19-
if err != nil {
20-
return nil, err
21-
}
22-
for _, file := range files {
23-
refs[file] = struct{}{}
24-
}
25-
}
11+
func readFiles(dir string, paths []string) ([]*pb.File, error) {
12+
files, err := sqlpath.Glob(paths)
13+
if err != nil {
14+
return nil, err
2615
}
27-
28-
var files []*pb.File
29-
for file, _ := range refs {
30-
contents, err := os.ReadFile(file)
16+
var out []*pb.File
17+
for _, file := range files {
18+
f, err := readFile(dir, file)
3119
if err != nil {
3220
return nil, err
3321
}
34-
files = append(files, &pb.File{
35-
Name: file,
36-
Contents: contents,
37-
})
22+
out = append(out, f)
3823
}
39-
return files, nil
24+
return out, nil
4025
}
4126

42-
func readOutputs(dir string, output map[string]string) ([]*pb.File, error) {
43-
var files []*pb.File
44-
for filename, contents := range output {
45-
rel, err := filepath.Rel(dir, filename)
46-
if err != nil {
47-
return nil, err
48-
}
49-
files = append(files, &pb.File{
50-
Name: rel,
51-
Contents: []byte(contents),
52-
})
27+
func readFile(dir string, path string) (*pb.File, error) {
28+
rel, err := filepath.Rel(dir, path)
29+
if err != nil {
30+
return nil, err
31+
}
32+
blob, err := os.ReadFile(path)
33+
if err != nil {
34+
return nil, err
5335
}
54-
return files, nil
36+
return &pb.File{
37+
Name: rel,
38+
Contents: blob,
39+
}, nil
5540
}

internal/bundler/upload.go

+78-13
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"log/slog"
8+
"os"
9+
"strings"
710

8-
"google.golang.org/protobuf/encoding/protojson"
11+
"google.golang.org/protobuf/proto"
912

1013
"github.com/sqlc-dev/sqlc/internal/config"
1114
"github.com/sqlc-dev/sqlc/internal/info"
15+
"github.com/sqlc-dev/sqlc/internal/plugin"
1216
"github.com/sqlc-dev/sqlc/internal/quickdb"
1317
pb "github.com/sqlc-dev/sqlc/internal/quickdb/v1"
1418
)
@@ -33,6 +37,13 @@ type Uploader struct {
3337
client pb.QuickClient
3438
}
3539

40+
type QuerySetArchive struct {
41+
Name string
42+
Queries []string
43+
Schema []string
44+
Request *plugin.GenerateRequest
45+
}
46+
3647
func NewUploader(configPath, dir string, conf *config.Config) *Uploader {
3748
return &Uploader{
3849
configPath: configPath,
@@ -58,32 +69,86 @@ func (up *Uploader) Validate() error {
5869
return nil
5970
}
6071

61-
func (up *Uploader) buildRequest(ctx context.Context, result map[string]string) (*pb.UploadArchiveRequest, error) {
62-
ins, err := readInputs(up.configPath, up.config)
63-
if err != nil {
64-
return nil, err
72+
var envvars = []string{
73+
"GITHUB_REPOSITORY",
74+
"GITHUB_REF",
75+
"GITHUB_REF_NAME",
76+
"GITHUB_REF_TYPE",
77+
"GITHUB_SHA",
78+
}
79+
80+
func annotate() map[string]string {
81+
labels := map[string]string{}
82+
for _, ev := range envvars {
83+
key := strings.ReplaceAll(strings.ToLower(ev), "_", ".")
84+
labels[key] = os.Getenv(ev)
6585
}
66-
outs, err := readOutputs(up.dir, result)
86+
return labels
87+
}
88+
89+
func BuildRequest(ctx context.Context, dir, configPath string, results []*QuerySetArchive) (*pb.UploadArchiveRequest, error) {
90+
conf, err := readFile(dir, configPath)
6791
if err != nil {
6892
return nil, err
6993
}
70-
return &pb.UploadArchiveRequest{
94+
res := &pb.UploadArchiveRequest{
7195
SqlcVersion: info.Version,
72-
Inputs: ins,
73-
Outputs: outs,
74-
}, nil
96+
Config: conf,
97+
Annotations: annotate(),
98+
}
99+
for i, result := range results {
100+
schema, err := readFiles(dir, result.Schema)
101+
if err != nil {
102+
return nil, err
103+
}
104+
queries, err := readFiles(dir, result.Queries)
105+
if err != nil {
106+
return nil, err
107+
}
108+
name := result.Name
109+
if name == "" {
110+
name = fmt.Sprintf("queryset_%d", i)
111+
}
112+
genreq, err := proto.Marshal(result.Request)
113+
if err != nil {
114+
return nil, err
115+
}
116+
res.QuerySets = append(res.QuerySets, &pb.QuerySet{
117+
Name: name,
118+
Schema: schema,
119+
Queries: queries,
120+
CodegenRequest: &pb.File{
121+
Name: "codegen_request.pb",
122+
Contents: genreq,
123+
},
124+
})
125+
}
126+
return res, nil
75127
}
76128

77-
func (up *Uploader) DumpRequestOut(ctx context.Context, result map[string]string) error {
129+
func (up *Uploader) buildRequest(ctx context.Context, results []*QuerySetArchive) (*pb.UploadArchiveRequest, error) {
130+
return BuildRequest(ctx, up.dir, up.configPath, results)
131+
}
132+
133+
func (up *Uploader) DumpRequestOut(ctx context.Context, result []*QuerySetArchive) error {
78134
req, err := up.buildRequest(ctx, result)
79135
if err != nil {
80136
return err
81137
}
82-
fmt.Println(protojson.Format(req))
138+
slog.Info("config", "file", req.Config.Name, "bytes", len(req.Config.Contents))
139+
for _, qs := range req.QuerySets {
140+
slog.Info("codegen_request", "queryset", qs.Name, "file", "codegen_request.pb")
141+
for _, file := range qs.Schema {
142+
slog.Info("schema", "queryset", qs.Name, "file", file.Name, "bytes", len(file.Contents))
143+
}
144+
for _, file := range qs.Queries {
145+
slog.Info("query", "queryset", qs.Name, "file", file.Name, "bytes", len(file.Contents))
146+
}
147+
}
83148
return nil
84149
}
85150

86-
func (up *Uploader) Upload(ctx context.Context, result map[string]string) error {
151+
func (up *Uploader) Upload(ctx context.Context, result []*QuerySetArchive) error {
87152
if err := up.Validate(); err != nil {
88153
return err
89154
}

internal/cmd/cmd.go

+3-20
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import (
2626

2727
func init() {
2828
createDBCmd.Flags().StringP("queryset", "", "", "name of the queryset to use")
29-
uploadCmd.Flags().BoolP("dry-run", "", false, "dump upload request (default: false)")
29+
pushCmd.Flags().BoolP("dry-run", "", false, "dump push request (default: false)")
3030
initCmd.Flags().BoolP("v1", "", false, "generate v1 config yaml file")
3131
initCmd.Flags().BoolP("v2", "", true, "generate v2 config yaml file")
3232
initCmd.MarkFlagsMutuallyExclusive("v1", "v2")
@@ -46,7 +46,8 @@ func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int
4646
rootCmd.AddCommand(genCmd)
4747
rootCmd.AddCommand(initCmd)
4848
rootCmd.AddCommand(versionCmd)
49-
rootCmd.AddCommand(uploadCmd)
49+
rootCmd.AddCommand(verifyCmd)
50+
rootCmd.AddCommand(pushCmd)
5051
rootCmd.AddCommand(NewCmdVet())
5152

5253
rootCmd.SetArgs(args)
@@ -214,24 +215,6 @@ var genCmd = &cobra.Command{
214215
},
215216
}
216217

217-
var uploadCmd = &cobra.Command{
218-
Use: "upload",
219-
Short: "Upload the schema, queries, and configuration for this project",
220-
RunE: func(cmd *cobra.Command, args []string) error {
221-
stderr := cmd.ErrOrStderr()
222-
dir, name := getConfigPath(stderr, cmd.Flag("file"))
223-
opts := &Options{
224-
Env: ParseEnv(cmd),
225-
Stderr: stderr,
226-
}
227-
if err := createPkg(cmd.Context(), dir, name, opts); err != nil {
228-
fmt.Fprintf(stderr, "error uploading: %s\n", err)
229-
os.Exit(1)
230-
}
231-
return nil
232-
},
233-
}
234-
235218
var checkCmd = &cobra.Command{
236219
Use: "compile",
237220
Short: "Statically check SQL for syntax and type errors",

0 commit comments

Comments
 (0)