github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/sources/versions.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package sources 19 20 import ( 21 "bytes" 22 "encoding/json" 23 "fmt" 24 "github.com/aacfactory/errors" 25 "io" 26 "net/http" 27 "net/url" 28 "os" 29 "strconv" 30 "strings" 31 "time" 32 ) 33 34 const ( 35 deps = "https://deps.dev/_/s/go/p" 36 ) 37 38 func LatestVersion(path string) (v string, err error) { 39 path = strings.TrimSpace(path) 40 if path == "" { 41 err = errors.Warning("sources: get version from deps.dev failed").WithCause(errors.Warning("sources: path is required")) 42 return 43 } 44 path = strings.ReplaceAll(url.PathEscape(path), "/", "%2F") 45 http.DefaultClient.Timeout = 2 * time.Second 46 resp, getErr := http.Get(fmt.Sprintf("%s/%s", deps, path)) 47 if getErr != nil { 48 if errors.Wrap(getErr).Contains(http.ErrHandlerTimeout) { 49 v, err = LatestVersionFromProxy(path) 50 return 51 } 52 err = errors.Warning("sources: get version from deps.dev failed"). 53 WithCause(getErr). 54 WithMeta("path", path) 55 return 56 } 57 if resp.StatusCode != http.StatusOK { 58 if resp.StatusCode == http.StatusRequestTimeout || resp.StatusCode == http.StatusGatewayTimeout { 59 v, err = LatestVersionFromProxy(path) 60 return 61 } 62 err = errors.Warning("sources: get version from deps.dev failed"). 63 WithCause(errors.Warning("status code is not ok").WithMeta("status", strconv.Itoa(resp.StatusCode))). 64 WithMeta("path", path) 65 return 66 } 67 body, readErr := io.ReadAll(resp.Body) 68 if readErr != nil { 69 err = errors.Warning("sources: get version from deps.dev failed").WithCause(readErr).WithMeta("path", path) 70 return 71 } 72 _ = resp.Body.Close() 73 result := DepResult{} 74 decodeErr := json.Unmarshal(body, &result) 75 if decodeErr != nil { 76 err = errors.Warning("sources: get version from deps.dev failed").WithCause(decodeErr).WithMeta("path", path) 77 return 78 } 79 v = result.Version.Version 80 if v == "" { 81 err = errors.Warning("sources: get version from deps.dev failed").WithCause(errors.Warning("sources: version was not found")).WithMeta("path", path) 82 return 83 } 84 return 85 } 86 87 type DepVersion struct { 88 Version string `json:"version"` 89 } 90 91 type DepResult struct { 92 Version DepVersion `json:"version"` 93 } 94 95 func LatestVersionFromProxy(path string) (v string, err error) { 96 goproxy, hasProxy := os.LookupEnv("GOPROXY") 97 if !hasProxy || goproxy == "" { 98 err = errors.Warning("sources: get version from goproxy failed").WithCause(errors.Warning("goproxy was not set")).WithMeta("path", path) 99 return 100 } 101 proxys := strings.Split(goproxy, ",") 102 proxy := "" 103 for _, p := range proxys { 104 p = strings.TrimSpace(p) 105 if strings.HasPrefix(p, "http://") || strings.HasPrefix(p, "https://") { 106 proxy = p 107 break 108 } 109 } 110 if proxy == "" { 111 err = errors.Warning("sources: get version from goproxy failed").WithCause(errors.Warning("goproxy is invalid")).WithMeta("path", path) 112 return 113 } 114 http.DefaultClient.Timeout = 2 * time.Second 115 resp, getErr := http.Get(fmt.Sprintf("%s/%s/@v/list", proxy, path)) 116 if getErr != nil { 117 err = errors.Warning("sources: get version from goproxy failed").WithCause(getErr).WithMeta("path", path) 118 return 119 } 120 body, readErr := io.ReadAll(resp.Body) 121 if readErr != nil { 122 err = errors.Warning("sources: get version from goproxy failed").WithCause(readErr).WithMeta("path", path) 123 return 124 } 125 _ = resp.Body.Close() 126 if len(body) == 0 { 127 err = errors.Warning("sources: get version from goproxy failed").WithCause(errors.Warning("sources: version was not found")).WithMeta("path", path) 128 return 129 } 130 idx := bytes.LastIndexByte(body, '\n') 131 if idx < 0 { 132 v = string(body) 133 return 134 } 135 body = body[0:idx] 136 idx = bytes.LastIndexByte(body, '\n') 137 if idx < 0 { 138 v = string(body) 139 return 140 } 141 v = string(body[idx+1:]) 142 if v == "" { 143 err = errors.Warning("sources: get version from goproxy failed").WithCause(errors.Warning("sources: version was not found")).WithMeta("path", path) 144 return 145 } 146 return 147 }