main.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package main
  2. import (
  3. "flag"
  4. "fmt"
  5. "go/build"
  6. "os"
  7. "os/exec"
  8. "path/filepath"
  9. "runtime"
  10. "strings"
  11. )
  12. var directory = flag.String("pwd", "", "Working directory of Xray vformat.")
  13. // envFile returns the name of the Go environment configuration file.
  14. // Copy from https://github.com/golang/go/blob/c4f2a9788a7be04daf931ac54382fbe2cb754938/src/cmd/go/internal/cfg/cfg.go#L150-L166
  15. func envFile() (string, error) {
  16. if file := os.Getenv("GOENV"); file != "" {
  17. if file == "off" {
  18. return "", fmt.Errorf("GOENV=off")
  19. }
  20. return file, nil
  21. }
  22. dir, err := os.UserConfigDir()
  23. if err != nil {
  24. return "", err
  25. }
  26. if dir == "" {
  27. return "", fmt.Errorf("missing user-config dir")
  28. }
  29. return filepath.Join(dir, "go", "env"), nil
  30. }
  31. // GetRuntimeEnv returns the value of runtime environment variable,
  32. // that is set by running following command: `go env -w key=value`.
  33. func GetRuntimeEnv(key string) (string, error) {
  34. file, err := envFile()
  35. if err != nil {
  36. return "", err
  37. }
  38. if file == "" {
  39. return "", fmt.Errorf("missing runtime env file")
  40. }
  41. var data []byte
  42. var runtimeEnv string
  43. data, readErr := os.ReadFile(file)
  44. if readErr != nil {
  45. return "", readErr
  46. }
  47. envStrings := strings.Split(string(data), "\n")
  48. for _, envItem := range envStrings {
  49. envItem = strings.TrimSuffix(envItem, "\r")
  50. envKeyValue := strings.Split(envItem, "=")
  51. if len(envKeyValue) == 2 && strings.TrimSpace(envKeyValue[0]) == key {
  52. runtimeEnv = strings.TrimSpace(envKeyValue[1])
  53. }
  54. }
  55. return runtimeEnv, nil
  56. }
  57. // GetGOBIN returns GOBIN environment variable as a string. It will NOT be empty.
  58. func GetGOBIN() string {
  59. // The one set by user explicitly by `export GOBIN=/path` or `env GOBIN=/path command`
  60. GOBIN := os.Getenv("GOBIN")
  61. if GOBIN == "" {
  62. var err error
  63. // The one set by user by running `go env -w GOBIN=/path`
  64. GOBIN, err = GetRuntimeEnv("GOBIN")
  65. if err != nil {
  66. // The default one that Golang uses
  67. return filepath.Join(build.Default.GOPATH, "bin")
  68. }
  69. if GOBIN == "" {
  70. return filepath.Join(build.Default.GOPATH, "bin")
  71. }
  72. return GOBIN
  73. }
  74. return GOBIN
  75. }
  76. func Run(binary string, args []string) ([]byte, error) {
  77. cmd := exec.Command(binary, args...)
  78. cmd.Env = append(cmd.Env, os.Environ()...)
  79. output, cmdErr := cmd.CombinedOutput()
  80. if cmdErr != nil {
  81. return nil, cmdErr
  82. }
  83. return output, nil
  84. }
  85. func RunMany(binary string, args, files []string) {
  86. fmt.Println("Processing...")
  87. maxTasks := make(chan struct{}, runtime.NumCPU())
  88. for _, file := range files {
  89. maxTasks <- struct{}{}
  90. go func(file string) {
  91. output, err := Run(binary, append(args, file))
  92. if err != nil {
  93. fmt.Println(err)
  94. } else if len(output) > 0 {
  95. fmt.Println(string(output))
  96. }
  97. <-maxTasks
  98. }(file)
  99. }
  100. }
  101. func main() {
  102. flag.Usage = func() {
  103. fmt.Fprintf(flag.CommandLine.Output(), "Usage of vformat:\n")
  104. flag.PrintDefaults()
  105. }
  106. flag.Parse()
  107. if !filepath.IsAbs(*directory) {
  108. pwd, wdErr := os.Getwd()
  109. if wdErr != nil {
  110. fmt.Println("Can not get current working directory.")
  111. os.Exit(1)
  112. }
  113. *directory = filepath.Join(pwd, *directory)
  114. }
  115. pwd := *directory
  116. GOBIN := GetGOBIN()
  117. binPath := os.Getenv("PATH")
  118. pathSlice := []string{pwd, GOBIN, binPath}
  119. binPath = strings.Join(pathSlice, string(os.PathListSeparator))
  120. os.Setenv("PATH", binPath)
  121. suffix := ""
  122. if runtime.GOOS == "windows" {
  123. suffix = ".exe"
  124. }
  125. gofmt := "gofumpt" + suffix
  126. goimports := "gci" + suffix
  127. if gofmtPath, err := exec.LookPath(gofmt); err != nil {
  128. fmt.Println("Can not find", gofmt, "in system path or current working directory.")
  129. os.Exit(1)
  130. } else {
  131. gofmt = gofmtPath
  132. }
  133. if goimportsPath, err := exec.LookPath(goimports); err != nil {
  134. fmt.Println("Can not find", goimports, "in system path or current working directory.")
  135. os.Exit(1)
  136. } else {
  137. goimports = goimportsPath
  138. }
  139. rawFilesSlice := make([]string, 0, 1000)
  140. walkErr := filepath.Walk(pwd, func(path string, info os.FileInfo, err error) error {
  141. if err != nil {
  142. fmt.Println(err)
  143. return err
  144. }
  145. if info.IsDir() {
  146. return nil
  147. }
  148. dir := filepath.Dir(path)
  149. filename := filepath.Base(path)
  150. if strings.HasSuffix(filename, ".go") &&
  151. !strings.HasSuffix(filename, ".pb.go") &&
  152. !strings.Contains(dir, filepath.Join("testing", "mocks")) &&
  153. !strings.Contains(path, filepath.Join("main", "distro", "all", "all.go")) {
  154. rawFilesSlice = append(rawFilesSlice, path)
  155. }
  156. return nil
  157. })
  158. if walkErr != nil {
  159. fmt.Println(walkErr)
  160. os.Exit(1)
  161. }
  162. gofmtArgs := []string{
  163. "-s", "-l", "-e", "-w",
  164. }
  165. goimportsArgs := []string{
  166. "write",
  167. }
  168. RunMany(gofmt, gofmtArgs, rawFilesSlice)
  169. RunMany(goimports, goimportsArgs, rawFilesSlice)
  170. fmt.Println("Do NOT forget to commit file changes.")
  171. }