github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/source/source.go (about)

     1  package source
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"sort"
     7  	"time"
     8  
     9  	"golang.org/x/mod/semver"
    10  
    11  	"github.com/kubri/kubri/pkg/slices"
    12  	"github.com/kubri/kubri/pkg/version"
    13  )
    14  
    15  type Release struct {
    16  	Name        string
    17  	Description string
    18  	Date        time.Time
    19  	Version     string
    20  	Prerelease  bool
    21  	Assets      []*Asset
    22  }
    23  
    24  type Asset struct {
    25  	Name string
    26  	URL  string
    27  	Size int
    28  }
    29  
    30  type Driver interface {
    31  	GetRelease(ctx context.Context, version string) (*Release, error)
    32  	ListReleases(ctx context.Context) ([]*Release, error)
    33  	DownloadAsset(ctx context.Context, version, name string) ([]byte, error)
    34  	UploadAsset(ctx context.Context, version, name string, data []byte) error
    35  }
    36  
    37  type Source struct {
    38  	s Driver
    39  }
    40  
    41  func New(driver Driver) *Source {
    42  	return &Source{s: driver}
    43  }
    44  
    45  type ListOptions struct {
    46  	// Version constraint e.g. 'v1.2.4', 'v1', '>= v1.1.0, < v2.1'
    47  	Version string
    48  
    49  	// Include pre-releases
    50  	Prerelease bool
    51  }
    52  
    53  func (s *Source) ListReleases(ctx context.Context, opt *ListOptions) ([]*Release, error) {
    54  	if s == nil || s.s == nil {
    55  		return nil, ErrMissingSource
    56  	}
    57  
    58  	var constraint version.Constraint
    59  	if opt != nil && opt.Version != "" && opt.Version != "latest" {
    60  		c, err := version.NewConstraint(opt.Version)
    61  		if err != nil {
    62  			return nil, err
    63  		}
    64  		constraint = c
    65  	}
    66  
    67  	releases, err := s.s.ListReleases(ctx)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	releases = slices.Filter(releases, func(r *Release) bool {
    73  		if !semver.IsValid(r.Version) {
    74  			log.Println("Skipping invalid version:", r.Version)
    75  			return false
    76  		}
    77  
    78  		if !constraint.Check(r.Version) {
    79  			log.Println("Skipping excluded version:", r.Version)
    80  			return false
    81  		}
    82  
    83  		if (opt == nil || !opt.Prerelease) && semver.Prerelease(r.Version) != "" {
    84  			log.Println("Skipping prerelease:", r.Version)
    85  			return false
    86  		}
    87  
    88  		processRelease(r)
    89  		return true
    90  	})
    91  
    92  	if len(releases) == 0 {
    93  		return nil, ErrNoReleaseFound
    94  	}
    95  
    96  	sort.Sort(ByVersion(releases))
    97  
    98  	if opt != nil && opt.Version == "latest" {
    99  		return releases[:1], nil
   100  	}
   101  
   102  	return releases, nil
   103  }
   104  
   105  func (s *Source) GetRelease(ctx context.Context, version string) (*Release, error) {
   106  	if s == nil || s.s == nil {
   107  		return nil, ErrMissingSource
   108  	}
   109  
   110  	r, err := s.s.GetRelease(ctx, version)
   111  	if err != nil {
   112  		return nil, err
   113  	}
   114  
   115  	processRelease(r)
   116  
   117  	return r, nil
   118  }
   119  
   120  func (s *Source) DownloadAsset(ctx context.Context, version, name string) ([]byte, error) {
   121  	if s == nil || s.s == nil {
   122  		return nil, ErrMissingSource
   123  	}
   124  
   125  	return s.s.DownloadAsset(ctx, version, name)
   126  }
   127  
   128  func (s *Source) UploadAsset(ctx context.Context, version, name string, data []byte) error {
   129  	if s == nil || s.s == nil {
   130  		return ErrMissingSource
   131  	}
   132  
   133  	return s.s.UploadAsset(ctx, version, name, data)
   134  }
   135  
   136  func processRelease(r *Release) {
   137  	if r.Name == "" {
   138  		r.Name = r.Version
   139  	}
   140  	if !r.Prerelease {
   141  		r.Prerelease = semver.Prerelease(r.Version) != ""
   142  	}
   143  }