210 lines
4.4 KiB
Go
210 lines
4.4 KiB
Go
// Copyright 2020 The Gitea Authors. All rights reserved.
|
|
// Use of this source code is governed by a MIT-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package service
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/google/go-github/v50/github"
|
|
"golang.org/x/oauth2"
|
|
)
|
|
|
|
// GitHub defines a GitHub service
|
|
type GitHub struct {
|
|
Milestone string
|
|
GitTag string
|
|
Token string
|
|
Repo string
|
|
Issues bool
|
|
client *github.Client
|
|
}
|
|
|
|
const (
|
|
stateClosed = "closed"
|
|
perPage = 100
|
|
)
|
|
|
|
// Generate returns a GitHub changelog
|
|
func (gh *GitHub) Generate() (string, []Entry, error) {
|
|
ctx := context.Background()
|
|
var entries []Entry
|
|
var err error
|
|
|
|
tagURL := fmt.Sprintf("## [%s](https://github.com/%s/releases/tag/%s) - %s", gh.Milestone, gh.Repo, gh.GitTag, time.Now().Format("2006-01-02"))
|
|
|
|
gh.client = github.NewClient(httpClient())
|
|
|
|
repoParts := strings.SplitN(gh.Repo, "/", 2)
|
|
if len(repoParts) != 2 {
|
|
return "", nil, fmt.Errorf("repo: '%s' can not be split into repoOwner and repoName", gh.Repo)
|
|
}
|
|
|
|
if gh.Issues {
|
|
entries, err = gh.generateByIssues(ctx, repoParts[0], repoParts[1])
|
|
} else {
|
|
entries, err = gh.generateByPulls(ctx, repoParts[0], repoParts[1])
|
|
}
|
|
|
|
return tagURL, entries, err
|
|
}
|
|
|
|
func (gh *GitHub) generateByIssues(ctx context.Context, repoOwner, repoName string) ([]Entry, error) {
|
|
page := 1
|
|
issues := make([]Entry, 0, 10)
|
|
|
|
for {
|
|
result, _, err := gh.client.Issues.ListByRepo(ctx, repoOwner, repoName, &github.IssueListByRepoOptions{
|
|
ListOptions: github.ListOptions{
|
|
Page: page,
|
|
PerPage: perPage,
|
|
},
|
|
State: stateClosed,
|
|
Milestone: gh.Milestone,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
page++
|
|
|
|
for _, item := range result {
|
|
if item.IsPullRequest() {
|
|
continue
|
|
}
|
|
|
|
e := Entry{
|
|
Title: CleanTitle(item.GetTitle()),
|
|
Index: int64(item.GetNumber()),
|
|
}
|
|
|
|
labels := make([]Label, len(item.Labels))
|
|
for idx, lbl := range item.Labels {
|
|
labels[idx] = Label{
|
|
Name: lbl.GetName(),
|
|
}
|
|
}
|
|
e.Labels = labels
|
|
|
|
issues = append(issues, e)
|
|
}
|
|
|
|
if len(result) != perPage {
|
|
break
|
|
}
|
|
}
|
|
|
|
return issues, nil
|
|
}
|
|
|
|
func (gh *GitHub) generateByPulls(ctx context.Context, repoOwner, repoName string) ([]Entry, error) {
|
|
page := 1
|
|
pulls := make([]Entry, 0, 10)
|
|
|
|
for {
|
|
result, _, err := gh.client.PullRequests.List(ctx, repoOwner, repoName, &github.PullRequestListOptions{
|
|
ListOptions: github.ListOptions{
|
|
Page: page,
|
|
PerPage: perPage,
|
|
},
|
|
State: stateClosed,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
page++
|
|
|
|
for _, item := range result {
|
|
// only merged pulls
|
|
if item.Merged == nil || !*item.Merged {
|
|
continue
|
|
}
|
|
|
|
// only with right milestone
|
|
if item.GetMilestone() == nil ||
|
|
item.GetMilestone().GetTitle() != gh.Milestone {
|
|
continue
|
|
}
|
|
|
|
p := Entry{
|
|
Title: CleanTitle(item.GetTitle()),
|
|
Index: int64(item.GetNumber()),
|
|
}
|
|
|
|
labels := make([]Label, len(item.Labels))
|
|
for idx, lbl := range item.Labels {
|
|
labels[idx] = Label{
|
|
Name: lbl.GetName(),
|
|
}
|
|
}
|
|
p.Labels = labels
|
|
|
|
pulls = append(pulls, p)
|
|
}
|
|
|
|
if len(result) != perPage {
|
|
break
|
|
}
|
|
}
|
|
|
|
return pulls, nil
|
|
}
|
|
|
|
// Contributors returns a list of contributors from GitHub
|
|
func (gh *GitHub) Contributors() (ContributorList, error) {
|
|
client := github.NewClient(httpClient())
|
|
ctx := context.Background()
|
|
|
|
contributorsMap := make(map[string]bool)
|
|
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, gh.Repo, gh.Milestone)
|
|
p := 1
|
|
perPage := 100
|
|
for {
|
|
result, _, err := client.Search.Issues(ctx, query, &github.SearchOptions{
|
|
ListOptions: github.ListOptions{
|
|
Page: p,
|
|
PerPage: perPage,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
p++
|
|
|
|
for _, pr := range result.Issues {
|
|
contributorsMap[pr.GetUser().GetLogin()] = true
|
|
}
|
|
|
|
if len(result.Issues) != perPage {
|
|
break
|
|
}
|
|
}
|
|
|
|
contributors := make(ContributorList, 0, len(contributorsMap))
|
|
for contributor := range contributorsMap {
|
|
contributors = append(contributors, Contributor{
|
|
Name: contributor,
|
|
Profile: fmt.Sprintf("https://github.com/%s", contributor),
|
|
})
|
|
}
|
|
|
|
return contributors, nil
|
|
}
|
|
|
|
func httpClient() *http.Client {
|
|
cl := http.DefaultClient
|
|
if token, ok := os.LookupEnv("CHANGELOG_GITHUB_TOKEN"); ok {
|
|
ctx := context.Background()
|
|
ts := oauth2.StaticTokenSource(
|
|
&oauth2.Token{AccessToken: token},
|
|
)
|
|
cl = oauth2.NewClient(ctx, ts)
|
|
}
|
|
return cl
|
|
}
|