install.go 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
  2. //
  3. // This Source Code Form is subject to the terms of the MIT License.
  4. // If a copy of the MIT was not distributed with this file,
  5. // You can obtain one at https://github.com/gogf/gf.
  6. package service
  7. import (
  8. "context"
  9. "runtime"
  10. "strings"
  11. "github.com/gogf/gf/v2/container/garray"
  12. "github.com/gogf/gf/v2/container/gset"
  13. "github.com/gogf/gf/v2/frame/g"
  14. "github.com/gogf/gf/v2/os/gcmd"
  15. "github.com/gogf/gf/v2/os/genv"
  16. "github.com/gogf/gf/v2/os/gfile"
  17. "github.com/gogf/gf/v2/text/gstr"
  18. "github.com/gogf/gf/v2/util/gconv"
  19. "github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
  20. "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
  21. )
  22. var (
  23. Install = serviceInstall{}
  24. )
  25. type serviceInstall struct{}
  26. type serviceInstallAvailablePath struct {
  27. dirPath string
  28. filePath string
  29. writable bool
  30. installed bool
  31. IsSelf bool
  32. }
  33. func (s serviceInstall) Run(ctx context.Context) (err error) {
  34. // Ask where to install.
  35. paths := s.getAvailablePaths()
  36. if len(paths) <= 0 {
  37. mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.")
  38. return
  39. }
  40. mlog.Printf("I found some installable paths for you(from $PATH): ")
  41. mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path")
  42. // Print all paths status and determine the default selectedID value.
  43. var (
  44. selectedID = -1
  45. newPaths []serviceInstallAvailablePath
  46. pathSet = gset.NewStrSet() // Used for repeated items filtering.
  47. )
  48. for _, path := range paths {
  49. if !pathSet.AddIfNotExist(path.dirPath) {
  50. continue
  51. }
  52. newPaths = append(newPaths, path)
  53. }
  54. paths = newPaths
  55. for id, path := range paths {
  56. mlog.Printf(" %2d | %8t | %9t | %s", id, path.writable, path.installed, path.dirPath)
  57. if selectedID == -1 {
  58. // Use the previously installed path as the most priority choice.
  59. if path.installed {
  60. selectedID = id
  61. }
  62. }
  63. }
  64. // If there's no previously installed path, use the first writable path.
  65. if selectedID == -1 {
  66. // Order by choosing priority.
  67. commonPaths := garray.NewStrArrayFrom(g.SliceStr{
  68. s.getGoPathBin(),
  69. `/usr/local/bin`,
  70. `/usr/bin`,
  71. `/usr/sbin`,
  72. `C:\Windows`,
  73. `C:\Windows\system32`,
  74. `C:\Go\bin`,
  75. `C:\Program Files`,
  76. `C:\Program Files (x86)`,
  77. })
  78. // Check the common installation directories.
  79. commonPaths.Iterator(func(k int, v string) bool {
  80. for id, aPath := range paths {
  81. if strings.EqualFold(aPath.dirPath, v) {
  82. selectedID = id
  83. return false
  84. }
  85. }
  86. return true
  87. })
  88. if selectedID == -1 {
  89. selectedID = 0
  90. }
  91. }
  92. if allyes.Check() {
  93. // Use the default selectedID.
  94. mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID)
  95. } else {
  96. for {
  97. // Get input and update selectedID.
  98. var (
  99. inputID int
  100. input = gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID)
  101. )
  102. if input != "" {
  103. inputID = gconv.Int(input)
  104. } else {
  105. break
  106. }
  107. // Check if out of range.
  108. if inputID >= len(paths) || inputID < 0 {
  109. mlog.Printf("invalid install destination Id: %d", inputID)
  110. continue
  111. }
  112. selectedID = inputID
  113. break
  114. }
  115. }
  116. // Get selected destination path.
  117. dstPath := paths[selectedID]
  118. // Install the new binary.
  119. mlog.Debugf(`copy file from "%s" to "%s"`, gfile.SelfPath(), dstPath.filePath)
  120. err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
  121. if err != nil {
  122. mlog.Printf("install gf binary to '%s' failed: %v", dstPath.dirPath, err)
  123. mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.dirPath)
  124. } else {
  125. mlog.Printf("gf binary is successfully installed to: %s", dstPath.filePath)
  126. }
  127. return
  128. }
  129. // IsInstalled checks and returns whether the binary is installed.
  130. func (s serviceInstall) IsInstalled() (*serviceInstallAvailablePath, bool) {
  131. paths := s.getAvailablePaths()
  132. for _, aPath := range paths {
  133. if aPath.installed {
  134. return &aPath, true
  135. }
  136. }
  137. return nil, false
  138. }
  139. // getGoPathBin retrieves ad returns the GOPATH/bin path for binary.
  140. func (s serviceInstall) getGoPathBin() string {
  141. if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
  142. return gfile.Join(goPath, "bin")
  143. }
  144. return ""
  145. }
  146. // getAvailablePaths returns the installation paths data for the binary.
  147. func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
  148. var (
  149. folderPaths []serviceInstallAvailablePath
  150. binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
  151. )
  152. // $GOPATH/bin
  153. if goPathBin := s.getGoPathBin(); goPathBin != "" {
  154. folderPaths = s.checkAndAppendToAvailablePath(
  155. folderPaths, goPathBin, binaryFileName,
  156. )
  157. }
  158. switch runtime.GOOS {
  159. case "darwin":
  160. darwinInstallationCheckPaths := []string{"/usr/local/bin"}
  161. for _, v := range darwinInstallationCheckPaths {
  162. folderPaths = s.checkAndAppendToAvailablePath(
  163. folderPaths, v, binaryFileName,
  164. )
  165. }
  166. fallthrough
  167. default:
  168. // Search and find the writable directory path.
  169. envPath := genv.Get("PATH", genv.Get("Path").String()).String()
  170. if gstr.Contains(envPath, ";") {
  171. // windows.
  172. for _, v := range gstr.SplitAndTrim(envPath, ";") {
  173. if v == "." {
  174. continue
  175. }
  176. folderPaths = s.checkAndAppendToAvailablePath(
  177. folderPaths, v, binaryFileName,
  178. )
  179. }
  180. } else if gstr.Contains(envPath, ":") {
  181. // *nix.
  182. for _, v := range gstr.SplitAndTrim(envPath, ":") {
  183. if v == "." {
  184. continue
  185. }
  186. folderPaths = s.checkAndAppendToAvailablePath(
  187. folderPaths, v, binaryFileName,
  188. )
  189. }
  190. } else if envPath != "" {
  191. folderPaths = s.checkAndAppendToAvailablePath(
  192. folderPaths, envPath, binaryFileName,
  193. )
  194. } else {
  195. folderPaths = s.checkAndAppendToAvailablePath(
  196. folderPaths, "/usr/local/bin", binaryFileName,
  197. )
  198. }
  199. }
  200. return folderPaths
  201. }
  202. // checkAndAppendToAvailablePath checks if `path` is writable and already installed.
  203. // It adds the `path` to `folderPaths` if it is writable or already installed, or else it ignores the `path`.
  204. func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInstallAvailablePath, dirPath string, binaryFileName string) []serviceInstallAvailablePath {
  205. var (
  206. filePath = gfile.Join(dirPath, binaryFileName)
  207. writable = gfile.IsWritable(dirPath)
  208. installed = gfile.Exists(filePath)
  209. self = gfile.SelfPath() == filePath
  210. )
  211. if !writable && !installed {
  212. return folderPaths
  213. }
  214. return append(
  215. folderPaths,
  216. serviceInstallAvailablePath{
  217. dirPath: dirPath,
  218. writable: writable,
  219. filePath: filePath,
  220. installed: installed,
  221. IsSelf: self,
  222. })
  223. }