本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
12KB

  1. // Copyright 2013 The go-github AUTHORS. All rights reserved.
  2. //
  3. // Use of this source code is governed by a BSD-style
  4. // license that can be found in the LICENSE file.
  5. package github
  6. import (
  7. "context"
  8. "errors"
  9. "fmt"
  10. "io"
  11. "mime"
  12. "net/http"
  13. "os"
  14. "path/filepath"
  15. "strings"
  16. )
  17. // RepositoryRelease represents a GitHub release in a repository.
  18. type RepositoryRelease struct {
  19. TagName *string `json:"tag_name,omitempty"`
  20. TargetCommitish *string `json:"target_commitish,omitempty"`
  21. Name *string `json:"name,omitempty"`
  22. Body *string `json:"body,omitempty"`
  23. Draft *bool `json:"draft,omitempty"`
  24. Prerelease *bool `json:"prerelease,omitempty"`
  25. // The following fields are not used in CreateRelease or EditRelease:
  26. ID *int64 `json:"id,omitempty"`
  27. CreatedAt *Timestamp `json:"created_at,omitempty"`
  28. PublishedAt *Timestamp `json:"published_at,omitempty"`
  29. URL *string `json:"url,omitempty"`
  30. HTMLURL *string `json:"html_url,omitempty"`
  31. AssetsURL *string `json:"assets_url,omitempty"`
  32. Assets []ReleaseAsset `json:"assets,omitempty"`
  33. UploadURL *string `json:"upload_url,omitempty"`
  34. ZipballURL *string `json:"zipball_url,omitempty"`
  35. TarballURL *string `json:"tarball_url,omitempty"`
  36. Author *User `json:"author,omitempty"`
  37. NodeID *string `json:"node_id,omitempty"`
  38. }
  39. func (r RepositoryRelease) String() string {
  40. return Stringify(r)
  41. }
  42. // ReleaseAsset represents a GitHub release asset in a repository.
  43. type ReleaseAsset struct {
  44. ID *int64 `json:"id,omitempty"`
  45. URL *string `json:"url,omitempty"`
  46. Name *string `json:"name,omitempty"`
  47. Label *string `json:"label,omitempty"`
  48. State *string `json:"state,omitempty"`
  49. ContentType *string `json:"content_type,omitempty"`
  50. Size *int `json:"size,omitempty"`
  51. DownloadCount *int `json:"download_count,omitempty"`
  52. CreatedAt *Timestamp `json:"created_at,omitempty"`
  53. UpdatedAt *Timestamp `json:"updated_at,omitempty"`
  54. BrowserDownloadURL *string `json:"browser_download_url,omitempty"`
  55. Uploader *User `json:"uploader,omitempty"`
  56. NodeID *string `json:"node_id,omitempty"`
  57. }
  58. func (r ReleaseAsset) String() string {
  59. return Stringify(r)
  60. }
  61. // ListReleases lists the releases for a repository.
  62. //
  63. // GitHub API docs: https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository
  64. func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryRelease, *Response, error) {
  65. u := fmt.Sprintf("repos/%s/%s/releases", owner, repo)
  66. u, err := addOptions(u, opt)
  67. if err != nil {
  68. return nil, nil, err
  69. }
  70. req, err := s.client.NewRequest("GET", u, nil)
  71. if err != nil {
  72. return nil, nil, err
  73. }
  74. var releases []*RepositoryRelease
  75. resp, err := s.client.Do(ctx, req, &releases)
  76. if err != nil {
  77. return nil, resp, err
  78. }
  79. return releases, resp, nil
  80. }
  81. // GetRelease fetches a single release.
  82. //
  83. // GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release
  84. func (s *RepositoriesService) GetRelease(ctx context.Context, owner, repo string, id int64) (*RepositoryRelease, *Response, error) {
  85. u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
  86. return s.getSingleRelease(ctx, u)
  87. }
  88. // GetLatestRelease fetches the latest published release for the repository.
  89. //
  90. // GitHub API docs: https://developer.github.com/v3/repos/releases/#get-the-latest-release
  91. func (s *RepositoriesService) GetLatestRelease(ctx context.Context, owner, repo string) (*RepositoryRelease, *Response, error) {
  92. u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo)
  93. return s.getSingleRelease(ctx, u)
  94. }
  95. // GetReleaseByTag fetches a release with the specified tag.
  96. //
  97. // GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name
  98. func (s *RepositoriesService) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*RepositoryRelease, *Response, error) {
  99. u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag)
  100. return s.getSingleRelease(ctx, u)
  101. }
  102. func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string) (*RepositoryRelease, *Response, error) {
  103. req, err := s.client.NewRequest("GET", url, nil)
  104. if err != nil {
  105. return nil, nil, err
  106. }
  107. release := new(RepositoryRelease)
  108. resp, err := s.client.Do(ctx, req, release)
  109. if err != nil {
  110. return nil, resp, err
  111. }
  112. return release, resp, nil
  113. }
  114. // repositoryReleaseRequest is a subset of RepositoryRelease and
  115. // is used internally by CreateRelease and EditRelease to pass
  116. // only the known fields for these endpoints.
  117. //
  118. // See https://github.com/google/go-github/issues/992 for more
  119. // information.
  120. type repositoryReleaseRequest struct {
  121. TagName *string `json:"tag_name,omitempty"`
  122. TargetCommitish *string `json:"target_commitish,omitempty"`
  123. Name *string `json:"name,omitempty"`
  124. Body *string `json:"body,omitempty"`
  125. Draft *bool `json:"draft,omitempty"`
  126. Prerelease *bool `json:"prerelease,omitempty"`
  127. }
  128. // CreateRelease adds a new release for a repository.
  129. //
  130. // Note that only a subset of the release fields are used.
  131. // See RepositoryRelease for more information.
  132. //
  133. // GitHub API docs: https://developer.github.com/v3/repos/releases/#create-a-release
  134. func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
  135. u := fmt.Sprintf("repos/%s/%s/releases", owner, repo)
  136. releaseReq := &repositoryReleaseRequest{
  137. TagName: release.TagName,
  138. TargetCommitish: release.TargetCommitish,
  139. Name: release.Name,
  140. Body: release.Body,
  141. Draft: release.Draft,
  142. Prerelease: release.Prerelease,
  143. }
  144. req, err := s.client.NewRequest("POST", u, releaseReq)
  145. if err != nil {
  146. return nil, nil, err
  147. }
  148. r := new(RepositoryRelease)
  149. resp, err := s.client.Do(ctx, req, r)
  150. if err != nil {
  151. return nil, resp, err
  152. }
  153. return r, resp, nil
  154. }
  155. // EditRelease edits a repository release.
  156. //
  157. // Note that only a subset of the release fields are used.
  158. // See RepositoryRelease for more information.
  159. //
  160. // GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release
  161. func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) {
  162. u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
  163. releaseReq := &repositoryReleaseRequest{
  164. TagName: release.TagName,
  165. TargetCommitish: release.TargetCommitish,
  166. Name: release.Name,
  167. Body: release.Body,
  168. Draft: release.Draft,
  169. Prerelease: release.Prerelease,
  170. }
  171. req, err := s.client.NewRequest("PATCH", u, releaseReq)
  172. if err != nil {
  173. return nil, nil, err
  174. }
  175. r := new(RepositoryRelease)
  176. resp, err := s.client.Do(ctx, req, r)
  177. if err != nil {
  178. return nil, resp, err
  179. }
  180. return r, resp, nil
  181. }
  182. // DeleteRelease delete a single release from a repository.
  183. //
  184. // GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release
  185. func (s *RepositoriesService) DeleteRelease(ctx context.Context, owner, repo string, id int64) (*Response, error) {
  186. u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id)
  187. req, err := s.client.NewRequest("DELETE", u, nil)
  188. if err != nil {
  189. return nil, err
  190. }
  191. return s.client.Do(ctx, req, nil)
  192. }
  193. // ListReleaseAssets lists the release's assets.
  194. //
  195. // GitHub API docs: https://developer.github.com/v3/repos/releases/#list-assets-for-a-release
  196. func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*ReleaseAsset, *Response, error) {
  197. u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id)
  198. u, err := addOptions(u, opt)
  199. if err != nil {
  200. return nil, nil, err
  201. }
  202. req, err := s.client.NewRequest("GET", u, nil)
  203. if err != nil {
  204. return nil, nil, err
  205. }
  206. var assets []*ReleaseAsset
  207. resp, err := s.client.Do(ctx, req, &assets)
  208. if err != nil {
  209. return nil, resp, err
  210. }
  211. return assets, resp, nil
  212. }
  213. // GetReleaseAsset fetches a single release asset.
  214. //
  215. // GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
  216. func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo string, id int64) (*ReleaseAsset, *Response, error) {
  217. u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
  218. req, err := s.client.NewRequest("GET", u, nil)
  219. if err != nil {
  220. return nil, nil, err
  221. }
  222. asset := new(ReleaseAsset)
  223. resp, err := s.client.Do(ctx, req, asset)
  224. if err != nil {
  225. return nil, resp, err
  226. }
  227. return asset, resp, nil
  228. }
  229. // DownloadReleaseAsset downloads a release asset or returns a redirect URL.
  230. //
  231. // DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the
  232. // specified release asset. It is the caller's responsibility to close the ReadCloser.
  233. // If a redirect is returned, the redirect URL will be returned as a string instead
  234. // of the io.ReadCloser. Exactly one of rc and redirectURL will be zero.
  235. //
  236. // GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset
  237. func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64) (rc io.ReadCloser, redirectURL string, err error) {
  238. u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
  239. req, err := s.client.NewRequest("GET", u, nil)
  240. if err != nil {
  241. return nil, "", err
  242. }
  243. req.Header.Set("Accept", defaultMediaType)
  244. s.client.clientMu.Lock()
  245. defer s.client.clientMu.Unlock()
  246. var loc string
  247. saveRedirect := s.client.client.CheckRedirect
  248. s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
  249. loc = req.URL.String()
  250. return errors.New("disable redirect")
  251. }
  252. defer func() { s.client.client.CheckRedirect = saveRedirect }()
  253. req = withContext(ctx, req)
  254. resp, err := s.client.client.Do(req)
  255. if err != nil {
  256. if !strings.Contains(err.Error(), "disable redirect") {
  257. return nil, "", err
  258. }
  259. return nil, loc, nil // Intentionally return no error with valid redirect URL.
  260. }
  261. if err := CheckResponse(resp); err != nil {
  262. resp.Body.Close()
  263. return nil, "", err
  264. }
  265. return resp.Body, "", nil
  266. }
  267. // EditReleaseAsset edits a repository release asset.
  268. //
  269. // GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release-asset
  270. func (s *RepositoriesService) EditReleaseAsset(ctx context.Context, owner, repo string, id int64, release *ReleaseAsset) (*ReleaseAsset, *Response, error) {
  271. u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
  272. req, err := s.client.NewRequest("PATCH", u, release)
  273. if err != nil {
  274. return nil, nil, err
  275. }
  276. asset := new(ReleaseAsset)
  277. resp, err := s.client.Do(ctx, req, asset)
  278. if err != nil {
  279. return nil, resp, err
  280. }
  281. return asset, resp, nil
  282. }
  283. // DeleteReleaseAsset delete a single release asset from a repository.
  284. //
  285. // GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release-asset
  286. func (s *RepositoriesService) DeleteReleaseAsset(ctx context.Context, owner, repo string, id int64) (*Response, error) {
  287. u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id)
  288. req, err := s.client.NewRequest("DELETE", u, nil)
  289. if err != nil {
  290. return nil, err
  291. }
  292. return s.client.Do(ctx, req, nil)
  293. }
  294. // UploadReleaseAsset creates an asset by uploading a file into a release repository.
  295. // To upload assets that cannot be represented by an os.File, call NewUploadRequest directly.
  296. //
  297. // GitHub API docs: https://developer.github.com/v3/repos/releases/#upload-a-release-asset
  298. func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) {
  299. u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id)
  300. u, err := addOptions(u, opt)
  301. if err != nil {
  302. return nil, nil, err
  303. }
  304. stat, err := file.Stat()
  305. if err != nil {
  306. return nil, nil, err
  307. }
  308. if stat.IsDir() {
  309. return nil, nil, errors.New("the asset to upload can't be a directory")
  310. }
  311. mediaType := mime.TypeByExtension(filepath.Ext(file.Name()))
  312. if opt.MediaType != "" {
  313. mediaType = opt.MediaType
  314. }
  315. req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType)
  316. if err != nil {
  317. return nil, nil, err
  318. }
  319. asset := new(ReleaseAsset)
  320. resp, err := s.client.Do(ctx, req, asset)
  321. if err != nil {
  322. return nil, resp, err
  323. }
  324. return asset, resp, nil
  325. }
上海开阖软件有限公司 沪ICP备12045867号-1