| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- // Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
- //
- // This Source Code Form is subject to the terms of the MIT License.
- // If a copy of the MIT was not distributed with this file,
- // You can obtain one at https://github.com/gogf/gf.
- package service
- import (
- "context"
- "runtime"
- "strings"
- "github.com/gogf/gf/v2/container/garray"
- "github.com/gogf/gf/v2/container/gset"
- "github.com/gogf/gf/v2/frame/g"
- "github.com/gogf/gf/v2/os/gcmd"
- "github.com/gogf/gf/v2/os/genv"
- "github.com/gogf/gf/v2/os/gfile"
- "github.com/gogf/gf/v2/text/gstr"
- "github.com/gogf/gf/v2/util/gconv"
- "github.com/gogf/gf/cmd/gf/v2/internal/utility/allyes"
- "github.com/gogf/gf/cmd/gf/v2/internal/utility/mlog"
- )
- var (
- Install = serviceInstall{}
- )
- type serviceInstall struct{}
- type serviceInstallAvailablePath struct {
- dirPath string
- filePath string
- writable bool
- installed bool
- IsSelf bool
- }
- func (s serviceInstall) Run(ctx context.Context) (err error) {
- // Ask where to install.
- paths := s.getAvailablePaths()
- if len(paths) <= 0 {
- mlog.Printf("no path detected, you can manually install gf by copying the binary to path folder.")
- return
- }
- mlog.Printf("I found some installable paths for you(from $PATH): ")
- mlog.Printf(" %2s | %8s | %9s | %s", "Id", "Writable", "Installed", "Path")
- // Print all paths status and determine the default selectedID value.
- var (
- selectedID = -1
- newPaths []serviceInstallAvailablePath
- pathSet = gset.NewStrSet() // Used for repeated items filtering.
- )
- for _, path := range paths {
- if !pathSet.AddIfNotExist(path.dirPath) {
- continue
- }
- newPaths = append(newPaths, path)
- }
- paths = newPaths
- for id, path := range paths {
- mlog.Printf(" %2d | %8t | %9t | %s", id, path.writable, path.installed, path.dirPath)
- if selectedID == -1 {
- // Use the previously installed path as the most priority choice.
- if path.installed {
- selectedID = id
- }
- }
- }
- // If there's no previously installed path, use the first writable path.
- if selectedID == -1 {
- // Order by choosing priority.
- commonPaths := garray.NewStrArrayFrom(g.SliceStr{
- s.getGoPathBin(),
- `/usr/local/bin`,
- `/usr/bin`,
- `/usr/sbin`,
- `C:\Windows`,
- `C:\Windows\system32`,
- `C:\Go\bin`,
- `C:\Program Files`,
- `C:\Program Files (x86)`,
- })
- // Check the common installation directories.
- commonPaths.Iterator(func(k int, v string) bool {
- for id, aPath := range paths {
- if strings.EqualFold(aPath.dirPath, v) {
- selectedID = id
- return false
- }
- }
- return true
- })
- if selectedID == -1 {
- selectedID = 0
- }
- }
- if allyes.Check() {
- // Use the default selectedID.
- mlog.Printf("please choose one installation destination [default %d]: %d", selectedID, selectedID)
- } else {
- for {
- // Get input and update selectedID.
- var (
- inputID int
- input = gcmd.Scanf("please choose one installation destination [default %d]: ", selectedID)
- )
- if input != "" {
- inputID = gconv.Int(input)
- } else {
- break
- }
- // Check if out of range.
- if inputID >= len(paths) || inputID < 0 {
- mlog.Printf("invalid install destination Id: %d", inputID)
- continue
- }
- selectedID = inputID
- break
- }
- }
- // Get selected destination path.
- dstPath := paths[selectedID]
- // Install the new binary.
- mlog.Debugf(`copy file from "%s" to "%s"`, gfile.SelfPath(), dstPath.filePath)
- err = gfile.CopyFile(gfile.SelfPath(), dstPath.filePath)
- if err != nil {
- mlog.Printf("install gf binary to '%s' failed: %v", dstPath.dirPath, err)
- mlog.Printf("you can manually install gf by copying the binary to folder: %s", dstPath.dirPath)
- } else {
- mlog.Printf("gf binary is successfully installed to: %s", dstPath.filePath)
- }
- return
- }
- // IsInstalled checks and returns whether the binary is installed.
- func (s serviceInstall) IsInstalled() (*serviceInstallAvailablePath, bool) {
- paths := s.getAvailablePaths()
- for _, aPath := range paths {
- if aPath.installed {
- return &aPath, true
- }
- }
- return nil, false
- }
- // getGoPathBin retrieves ad returns the GOPATH/bin path for binary.
- func (s serviceInstall) getGoPathBin() string {
- if goPath := genv.Get(`GOPATH`).String(); goPath != "" {
- return gfile.Join(goPath, "bin")
- }
- return ""
- }
- // getAvailablePaths returns the installation paths data for the binary.
- func (s serviceInstall) getAvailablePaths() []serviceInstallAvailablePath {
- var (
- folderPaths []serviceInstallAvailablePath
- binaryFileName = "gf" + gfile.Ext(gfile.SelfPath())
- )
- // $GOPATH/bin
- if goPathBin := s.getGoPathBin(); goPathBin != "" {
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, goPathBin, binaryFileName,
- )
- }
- switch runtime.GOOS {
- case "darwin":
- darwinInstallationCheckPaths := []string{"/usr/local/bin"}
- for _, v := range darwinInstallationCheckPaths {
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, v, binaryFileName,
- )
- }
- fallthrough
- default:
- // Search and find the writable directory path.
- envPath := genv.Get("PATH", genv.Get("Path").String()).String()
- if gstr.Contains(envPath, ";") {
- // windows.
- for _, v := range gstr.SplitAndTrim(envPath, ";") {
- if v == "." {
- continue
- }
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, v, binaryFileName,
- )
- }
- } else if gstr.Contains(envPath, ":") {
- // *nix.
- for _, v := range gstr.SplitAndTrim(envPath, ":") {
- if v == "." {
- continue
- }
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, v, binaryFileName,
- )
- }
- } else if envPath != "" {
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, envPath, binaryFileName,
- )
- } else {
- folderPaths = s.checkAndAppendToAvailablePath(
- folderPaths, "/usr/local/bin", binaryFileName,
- )
- }
- }
- return folderPaths
- }
- // checkAndAppendToAvailablePath checks if `path` is writable and already installed.
- // It adds the `path` to `folderPaths` if it is writable or already installed, or else it ignores the `path`.
- func (s serviceInstall) checkAndAppendToAvailablePath(folderPaths []serviceInstallAvailablePath, dirPath string, binaryFileName string) []serviceInstallAvailablePath {
- var (
- filePath = gfile.Join(dirPath, binaryFileName)
- writable = gfile.IsWritable(dirPath)
- installed = gfile.Exists(filePath)
- self = gfile.SelfPath() == filePath
- )
- if !writable && !installed {
- return folderPaths
- }
- return append(
- folderPaths,
- serviceInstallAvailablePath{
- dirPath: dirPath,
- writable: writable,
- filePath: filePath,
- installed: installed,
- IsSelf: self,
- })
- }
|