github.com/abemedia/appcast@v0.4.0/source/github/github.go (about)

     1  package github
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"mime"
     9  	"net/http"
    10  	"os"
    11  	"path"
    12  
    13  	"github.com/abemedia/appcast/source"
    14  	"github.com/google/go-github/github"
    15  	"golang.org/x/oauth2"
    16  )
    17  
    18  type Config struct {
    19  	Owner string
    20  	Repo  string
    21  }
    22  
    23  type githubSource struct {
    24  	client *github.Client
    25  	owner  string
    26  	repo   string
    27  }
    28  
    29  func New(c Config) (*source.Source, error) {
    30  	var client *http.Client
    31  	if token := os.Getenv("GITHUB_TOKEN"); token != "" {
    32  		ts := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: token})
    33  		client = oauth2.NewClient(context.Background(), ts)
    34  	}
    35  
    36  	s := &githubSource{
    37  		client: github.NewClient(client),
    38  		owner:  c.Owner,
    39  		repo:   c.Repo,
    40  	}
    41  
    42  	return source.New(s), nil
    43  }
    44  
    45  func (s *githubSource) ListReleases(ctx context.Context) ([]*source.Release, error) {
    46  	releases, _, err := s.client.Repositories.ListReleases(ctx, s.owner, s.repo, nil)
    47  	if err != nil {
    48  		return nil, err
    49  	}
    50  
    51  	r := make([]*source.Release, 0, len(releases))
    52  	for _, release := range releases {
    53  		r = append(r, parseRelease(release))
    54  	}
    55  
    56  	return r, nil
    57  }
    58  
    59  func (s *githubSource) GetRelease(ctx context.Context, version string) (*source.Release, error) {
    60  	r, _, err := s.client.Repositories.GetReleaseByTag(ctx, s.owner, s.repo, version)
    61  	if err != nil {
    62  		return nil, err
    63  	}
    64  
    65  	return parseRelease(r), nil
    66  }
    67  
    68  func parseRelease(release *github.RepositoryRelease) *source.Release {
    69  	r := &source.Release{
    70  		Name:        release.GetName(),
    71  		Description: release.GetBody(),
    72  		Version:     release.GetTagName(),
    73  		Date:        release.PublishedAt.Time,
    74  		Prerelease:  release.GetPrerelease(),
    75  		Assets:      make([]*source.Asset, 0, len(release.Assets)),
    76  	}
    77  
    78  	for _, asset := range release.Assets {
    79  		r.Assets = append(r.Assets, &source.Asset{
    80  			Name: asset.GetName(),
    81  			URL:  asset.GetBrowserDownloadURL(),
    82  			Size: asset.GetSize(),
    83  		})
    84  	}
    85  
    86  	return r
    87  }
    88  
    89  func (s *githubSource) UploadAsset(ctx context.Context, version, name string, data []byte) error {
    90  	release, _, err := s.client.Repositories.GetReleaseByTag(ctx, s.owner, s.repo, version)
    91  	if err != nil {
    92  		return err
    93  	}
    94  
    95  	u := fmt.Sprintf("repos/%s/%s/releases/%d/assets?name=%s", s.owner, s.repo, release.GetID(), name)
    96  	mediaType := mime.TypeByExtension(path.Ext(name))
    97  	req, err := s.client.NewUploadRequest(u, bytes.NewReader(data), int64(len(data)), mediaType)
    98  	if err != nil {
    99  		return err
   100  	}
   101  
   102  	_, err = s.client.Do(ctx, req, nil)
   103  	return err
   104  }
   105  
   106  func (s *githubSource) DownloadAsset(ctx context.Context, version, name string) ([]byte, error) {
   107  	release, _, err := s.client.Repositories.GetReleaseByTag(ctx, s.owner, s.repo, version)
   108  	if err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	for _, asset := range release.Assets {
   113  		if asset.GetName() == name {
   114  			r, u, err := s.client.Repositories.DownloadReleaseAsset(ctx, s.owner, s.repo, asset.GetID())
   115  			if err != nil {
   116  				return nil, err
   117  			}
   118  			if r != nil {
   119  				defer r.Close()
   120  				return io.ReadAll(r)
   121  			}
   122  
   123  			req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, nil)
   124  			if err != nil {
   125  				return nil, err
   126  			}
   127  			res, err := http.DefaultClient.Do(req)
   128  			if err != nil {
   129  				return nil, err
   130  			}
   131  			defer res.Body.Close()
   132  			return io.ReadAll(res.Body)
   133  		}
   134  	}
   135  
   136  	return nil, source.ErrAssetNotFound
   137  }