...

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

Documentation: github.com/google/go-github/v45/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  	GenerateReleaseNotes   *bool   `json:"generate_release_notes,omitempty"`
    30  
    31  	// The following fields are not used in CreateRelease or EditRelease:
    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  // RepositoryReleaseNotes represents a GitHub-generated release notes.
    51  type RepositoryReleaseNotes struct {
    52  	Name string `json:"name"`
    53  	Body string `json:"body"`
    54  }
    55  
    56  // GenerateNotesOptions represents the options to generate release notes.
    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  // ReleaseAsset represents a GitHub release asset in a repository.
    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  // ListReleases lists the releases for a repository.
    85  //
    86  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#list-releases
    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  // GetRelease fetches a single release.
   108  //
   109  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-a-release
   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  // GetLatestRelease fetches the latest published release for the repository.
   116  //
   117  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-the-latest-release
   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  // GetReleaseByTag fetches a release with the specified tag.
   124  //
   125  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#get-a-release-by-tag-name
   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  // GenerateReleaseNotes generates the release notes for the given tag.
   132  //
   133  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#generate-release-notes-content-for-a-release
   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  // repositoryReleaseRequest is a subset of RepositoryRelease and
   165  // is used internally by CreateRelease and EditRelease to pass
   166  // only the known fields for these endpoints.
   167  //
   168  // See https://github.com/google/go-github/issues/992 for more
   169  // information.
   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  // CreateRelease adds a new release for a repository.
   182  //
   183  // Note that only a subset of the release fields are used.
   184  // See RepositoryRelease for more information.
   185  //
   186  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#create-a-release
   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  // EditRelease edits a repository release.
   215  //
   216  // Note that only a subset of the release fields are used.
   217  // See RepositoryRelease for more information.
   218  //
   219  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#update-a-release
   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  // DeleteRelease delete a single release from a repository.
   248  //
   249  // GitHub API docs: https://docs.github.com/en/rest/releases/releases#delete-a-release
   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  // ListReleaseAssets lists the release's assets.
   261  //
   262  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#list-release-assets
   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  // GetReleaseAsset fetches a single release asset.
   284  //
   285  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#get-a-release-asset
   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  // DownloadReleaseAsset downloads a release asset or returns a redirect URL.
   303  //
   304  // DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the
   305  // specified release asset. It is the caller's responsibility to close the ReadCloser.
   306  // If a redirect is returned, the redirect URL will be returned as a string instead
   307  // of the io.ReadCloser. Exactly one of rc and redirectURL will be zero.
   308  //
   309  // followRedirectsClient can be passed to download the asset from a redirected
   310  // location. Passing http.DefaultClient is recommended unless special circumstances
   311  // exist, but it's possible to pass any http.Client. If nil is passed the
   312  // redirectURL will be returned instead.
   313  //
   314  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#get-a-release-asset
   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 // Intentionally return no error with valid redirect URL.
   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  // EditReleaseAsset edits a repository release asset.
   375  //
   376  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#update-a-release-asset
   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  // DeleteReleaseAsset delete a single release asset from a repository.
   394  //
   395  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#delete-a-release-asset
   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  // UploadReleaseAsset creates an asset by uploading a file into a release repository.
   407  // To upload assets that cannot be represented by an os.File, call NewUploadRequest directly.
   408  //
   409  // GitHub API docs: https://docs.github.com/en/rest/releases/assets#upload-a-release-asset
   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