1
2
3
4
5
6 package github
7
8 import (
9 "context"
10 "errors"
11 "fmt"
12 "io"
13 "mime"
14 "net/http"
15 "os"
16 "path/filepath"
17 "strings"
18 )
19
20
21 type RepositoryRelease struct {
22 TagName *string `json:"tag_name,omitempty"`
23 TargetCommitish *string `json:"target_commitish,omitempty"`
24 Name *string `json:"name,omitempty"`
25 Body *string `json:"body,omitempty"`
26 Draft *bool `json:"draft,omitempty"`
27 Prerelease *bool `json:"prerelease,omitempty"`
28
29 MakeLatest *string `json:"make_latest,omitempty"`
30 DiscussionCategoryName *string `json:"discussion_category_name,omitempty"`
31
32
33 GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"`
34
35
36 ID *int64 `json:"id,omitempty"`
37 CreatedAt *Timestamp `json:"created_at,omitempty"`
38 PublishedAt *Timestamp `json:"published_at,omitempty"`
39 URL *string `json:"url,omitempty"`
40 HTMLURL *string `json:"html_url,omitempty"`
41 AssetsURL *string `json:"assets_url,omitempty"`
42 Assets []*ReleaseAsset `json:"assets,omitempty"`
43 UploadURL *string `json:"upload_url,omitempty"`
44 ZipballURL *string `json:"zipball_url,omitempty"`
45 TarballURL *string `json:"tarball_url,omitempty"`
46 Author *User `json:"author,omitempty"`
47 NodeID *string `json:"node_id,omitempty"`
48 }
49
50 func (r RepositoryRelease) String() string {
51 return Stringify(r)
52 }
53
54
55 type RepositoryReleaseNotes struct {
56 Name string `json:"name"`
57 Body string `json:"body"`
58 }
59
60
61 type GenerateNotesOptions struct {
62 TagName string `json:"tag_name"`
63 PreviousTagName *string `json:"previous_tag_name,omitempty"`
64 TargetCommitish *string `json:"target_commitish,omitempty"`
65 }
66
67
68 type ReleaseAsset struct {
69 ID *int64 `json:"id,omitempty"`
70 URL *string `json:"url,omitempty"`
71 Name *string `json:"name,omitempty"`
72 Label *string `json:"label,omitempty"`
73 State *string `json:"state,omitempty"`
74 ContentType *string `json:"content_type,omitempty"`
75 Size *int `json:"size,omitempty"`
76 DownloadCount *int `json:"download_count,omitempty"`
77 CreatedAt *Timestamp `json:"created_at,omitempty"`
78 UpdatedAt *Timestamp `json:"updated_at,omitempty"`
79 BrowserDownloadURL *string `json:"browser_download_url,omitempty"`
80 Uploader *User `json:"uploader,omitempty"`
81 NodeID *string `json:"node_id,omitempty"`
82 }
83
84 func (r ReleaseAsset) String() string {
85 return Stringify(r)
86 }
87
88
89
90
91 func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opts *ListOptions) ([]*RepositoryRelease, *Response, error) {
92 u := fmt.Sprintf("repos/%s/%s/releases", owner, repo)
93 u, err := addOptions(u, opts)
94 if err != nil {
95 return nil, nil, err
96 }
97
98 req, err := s.client.NewRequest("GET", u, nil)
99 if err != nil {
100 return nil, nil, err
101 }
102
103 var releases []*RepositoryRelease
104 resp, err := s.client.Do(ctx, req, &releases)
105 if err != nil {
106 return nil, resp, err
107 }
108 return releases, resp, nil
109 }
110
111
112
113
114 func (s *RepositoriesService) GetRelease(ctx context.Context, owner, repo string, id int64) (*RepositoryRelease, *Response, error) {
115 u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
116 return s.getSingleRelease(ctx, u)
117 }
118
119
120
121
122 func (s *RepositoriesService) GetLatestRelease(ctx context.Context, owner, repo string) (*RepositoryRelease, *Response, error) {
123 u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo)
124 return s.getSingleRelease(ctx, u)
125 }
126
127
128
129
130 func (s *RepositoriesService) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*RepositoryRelease, *Response, error) {
131 u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag)
132 return s.getSingleRelease(ctx, u)
133 }
134
135
136
137
138 func (s *RepositoriesService) GenerateReleaseNotes(ctx context.Context, owner, repo string, opts *GenerateNotesOptions) (*RepositoryReleaseNotes, *Response, error) {
139 u := fmt.Sprintf("repos/%s/%s/releases/generate-notes", owner, repo)
140 req, err := s.client.NewRequest("POST", u, opts)
141 if err != nil {
142 return nil, nil, err
143 }
144
145 r := new(RepositoryReleaseNotes)
146 resp, err := s.client.Do(ctx, req, r)
147 if err != nil {
148 return nil, resp, err
149 }
150
151 return r, resp, nil
152 }
153
154 func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string) (*RepositoryRelease, *Response, error) {
155 req, err := s.client.NewRequest("GET", url, nil)
156 if err != nil {
157 return nil, nil, err
158 }
159
160 release := new(RepositoryRelease)
161 resp, err := s.client.Do(ctx, req, release)
162 if err != nil {
163 return nil, resp, err
164 }
165 return release, resp, nil
166 }
167
168
169
170
171
172
173
174 type repositoryReleaseRequest struct {
175 TagName *string `json:"tag_name,omitempty"`
176 TargetCommitish *string `json:"target_commitish,omitempty"`
177 Name *string `json:"name,omitempty"`
178 Body *string `json:"body,omitempty"`
179 Draft *bool `json:"draft,omitempty"`
180 Prerelease *bool `json:"prerelease,omitempty"`
181 MakeLatest *string `json:"make_latest,omitempty"`
182 GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"`
183 DiscussionCategoryName *string `json:"discussion_category_name,omitempty"`
184 }
185
186
187
188
189
190
191
192 func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
193 u := fmt.Sprintf("repos/%s/%s/releases", owner, repo)
194
195 releaseReq := &repositoryReleaseRequest{
196 TagName: release.TagName,
197 TargetCommitish: release.TargetCommitish,
198 Name: release.Name,
199 Body: release.Body,
200 Draft: release.Draft,
201 Prerelease: release.Prerelease,
202 MakeLatest: release.MakeLatest,
203 DiscussionCategoryName: release.DiscussionCategoryName,
204 GenerateReleaseNotes: release.GenerateReleaseNotes,
205 }
206
207 req, err := s.client.NewRequest("POST", u, releaseReq)
208 if err != nil {
209 return nil, nil, err
210 }
211
212 r := new(RepositoryRelease)
213 resp, err := s.client.Do(ctx, req, r)
214 if err != nil {
215 return nil, resp, err
216 }
217 return r, resp, nil
218 }
219
220
221
222
223
224
225
226 func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
227 u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
228
229 releaseReq := &repositoryReleaseRequest{
230 TagName: release.TagName,
231 TargetCommitish: release.TargetCommitish,
232 Name: release.Name,
233 Body: release.Body,
234 Draft: release.Draft,
235 Prerelease: release.Prerelease,
236 MakeLatest: release.MakeLatest,
237 DiscussionCategoryName: release.DiscussionCategoryName,
238 }
239
240 req, err := s.client.NewRequest("PATCH", u, releaseReq)
241 if err != nil {
242 return nil, nil, err
243 }
244
245 r := new(RepositoryRelease)
246 resp, err := s.client.Do(ctx, req, r)
247 if err != nil {
248 return nil, resp, err
249 }
250 return r, resp, nil
251 }
252
253
254
255
256 func (s *RepositoriesService) DeleteRelease(ctx context.Context, owner, repo string, id int64) (*Response, error) {
257 u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
258
259 req, err := s.client.NewRequest("DELETE", u, nil)
260 if err != nil {
261 return nil, err
262 }
263 return s.client.Do(ctx, req, nil)
264 }
265
266
267
268
269 func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opts *ListOptions) ([]*ReleaseAsset, *Response, error) {
270 u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id)
271 u, err := addOptions(u, opts)
272 if err != nil {
273 return nil, nil, err
274 }
275
276 req, err := s.client.NewRequest("GET", u, nil)
277 if err != nil {
278 return nil, nil, err
279 }
280
281 var assets []*ReleaseAsset
282 resp, err := s.client.Do(ctx, req, &assets)
283 if err != nil {
284 return nil, resp, err
285 }
286 return assets, resp, nil
287 }
288
289
290
291
292 func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo string, id int64) (*ReleaseAsset, *Response, error) {
293 u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
294
295 req, err := s.client.NewRequest("GET", u, nil)
296 if err != nil {
297 return nil, nil, err
298 }
299
300 asset := new(ReleaseAsset)
301 resp, err := s.client.Do(ctx, req, asset)
302 if err != nil {
303 return nil, resp, err
304 }
305 return asset, resp, nil
306 }
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64, followRedirectsClient *http.Client) (rc io.ReadCloser, redirectURL string, err error) {
322 u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
323
324 req, err := s.client.NewRequest("GET", u, nil)
325 if err != nil {
326 return nil, "", err
327 }
328 req.Header.Set("Accept", defaultMediaType)
329
330 s.client.clientMu.Lock()
331 defer s.client.clientMu.Unlock()
332
333 var loc string
334 saveRedirect := s.client.client.CheckRedirect
335 s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
336 loc = req.URL.String()
337 return errors.New("disable redirect")
338 }
339 defer func() { s.client.client.CheckRedirect = saveRedirect }()
340
341 req = withContext(ctx, req)
342 resp, err := s.client.client.Do(req)
343 if err != nil {
344 if !strings.Contains(err.Error(), "disable redirect") {
345 return nil, "", err
346 }
347 if followRedirectsClient != nil {
348 rc, err := s.downloadReleaseAssetFromURL(ctx, followRedirectsClient, loc)
349 return rc, "", err
350 }
351 return nil, loc, nil
352 }
353
354 if err := CheckResponse(resp); err != nil {
355 resp.Body.Close()
356 return nil, "", err
357 }
358
359 return resp.Body, "", nil
360 }
361
362 func (s *RepositoriesService) downloadReleaseAssetFromURL(ctx context.Context, followRedirectsClient *http.Client, url string) (rc io.ReadCloser, err error) {
363 req, err := http.NewRequest("GET", url, nil)
364 if err != nil {
365 return nil, err
366 }
367 req = withContext(ctx, req)
368 req.Header.Set("Accept", "*/*")
369 resp, err := followRedirectsClient.Do(req)
370 if err != nil {
371 return nil, err
372 }
373 if err := CheckResponse(resp); err != nil {
374 resp.Body.Close()
375 return nil, err
376 }
377 return resp.Body, nil
378 }
379
380
381
382
383 func (s *RepositoriesService) EditReleaseAsset(ctx context.Context, owner, repo string, id int64, release *ReleaseAsset) (*ReleaseAsset, *Response, error) {
384 u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
385
386 req, err := s.client.NewRequest("PATCH", u, release)
387 if err != nil {
388 return nil, nil, err
389 }
390
391 asset := new(ReleaseAsset)
392 resp, err := s.client.Do(ctx, req, asset)
393 if err != nil {
394 return nil, resp, err
395 }
396 return asset, resp, nil
397 }
398
399
400
401
402 func (s *RepositoriesService) DeleteReleaseAsset(ctx context.Context, owner, repo string, id int64) (*Response, error) {
403 u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
404
405 req, err := s.client.NewRequest("DELETE", u, nil)
406 if err != nil {
407 return nil, err
408 }
409 return s.client.Do(ctx, req, nil)
410 }
411
412
413
414
415
416 func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opts *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) {
417 u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id)
418 u, err := addOptions(u, opts)
419 if err != nil {
420 return nil, nil, err
421 }
422
423 stat, err := file.Stat()
424 if err != nil {
425 return nil, nil, err
426 }
427 if stat.IsDir() {
428 return nil, nil, errors.New("the asset to upload can't be a directory")
429 }
430
431 mediaType := mime.TypeByExtension(filepath.Ext(file.Name()))
432 if opts.MediaType != "" {
433 mediaType = opts.MediaType
434 }
435
436 req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType)
437 if err != nil {
438 return nil, nil, err
439 }
440
441 asset := new(ReleaseAsset)
442 resp, err := s.client.Do(ctx, req, asset)
443 if err != nil {
444 return nil, resp, err
445 }
446 return asset, resp, nil
447 }
448
View as plain text