github.com/ungtb10d/cli/v2@v2.0.0-20221110210412-98537dd9d6a1/pkg/cmd/release/create/http.go (about) 1 package create 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io" 9 "net/http" 10 "net/url" 11 12 "github.com/ungtb10d/cli/v2/api" 13 "github.com/ungtb10d/cli/v2/internal/ghinstance" 14 "github.com/ungtb10d/cli/v2/internal/ghrepo" 15 "github.com/ungtb10d/cli/v2/pkg/cmd/release/shared" 16 "github.com/shurcooL/githubv4" 17 ) 18 19 type tag struct { 20 Name string `json:"name"` 21 } 22 23 type releaseNotes struct { 24 Name string `json:"name"` 25 Body string `json:"body"` 26 } 27 28 var notImplementedError = errors.New("not implemented") 29 30 func remoteTagExists(httpClient *http.Client, repo ghrepo.Interface, tagName string) (bool, error) { 31 gql := api.NewClientFromHTTP(httpClient) 32 qualifiedTagName := fmt.Sprintf("refs/tags/%s", tagName) 33 var query struct { 34 Repository struct { 35 Ref struct { 36 ID string 37 } `graphql:"ref(qualifiedName: $tagName)"` 38 } `graphql:"repository(owner: $owner, name: $name)"` 39 } 40 variables := map[string]interface{}{ 41 "owner": githubv4.String(repo.RepoOwner()), 42 "name": githubv4.String(repo.RepoName()), 43 "tagName": githubv4.String(qualifiedTagName), 44 } 45 err := gql.Query(repo.RepoHost(), "RepositoryFindRef", &query, variables) 46 return query.Repository.Ref.ID != "", err 47 } 48 49 func getTags(httpClient *http.Client, repo ghrepo.Interface, limit int) ([]tag, error) { 50 path := fmt.Sprintf("repos/%s/%s/tags?per_page=%d", repo.RepoOwner(), repo.RepoName(), limit) 51 url := ghinstance.RESTPrefix(repo.RepoHost()) + path 52 req, err := http.NewRequest("GET", url, nil) 53 if err != nil { 54 return nil, err 55 } 56 57 req.Header.Set("Content-Type", "application/json; charset=utf-8") 58 59 resp, err := httpClient.Do(req) 60 if err != nil { 61 return nil, err 62 } 63 defer resp.Body.Close() 64 65 success := resp.StatusCode >= 200 && resp.StatusCode < 300 66 if !success { 67 return nil, api.HandleHTTPError(resp) 68 } 69 70 b, err := io.ReadAll(resp.Body) 71 if err != nil { 72 return nil, err 73 } 74 75 var tags []tag 76 err = json.Unmarshal(b, &tags) 77 return tags, err 78 } 79 80 func generateReleaseNotes(httpClient *http.Client, repo ghrepo.Interface, tagName, target, previousTagName string) (*releaseNotes, error) { 81 params := map[string]interface{}{ 82 "tag_name": tagName, 83 } 84 if target != "" { 85 params["target_commitish"] = target 86 } 87 if previousTagName != "" { 88 params["previous_tag_name"] = previousTagName 89 } 90 91 bodyBytes, err := json.Marshal(params) 92 if err != nil { 93 return nil, err 94 } 95 96 path := fmt.Sprintf("repos/%s/%s/releases/generate-notes", repo.RepoOwner(), repo.RepoName()) 97 url := ghinstance.RESTPrefix(repo.RepoHost()) + path 98 req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes)) 99 if err != nil { 100 return nil, err 101 } 102 103 req.Header.Set("Accept", "application/vnd.github.v3+json") 104 req.Header.Set("Content-Type", "application/json; charset=utf-8") 105 106 resp, err := httpClient.Do(req) 107 if err != nil { 108 return nil, err 109 } 110 defer resp.Body.Close() 111 112 if resp.StatusCode == 404 { 113 return nil, notImplementedError 114 } 115 116 success := resp.StatusCode >= 200 && resp.StatusCode < 300 117 if !success { 118 return nil, api.HandleHTTPError(resp) 119 } 120 121 b, err := io.ReadAll(resp.Body) 122 if err != nil { 123 return nil, err 124 } 125 126 var rn releaseNotes 127 err = json.Unmarshal(b, &rn) 128 return &rn, err 129 } 130 131 func publishedReleaseExists(httpClient *http.Client, repo ghrepo.Interface, tagName string) (bool, error) { 132 path := fmt.Sprintf("repos/%s/%s/releases/tags/%s", repo.RepoOwner(), repo.RepoName(), url.PathEscape(tagName)) 133 url := ghinstance.RESTPrefix(repo.RepoHost()) + path 134 req, err := http.NewRequest("HEAD", url, nil) 135 if err != nil { 136 return false, err 137 } 138 139 resp, err := httpClient.Do(req) 140 if err != nil { 141 return false, err 142 } 143 if resp.Body != nil { 144 defer resp.Body.Close() 145 } 146 147 if resp.StatusCode == 200 { 148 return true, nil 149 } else if resp.StatusCode == 404 { 150 return false, nil 151 } else { 152 return false, api.HandleHTTPError(resp) 153 } 154 } 155 156 func createRelease(httpClient *http.Client, repo ghrepo.Interface, params map[string]interface{}) (*shared.Release, error) { 157 bodyBytes, err := json.Marshal(params) 158 if err != nil { 159 return nil, err 160 } 161 162 path := fmt.Sprintf("repos/%s/%s/releases", repo.RepoOwner(), repo.RepoName()) 163 url := ghinstance.RESTPrefix(repo.RepoHost()) + path 164 req, err := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes)) 165 if err != nil { 166 return nil, err 167 } 168 169 req.Header.Set("Content-Type", "application/json; charset=utf-8") 170 171 resp, err := httpClient.Do(req) 172 if err != nil { 173 return nil, err 174 } 175 defer resp.Body.Close() 176 177 success := resp.StatusCode >= 200 && resp.StatusCode < 300 178 if !success { 179 return nil, api.HandleHTTPError(resp) 180 } 181 182 b, err := io.ReadAll(resp.Body) 183 if err != nil { 184 return nil, err 185 } 186 187 var newRelease shared.Release 188 err = json.Unmarshal(b, &newRelease) 189 return &newRelease, err 190 } 191 192 func publishRelease(httpClient *http.Client, releaseURL string, discussionCategory string) (*shared.Release, error) { 193 params := map[string]interface{}{"draft": false} 194 if discussionCategory != "" { 195 params["discussion_category_name"] = discussionCategory 196 } 197 198 bodyBytes, err := json.Marshal(params) 199 if err != nil { 200 return nil, err 201 } 202 req, err := http.NewRequest("PATCH", releaseURL, bytes.NewBuffer(bodyBytes)) 203 if err != nil { 204 return nil, err 205 } 206 207 req.Header.Add("Content-Type", "application/json") 208 209 resp, err := httpClient.Do(req) 210 if err != nil { 211 return nil, err 212 } 213 214 defer resp.Body.Close() 215 if resp.StatusCode > 299 { 216 return nil, api.HandleHTTPError(resp) 217 } 218 219 b, err := io.ReadAll(resp.Body) 220 if err != nil { 221 return nil, err 222 } 223 224 var release shared.Release 225 err = json.Unmarshal(b, &release) 226 return &release, err 227 }