cmd.go 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. package cmd
  2. import (
  3. "context"
  4. "fmt"
  5. "io"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "runtime/trace"
  10. "github.com/spf13/cobra"
  11. "github.com/spf13/pflag"
  12. yaml "gopkg.in/yaml.v3"
  13. "github.com/kyleconroy/sqlc/internal/config"
  14. "github.com/kyleconroy/sqlc/internal/debug"
  15. "github.com/kyleconroy/sqlc/internal/tracer"
  16. )
  17. // Do runs the command logic.
  18. func Do(args []string, stdin io.Reader, stdout io.Writer, stderr io.Writer) int {
  19. rootCmd := &cobra.Command{Use: "sqlc", SilenceUsage: true}
  20. rootCmd.PersistentFlags().StringP("file", "f", "", "specify an alternate config file (default: sqlc.yaml)")
  21. rootCmd.PersistentFlags().BoolP("experimental", "x", false, "enable experimental features (default: false)")
  22. rootCmd.AddCommand(checkCmd)
  23. rootCmd.AddCommand(genCmd)
  24. rootCmd.AddCommand(initCmd)
  25. rootCmd.AddCommand(versionCmd)
  26. rootCmd.AddCommand(packageCmd)
  27. rootCmd.SetArgs(args)
  28. rootCmd.SetIn(stdin)
  29. rootCmd.SetOut(stdout)
  30. rootCmd.SetErr(stderr)
  31. ctx, cleanup, err := tracer.Start(context.Background())
  32. if err != nil {
  33. fmt.Printf("failed to start trace: %v\n", err)
  34. return 1
  35. }
  36. defer cleanup()
  37. if err := rootCmd.ExecuteContext(ctx); err == nil {
  38. return 0
  39. }
  40. if exitError, ok := err.(*exec.ExitError); ok {
  41. return exitError.ExitCode()
  42. }
  43. return 1
  44. }
  45. var version string
  46. var versionCmd = &cobra.Command{
  47. Use: "version",
  48. Short: "Print the sqlc version number",
  49. Run: func(cmd *cobra.Command, args []string) {
  50. if debug.Traced {
  51. defer trace.StartRegion(cmd.Context(), "version").End()
  52. }
  53. if version == "" {
  54. // When no version is set, return the next bug fix version
  55. // after the most recent tag
  56. fmt.Printf("%s\n", "v1.12.0")
  57. } else {
  58. fmt.Printf("%s\n", version)
  59. }
  60. },
  61. }
  62. var initCmd = &cobra.Command{
  63. Use: "init",
  64. Short: "Create an empty sqlc.yaml settings file",
  65. RunE: func(cmd *cobra.Command, args []string) error {
  66. if debug.Traced {
  67. defer trace.StartRegion(cmd.Context(), "init").End()
  68. }
  69. file := "sqlc.yaml"
  70. if f := cmd.Flag("file"); f != nil && f.Changed {
  71. file = f.Value.String()
  72. if file == "" {
  73. return fmt.Errorf("file argument is empty")
  74. }
  75. }
  76. if _, err := os.Stat(file); !os.IsNotExist(err) {
  77. return nil
  78. }
  79. blob, err := yaml.Marshal(config.V1GenerateSettings{Version: "1"})
  80. if err != nil {
  81. return err
  82. }
  83. return os.WriteFile(file, blob, 0644)
  84. },
  85. }
  86. type Env struct {
  87. ExperimentalFeatures bool
  88. }
  89. func ParseEnv(c *cobra.Command) Env {
  90. x := c.Flag("experimental")
  91. return Env{ExperimentalFeatures: x != nil && x.Changed}
  92. }
  93. func getConfigPath(stderr io.Writer, f *pflag.Flag) (string, string) {
  94. if f != nil && f.Changed {
  95. file := f.Value.String()
  96. if file == "" {
  97. fmt.Fprintln(stderr, "error parsing config: file argument is empty")
  98. os.Exit(1)
  99. }
  100. abspath, err := filepath.Abs(file)
  101. if err != nil {
  102. fmt.Fprintf(stderr, "error parsing config: absolute file path lookup failed: %s\n", err)
  103. os.Exit(1)
  104. }
  105. return filepath.Dir(abspath), filepath.Base(abspath)
  106. } else {
  107. wd, err := os.Getwd()
  108. if err != nil {
  109. fmt.Fprintln(stderr, "error parsing sqlc.json: file does not exist")
  110. os.Exit(1)
  111. }
  112. return wd, ""
  113. }
  114. }
  115. var genCmd = &cobra.Command{
  116. Use: "generate",
  117. Short: "Generate Go code from SQL",
  118. Run: func(cmd *cobra.Command, args []string) {
  119. if debug.Traced {
  120. defer trace.StartRegion(cmd.Context(), "generate").End()
  121. }
  122. stderr := cmd.ErrOrStderr()
  123. dir, name := getConfigPath(stderr, cmd.Flag("file"))
  124. output, err := Generate(cmd.Context(), ParseEnv(cmd), dir, name, stderr)
  125. if err != nil {
  126. os.Exit(1)
  127. }
  128. if debug.Traced {
  129. defer trace.StartRegion(cmd.Context(), "writefiles").End()
  130. }
  131. for filename, source := range output {
  132. os.MkdirAll(filepath.Dir(filename), 0755)
  133. if err := os.WriteFile(filename, []byte(source), 0644); err != nil {
  134. fmt.Fprintf(stderr, "%s: %s\n", filename, err)
  135. os.Exit(1)
  136. }
  137. }
  138. },
  139. }
  140. var checkCmd = &cobra.Command{
  141. Use: "compile",
  142. Short: "Statically check SQL for syntax and type errors",
  143. RunE: func(cmd *cobra.Command, args []string) error {
  144. if debug.Traced {
  145. defer trace.StartRegion(cmd.Context(), "compile").End()
  146. }
  147. stderr := cmd.ErrOrStderr()
  148. dir, name := getConfigPath(stderr, cmd.Flag("file"))
  149. if _, err := Generate(cmd.Context(), ParseEnv(cmd), dir, name, stderr); err != nil {
  150. os.Exit(1)
  151. }
  152. return nil
  153. },
  154. }