Changelog Overhaul 2 (#19)

Default gitea service

Signed-off-by: jolheiser <john.olheiser@gmail.com>
Reviewed-on: https://gitea.com/gitea/changelog/pulls/19
Reviewed-by: 6543 <6543@noreply.gitea.io>
Reviewed-by: Guillermo Prandi <guillep2k@noreply.gitea.io>
This commit is contained in:
John Olheiser 2020-01-24 16:30:16 +00:00 committed by Andrew Thornton
parent 56048d3f35
commit 747f3cb162
18 changed files with 663 additions and 239 deletions

View File

@ -1,3 +1,7 @@
// 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.
// +build ignore
package main
@ -10,8 +14,12 @@ import (
const (
exampleFile = "changelog.example.yml"
writeFile = "config_default.go"
tmpl = `package main
writeFile = "config/config_default.go"
tmpl = `// 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 config
func init() {
defaultConfig = []byte(` + "`" + `%s` + "`" + `)

View File

@ -1,6 +1,13 @@
# The full repository name
repo: go-gitea/gitea
# Service type (gitea or github)
service: github
# Base URL for Gitea instance if using gitea service type (optional)
# Default: https://gitea.com
base-url:
# Changelog groups and which labeled PRs to add to each group
groups:
-

13
cmd/cmd.go Normal file
View File

@ -0,0 +1,13 @@
// 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 (
MilestoneFlag string
ConfigPathFlag string
TokenFlag string
DetailsFlag bool
AfterFlag int64
)

46
cmd/contributors.go Normal file
View File

@ -0,0 +1,46 @@
// Copyright 2018 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
import (
"fmt"
"sort"
"code.gitea.io/changelog/config"
"code.gitea.io/changelog/service"
"github.com/urfave/cli/v2"
)
var Contributors = &cli.Command{
Name: "contributors",
Usage: "Generates a contributors list",
Action: runContributors,
}
func runContributors(cmd *cli.Context) error {
cfg, err := config.New(ConfigPathFlag)
if err != nil {
return err
}
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, MilestoneFlag, TokenFlag)
if err != nil {
return err
}
contributors, err := s.Contributors()
if err != nil {
return err
}
sort.Sort(contributors)
for _, contributor := range contributors {
fmt.Printf("* [@%s](%s)\n", contributor.Name, contributor.Profile)
}
return nil
}

101
cmd/generate.go Normal file
View File

@ -0,0 +1,101 @@
// 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
import (
"fmt"
"code.gitea.io/changelog/config"
"code.gitea.io/changelog/service"
"github.com/urfave/cli/v2"
)
var Generate = &cli.Command{
Name: "generate",
Usage: "Generates a changelog",
Action: runGenerate,
}
func runGenerate(cmd *cli.Context) error {
cfg, err := config.New(ConfigPathFlag)
if err != nil {
return err
}
labels := make(map[string]string)
entries := make(map[string][]service.PullRequest)
var defaultGroup string
for _, g := range 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 = cfg.Groups[len(cfg.Groups)-1].Name
}
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, MilestoneFlag, TokenFlag)
if err != nil {
return err
}
title, prs, err := s.Generate()
if err != nil {
return err
}
PRLoop: // labels in Go, let's get old school
for _, pr := range prs {
if pr.Index < AfterFlag {
continue
}
var label string
for _, lb := range pr.Labels {
if cfg.SkipRegex != nil && 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.Println(title)
for _, g := range cfg.Groups {
if len(entries[g.Name]) == 0 {
continue
}
if DetailsFlag {
fmt.Println("<details><summary>" + g.Name + "</summary>")
fmt.Println()
for _, entry := range entries[g.Name] {
fmt.Printf("* %s (#%d)\n", entry.Title, entry.Index)
}
fmt.Println("</details>")
} else {
fmt.Println("* " + g.Name)
for _, entry := range entries[g.Name] {
fmt.Printf(" * %s (#%d)\n", entry.Title, entry.Index)
}
}
}
return nil
}

View File

@ -1,54 +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 main
//go:generate go run changelog.example.go
//go:generate go fmt ./...
import (
"io/ioutil"
"regexp"
"gopkg.in/yaml.v2"
)
var defaultConfig []byte
type Config struct {
Repo string `yaml:"repo"`
Groups []struct {
Name string `yaml:"name"`
Labels []string `yaml:"labels"`
Default bool `yaml:"default"`
} `yaml:"groups"`
SkipLabels string `yaml:"skip-labels"`
SkipRegex *regexp.Regexp `yaml:"-"`
}
func LoadConfig() (*Config, error) {
var err error
var configContent []byte
if len(configPath) == 0 {
configContent = defaultConfig
} else {
configContent, err = ioutil.ReadFile(configPath)
if err != nil {
return nil, err
}
}
var config *Config
if err = yaml.Unmarshal(configContent, &config); err != nil {
return nil, err
}
if len(config.SkipLabels) > 0 {
if config.SkipRegex, err = regexp.Compile(config.SkipLabels); err != nil {
return nil, err
}
}
return config, nil
}

58
config/config.go Normal file
View File

@ -0,0 +1,58 @@
// 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 config
import (
"io/ioutil"
"regexp"
"gopkg.in/yaml.v2"
)
var defaultConfig []byte
// Group is a grouping of PRs
type Group struct {
Name string `yaml:"name"`
Labels []string `yaml:"labels"`
Default bool `yaml:"default"`
}
// Config is the changelog settings
type Config struct {
Repo string `yaml:"repo"`
Service string `yaml:"service"`
BaseURL string `yaml:"base-url"`
Groups []Group `yaml:"groups"`
SkipLabels string `yaml:"skip-labels"`
SkipRegex *regexp.Regexp `yaml:"-"`
}
// Load a config from a path, defaulting to changelog.example.yml
func New(configPath string) (*Config, error) {
var err error
var configContent []byte
if len(configPath) == 0 {
configContent = defaultConfig
} else {
configContent, err = ioutil.ReadFile(configPath)
if err != nil {
return nil, err
}
}
var cfg *Config
if err = yaml.Unmarshal(configContent, &cfg); err != nil {
return nil, err
}
if len(cfg.SkipLabels) > 0 {
if cfg.SkipRegex, err = regexp.Compile(cfg.SkipLabels); err != nil {
return nil, err
}
}
return cfg, nil
}

View File

@ -1,9 +1,20 @@
package main
// 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 config
func init() {
defaultConfig = []byte(`# The full repository name
repo: go-gitea/gitea
# Service type (gitea or github)
service: github
# Base URL for Gitea instance if using gitea service type (optional)
# Default: https://gitea.com
base-url:
# Changelog groups and which labeled PRs to add to each group
groups:
-

View File

@ -1,70 +0,0 @@
// Copyright 2018 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 main
import (
"context"
"fmt"
"log"
"sort"
"github.com/google/go-github/github"
"github.com/urfave/cli/v2"
)
var cmdContributors = &cli.Command{
Name: "contributors",
Usage: "generate contributors list",
Description: "generate contributors list",
Action: runContributors,
}
func runContributors(cmd *cli.Context) error {
config, err := LoadConfig()
if err != nil {
return err
}
client := github.NewClient(nil)
ctx := context.Background()
contributorsMap := make(map[string]bool)
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Repo, milestone)
p := 1
perPage := 100
for {
result, _, err := client.Search.Issues(ctx, query, &github.SearchOptions{
ListOptions: github.ListOptions{
Page: p,
PerPage: perPage,
},
})
p++
if err != nil {
log.Fatal(err.Error())
}
for _, pr := range result.Issues {
contributorsMap[*pr.User.Login] = true
}
if len(result.Issues) != perPage {
break
}
}
contributors := make([]string, 0, len(contributorsMap))
for contributor, _ := range contributorsMap {
contributors = append(contributors, contributor)
}
sort.Strings(contributors)
for _, contributor := range contributors {
fmt.Printf("* [@%s](https://github.com/%s)\n", contributor, contributor)
}
return nil
}

View File

@ -1,103 +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 main
import (
"context"
"fmt"
"log"
"time"
"github.com/google/go-github/github"
"github.com/urfave/cli/v2"
)
var cmdGenerate = &cli.Command{
Name: "generate",
Usage: "generate changelog",
Description: "generate changelog",
Action: runGenerate,
}
func runGenerate(cmd *cli.Context) error {
config, err := LoadConfig()
if err != nil {
return err
}
client := github.NewClient(nil)
ctx := context.Background()
labels := make(map[string]string)
changelogs := make(map[string][]github.Issue)
var defaultGroup string
for _, g := range config.Groups {
changelogs[g.Name] = []github.Issue{}
for _, l := range g.Labels {
labels[l] = g.Name
}
if g.Default {
defaultGroup = g.Name
}
}
if defaultGroup == "" {
defaultGroup = config.Groups[len(config.Groups)-1].Name
}
query := fmt.Sprintf(`repo:%s is:merged milestone:"%s"`, config.Repo, milestone)
p := 1
perPage := 100
for {
result, _, err := client.Search.Issues(ctx, query, &github.SearchOptions{
ListOptions: github.ListOptions{
Page: p,
PerPage: perPage,
},
})
p++
if err != nil {
log.Fatal(err.Error())
}
PRLoop: // labels in Go, let's get old school
for _, pr := range result.Issues {
var label string
for _, lb := range pr.Labels {
if config.SkipRegex != nil && config.SkipRegex.MatchString(lb.GetName()) {
continue PRLoop
}
if g, ok := labels[lb.GetName()]; ok && len(label) == 0 {
label = g
}
}
if len(label) > 0 {
changelogs[label] = append(changelogs[label], pr)
} else {
changelogs[defaultGroup] = append(changelogs[defaultGroup], pr)
}
}
if len(result.Issues) != perPage {
break
}
}
fmt.Printf("## [%s](https://github.com/%s/releases/tag/v%s) - %s\n", milestone, config.Repo, milestone, time.Now().Format("2006-01-02"))
for _, g := range config.Groups {
if len(changelogs[g.Name]) == 0 {
continue
}
fmt.Println("* " + g.Name)
for _, pr := range changelogs[g.Name] {
fmt.Printf(" * %s (#%d)\n", *pr.Title, *pr.Number)
}
}
return nil
}

1
go.mod
View File

@ -3,6 +3,7 @@ module code.gitea.io/changelog
go 1.13
require (
code.gitea.io/sdk/gitea v0.0.0-20200116035226-b24cfd841cda
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect

7
go.sum
View File

@ -1,7 +1,11 @@
code.gitea.io/sdk/gitea v0.0.0-20200116035226-b24cfd841cda h1:J+qDCjmjcewNcPNfHIex5z726cgv/URXK0MnXHTIo1U=
code.gitea.io/sdk/gitea v0.0.0-20200116035226-b24cfd841cda/go.mod h1:SXOCD/+QP5txLJQ2bPkgHGSQs1YQ4s1ep1ZpI6ItO4A=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY=
github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk=
@ -12,6 +16,9 @@ github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k=
github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

35
main.go
View File

@ -4,10 +4,14 @@
package main
//go:generate go run changelog.example.go
//go:generate go fmt ./...
import (
"fmt"
"os"
"code.gitea.io/changelog/cmd"
"github.com/urfave/cli/v2"
)
@ -16,11 +20,6 @@ const (
Version = "0.2"
)
var (
milestone string
configPath string
)
func main() {
app := &cli.App{
Name: "changelog",
@ -32,18 +31,36 @@ func main() {
Aliases: []string{"m"},
Usage: "Targeted milestone",
Required: true,
Destination: &milestone,
Destination: &cmd.MilestoneFlag,
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Specify a config file",
Destination: &configPath,
Destination: &cmd.ConfigPathFlag,
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Usage: "Access token for private repositories/instances",
Destination: &cmd.TokenFlag,
},
&cli.BoolFlag{
Name: "details",
Aliases: []string{"d"},
Usage: "Generate detail lists instead of long lists",
Destination: &cmd.DetailsFlag,
},
&cli.Int64Flag{
Name: "after",
Aliases: []string{"a"},
Usage: "Only select PRs after a given index (continuing a previous changelog)",
Destination: &cmd.AfterFlag,
},
},
Commands: []*cli.Command{
cmdGenerate,
cmdContributors,
cmd.Generate,
cmd.Contributors,
},
}

138
service/gitea.go Normal file
View File

@ -0,0 +1,138 @@
// 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 (
"fmt"
"time"
"code.gitea.io/sdk/gitea"
)
// Gitea defines a Gitea service
type Gitea struct {
Milestone string
Token string
BaseURL string
Owner string
Repo string
}
// Generate returns a Gitea changelog
func (ge *Gitea) Generate() (string, []PullRequest, error) {
client := gitea.NewClient(ge.BaseURL, ge.Token)
prs := make([]PullRequest, 0)
milestoneID, err := ge.milestoneID(client)
if err != nil {
return "", nil, err
}
tagURL := fmt.Sprintf("## [%s](%s/%s/%s/pulls?q=&type=all&state=closed&milestone=%d) - %s", ge.Milestone, ge.BaseURL, ge.Owner, ge.Repo, milestoneID, time.Now().Format("2006-01-02"))
p := 1
// https://github.com/go-gitea/gitea/blob/d92781bf941972761177ac9e07441f8893758fd3/models/repo.go#L63
// https://github.com/go-gitea/gitea/blob/e3c3b33ea7a5a223e22688c3f0eb2d3dab9f991c/models/pull_list.go#L104
// FIXME Gitea has this hard-coded at 40
perPage := 40
for {
results, err := client.ListRepoPullRequests(ge.Owner, ge.Repo, gitea.ListPullRequestsOptions{
Page: p,
State: "closed",
Milestone: milestoneID,
})
if err != nil {
return "", nil, err
}
p++
for _, pr := range results {
if pr != nil && pr.HasMerged {
p := PullRequest{
Title: pr.Title,
Index: pr.Index,
}
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)
}
}
if len(results) != perPage {
break
}
}
return tagURL, prs, nil
}
// Contributors returns a list of contributors from Gitea
func (ge *Gitea) Contributors() (ContributorList, error) {
client := gitea.NewClient(ge.BaseURL, ge.Token)
contributorsMap := make(map[string]bool)
milestoneID, err := ge.milestoneID(client)
if err != nil {
return nil, err
}
p := 1
perPage := 100
for {
results, err := client.ListRepoPullRequests(ge.Owner, ge.Repo, gitea.ListPullRequestsOptions{
Page: p,
State: "closed",
Milestone: milestoneID,
})
if err != nil {
return nil, err
}
p++
for _, pr := range results {
if pr != nil && pr.HasMerged {
contributorsMap[pr.Poster.UserName] = true
}
}
if len(results) != perPage {
break
}
}
contributors := make(ContributorList, 0, len(contributorsMap))
for contributor, _ := range contributorsMap {
contributors = append(contributors, Contributor{
Name: contributor,
Profile: fmt.Sprintf("%s/%s", ge.BaseURL, contributor),
})
}
return contributors, nil
}
func (ge *Gitea) milestoneID(client *gitea.Client) (int64, error) {
milestones, err := client.ListRepoMilestones(ge.Owner, ge.Repo)
if err != nil {
return 0, err
}
for _, ms := range milestones {
if ms.Title == ge.Milestone {
return ms.ID, nil
}
}
return 0, fmt.Errorf("no milestone found for %s", ge.Milestone)
}

112
service/github.go Normal file
View File

@ -0,0 +1,112 @@
// 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"
"time"
"github.com/google/go-github/github"
)
// GitHub defines a GitHub service
type GitHub struct {
Milestone string
Token string
Repo string
}
// Generate returns a GitHub changelog
func (gh *GitHub) Generate() (string, []PullRequest, error) {
tagURL := fmt.Sprintf("## [%s](https://github.com/%s/releases/tag/v%s) - %s", gh.Milestone, gh.Repo, gh.Milestone, time.Now().Format("2006-01-02"))
client := github.NewClient(nil)
ctx := context.Background()
prs := make([]PullRequest, 0)
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 {
if pr.IsPullRequest() {
p := PullRequest{
Title: pr.GetTitle(),
Index: int64(pr.GetNumber()),
}
labels := make([]Label, len(pr.Labels))
for idx, lbl := range pr.Labels {
labels[idx] = Label{
Name: lbl.GetName(),
}
}
p.Labels = labels
prs = append(prs, p)
}
}
if len(result.Issues) != perPage {
break
}
}
return tagURL, prs, nil
}
// Contributors returns a list of contributors from GitHub
func (gh *GitHub) Contributors() (ContributorList, error) {
client := github.NewClient(nil)
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
}

38
service/github_test.go Normal file
View File

@ -0,0 +1,38 @@
// 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 "testing"
var gh = &GitHub{
Milestone: "1.1.0", // https://github.com/go-gitea/test_repo/milestone/2?closed=1
Repo: "go-gitea/test_repo",
}
func TestGitHubGenerate(t *testing.T) {
_, entries, err := gh.Generate()
if err != nil {
t.Log(err)
t.FailNow()
}
if len(entries) != 1 {
t.Logf("Expected 1 changelog entry, but got %d", len(entries))
t.Fail()
}
}
func TestGitHubContributors(t *testing.T) {
contributors, err := gh.Contributors()
if err != nil {
t.Log(err)
t.FailNow()
}
if len(contributors) != 1 {
t.Logf("Expected 1 contributor, but got %d", len(contributors))
t.Fail()
}
}

80
service/service.go Normal file
View File

@ -0,0 +1,80 @@
// 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 (
"fmt"
"strings"
)
const defaultGitea = "https://gitea.com"
// Load returns a service from a string
func New(serviceType, repo, baseURL, milestone, token string) (Service, error) {
switch strings.ToLower(serviceType) {
case "github":
return &GitHub{
Milestone: milestone,
Token: token,
Repo: repo,
}, nil
case "gitea":
ownerRepo := strings.Split(repo, "/")
if strings.TrimSpace(baseURL) == "" {
baseURL = defaultGitea
}
return &Gitea{
Milestone: milestone,
Token: token,
BaseURL: baseURL,
Owner: ownerRepo[0],
Repo: ownerRepo[1],
}, nil
default:
return nil, fmt.Errorf("unknown service type %s", serviceType)
}
}
// Service defines how a struct can be a Changelog Service
type Service interface {
Generate() (string, []PullRequest, error)
Contributors() (ContributorList, error)
}
// Label is the minimum information needed for a PR label
type Label struct {
Name string
}
// PullRequest is the minimum information needed to make a changelog entry
type PullRequest struct {
Title string
Index int64
Labels []Label
}
// Contributor is a project contributor
type Contributor struct {
Name string
Profile string
}
// ContributorList is a slice of Contributors that can be sorted
type ContributorList []Contributor
// Len is the length of the ContributorList
func (cl ContributorList) Len() int {
return len(cl)
}
// Less determines whether a Contributor comes before another Contributor
func (cl ContributorList) Less(i, j int) bool {
return cl[i].Name < cl[j].Name
}
// Swap swaps Contributors in a ContributorList
func (cl ContributorList) Swap(i, j int) {
cl[i], cl[j] = cl[j], cl[i]
}

14
service/service_test.go Normal file
View File

@ -0,0 +1,14 @@
// 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 (
"os"
"testing"
)
func TestMain(m *testing.M) {
os.Exit(m.Run())
}