upload.go 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package bundler
  2. import (
  3. "context"
  4. "errors"
  5. "fmt"
  6. "log/slog"
  7. "os"
  8. "strings"
  9. "google.golang.org/protobuf/proto"
  10. "github.com/sqlc-dev/sqlc/internal/config"
  11. "github.com/sqlc-dev/sqlc/internal/info"
  12. "github.com/sqlc-dev/sqlc/internal/plugin"
  13. "github.com/sqlc-dev/sqlc/internal/quickdb"
  14. pb "github.com/sqlc-dev/sqlc/internal/quickdb/v1"
  15. )
  16. var ErrNoProject = errors.New(`project uploads require a cloud project
  17. If you don't have a project, you can create one from the sqlc Cloud
  18. dashboard at https://dashboard.sqlc.dev/. If you have a project, ensure
  19. you've set its id as the value of the "project" field within the "cloud"
  20. section of your sqlc configuration. The id will look similar to
  21. "01HA8TWGMYPHK0V2GGMB3R2TP9".`)
  22. var ErrNoAuthToken = errors.New(`project uploads require an auth token
  23. If you don't have an auth token, you can create one from the sqlc Cloud
  24. dashboard at https://dashboard.sqlc.dev/. If you have an auth token, ensure
  25. you've set it as the value of the SQLC_AUTH_TOKEN environment variable.`)
  26. type Uploader struct {
  27. configPath string
  28. config *config.Config
  29. dir string
  30. client pb.QuickClient
  31. }
  32. type QuerySetArchive struct {
  33. Name string
  34. Queries []string
  35. Schema []string
  36. Request *plugin.GenerateRequest
  37. }
  38. func NewUploader(configPath, dir string, conf *config.Config) *Uploader {
  39. return &Uploader{
  40. configPath: configPath,
  41. config: conf,
  42. dir: dir,
  43. }
  44. }
  45. func (up *Uploader) Validate() error {
  46. if up.config.Cloud.Project == "" {
  47. return ErrNoProject
  48. }
  49. if up.config.Cloud.AuthToken == "" {
  50. return ErrNoAuthToken
  51. }
  52. if up.client == nil {
  53. client, err := quickdb.NewClientFromConfig(up.config.Cloud)
  54. if err != nil {
  55. return fmt.Errorf("client init failed: %w", err)
  56. }
  57. up.client = client
  58. }
  59. return nil
  60. }
  61. var envvars = []string{
  62. "GITHUB_REPOSITORY",
  63. "GITHUB_REF",
  64. "GITHUB_REF_NAME",
  65. "GITHUB_REF_TYPE",
  66. "GITHUB_SHA",
  67. }
  68. func annotate() map[string]string {
  69. labels := map[string]string{}
  70. for _, ev := range envvars {
  71. key := strings.ReplaceAll(strings.ToLower(ev), "_", ".")
  72. labels[key] = os.Getenv(ev)
  73. }
  74. return labels
  75. }
  76. func BuildRequest(ctx context.Context, dir, configPath string, results []*QuerySetArchive) (*pb.UploadArchiveRequest, error) {
  77. conf, err := readFile(dir, configPath)
  78. if err != nil {
  79. return nil, err
  80. }
  81. res := &pb.UploadArchiveRequest{
  82. SqlcVersion: info.Version,
  83. Config: conf,
  84. Annotations: annotate(),
  85. }
  86. for i, result := range results {
  87. schema, err := readFiles(dir, result.Schema)
  88. if err != nil {
  89. return nil, err
  90. }
  91. queries, err := readFiles(dir, result.Queries)
  92. if err != nil {
  93. return nil, err
  94. }
  95. name := result.Name
  96. if name == "" {
  97. name = fmt.Sprintf("queryset_%d", i)
  98. }
  99. genreq, err := proto.Marshal(result.Request)
  100. if err != nil {
  101. return nil, err
  102. }
  103. res.QuerySets = append(res.QuerySets, &pb.QuerySet{
  104. Name: name,
  105. Schema: schema,
  106. Queries: queries,
  107. CodegenRequest: &pb.File{
  108. Name: "codegen_request.pb",
  109. Contents: genreq,
  110. },
  111. })
  112. }
  113. return res, nil
  114. }
  115. func (up *Uploader) buildRequest(ctx context.Context, results []*QuerySetArchive) (*pb.UploadArchiveRequest, error) {
  116. return BuildRequest(ctx, up.dir, up.configPath, results)
  117. }
  118. func (up *Uploader) DumpRequestOut(ctx context.Context, result []*QuerySetArchive) error {
  119. req, err := up.buildRequest(ctx, result)
  120. if err != nil {
  121. return err
  122. }
  123. slog.Info("config", "file", req.Config.Name, "bytes", len(req.Config.Contents))
  124. for _, qs := range req.QuerySets {
  125. slog.Info("codegen_request", "queryset", qs.Name, "file", "codegen_request.pb")
  126. for _, file := range qs.Schema {
  127. slog.Info("schema", "queryset", qs.Name, "file", file.Name, "bytes", len(file.Contents))
  128. }
  129. for _, file := range qs.Queries {
  130. slog.Info("query", "queryset", qs.Name, "file", file.Name, "bytes", len(file.Contents))
  131. }
  132. }
  133. return nil
  134. }
  135. func (up *Uploader) Upload(ctx context.Context, result []*QuerySetArchive) error {
  136. if err := up.Validate(); err != nil {
  137. return err
  138. }
  139. req, err := up.buildRequest(ctx, result)
  140. if err != nil {
  141. return err
  142. }
  143. if _, err := up.client.UploadArchive(ctx, req); err != nil {
  144. return fmt.Errorf("upload error: %w", err)
  145. }
  146. return nil
  147. }