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