github.com/kubri/kubri@v0.5.1-0.20240317001612-bda2aaef967e/source/gitlab/gitlab.go (about) 1 package gitlab 2 3 import ( 4 "bytes" 5 "context" 6 "log" 7 "net/http" 8 "os" 9 "path" 10 "strconv" 11 12 "github.com/hashicorp/go-retryablehttp" 13 "github.com/xanzy/go-gitlab" 14 15 "github.com/kubri/kubri/source" 16 ) 17 18 type Config struct { 19 Owner string 20 Repo string 21 URL string 22 } 23 24 type gitlabSource struct { 25 client *gitlab.Client 26 repo string 27 } 28 29 func New(c Config) (*source.Source, error) { 30 var opt []gitlab.ClientOptionFunc 31 if c.URL != "" { 32 opt = append(opt, gitlab.WithBaseURL(c.URL)) 33 } 34 35 client, err := gitlab.NewClient(os.Getenv("GITLAB_TOKEN"), opt...) 36 if err != nil { 37 return nil, err 38 } 39 40 s := &gitlabSource{ 41 client: client, 42 repo: c.Owner + "/" + c.Repo, 43 } 44 45 return source.New(s), nil 46 } 47 48 func (s *gitlabSource) ListReleases(ctx context.Context) ([]*source.Release, error) { 49 releases, _, err := s.client.Releases.ListReleases(s.repo, nil, gitlab.WithContext(ctx)) 50 if err != nil { 51 return nil, err 52 } 53 54 r := make([]*source.Release, 0, len(releases)) 55 for _, release := range releases { 56 r = append(r, s.parseRelease(ctx, release)) 57 } 58 59 return r, nil 60 } 61 62 func (s *gitlabSource) GetRelease(ctx context.Context, version string) (*source.Release, error) { 63 r, _, err := s.client.Releases.GetRelease(s.repo, version, gitlab.WithContext(ctx)) 64 if err != nil { 65 return nil, err 66 } 67 68 return s.parseRelease(ctx, r), nil 69 } 70 71 func (s *gitlabSource) parseRelease(ctx context.Context, release *gitlab.Release) *source.Release { 72 r := &source.Release{ 73 Name: release.Name, 74 Description: release.Description, 75 Version: release.TagName, 76 Date: *release.CreatedAt, 77 Assets: make([]*source.Asset, 0, len(release.Assets.Links)), 78 } 79 80 for _, l := range release.Assets.Links { 81 size, err := s.getSize(ctx, l.URL) 82 if err != nil { 83 log.Printf("failed to get size for %s: %s\n", l.Name, err) 84 } 85 86 r.Assets = append(r.Assets, &source.Asset{ 87 Name: l.Name, 88 URL: l.URL, 89 Size: size, 90 }) 91 } 92 93 return r 94 } 95 96 func (s *gitlabSource) UploadAsset(ctx context.Context, version, name string, data []byte) error { 97 file, _, err := s.client.Projects.UploadFile(s.repo, bytes.NewReader(data), name, gitlab.WithContext(ctx)) 98 if err != nil { 99 return err 100 } 101 102 u := s.client.BaseURL() 103 u.Path = path.Join(s.repo, file.URL) 104 url := u.String() 105 106 opt := &gitlab.CreateReleaseLinkOptions{Name: &name, URL: &url} 107 _, _, err = s.client.ReleaseLinks.CreateReleaseLink(s.repo, version, opt, gitlab.WithContext(ctx)) 108 109 return err 110 } 111 112 func (s *gitlabSource) DownloadAsset(ctx context.Context, version, name string) ([]byte, error) { 113 links, _, err := s.client.ReleaseLinks.ListReleaseLinks(s.repo, version, nil) 114 if err != nil { 115 return nil, err 116 } 117 118 for _, link := range links { 119 if link.Name == name { 120 req, err := retryablehttp.NewRequest(http.MethodGet, link.URL, nil) 121 if err != nil { 122 return nil, err 123 } 124 125 var buf bytes.Buffer 126 _, err = s.client.Do(req.WithContext(ctx), &buf) 127 if err != nil { 128 return nil, err 129 } 130 131 return buf.Bytes(), nil 132 } 133 } 134 135 return nil, source.ErrAssetNotFound 136 } 137 138 func (s *gitlabSource) getSize(ctx context.Context, url string) (int, error) { 139 req, err := retryablehttp.NewRequest(http.MethodHead, url, nil) 140 if err != nil { 141 return 0, err 142 } 143 144 r, err := s.client.Do(req.WithContext(ctx), nil) 145 if err != nil { 146 return 0, err 147 } 148 149 return strconv.Atoi(r.Header.Get("Content-Length")) 150 }