Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
`sqlc upload` will package up schema, queries and output code and upload it to sqlc Cloud. Uploaded projects are used to verify future sqlc releases; all uploaded code is tested for backwards-incompaitble changes. * bundler: Add flag to dump request data Add the `--dry-run` flag to see the exact HTTP request that sqlc will make to the the upload endpoint. * docs: Add documentation for the upload subcommand
- Loading branch information
1 parent
66d4310
commit bde6281
Showing
14 changed files
with
347 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
# Uploading projects | ||
|
||
*This feature requires signing up for [sqlc Cloud](https://app.sqlc.dev), which is currently in beta.* | ||
|
||
Uploading your project ensures that future releases of sqlc do not break your | ||
existing code. Similar to Rust's [crater](https://github.com/rust-lang/crater) | ||
project, uploaded projects are tested against development releases of sqlc to | ||
verify correctness. | ||
|
||
## Add configuration | ||
|
||
After creating a project, add the project ID to your sqlc configuration file. | ||
|
||
```yaml | ||
version: "1" | ||
project: | ||
id: "<PROJECT-ID>" | ||
packages: [] | ||
``` | ||
|
||
```json | ||
{ | ||
"version": "1", | ||
"project": { | ||
"id": "<PROJECT-ID>" | ||
}, | ||
"packages": [ | ||
] | ||
} | ||
``` | ||
|
||
You'll also need to create an API token and make it available via the | ||
`SQLC_AUTH_TOKEN` environment variable. | ||
|
||
```shell | ||
export SQLC_AUTH_TOKEN=sqlc_xxxxxxxx | ||
``` | ||
|
||
## Dry run | ||
|
||
You can see what's included when uploading your project by using using the `--dry-run` flag: | ||
|
||
```shell | ||
sqlc upload --dry-run | ||
``` | ||
|
||
The output will be the exact HTTP request sent by `sqlc`. | ||
|
||
## Upload | ||
|
||
Once you're ready to upload, remove the `--dry-run` flag. | ||
|
||
```shell | ||
sqlc upload | ||
``` | ||
|
||
By uploading your project, you're making sqlc more stable and reliable. Thanks! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package bundler | ||
|
||
import ( | ||
"runtime" | ||
|
||
"github.com/kyleconroy/sqlc/internal/info" | ||
) | ||
|
||
func projectMetadata() ([][2]string, error) { | ||
return [][2]string{ | ||
{"sqlc_version", info.Version}, | ||
{"go_version", runtime.Version()}, | ||
{"goos", runtime.GOOS}, | ||
{"goarch", runtime.GOARCH}, | ||
}, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package bundler | ||
|
||
import ( | ||
"io" | ||
"mime/multipart" | ||
"os" | ||
"path/filepath" | ||
|
||
"github.com/kyleconroy/sqlc/internal/config" | ||
"github.com/kyleconroy/sqlc/internal/sql/sqlpath" | ||
) | ||
|
||
func writeInputs(w *multipart.Writer, file string, conf *config.Config) error { | ||
refs := map[string]struct{}{} | ||
refs[filepath.Base(file)] = struct{}{} | ||
|
||
for _, pkg := range conf.SQL { | ||
for _, paths := range []config.Paths{pkg.Schema, pkg.Queries} { | ||
files, err := sqlpath.Glob(paths) | ||
if err != nil { | ||
return err | ||
} | ||
for _, file := range files { | ||
refs[file] = struct{}{} | ||
} | ||
} | ||
} | ||
|
||
for file, _ := range refs { | ||
if err := addPart(w, file); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
params, err := projectMetadata() | ||
if err != nil { | ||
return err | ||
} | ||
params = append(params, [2]string{"project_id", conf.Project.ID}) | ||
for _, val := range params { | ||
if err = w.WriteField(val[0], val[1]); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func addPart(w *multipart.Writer, file string) error { | ||
h, err := os.Open(file) | ||
if err != nil { | ||
return err | ||
} | ||
defer h.Close() | ||
part, err := w.CreateFormFile("inputs", file) | ||
if err != nil { | ||
return err | ||
} | ||
_, err = io.Copy(part, h) | ||
if err != nil { | ||
return err | ||
} | ||
return nil | ||
} | ||
|
||
func writeOutputs(w *multipart.Writer, dir string, output map[string]string) error { | ||
for filename, contents := range output { | ||
rel, err := filepath.Rel(dir, filename) | ||
if err != nil { | ||
return err | ||
} | ||
part, err := w.CreateFormFile("outputs", rel) | ||
if err != nil { | ||
return err | ||
} | ||
if _, err := io.WriteString(part, contents); err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
package bundler | ||
|
||
import ( | ||
"bytes" | ||
"context" | ||
"fmt" | ||
"io" | ||
"mime/multipart" | ||
"net/http" | ||
"net/http/httputil" | ||
"os" | ||
|
||
"github.com/kyleconroy/sqlc/internal/config" | ||
) | ||
|
||
type Uploader struct { | ||
token string | ||
configPath string | ||
config *config.Config | ||
dir string | ||
} | ||
|
||
func NewUploader(configPath, dir string, conf *config.Config) *Uploader { | ||
return &Uploader{ | ||
token: os.Getenv("SQLC_AUTH_TOKEN"), | ||
configPath: configPath, | ||
config: conf, | ||
dir: dir, | ||
} | ||
} | ||
|
||
func (up *Uploader) Validate() error { | ||
if up.config.Project.ID == "" { | ||
return fmt.Errorf("project.id is not set") | ||
} | ||
if up.token == "" { | ||
return fmt.Errorf("SQLC_AUTH_TOKEN environment variable is not set") | ||
} | ||
return nil | ||
} | ||
|
||
func (up *Uploader) buildRequest(ctx context.Context, result map[string]string) (*http.Request, error) { | ||
body := bytes.NewBuffer([]byte{}) | ||
|
||
w := multipart.NewWriter(body) | ||
defer w.Close() | ||
if err := writeInputs(w, up.configPath, up.config); err != nil { | ||
return nil, err | ||
} | ||
if err := writeOutputs(w, up.dir, result); err != nil { | ||
return nil, err | ||
} | ||
w.Close() | ||
|
||
req, err := http.NewRequest("POST", "https://api.sqlc.dev/upload", body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
// Set sqlc-version header | ||
req.Header.Set("Content-Type", w.FormDataContentType()) | ||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", up.token)) | ||
return req.WithContext(ctx), nil | ||
} | ||
|
||
func (up *Uploader) DumpRequestOut(ctx context.Context, result map[string]string) error { | ||
req, err := up.buildRequest(ctx, result) | ||
if err != nil { | ||
return err | ||
} | ||
dump, err := httputil.DumpRequest(req, true) | ||
if err != nil { | ||
return err | ||
} | ||
os.Stdout.Write(dump) | ||
return nil | ||
} | ||
|
||
func (up *Uploader) Upload(ctx context.Context, result map[string]string) error { | ||
if err := up.Validate(); err != nil { | ||
return err | ||
} | ||
req, err := up.buildRequest(ctx, result) | ||
if err != nil { | ||
return err | ||
} | ||
client := &http.Client{} | ||
resp, err := client.Do(req) | ||
if err != nil { | ||
return err | ||
} | ||
if resp.StatusCode >= 400 { | ||
body, err := io.ReadAll(resp.Body) | ||
defer resp.Body.Close() | ||
if err != nil { | ||
return fmt.Errorf("upload error: endpoint returned non-200 status code: %d", resp.StatusCode) | ||
} | ||
return fmt.Errorf("upload error: %d: %s", resp.StatusCode, string(body)) | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.