...

Source file src/github.com/google/go-github/v47/github/repos_releases.go

Documentation: github.com/google/go-github/v47/github

     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  
     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  // RepositoryRelease represents a GitHub release in a repository.
    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  	// The following fields are not used in EditRelease:
    31  	GenerateReleaseNotes *bool `json:"generate_release_notes,omitempty"`
    32  
    33  	// The following fields are not used in CreateRelease or EditRelease:
    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  // RepositoryReleaseNotes represents a GitHub-generated release notes.
    53  type RepositoryReleaseNotes struct {
    54  	Name string `json:"name"`
    55  	Body string `json:"body"`
    56  }
    57  
    58  // GenerateNotesOptions represents the options to generate release notes.
    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  // ReleaseAsset represents a GitHub release asset in a repository.
    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  // ListReleases lists the releases for a repository.
    87  //
    88  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#list-releases
    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  // GetRelease fetches a single release.
   110  //
   111  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-a-release
   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  // GetLatestRelease fetches the latest published release for the repository.
   118  //
   119  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-the-latest-release
   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  // GetReleaseByTag fetches a release with the specified tag.
   126  //
   127  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-a-release-by-tag-name
   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  // GenerateReleaseNotes generates the release notes for the given tag.
   134  //
   135  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#generate-release-notes-content-for-a-release
   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  // repositoryReleaseRequest is a subset of RepositoryRelease and
   167  // is used internally by CreateRelease and EditRelease to pass
   168  // only the known fields for these endpoints.
   169  //
   170  // See https://github.com/google/go-github/issues/992 for more
   171  // information.
   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  // CreateRelease adds a new release for a repository.
   184  //
   185  // Note that only a subset of the release fields are used.
   186  // See RepositoryRelease for more information.
   187  //
   188  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#create-a-release
   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  // EditRelease edits a repository release.
   217  //
   218  // Note that only a subset of the release fields are used.
   219  // See RepositoryRelease for more information.
   220  //
   221  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#update-a-release
   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  // DeleteRelease delete a single release from a repository.
   249  //
   250  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#delete-a-release
   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  // ListReleaseAssets lists the release's assets.
   262  //
   263  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#list-release-assets
   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  // GetReleaseAsset fetches a single release asset.
   285  //
   286  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#get-a-release-asset
   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  // DownloadReleaseAsset downloads a release asset or returns a redirect URL.
   304  //
   305  // DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the
   306  // specified release asset. It is the caller's responsibility to close the ReadCloser.
   307  // If a redirect is returned, the redirect URL will be returned as a string instead
   308  // of the io.ReadCloser. Exactly one of rc and redirectURL will be zero.
   309  //
   310  // followRedirectsClient can be passed to download the asset from a redirected
   311  // location. Passing http.DefaultClient is recommended unless special circumstances
   312  // exist, but it's possible to pass any http.Client. If nil is passed the
   313  // redirectURL will be returned instead.
   314  //
   315  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#get-a-release-asset
   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 // Intentionally return no error with valid redirect URL.
   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  // EditReleaseAsset edits a repository release asset.
   376  //
   377  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#update-a-release-asset
   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  // DeleteReleaseAsset delete a single release asset from a repository.
   395  //
   396  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#delete-a-release-asset
   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  // UploadReleaseAsset creates an asset by uploading a file into a release repository.
   408  // To upload assets that cannot be represented by an os.File, call NewUploadRequest directly.
   409  //
   410  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#upload-a-release-asset
   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