xref: /aosp_15_r20/external/bazelbuild-rules_go/go/tools/releaser/github.go (revision 9bb1b549b6a84214c53be0924760be030e66b93a)
1*9bb1b549SSpandan Das// Copyright 2021 The Bazel Authors. All rights reserved.
2*9bb1b549SSpandan Das//
3*9bb1b549SSpandan Das// Licensed under the Apache License, Version 2.0 (the "License");
4*9bb1b549SSpandan Das// you may not use this file except in compliance with the License.
5*9bb1b549SSpandan Das// You may obtain a copy of the License at
6*9bb1b549SSpandan Das//
7*9bb1b549SSpandan Das//    http://www.apache.org/licenses/LICENSE-2.0
8*9bb1b549SSpandan Das//
9*9bb1b549SSpandan Das// Unless required by applicable law or agreed to in writing, software
10*9bb1b549SSpandan Das// distributed under the License is distributed on an "AS IS" BASIS,
11*9bb1b549SSpandan Das// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*9bb1b549SSpandan Das// See the License for the specific language governing permissions and
13*9bb1b549SSpandan Das// limitations under the License.
14*9bb1b549SSpandan Das
15*9bb1b549SSpandan Daspackage main
16*9bb1b549SSpandan Das
17*9bb1b549SSpandan Dasimport (
18*9bb1b549SSpandan Das	"bytes"
19*9bb1b549SSpandan Das	"context"
20*9bb1b549SSpandan Das	"errors"
21*9bb1b549SSpandan Das	"fmt"
22*9bb1b549SSpandan Das	"os"
23*9bb1b549SSpandan Das	"strings"
24*9bb1b549SSpandan Das
25*9bb1b549SSpandan Das	"github.com/google/go-github/v36/github"
26*9bb1b549SSpandan Das)
27*9bb1b549SSpandan Das
28*9bb1b549SSpandan Dastype githubClient struct {
29*9bb1b549SSpandan Das	*github.Client
30*9bb1b549SSpandan Das}
31*9bb1b549SSpandan Das
32*9bb1b549SSpandan Dasfunc (gh *githubClient) listTags(ctx context.Context, org, repo string) (_ []*github.RepositoryTag, err error) {
33*9bb1b549SSpandan Das	defer func() {
34*9bb1b549SSpandan Das		if err != nil {
35*9bb1b549SSpandan Das			err = fmt.Errorf("listing tags in github.com/%s/%s: %w", org, repo, err)
36*9bb1b549SSpandan Das		}
37*9bb1b549SSpandan Das	}()
38*9bb1b549SSpandan Das
39*9bb1b549SSpandan Das	var allTags []*github.RepositoryTag
40*9bb1b549SSpandan Das	err = gh.listPages(func(opts *github.ListOptions) (*github.Response, error) {
41*9bb1b549SSpandan Das		tags, resp, err := gh.Repositories.ListTags(ctx, org, repo, opts)
42*9bb1b549SSpandan Das		if err != nil {
43*9bb1b549SSpandan Das			return nil, err
44*9bb1b549SSpandan Das		}
45*9bb1b549SSpandan Das		allTags = append(allTags, tags...)
46*9bb1b549SSpandan Das		return resp, nil
47*9bb1b549SSpandan Das	})
48*9bb1b549SSpandan Das	if err != nil {
49*9bb1b549SSpandan Das		return nil, err
50*9bb1b549SSpandan Das	}
51*9bb1b549SSpandan Das	return allTags, nil
52*9bb1b549SSpandan Das}
53*9bb1b549SSpandan Das
54*9bb1b549SSpandan Dasfunc (gh *githubClient) listReleases(ctx context.Context, org, repo string) (_ []*github.RepositoryRelease, err error) {
55*9bb1b549SSpandan Das	defer func() {
56*9bb1b549SSpandan Das		if err != nil {
57*9bb1b549SSpandan Das			err = fmt.Errorf("listing releases in github.com/%s/%s: %w", org, repo, err)
58*9bb1b549SSpandan Das		}
59*9bb1b549SSpandan Das	}()
60*9bb1b549SSpandan Das
61*9bb1b549SSpandan Das	var allReleases []*github.RepositoryRelease
62*9bb1b549SSpandan Das	err = gh.listPages(func(opts *github.ListOptions) (*github.Response, error) {
63*9bb1b549SSpandan Das		releases, resp, err := gh.Repositories.ListReleases(ctx, org, repo, opts)
64*9bb1b549SSpandan Das		if err != nil {
65*9bb1b549SSpandan Das			return nil, err
66*9bb1b549SSpandan Das		}
67*9bb1b549SSpandan Das		allReleases = append(allReleases, releases...)
68*9bb1b549SSpandan Das		return resp, nil
69*9bb1b549SSpandan Das	})
70*9bb1b549SSpandan Das	if err != nil {
71*9bb1b549SSpandan Das		return nil, err
72*9bb1b549SSpandan Das	}
73*9bb1b549SSpandan Das	return allReleases, nil
74*9bb1b549SSpandan Das}
75*9bb1b549SSpandan Das
76*9bb1b549SSpandan Das// getReleaseByTagIncludingDraft is like
77*9bb1b549SSpandan Das// github.RepositoriesService.GetReleaseByTag, but it also considers draft
78*9bb1b549SSpandan Das// releases that aren't tagged yet.
79*9bb1b549SSpandan Dasfunc (gh *githubClient) getReleaseByTagIncludingDraft(ctx context.Context, org, repo, tag string) (*github.RepositoryRelease, error) {
80*9bb1b549SSpandan Das	releases, err := gh.listReleases(ctx, org, repo)
81*9bb1b549SSpandan Das	if err != nil {
82*9bb1b549SSpandan Das		return nil, err
83*9bb1b549SSpandan Das	}
84*9bb1b549SSpandan Das	for _, release := range releases {
85*9bb1b549SSpandan Das		if release.GetTagName() == tag {
86*9bb1b549SSpandan Das			return release, nil
87*9bb1b549SSpandan Das		}
88*9bb1b549SSpandan Das	}
89*9bb1b549SSpandan Das	return nil, errReleaseNotFound
90*9bb1b549SSpandan Das}
91*9bb1b549SSpandan Das
92*9bb1b549SSpandan Dasvar errReleaseNotFound = errors.New("release not found")
93*9bb1b549SSpandan Das
94*9bb1b549SSpandan Das// githubListPages calls fn repeatedly to get all pages of a large result.
95*9bb1b549SSpandan Das// This is useful for fetching all tags or all comments or something similar.
96*9bb1b549SSpandan Dasfunc (gh *githubClient) listPages(fn func(opt *github.ListOptions) (*github.Response, error)) error {
97*9bb1b549SSpandan Das	opt := &github.ListOptions{PerPage: 50}
98*9bb1b549SSpandan Das	for {
99*9bb1b549SSpandan Das		resp, err := fn(opt)
100*9bb1b549SSpandan Das		if err != nil {
101*9bb1b549SSpandan Das			return err
102*9bb1b549SSpandan Das		}
103*9bb1b549SSpandan Das		if resp.NextPage == 0 {
104*9bb1b549SSpandan Das			return nil
105*9bb1b549SSpandan Das		}
106*9bb1b549SSpandan Das		opt.Page = resp.NextPage
107*9bb1b549SSpandan Das	}
108*9bb1b549SSpandan Das}
109*9bb1b549SSpandan Das
110*9bb1b549SSpandan Das// githubTokenFlag is used to find a GitHub personal access token on the
111*9bb1b549SSpandan Das// command line. It accepts a raw token or a path to a file containing a token.
112*9bb1b549SSpandan Dastype githubTokenFlag string
113*9bb1b549SSpandan Das
114*9bb1b549SSpandan Dasfunc (f *githubTokenFlag) Set(v string) error {
115*9bb1b549SSpandan Das	if strings.HasPrefix(v, "ghp_") {
116*9bb1b549SSpandan Das		*(*string)(f) = v
117*9bb1b549SSpandan Das		return nil
118*9bb1b549SSpandan Das	}
119*9bb1b549SSpandan Das	data, err := os.ReadFile(v)
120*9bb1b549SSpandan Das	if err != nil {
121*9bb1b549SSpandan Das		return fmt.Errorf("reading GitHub token: %w", err)
122*9bb1b549SSpandan Das	}
123*9bb1b549SSpandan Das	*(*string)(f) = string(bytes.TrimSpace(data))
124*9bb1b549SSpandan Das	return nil
125*9bb1b549SSpandan Das}
126*9bb1b549SSpandan Das
127*9bb1b549SSpandan Dasfunc (f *githubTokenFlag) String() string {
128*9bb1b549SSpandan Das	if f == nil {
129*9bb1b549SSpandan Das		return ""
130*9bb1b549SSpandan Das	}
131*9bb1b549SSpandan Das	return string(*f)
132*9bb1b549SSpandan Das}
133