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