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