Changelog Overhaul 2 #19
11
cmd/cmd.go
11
cmd/cmd.go
|
@ -1,11 +0,0 @@
|
|||
// 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 cmd
|
||||
|
||||
var (
|
||||
// CLI Flags
|
||||
Milestone string
|
||||
ConfigPath string
|
||||
)
|
|
@ -21,7 +21,7 @@ var Contributors = &cli.Command{
|
|||
}
|
||||
|
||||
func runContributors(cmd *cli.Context) error {
|
||||
if err := config.Load(ConfigPath); err != nil {
|
||||
if err := config.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ func runContributors(cmd *cli.Context) error {
|
|||
return err
|
||||
}
|
||||
|
||||
contributors, err := s.Contributors(Milestone)
|
||||
contributors, err := s.Contributors()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -21,28 +21,65 @@ var Generate = &cli.Command{
|
|||
}
|
||||
|
||||
func runGenerate(cmd *cli.Context) error {
|
||||
if err := config.Load(ConfigPath); err != nil {
|
||||
if err := config.Load(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
labels := make(map[string]string)
|
||||
entries := make(map[string][]service.PullRequest)
|
||||
var defaultGroup string
|
||||
for _, g := range config.Cfg.Groups {
|
||||
entries[g.Name] = []service.PullRequest{}
|
||||
for _, l := range g.Labels {
|
||||
labels[l] = g.Name
|
||||
}
|
||||
if g.Default {
|
||||
defaultGroup = g.Name
|
||||
}
|
||||
}
|
||||
|
||||
if defaultGroup == "" {
|
||||
defaultGroup = config.Cfg.Groups[len(config.Cfg.Groups)-1].Name
|
||||
}
|
||||
|
||||
s, err := service.Load()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
changelog, err := s.Changelog(Milestone)
|
||||
title, prs, err := s.Changelog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf(changelog.TagURL)
|
||||
PRLoop: // labels in Go, let's get old school
|
||||
for _, pr := range prs {
|
||||
var label string
|
||||
for _, lb := range pr.Labels {
|
||||
if config.Cfg.SkipRegex != nil && config.Cfg.SkipRegex.MatchString(lb.Name) {
|
||||
continue PRLoop
|
||||
}
|
||||
|
||||
if g, ok := labels[lb.Name]; ok && len(label) == 0 {
|
||||
label = g
|
||||
}
|
||||
}
|
||||
|
||||
if len(label) > 0 {
|
||||
entries[label] = append(entries[label], pr)
|
||||
} else {
|
||||
entries[defaultGroup] = append(entries[defaultGroup], pr)
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf(title)
|
||||
for _, g := range config.Cfg.Groups {
|
||||
if len(changelog.Entries[g.Name]) == 0 {
|
||||
if len(entries[g.Name]) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Println("* " + g.Name)
|
||||
for _, entry := range changelog.Entries[g.Name] {
|
||||
for _, entry := range entries[g.Name] {
|
||||
fmt.Printf(" * %s (#%d)\n", entry.Title, entry.Index)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import (
|
|||
|
||||
var (
|
||||
defaultConfig []byte
|
||||
ConfigPath string
|
||||
Cfg *Config
|
||||
)
|
||||
|
||||
|
@ -34,13 +35,13 @@ type Config struct {
|
|||
}
|
||||
|
||||
// Load a config from a path, defaulting to changelog.example.yml
|
||||
func Load(configPath string) error {
|
||||
func Load() error {
|
||||
var err error
|
||||
var configContent []byte
|
||||
if len(configPath) == 0 {
|
||||
if len(ConfigPath) == 0 {
|
||||
configContent = defaultConfig
|
||||
} else {
|
||||
configContent, err = ioutil.ReadFile(configPath)
|
||||
configContent, err = ioutil.ReadFile(ConfigPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
12
main.go
12
main.go
|
@ -9,6 +9,8 @@ package main
|
|||
|
||||
import (
|
||||
"code.gitea.io/changelog/cmd"
|
||||
"code.gitea.io/changelog/config"
|
||||
"code.gitea.io/changelog/service"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
|
@ -31,13 +33,19 @@ func main() {
|
|||
Aliases: []string{"m"},
|
||||
Usage: "Targeted milestone",
|
||||
Required: true,
|
||||
Destination: &cmd.Milestone,
|
||||
Destination: &service.Milestone,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "config",
|
||||
Aliases: []string{"c"},
|
||||
Usage: "Specify a config file",
|
||||
Destination: &cmd.ConfigPath,
|
||||
Destination: &config.ConfigPath,
|
||||
},
|
||||
&cli.StringFlag{
|
||||
Name: "token",
|
||||
Aliases: []string{"t"},
|
||||
Usage: "Access token for private repositories/instances",
|
||||
Destination: &service.Token,
|
||||
},
|
||||
},
|
||||
Commands: []*cli.Command{
|
||||
|
|
140
service/gitea.go
140
service/gitea.go
|
@ -22,11 +22,8 @@ import (
|
|||
type Gitea struct{}
|
||||
|
||||
// Changelog returns a Gitea changelog
|
||||
func (ge Gitea) Changelog(milestone string) (Changelog, error) {
|
||||
changelog := Changelog{
|
||||
// FIXME Update tag location if Gitea ever has a page specifically for tags like GitHub does
|
||||
TagURL: fmt.Sprintf("## [%s](%s/%s/src/tag/v%s) - %s\n", milestone, config.Cfg.BaseURL, config.Cfg.Repo, milestone, time.Now().Format("2006-01-02")),
|
||||
}
|
||||
func (ge Gitea) Changelog() (string, []PullRequest, error) {
|
||||
tagURL := fmt.Sprintf("## [%s](%s/%s/src/tag/v%s) - %s\n", Milestone, config.Cfg.BaseURL, config.Cfg.Repo, Milestone, time.Now().Format("2006-01-02"))
|
||||
|
||||
client := gitea.NewAPIClient(&gitea.Configuration{
|
||||
BasePath: config.Cfg.BaseURL + "/api/v1",
|
||||
|
@ -34,80 +31,53 @@ func (ge Gitea) Changelog(milestone string) (Changelog, error) {
|
|||
UserAgent: "Gitea-Changelog/1.0.0/go",
|
||||
})
|
||||
|
||||
labels := make(map[string]string)
|
||||
entries := make(map[string][]Entry)
|
||||
var defaultGroup string
|
||||
for _, g := range config.Cfg.Groups {
|
||||
entries[g.Name] = []Entry{}
|
||||
for _, l := range g.Labels {
|
||||
labels[l] = g.Name
|
||||
}
|
||||
if g.Default {
|
||||
defaultGroup = g.Name
|
||||
}
|
||||
var ctx context.Context
|
||||
if len(Token) > 0 {
|
||||
ctx = context.WithValue(context.Background(), gitea.ContextAccessToken, Token)
|
||||
} else {
|
||||
ctx = context.Background()
|
||||
|
||||
}
|
||||
|
||||
if defaultGroup == "" {
|
||||
defaultGroup = config.Cfg.Groups[len(config.Cfg.Groups)-1].Name
|
||||
}
|
||||
prs := make([]PullRequest, 0)
|
||||
|
||||
repoOwner := strings.Split(config.Cfg.Repo, "/")
|
||||
repo := repoOwner[0]
|
||||
owner := repoOwner[1]
|
||||
owner := repoOwner[0]
|
||||
repo := repoOwner[1]
|
||||
|
||||
milestones, _, err := client.IssueApi.IssueGetMilestonesList(context.Background(), repo, owner, nil)
|
||||
milestoneID, err := getMilestoneID(client, ctx, owner, repo)
|
||||
if err != nil {
|
||||
return changelog, err
|
||||
}
|
||||
|
||||
var milestoneID int64
|
||||
for _, ms := range milestones {
|
||||
if ms.Title == milestone {
|
||||
milestoneID = ms.Id
|
||||
break
|
||||
}
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
p := 1
|
||||
perPage := 100
|
||||
for {
|
||||
results, _, err := client.RepositoryApi.RepoListPullRequests(context.Background(), repo, owner, &gitea.RepoListPullRequestsOpts{
|
||||
results, _, err := client.RepositoryApi.RepoListPullRequests(ctx, owner, repo, &gitea.RepoListPullRequestsOpts{
|
||||
Page: optional.NewInt32(int32(p)),
|
||||
State: optional.NewString("closed"),
|
||||
Milestone: optional.NewInt64(milestoneID),
|
||||
})
|
||||
p++
|
||||
if err != nil {
|
||||
return changelog, err
|
||||
return "", nil, err
|
||||
}
|
||||
p++
|
||||
|
||||
PRLoop: // labels in Go, let's get old school
|
||||
for _, pr := range results {
|
||||
if !pr.Merged {
|
||||
continue
|
||||
}
|
||||
|
||||
var label string
|
||||
for _, lb := range pr.Labels {
|
||||
if config.Cfg.SkipRegex != nil && config.Cfg.SkipRegex.MatchString(lb.Name) {
|
||||
continue PRLoop
|
||||
}
|
||||
|
||||
if g, ok := labels[lb.Name]; ok && len(label) == 0 {
|
||||
label = g
|
||||
}
|
||||
}
|
||||
|
||||
if len(label) > 0 {
|
||||
entries[label] = append(entries[label], Entry{
|
||||
if pr.Merged {
|
||||
p := PullRequest{
|
||||
Title: pr.Title,
|
||||
Index: pr.Number,
|
||||
})
|
||||
} else {
|
||||
entries[defaultGroup] = append(entries[defaultGroup], Entry{
|
||||
Title: pr.Title,
|
||||
Index: pr.Number,
|
||||
})
|
||||
}
|
||||
|
||||
labels := make([]Label, len(pr.Labels))
|
||||
for idx, lbl := range pr.Labels {
|
||||
labels[idx] = Label{
|
||||
Name: lbl.Name,
|
||||
}
|
||||
}
|
||||
p.Labels = labels
|
||||
|
||||
prs = append(prs, p)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -116,49 +86,46 @@ func (ge Gitea) Changelog(milestone string) (Changelog, error) {
|
|||
}
|
||||
}
|
||||
|
||||
changelog.Entries = entries
|
||||
|
||||
return changelog, nil
|
||||
return tagURL, prs, nil
|
||||
}
|
||||
|
||||
// Contributors returns a list of contributors from Gitea
|
||||
func (ge Gitea) Contributors(milestone string) ([]string, error) {
|
||||
func (ge Gitea) Contributors() ([]string, error) {
|
||||
client := gitea.NewAPIClient(&gitea.Configuration{
|
||||
BasePath: config.Cfg.BaseURL + "/api/v1",
|
||||
DefaultHeader: make(map[string]string),
|
||||
UserAgent: "Gitea-Changelog/1.0.0/go",
|
||||
})
|
||||
|
||||
contributorsMap := make(map[string]bool)
|
||||
repoOwner := strings.Split(config.Cfg.Repo, "/")
|
||||
repo := repoOwner[0]
|
||||
owner := repoOwner[1]
|
||||
|
||||
milestones, _, err := client.IssueApi.IssueGetMilestonesList(context.Background(), repo, owner, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
var ctx context.Context
|
||||
if len(Token) > 0 {
|
||||
ctx = context.WithValue(context.Background(), gitea.ContextAccessToken, Token)
|
||||
} else {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
var milestoneID int64
|
||||
for _, ms := range milestones {
|
||||
if ms.Title == milestone {
|
||||
milestoneID = ms.Id
|
||||
break
|
||||
}
|
||||
contributorsMap := make(map[string]bool)
|
||||
repoOwner := strings.Split(config.Cfg.Repo, "/")
|
||||
owner := repoOwner[0]
|
||||
repo := repoOwner[1]
|
||||
|
||||
milestoneID, err := getMilestoneID(client, ctx, owner, repo)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := 1
|
||||
perPage := 100
|
||||
for {
|
||||
results, _, err := client.RepositoryApi.RepoListPullRequests(context.Background(), repo, owner, &gitea.RepoListPullRequestsOpts{
|
||||
results, _, err := client.RepositoryApi.RepoListPullRequests(ctx, owner, repo, &gitea.RepoListPullRequestsOpts{
|
||||
Page: optional.NewInt32(int32(p)),
|
||||
State: optional.NewString("closed"),
|
||||
Milestone: optional.NewInt64(milestoneID),
|
||||
})
|
||||
p++
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p++
|
||||
|
||||
for _, pr := range results {
|
||||
if !pr.Merged {
|
||||
|
@ -186,3 +153,20 @@ func (ge Gitea) Contributors(milestone string) ([]string, error) {
|
|||
func (ge Gitea) BaseURL() string {
|
||||
return config.Cfg.BaseURL
|
||||
}
|
||||
|
||||
func getMilestoneID(client *gitea.APIClient, ctx context.Context, owner, repo string) (int64, error) {
|
||||
milestones, _, err := client.IssueApi.IssueGetMilestonesList(ctx, owner, repo, &gitea.IssueGetMilestonesListOpts{
|
||||
State: optional.NewString("all"),
|
||||
})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
for _, ms := range milestones {
|
||||
if ms.Title == Milestone {
|
||||
return ms.Id, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("no milestone found for %s", Milestone)
|
||||
}
|
||||
|
|
|
@ -19,32 +19,15 @@ import (
|
|||
type GitHub struct{}
|
||||
|
||||
// Changelog returns a GitHub changelog
|
||||
func (gh GitHub) Changelog(milestone string) (Changelog, error) {
|
||||
changelog := Changelog{
|
||||
TagURL: fmt.Sprintf("## [%s](https://github.com/%s/releases/tag/v%s) - %s\n", milestone, config.Cfg.Repo, milestone, time.Now().Format("2006-01-02")),
|
||||
}
|
||||
func (gh GitHub) Changelog() (string, []PullRequest, error) {
|
||||
tagURL := fmt.Sprintf("## [%s](https://github.com/%s/releases/tag/v%s) - %s\n", Milestone, config.Cfg.Repo, Milestone, time.Now().Format("2006-01-02"))
|
||||
|
||||
client := github.NewClient(nil)
|
||||
ctx := context.Background()
|
||||
|
||||
labels := make(map[string]string)
|
||||
entries := make(map[string][]Entry)
|
||||
var defaultGroup string
|
||||
for _, g := range config.Cfg.Groups {
|
||||
entries[g.Name] = []Entry{}
|
||||
for _, l := range g.Labels {
|
||||
labels[l] = g.Name
|
||||
}
|
||||
if g.Default {
|
||||
defaultGroup = g.Name
|
||||
}
|
||||
}
|
||||
prs := make([]PullRequest, 0)
|
||||
|
||||
if defaultGroup == "" {
|
||||
defaultGroup = config.Cfg.Groups[len(config.Cfg.Groups)-1].Name
|
||||
}
|
||||
|
||||
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Cfg.Repo, milestone)
|
||||
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Cfg.Repo, Milestone)
|
||||
p := 1
|
||||
perPage := 100
|
||||
for {
|
||||
|
@ -54,34 +37,27 @@ func (gh GitHub) Changelog(milestone string) (Changelog, error) {
|
|||
PerPage: perPage,
|
||||
},
|
||||
})
|
||||
p++
|
||||
if err != nil {
|
||||
return changelog, err
|
||||
return "", nil, err
|
||||
}
|
||||
p++
|
||||
|
||||
PRLoop: // labels in Go, let's get old school
|
||||
for _, pr := range result.Issues {
|
||||
var label string
|
||||
for _, lb := range pr.Labels {
|
||||
if config.Cfg.SkipRegex != nil && config.Cfg.SkipRegex.MatchString(lb.GetName()) {
|
||||
continue PRLoop
|
||||
if pr.IsPullRequest() {
|
||||
p := PullRequest{
|
||||
Title: pr.GetTitle(),
|
||||
Index: int64(pr.GetNumber()),
|
||||
}
|
||||
|
||||
if g, ok := labels[lb.GetName()]; ok && len(label) == 0 {
|
||||
label = g
|
||||
labels := make([]Label, len(pr.Labels))
|
||||
for idx, lbl := range pr.Labels {
|
||||
labels[idx] = Label{
|
||||
Name: lbl.GetName(),
|
||||
}
|
||||
}
|
||||
}
|
||||
p.Labels = labels
|
||||
|
||||
if len(label) > 0 {
|
||||
entries[label] = append(entries[label], Entry{
|
||||
Title: *pr.Title,
|
||||
Index: int64(*pr.Number),
|
||||
})
|
||||
} else {
|
||||
entries[defaultGroup] = append(entries[defaultGroup], Entry{
|
||||
Title: *pr.Title,
|
||||
Index: int64(*pr.Number),
|
||||
})
|
||||
prs = append(prs, p)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -90,18 +66,16 @@ func (gh GitHub) Changelog(milestone string) (Changelog, error) {
|
|||
}
|
||||
}
|
||||
|
||||
changelog.Entries = entries
|
||||
|
||||
return changelog, nil
|
||||
return tagURL, prs, nil
|
||||
}
|
||||
|
||||
// Contributors returns a list of contributors from GitHub
|
||||
func (gh GitHub) Contributors(milestone string) ([]string, error) {
|
||||
func (gh GitHub) Contributors() ([]string, error) {
|
||||
client := github.NewClient(nil)
|
||||
ctx := context.Background()
|
||||
|
||||
contributorsMap := make(map[string]bool)
|
||||
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Cfg.Repo, milestone)
|
||||
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Cfg.Repo, Milestone)
|
||||
p := 1
|
||||
perPage := 100
|
||||
for {
|
||||
|
@ -111,13 +85,13 @@ func (gh GitHub) Contributors(milestone string) ([]string, error) {
|
|||
PerPage: perPage,
|
||||
},
|
||||
})
|
||||
p++
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
p++
|
||||
|
||||
for _, pr := range result.Issues {
|
||||
contributorsMap[*pr.User.Login] = true
|
||||
contributorsMap[pr.GetUser().GetLogin()] = true
|
||||
}
|
||||
|
||||
if len(result.Issues) != perPage {
|
||||
|
|
|
@ -5,12 +5,17 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"code.gitea.io/changelog/config"
|
||||
)
|
||||
|
||||
var (
|
||||
Milestone string
|
||||
Token string
|
||||
)
|
||||
|
||||
// Parse returns a service from a string
|
||||
func Load() (Service, error) {
|
||||
switch strings.ToLower(config.Cfg.Service) {
|
||||
|
@ -19,25 +24,25 @@ func Load() (Service, error) {
|
|||
case "gitea":
|
||||
return Gitea{}, nil
|
||||
default:
|
||||
return nil, errors.New("unknown service type")
|
||||
return nil, fmt.Errorf("unknown service type %s", config.Cfg.Service)
|
||||
}
|
||||
}
|
||||
|
||||
// Service defines how a struct can be a Changelog Service
|
||||
type Service interface {
|
||||
Changelog(string) (Changelog, error)
|
||||
Contributors(string) ([]string, error)
|
||||
Changelog() (string, []PullRequest, error)
|
||||
Contributors() ([]string, error)
|
||||
BaseURL() string
|
||||
}
|
||||
|
||||
// Entry defines a changelog entry (PR title and index)
|
||||
type Entry struct {
|
||||
Title string
|
||||
Index int64
|
||||
// Label is the minimum information needed for a PR label
|
||||
type Label struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
// Changelog defines a full changelog
|
||||
type Changelog struct {
|
||||
TagURL string
|
||||
Entries map[string][]Entry
|
||||
// PullRequest is the minimum information needed to make a changelog entry
|
||||
type PullRequest struct {
|
||||
Title string
|
||||
Index int64
|
||||
Labels []Label
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user
can you write the reason instead of linking to source code?
Done.