...

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

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

View as plain text