github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/extension/http.go (about) 1 package extension 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "io" 8 "net/http" 9 "os" 10 11 "github.com/ungtb10d/cli/v2/api" 12 "github.com/ungtb10d/cli/v2/internal/ghinstance" 13 "github.com/ungtb10d/cli/v2/internal/ghrepo" 14 ) 15 16 func repoExists(httpClient *http.Client, repo ghrepo.Interface) (bool, error) { 17 url := fmt.Sprintf("%srepos/%s/%s", ghinstance.RESTPrefix(repo.RepoHost()), repo.RepoOwner(), repo.RepoName()) 18 req, err := http.NewRequest("GET", url, nil) 19 if err != nil { 20 return false, err 21 } 22 23 resp, err := httpClient.Do(req) 24 if err != nil { 25 return false, err 26 } 27 defer resp.Body.Close() 28 29 switch resp.StatusCode { 30 case 200: 31 return true, nil 32 case 404: 33 return false, nil 34 default: 35 return false, api.HandleHTTPError(resp) 36 } 37 } 38 39 func hasScript(httpClient *http.Client, repo ghrepo.Interface) (bool, error) { 40 path := fmt.Sprintf("repos/%s/%s/contents/%s", 41 repo.RepoOwner(), repo.RepoName(), repo.RepoName()) 42 url := ghinstance.RESTPrefix(repo.RepoHost()) + path 43 req, err := http.NewRequest("GET", url, nil) 44 if err != nil { 45 return false, err 46 } 47 48 resp, err := httpClient.Do(req) 49 if err != nil { 50 return false, err 51 } 52 defer resp.Body.Close() 53 54 if resp.StatusCode == 404 { 55 return false, nil 56 } 57 58 if resp.StatusCode > 299 { 59 err = api.HandleHTTPError(resp) 60 return false, err 61 } 62 63 return true, nil 64 } 65 66 type releaseAsset struct { 67 Name string 68 APIURL string `json:"url"` 69 } 70 71 type release struct { 72 Tag string `json:"tag_name"` 73 Assets []releaseAsset 74 } 75 76 // downloadAsset downloads a single asset to the given file path. 77 func downloadAsset(httpClient *http.Client, asset releaseAsset, destPath string) error { 78 req, err := http.NewRequest("GET", asset.APIURL, nil) 79 if err != nil { 80 return err 81 } 82 83 req.Header.Set("Accept", "application/octet-stream") 84 85 resp, err := httpClient.Do(req) 86 if err != nil { 87 return err 88 } 89 defer resp.Body.Close() 90 91 if resp.StatusCode > 299 { 92 return api.HandleHTTPError(resp) 93 } 94 95 f, err := os.OpenFile(destPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755) 96 if err != nil { 97 return err 98 } 99 defer f.Close() 100 101 _, err = io.Copy(f, resp.Body) 102 return err 103 } 104 105 var commitNotFoundErr = errors.New("commit not found") 106 var releaseNotFoundErr = errors.New("release not found") 107 var repositoryNotFoundErr = errors.New("repository not found") 108 109 // fetchLatestRelease finds the latest published release for a repository. 110 func fetchLatestRelease(httpClient *http.Client, baseRepo ghrepo.Interface) (*release, error) { 111 path := fmt.Sprintf("repos/%s/%s/releases/latest", baseRepo.RepoOwner(), baseRepo.RepoName()) 112 url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path 113 req, err := http.NewRequest("GET", url, nil) 114 if err != nil { 115 return nil, err 116 } 117 118 resp, err := httpClient.Do(req) 119 if err != nil { 120 return nil, err 121 } 122 defer resp.Body.Close() 123 124 if resp.StatusCode == 404 { 125 return nil, releaseNotFoundErr 126 } 127 if resp.StatusCode > 299 { 128 return nil, api.HandleHTTPError(resp) 129 } 130 131 b, err := io.ReadAll(resp.Body) 132 if err != nil { 133 return nil, err 134 } 135 136 var r release 137 err = json.Unmarshal(b, &r) 138 if err != nil { 139 return nil, err 140 } 141 142 return &r, nil 143 } 144 145 // fetchReleaseFromTag finds release by tag name for a repository 146 func fetchReleaseFromTag(httpClient *http.Client, baseRepo ghrepo.Interface, tagName string) (*release, error) { 147 fullRepoName := fmt.Sprintf("%s/%s", baseRepo.RepoOwner(), baseRepo.RepoName()) 148 path := fmt.Sprintf("repos/%s/releases/tags/%s", fullRepoName, tagName) 149 url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path 150 req, err := http.NewRequest("GET", url, nil) 151 if err != nil { 152 return nil, err 153 } 154 155 resp, err := httpClient.Do(req) 156 if err != nil { 157 return nil, err 158 } 159 160 defer resp.Body.Close() 161 if resp.StatusCode == 404 { 162 return nil, releaseNotFoundErr 163 } 164 if resp.StatusCode > 299 { 165 return nil, api.HandleHTTPError(resp) 166 } 167 168 b, err := io.ReadAll(resp.Body) 169 if err != nil { 170 return nil, err 171 } 172 173 var r release 174 err = json.Unmarshal(b, &r) 175 if err != nil { 176 return nil, err 177 } 178 179 return &r, nil 180 } 181 182 // fetchCommitSHA finds full commit SHA from a target ref in a repo 183 func fetchCommitSHA(httpClient *http.Client, baseRepo ghrepo.Interface, targetRef string) (string, error) { 184 path := fmt.Sprintf("repos/%s/%s/commits/%s", baseRepo.RepoOwner(), baseRepo.RepoName(), targetRef) 185 url := ghinstance.RESTPrefix(baseRepo.RepoHost()) + path 186 req, err := http.NewRequest("GET", url, nil) 187 if err != nil { 188 return "", err 189 } 190 191 req.Header.Set("Accept", "application/vnd.github.v3.sha") 192 resp, err := httpClient.Do(req) 193 if err != nil { 194 return "", err 195 } 196 197 defer resp.Body.Close() 198 if resp.StatusCode == 422 { 199 return "", commitNotFoundErr 200 } 201 if resp.StatusCode > 299 { 202 return "", api.HandleHTTPError(resp) 203 } 204 205 body, err := io.ReadAll(resp.Body) 206 if err != nil { 207 return "", err 208 } 209 210 return string(body), nil 211 }