github.com/cloudwego/kitex@v0.9.0/tool/internal_pkg/util/util.go (about) 1 // Copyright 2021 CloudWeGo Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package util 16 17 import ( 18 "fmt" 19 "go/build" 20 "go/format" 21 "io/ioutil" 22 "net/http" 23 "os" 24 "os/exec" 25 "os/user" 26 "path/filepath" 27 "regexp" 28 "runtime" 29 "strings" 30 "unicode" 31 32 "github.com/cloudwego/kitex/tool/internal_pkg/log" 33 ) 34 35 // StringSlice implements the flag.Value interface on string slices 36 // to allow a flag to be set multiple times. 37 type StringSlice []string 38 39 func (ss *StringSlice) String() string { 40 return fmt.Sprintf("%v", *ss) 41 } 42 43 // Set implements the flag.Value interface. 44 func (ss *StringSlice) Set(value string) error { 45 *ss = append(*ss, value) 46 return nil 47 } 48 49 // FormatCode formats go source codes. 50 func FormatCode(code []byte) ([]byte, error) { 51 formatCode, err := format.Source(code) 52 if err != nil { 53 return code, fmt.Errorf("format code error: %s", err) 54 } 55 return formatCode, nil 56 } 57 58 // GetGOPATH retrieves the GOPATH from environment variables or the `go env` command. 59 func GetGOPATH() string { 60 goPath := os.Getenv("GOPATH") 61 // If there are many path in GOPATH, pick up the first one. 62 if GoPaths := strings.Split(goPath, ":"); len(GoPaths) >= 1 && strings.TrimSpace(GoPaths[0]) != "" { 63 return strings.TrimSpace(GoPaths[0]) 64 } 65 // GOPATH not set through environment variables, try to get one by executing "go env GOPATH" 66 output, err := exec.Command("go", "env", "GOPATH").Output() 67 if err != nil { 68 log.Warn(err) 69 os.Exit(1) 70 } 71 72 goPath = strings.TrimSpace(string(output)) 73 if len(goPath) == 0 { 74 buildContext := build.Default 75 goPath = buildContext.GOPATH 76 } 77 78 if len(goPath) == 0 { 79 panic("GOPATH not found") 80 } 81 return goPath 82 } 83 84 // Exists reports whether a file exists. 85 func Exists(path string) bool { 86 fi, err := os.Stat(path) 87 if err != nil { 88 return os.IsExist(err) 89 } 90 return !fi.IsDir() 91 } 92 93 // LowerFirst converts the first letter to upper case for the given string. 94 func LowerFirst(s string) string { 95 rs := []rune(s) 96 rs[0] = unicode.ToLower(rs[0]) 97 return string(rs) 98 } 99 100 // ReplaceString be used in string substitution. 101 func ReplaceString(s, old, new string, n int) string { 102 return strings.Replace(s, old, new, n) 103 } 104 105 // SnakeString converts the string 's' to a snake string 106 func SnakeString(s string) string { 107 data := make([]byte, 0, len(s)*2) 108 j := false 109 for _, d := range []byte(s) { 110 if d >= 'A' && d <= 'Z' { 111 if j { 112 data = append(data, '_') 113 j = false 114 } 115 } else if d != '_' { 116 j = true 117 } 118 data = append(data, d) 119 } 120 return strings.ToLower(string(data)) 121 } 122 123 // UpperFirst converts the first letter to upper case for the given string. 124 func UpperFirst(s string) string { 125 rs := []rune(s) 126 rs[0] = unicode.ToUpper(rs[0]) 127 return string(rs) 128 } 129 130 // NotPtr converts an pointer type into non-pointer type. 131 func NotPtr(s string) string { 132 return strings.ReplaceAll(s, "*", "") 133 } 134 135 // SearchGoMod searches go.mod from the given directory (which must be an absolute path) to 136 // the root directory. When the go.mod is found, its module name and path will be returned. 137 func SearchGoMod(cwd string) (moduleName, path string, found bool) { 138 for { 139 path = filepath.Join(cwd, "go.mod") 140 data, err := ioutil.ReadFile(path) 141 if err == nil { 142 re := regexp.MustCompile(`^\s*module\s+(\S+)\s*`) 143 for _, line := range strings.Split(string(data), "\n") { 144 m := re.FindStringSubmatch(line) 145 if m != nil { 146 return m[1], cwd, true 147 } 148 } 149 return fmt.Sprintf("<module name not found in '%s'>", path), path, true 150 } 151 152 if !os.IsNotExist(err) { 153 return 154 } 155 parentCwd := filepath.Dir(cwd) 156 if parentCwd == cwd { 157 break 158 } 159 cwd = parentCwd 160 } 161 return 162 } 163 164 func RunGitCommand(gitLink string) (string, string, error) { 165 u, err := user.Current() 166 if err != nil { 167 return "", "Failed to get home dir", err 168 } 169 cachePath := JoinPath(u.HomeDir, ".kitex", "cache") 170 171 branch := "" 172 if strings.Contains(gitLink, ".git@") { 173 strs := strings.Split(gitLink, ".git@") 174 branch = strs[1] 175 gitLink = strs[0] + ".git" 176 } 177 pullLink := gitLink 178 179 gitLink = strings.TrimPrefix(gitLink, "git@") 180 181 gitLink = strings.TrimSuffix(gitLink, ".git") 182 183 repoLink := "" 184 if strings.Contains(gitLink, "://") { 185 repoLink = strings.Split(gitLink, "://")[1] 186 } else { 187 repoLink = strings.ReplaceAll(gitLink, ":", "/") 188 } 189 190 branchSuffix := "" 191 if branch != "" { 192 branchSuffix = "@" + branch 193 } 194 gitPath := JoinPath(cachePath, repoLink+branchSuffix) 195 196 _, err = os.Stat(JoinPath(gitPath, ".git")) 197 if err != nil && !os.IsExist(err) { 198 err = os.MkdirAll(gitPath, os.ModePerm) 199 if err != nil { 200 return "", "Failed to create cache directory,please check your permission for ~/.kitex/cache", err 201 } 202 cmdClone := exec.Command("git", "clone", pullLink, ".") 203 cmdClone.Dir = gitPath 204 out, gitErr := cmdClone.CombinedOutput() 205 if gitErr != nil { 206 return "", string(out), gitErr 207 } 208 if branch != "" { 209 cmdCheckout := exec.Command("git", "checkout", branch) 210 cmdCheckout.Dir = gitPath 211 out, gitErr = cmdCheckout.CombinedOutput() 212 return gitPath, string(out), gitErr 213 } else { 214 return gitPath, "", nil 215 } 216 } 217 218 cmdPull := exec.Command("git", "pull") 219 cmdPull.Dir = gitPath 220 out, gitErr := cmdPull.CombinedOutput() 221 if gitErr != nil { 222 return "", string(out), gitErr 223 } 224 225 return gitPath, "", nil 226 } 227 228 // CombineOutputPath read the output and path variables and render them into the final path 229 func CombineOutputPath(outputPath, ns string) string { 230 if ns != "" { 231 ns = strings.ReplaceAll(ns, ".", "/") 232 } 233 hasVarNs := strings.Contains(outputPath, "{namespace}") 234 hasVarNsUnderscore := strings.Contains(outputPath, "{namespaceUnderscore}") 235 if hasVarNs || hasVarNsUnderscore { 236 if hasVarNs { 237 outputPath = strings.ReplaceAll(outputPath, "{namespace}", ns) 238 } else if hasVarNsUnderscore { 239 outputPath = strings.ReplaceAll(outputPath, "{namespaceUnderscore}", strings.ReplaceAll(ns, "/", "_")) 240 } 241 } else { 242 outputPath = JoinPath(outputPath, ns) 243 } 244 return outputPath 245 } 246 247 // JoinPath joins dirs as golang import format, such as xx/xx/xx 248 func JoinPath(elem ...string) string { 249 if runtime.GOOS == "windows" { 250 return strings.ReplaceAll(filepath.Join(elem...), "\\", "/") 251 } 252 return filepath.Join(elem...) 253 } 254 255 // DownloadFile Download file to local 256 func DownloadFile(remotePath, localPath string) error { 257 resp, err := http.Get(remotePath) 258 if err != nil { 259 return err 260 } 261 defer resp.Body.Close() 262 if resp.StatusCode != http.StatusOK { 263 return fmt.Errorf("failed to download file, http status: %s", resp.Status) 264 } 265 266 body, err := ioutil.ReadAll(resp.Body) 267 if err != nil { 268 return err 269 } 270 err = ioutil.WriteFile(localPath, body, 0o644) 271 if err != nil { 272 return err 273 } 274 return nil 275 } 276 277 // IDLName returns the name of the IDL file. 278 func IDLName(filename string) string { 279 return filepath.Base(filename) 280 }