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