docker.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. package docker
  2. import (
  3. "errors"
  4. "fmt"
  5. "os"
  6. "path/filepath"
  7. "strings"
  8. "text/template"
  9. "time"
  10. "github.com/logrusorgru/aurora"
  11. "github.com/urfave/cli"
  12. "github.com/zeromicro/go-zero/tools/goctl/util"
  13. "github.com/zeromicro/go-zero/tools/goctl/util/pathx"
  14. )
  15. const (
  16. dockerfileName = "Dockerfile"
  17. etcDir = "etc"
  18. yamlEtx = ".yaml"
  19. cstOffset = 60 * 60 * 8 // 8 hours offset for Chinese Standard Time
  20. )
  21. // Docker describes a dockerfile
  22. type Docker struct {
  23. Chinese bool
  24. GoRelPath string
  25. GoFile string
  26. ExeFile string
  27. Scratch bool
  28. HasPort bool
  29. Port int
  30. Argument string
  31. Version string
  32. HasTimezone bool
  33. Timezone string
  34. }
  35. // DockerCommand provides the entry for goctl docker
  36. func DockerCommand(c *cli.Context) (err error) {
  37. defer func() {
  38. if err == nil {
  39. fmt.Println(aurora.Green("Done."))
  40. }
  41. }()
  42. goFile := c.String("go")
  43. home := c.String("home")
  44. version := c.String("version")
  45. remote := c.String("remote")
  46. branch := c.String("branch")
  47. timezone := c.String("tz")
  48. if len(remote) > 0 {
  49. repo, _ := util.CloneIntoGitHome(remote, branch)
  50. if len(repo) > 0 {
  51. home = repo
  52. }
  53. }
  54. if len(version) > 0 {
  55. version = version + "-"
  56. }
  57. if len(home) > 0 {
  58. pathx.RegisterGoctlHome(home)
  59. }
  60. if len(goFile) == 0 {
  61. return errors.New("-go can't be empty")
  62. }
  63. if !pathx.FileExists(goFile) {
  64. return fmt.Errorf("file %q not found", goFile)
  65. }
  66. scratch := c.Bool("scratch")
  67. port := c.Int("port")
  68. if _, err := os.Stat(etcDir); os.IsNotExist(err) {
  69. return generateDockerfile(goFile, scratch, port, version, timezone)
  70. }
  71. cfg, err := findConfig(goFile, etcDir)
  72. if err != nil {
  73. return err
  74. }
  75. if err := generateDockerfile(goFile, scratch, port, version, timezone, "-f", "etc/"+cfg); err != nil {
  76. return err
  77. }
  78. projDir, ok := pathx.FindProjectPath(goFile)
  79. if ok {
  80. fmt.Printf("Hint: run \"docker build ...\" command in dir:\n %s\n", projDir)
  81. }
  82. return nil
  83. }
  84. func findConfig(file, dir string) (string, error) {
  85. var files []string
  86. err := filepath.Walk(dir, func(path string, f os.FileInfo, _ error) error {
  87. if !f.IsDir() {
  88. if filepath.Ext(f.Name()) == yamlEtx {
  89. files = append(files, f.Name())
  90. }
  91. }
  92. return nil
  93. })
  94. if err != nil {
  95. return "", err
  96. }
  97. if len(files) == 0 {
  98. return "", errors.New("no yaml file")
  99. }
  100. name := strings.TrimSuffix(filepath.Base(file), ".go")
  101. for _, f := range files {
  102. if strings.Index(f, name) == 0 {
  103. return f, nil
  104. }
  105. }
  106. return files[0], nil
  107. }
  108. func generateDockerfile(goFile string, scratch bool, port int, version, timezone string, args ...string) error {
  109. projPath, err := getFilePath(filepath.Dir(goFile))
  110. if err != nil {
  111. return err
  112. }
  113. if len(projPath) == 0 {
  114. projPath = "."
  115. }
  116. out, err := pathx.CreateIfNotExist(dockerfileName)
  117. if err != nil {
  118. return err
  119. }
  120. defer out.Close()
  121. text, err := pathx.LoadTemplate(category, dockerTemplateFile, dockerTemplate)
  122. if err != nil {
  123. return err
  124. }
  125. var builder strings.Builder
  126. for _, arg := range args {
  127. builder.WriteString(`, "` + arg + `"`)
  128. }
  129. _, offset := time.Now().Zone()
  130. t := template.Must(template.New("dockerfile").Parse(text))
  131. return t.Execute(out, Docker{
  132. Chinese: offset == cstOffset,
  133. GoRelPath: projPath,
  134. GoFile: goFile,
  135. ExeFile: pathx.FileNameWithoutExt(filepath.Base(goFile)),
  136. Scratch: scratch,
  137. HasPort: port > 0,
  138. Port: port,
  139. Argument: builder.String(),
  140. Version: version,
  141. HasTimezone: len(timezone) > 0,
  142. Timezone: timezone,
  143. })
  144. }
  145. func getFilePath(file string) (string, error) {
  146. wd, err := os.Getwd()
  147. if err != nil {
  148. return "", err
  149. }
  150. projPath, ok := pathx.FindGoModPath(filepath.Join(wd, file))
  151. if !ok {
  152. projPath, err = pathx.PathFromGoSrc()
  153. if err != nil {
  154. return "", errors.New("no go.mod found, or not in GOPATH")
  155. }
  156. // ignore project root directory for GOPATH mode
  157. pos := strings.IndexByte(projPath, os.PathSeparator)
  158. if pos >= 0 {
  159. projPath = projPath[pos+1:]
  160. }
  161. }
  162. return projPath, nil
  163. }