trpc.group/trpc-go/trpc-cmdline@v1.0.9/config/def.go (about) 1 // Tencent is pleased to support the open source community by making tRPC available. 2 // 3 // Copyright (C) 2023 THL A29 Limited, a Tencent company. 4 // All rights reserved. 5 // 6 // If you have downloaded a copy of the tRPC source code from Tencent, 7 // please note that tRPC source code is licensed under the Apache 2.0 License, 8 // A copy of the Apache 2.0 License is included in this file. 9 10 package config 11 12 import ( 13 "fmt" 14 "io" 15 "net/http" 16 "os" 17 "os/exec" 18 "path/filepath" 19 "runtime" 20 "strings" 21 22 "trpc.group/trpc-go/trpc-cmdline/util/semver" 23 ) 24 25 // IDLType is IDL type. 26 type IDLType uint 27 28 // Related constants of IDL type. 29 const ( 30 IDLTypeProtobuf IDLType = iota // IDL is protobuf 31 IDLTypeFlatBuffers // IDL is flatbuffers 32 IDLTypeInvalid 33 ) 34 35 var idlTypeDesc = map[IDLType]string{ 36 IDLTypeProtobuf: "protobuf", 37 IDLTypeFlatBuffers: "flatbuffers", 38 } 39 40 // Valid Check if the IDL type is valid. 41 func (t IDLType) Valid() bool { 42 return t < IDLTypeInvalid 43 } 44 45 // String Return the description of the IDL type. 46 func (t IDLType) String() string { 47 if !t.Valid() { 48 return fmt.Sprintf("unknown type: %d", t) 49 } 50 return idlTypeDesc[t] 51 } 52 53 // Template is language templates. 54 type Template struct { 55 Language string `yaml:"language"` // Programming language 56 LangFileExt string `yaml:"lang_file_ext"` // Source file extension for the programming language 57 // The output filename for the stub corresponds to: service name + separator + original file name 58 Separator string `yaml:"separator"` 59 // Keep the original file name as a suffix for the generated files 60 KeepOrigName bool `yaml:"keep_orig_name"` 61 // Follow the camelcase style for generated file names 62 CamelCaseName bool `yaml:"camelcase_name"` 63 AssetDir string `yaml:"asset_dir"` // Code template directory 64 RPCServerStub string `yaml:"rpc_server_stub"` // Server stub 65 RPCServerTestStub string `yaml:"rpc_server_test_stub"` // Server test stub 66 RPCClientStub []string `yaml:"rpc_client_stub"` // Client stub 67 // Whether the client stub includes all service definitions 68 RPCClientStubPerService bool `yaml:"rpc_client_stub_per_service"` 69 } 70 71 // OpSys is the system of operation (运营体系). 72 type OpSys struct { 73 Name string `yaml:"name"` 74 Imports []string `yaml:"imports"` 75 } 76 77 // Config is the global config. 78 type Config struct { 79 Domain string `yaml:"domain"` // host in importPath 80 TplFileExt string `yaml:"tpl_file_ext"` 81 IDL map[string]*Dependency `yaml:"idl"` // IDL name -> IDL tool 82 Tools map[string][]*Dependency `yaml:"tools"` // Programming language -> Dependency tools 83 Plugins map[string][]string `yaml:"plugins"` // Programming language -> Dependency plugins 84 Templates map[string]map[string]*Template `yaml:"templates"` // idltype -> Code templates for each language 85 } 86 87 // Dependency is the description of dependencies. 88 type Dependency struct { 89 Executable string `yaml:"executable"` // Executable file name. 90 VersionMin string `yaml:"version_min"` // Min version. 91 VersionCmd string `yaml:"version_cmd"` // Max version. 92 ArtifactURL string `yaml:"artifact_url"` // Artifact download URL. 93 Repository string `yaml:"repository"` // Repository URL. 94 MD5 string `yaml:"md5"` // md5 sum up. 95 Fallback string `yaml:"fallback"` // Failure prompt. 96 } 97 98 // TryInstallTo tries to install the dependency to the given path. 99 func (d *Dependency) TryInstallTo(path string) error { 100 d.ArtifactURL = strings.ReplaceAll(d.ArtifactURL, "${os}", runtime.GOOS) 101 rsp, err := http.Get(d.ArtifactURL) 102 if err != nil { 103 return fmt.Errorf("install %s from %s err: %w", d.Executable, d.ArtifactURL, err) 104 } 105 defer rsp.Body.Close() 106 if rsp.StatusCode != http.StatusOK { 107 body, err := io.ReadAll(rsp.Body) 108 return fmt.Errorf("download from %s, rsp body: %q, body read err: %w", d.ArtifactURL, body, err) 109 } 110 dst := filepath.Join(path, d.Executable) 111 bs, err := io.ReadAll(rsp.Body) 112 if err != nil { 113 return fmt.Errorf("read from %s err: %w", d.ArtifactURL, err) 114 } 115 if err := os.WriteFile(dst, bs, os.ModePerm); err != nil { 116 return fmt.Errorf("write to %s err: %w", dst, err) 117 } 118 return nil 119 } 120 121 // Installed checks installed or not. 122 func (d *Dependency) Installed() bool { 123 if _, err := exec.LookPath(d.Executable); err != nil { 124 return false 125 } 126 return true 127 } 128 129 // version returns installed version. 130 func (d *Dependency) version() (string, error) { 131 if d.VersionCmd == "" { 132 return "", nil 133 } 134 135 cmd := exec.Command("sh", "-c", d.VersionCmd) 136 if runtime.GOOS == "windows" { 137 cmd = exec.Command("cmd", "/C", d.VersionCmd) 138 } 139 140 buf, err := cmd.CombinedOutput() 141 if err != nil { 142 return "", fmt.Errorf("read version failed: %w, command: %s", err, d.VersionCmd) 143 } 144 145 s := string(buf) 146 strs := versionRE.FindStringSubmatch(s) 147 if len(strs) < 2 { 148 return "", fmt.Errorf("version %s does not match pattern %s", s, versionPattern) 149 } 150 version := strings.Join(strs[1:], ".") 151 return version, nil 152 } 153 154 // CheckVersion check if installed version meet the version requirement. 155 func (d *Dependency) CheckVersion() (passed bool, error error) { 156 // skip checking if cmd/version not specified 157 if d.VersionMin == "" || d.VersionCmd == "" { 158 return true, nil 159 } 160 161 // load version 162 v, err := d.version() 163 if err != nil { 164 return false, err 165 } 166 167 // compare version and required version 168 version := v 169 required := d.VersionMin 170 171 version = versionNumber(version) 172 required = versionNumber(required) 173 return semver.NewerThan(version, required), nil 174 } 175 176 func versionNumber(v string) string { 177 if len(v) != 0 && (v[0] == 'v' || v[0] == 'V') { 178 return v[1:] 179 } 180 return v 181 }