Browse Source

changelog generator added.

pull/2282/head
Arkat Erol 6 years ago
parent
commit
46ca79bc9e
  1. 14
      nupkg/push_packages.ps1
  2. 36
      tools/github-changelog-generator/README.md
  3. BIN
      tools/github-changelog-generator/changelog.md
  4. 54
      tools/github-changelog-generator/config.yaml
  5. BIN
      tools/github-changelog-generator/generator.exe
  6. 162
      tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/generate.go
  7. 24
      tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/helpers.go
  8. 50
      tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/root.go
  9. 22
      tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/version.go
  10. 54
      tools/github-changelog-generator/src/GithubChangelogGenerator/config/config.yaml
  11. 13
      tools/github-changelog-generator/src/GithubChangelogGenerator/go.mod
  12. 63
      tools/github-changelog-generator/src/GithubChangelogGenerator/go.sum
  13. 28
      tools/github-changelog-generator/src/GithubChangelogGenerator/internal/config.go
  14. 70
      tools/github-changelog-generator/src/GithubChangelogGenerator/internal/issues.go
  15. 7
      tools/github-changelog-generator/src/GithubChangelogGenerator/main.go
  16. 329
      tools/github-changelog-generator/src/GithubChangelogGenerator/pkg/repo.go

14
nupkg/push_packages.ps1

@ -0,0 +1,14 @@
. ".\common.ps1"
# Get the version
[xml]$commonPropsXml = Get-Content (Join-Path $rootFolder "common.props")
$version = $commonPropsXml.Project.PropertyGroup.Version
# Publish all packages
foreach($project in $projects) {
$projectName = $project.Substring($project.LastIndexOf("/") + 1)
& dotnet nuget push ($projectName + "." + $version + ".nupkg") -s "https://nuget.abp.io/API_KEY/v3/index.json"
}
# Go back to the pack folder
Set-Location $packFolder

36
tools/github-changelog-generator/README.md

@ -0,0 +1,36 @@
# GitHub Change Log Generator
## Configuration
Edit the config.yaml file to customize the generated output.
## Usage
```
.\generator.exe generate [flags] > changelog.md
```
### Options
```
Flags:
-h, --help help for generate
-m, --milestone string milestone title to get issues and pull requests for
-r, --repo string repository name to generate the Changelog for, in the form user/repo
--since-tag string issues and pull requests since tag
-s, --state string state of the issues and pull requests to get (open,closed or all)
-t, --token string personal access token
--until-tag string issues and pull requests until tag
Global Flags:
--config string config file (default is config.yaml)
```
## Example
Generate change logs for the ABP repository for the 0.19 milestone:
````
.\generator.exe generate -r abpframework/abp -m "0.19" > changelog.md
````

BIN
tools/github-changelog-generator/changelog.md

Binary file not shown.

54
tools/github-changelog-generator/config.yaml

@ -0,0 +1,54 @@
---
repo: abpframework/abp
# token:
milestone: 0.18.1
# since-tag: 0.18.0
# until-tag: 0.17.0
state: closed
groups:
- labels:
- breaking change
title: "Breaking Changes"
- labels:
- feature
title: "Features"
- labels:
- enhancement
title: "Enhancements"
- labels:
- bug
title: "Bug Fixes"
- labels:
title: "Others"
#template: |-
# {{if .Milestone}}## {{.Milestone.GetTitle}} ({{.Milestone.GetClosedAt.Format "2006-01-02"}}){{end -}}
# {{if .IssuesByMilestone}}
# {{range .IssuesByMilestone}}
# ### {{.Title}}
# {{range .Issues}}
# {{if .IsPullRequest -}}
# - PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
# {{- else -}}
# - ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
# {{- end -}}
# {{end}}
# {{end}}
# {{end -}}
# {{if .SinceTagCommit}}## {{.SinceTag}}{{if .UntilTagCommit}} - {{.UntilTag}}{{end}}{{end -}}
# {{if .IssuesByTag}}
# {{range .IssuesByTag}}
# ### {{.Title}}
# {{range .Issues}}
# {{if .IsPullRequest -}}
# - PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
# {{- else -}}
# - ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
# {{- end -}}
# {{end}}
# {{end}}
# {{- end -}}

BIN
tools/github-changelog-generator/generator.exe

Binary file not shown.

162
tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/generate.go

@ -0,0 +1,162 @@
package cmd
import (
"GithubChangelogGenerator/internal"
"GithubChangelogGenerator/pkg"
"fmt"
"io"
"os"
"github.com/google/go-github/github"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
// generateCmd represents the generate command
var generateCmd = &cobra.Command{
Use: "generate",
Short: "Generate changelog",
Long: `Generates changelog based on a milestone or tag(s)
Edit the config.yaml file to customize the generated output.`,
Run: func(cmd *cobra.Command, args []string) {
c := &internal.Config{}
err := viper.Unmarshal(c)
if err != nil {
er(err)
}
repo, err := pkg.NewRepo(c.Repo, c.Token)
if err != nil {
er(err)
}
issuesByMilestone, err := repo.IssuesByMilestone(c.Milestone, c.State)
if err != nil {
er(err)
}
milestone, err := repo.Milestone(c.Milestone)
if err != nil {
er(err)
}
sinceTagCommit, err := repo.TagCommit(c.SinceTag)
if err != nil {
er(err)
}
untilTagCommit, err := repo.TagCommit(c.UntilTag)
if err != nil {
er(err)
}
var issuesByTag []*github.Issue
switch {
case sinceTagCommit != nil:
issuesByTag, err = repo.IssuesSince(sinceTagCommit.Committer.GetDate())
if err != nil {
er(err)
}
if c.UntilTag != "" {
issuesByTag = pkg.FilterUntil(issuesByTag, untilTagCommit.Committer.GetDate())
}
case untilTagCommit != nil:
issuesByTag, err = repo.AllIssues(c.State)
if err != nil {
er(err)
}
issuesByTag = pkg.FilterUntil(issuesByTag, untilTagCommit.Committer.GetDate())
}
groupedIssuesByMilestone := internal.GroupIssues(c.Groups, issuesByMilestone)
if err != nil {
er(err)
}
groupedIssuesByTag := internal.GroupIssues(c.Groups, issuesByTag)
if err != nil {
er(err)
}
err = writeChangelog(os.Stdout, &TemplateData{
Repository: repo.Repository(),
IssuesByMilestone: groupedIssuesByMilestone,
IssuesByTag: groupedIssuesByTag,
Milestone: milestone,
SinceTag: c.SinceTag,
SinceTagCommit: sinceTagCommit,
UntilTag: c.UntilTag,
UntilTagCommit: untilTagCommit,
})
if err != nil {
er(err)
}
},
}
func init() {
rootCmd.AddCommand(generateCmd)
generateCmd.Flags().StringP("repo", "r", "", "repository name to generate the Changelog for, in the form user/repo")
generateCmd.Flags().StringP("token", "t", "", "personal access token")
generateCmd.Flags().StringP("milestone", "m", "", "milestone title to get issues and pull requests for")
generateCmd.Flags().String("since-tag", "", "issues and pull requests since tag")
generateCmd.Flags().String("until-tag", "", "issues and pull requests until tag")
generateCmd.Flags().StringP("state", "s", "", "state of the issues and pull requests to get (open,closed or all)")
err := viper.BindPFlags(generateCmd.Flags())
if err != nil {
er(err)
}
}
type TemplateData struct {
Repository *github.Repository
IssuesByMilestone []*internal.GroupedIssues
IssuesByTag []*internal.GroupedIssues
Milestone *github.Milestone
SinceTag string
SinceTagCommit *github.Commit
UntilTag string
UntilTagCommit *github.Commit
}
func writeChangelog(w io.WriteCloser, td *TemplateData) error {
template := viper.GetString("template")
if template == "" {
template = `{{if .Milestone}}## {{.Milestone.GetTitle}} ({{.Milestone.GetClosedAt.Format "2006-01-02"}}){{end -}}
{{if .IssuesByMilestone}}
{{range .IssuesByMilestone}}
### {{.Title}}
{{range .Issues}}
{{if .IsPullRequest -}}
- PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
{{- else -}}
- ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
{{- end -}}
{{end}}
{{end}}
{{end -}}
{{if .SinceTagCommit}}## {{.SinceTag}}{{if .UntilTagCommit}} - {{.UntilTag}}{{end}}{{end -}}
{{if .IssuesByTag}}
{{range .IssuesByTag}}
### {{.Title}}
{{range .Issues}}
{{if .IsPullRequest -}}
- PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
{{- else -}}
- ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
{{- end -}}
{{end}}
{{end}}
{{- end -}}
`
}
result, err := executeTemplate(template, td)
if err != nil {
return err
}
_, err = fmt.Fprintf(w, "%s", result)
if err != nil {
return err
}
return w.Close()
}

24
tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/helpers.go

@ -0,0 +1,24 @@
package cmd
import (
"bytes"
"fmt"
"os"
"text/template"
)
func executeTemplate(tmplStr string, data interface{}) ([]byte, error) {
tmpl, err := template.New("").Parse(tmplStr)
if err != nil {
return nil, err
}
buf := new(bytes.Buffer)
err = tmpl.Execute(buf, data)
return buf.Bytes(), err
}
func er(msg interface{}) {
fmt.Println("Error:", msg)
os.Exit(1)
}

50
tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/root.go

@ -0,0 +1,50 @@
package cmd
import (
"fmt"
"os"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
var cfgFile string
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "GithubChangelogGenerator",
Short: "Changelog generator",
Long: `This application is a tool to generate changelog from a Github milestone.`,
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
os.Exit(1)
}
}
func init() {
cobra.OnInitialize(initConfig)
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is config.yaml)")
}
// initConfig reads in config file and ENV variables if set.
func initConfig() {
if cfgFile != "" {
// Use config file from the flag.
viper.SetConfigFile(cfgFile)
} else {
viper.AddConfigPath(".")
viper.SetConfigName("config")
}
viper.AutomaticEnv() // read in environment variables that match
// If a config file is found, read it in.
if err := viper.ReadInConfig(); err == nil {
//fmt.Println("Using config file:", viper.ConfigFileUsed())
}
}

22
tools/github-changelog-generator/src/GithubChangelogGenerator/cmd/version.go

@ -0,0 +1,22 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
const version = "0.2.0"
// versionCmd represents the version command
var versionCmd = &cobra.Command{
Use: "version",
Short: "Print the version number.",
Run: func(cmd *cobra.Command, args []string) {
fmt.Println(version)
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}

54
tools/github-changelog-generator/src/GithubChangelogGenerator/config/config.yaml

@ -0,0 +1,54 @@
---
repo: abpframework/abp
# token:
milestone: 0.17
# since-tag: 0.15.0
# until-tag: 0.16.0
state: closed
groups:
- labels:
- breaking change
title: "Breaking Changes"
- labels:
- feature
title: "Features"
- labels:
- enhancement
title: "Enhancements"
- labels:
- bug
title: "Bug Fixes"
- labels:
title: "Others"
#template: |-
# {{if .Milestone}}## {{.Milestone.GetTitle}} ({{.Milestone.GetClosedAt.Format "2006-01-02"}}){{end -}}
# {{if .IssuesByMilestone}}
# {{range .IssuesByMilestone}}
# ### {{.Title}}
# {{range .Issues}}
# {{if .IsPullRequest -}}
# - PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
# {{- else -}}
# - ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
# {{- end -}}
# {{end}}
# {{end}}
# {{end -}}
# {{if .SinceTagCommit}}## {{.SinceTag}}{{if .UntilTagCommit}} - {{.UntilTag}}{{end}}{{end -}}
# {{if .IssuesByTag}}
# {{range .IssuesByTag}}
# ### {{.Title}}
# {{range .Issues}}
# {{if .IsPullRequest -}}
# - PR [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}} (by [{{.GetUser.GetLogin}}]({{.GetUser.GetHTMLURL}}))
# {{- else -}}
# - ISSUE [\#{{.GetNumber}}]({{.GetHTMLURL}}): {{.GetTitle}}
# {{- end -}}
# {{end}}
# {{end}}
# {{- end -}}

13
tools/github-changelog-generator/src/GithubChangelogGenerator/go.mod

@ -0,0 +1,13 @@
module GithubChangelogGenerator
go 1.12
require (
github.com/BurntSushi/toml v0.3.1 // indirect
github.com/google/go-github v17.0.0+incompatible
github.com/google/go-querystring v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/spf13/cobra v0.0.3
github.com/spf13/viper v1.3.2
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a
)

63
tools/github-changelog-generator/src/GithubChangelogGenerator/go.sum

@ -0,0 +1,63 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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=
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e h1:bRhVy7zSSasaqNksaRZiA5EEI+Ei4I1nO5Jh72wfHlg=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA=
golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4 h1:YUO/7uOKsKeq9UokNS62b8FYywz3ker1l1vDZRCRefw=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a h1:1n5lsVfiQW3yfsRGu98756EH1YthsFqr/5mxHduZW2A=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

28
tools/github-changelog-generator/src/GithubChangelogGenerator/internal/config.go

@ -0,0 +1,28 @@
package internal
type Config struct {
Repo string `mapstructure:"repo"`
Token string `mapstructure:"token"`
Milestone string `mapstructure:"milestone"`
SinceTag string `mapstructure:"since-tag"`
UntilTag string `mapstructure:"until-tag"`
State string `mapstructure:"state"`
Groups []*Group `mapstructure:"groups"`
Template string `mapstructure:"template"`
}
type Group struct {
Labels []string `mapstructure:"labels"`
Title string `mapstructure:"title"`
}
func AllLabels(groups []*Group) map[int][]string {
allLabels := make(map[int][]string)
for i, group := range groups {
if allLabels[i] == nil {
allLabels[i] = make([]string, 0)
}
allLabels[i] = append(allLabels[i], group.Labels...)
}
return allLabels
}

70
tools/github-changelog-generator/src/GithubChangelogGenerator/internal/issues.go

@ -0,0 +1,70 @@
package internal
import (
"fmt"
"regexp"
"sort"
"github.com/google/go-github/github"
)
type GroupedIssues struct {
Title string
Issues []*github.Issue
}
func GroupIssues(groups []*Group, issues []*github.Issue) []*GroupedIssues {
if issues == nil {
return nil
}
var result []*GroupedIssues
grouped := make(map[string][]*github.Issue)
for _, issue := range issues {
if i, ok := containsAny(issue.Labels, AllLabels(groups)); ok {
grouped[groups[i].Title] = append(grouped[groups[i].Title], issue)
} else {
grouped["no_label"] = append(grouped["no_label"], issue)
}
}
for _, group := range groups {
if len(group.Labels) == 0 {
result = append(result, &GroupedIssues{Title: group.Title, Issues: grouped["no_label"]})
continue
}
result = append(result, &GroupedIssues{Title: group.Title, Issues: grouped[group.Title]})
}
return result
}
func containsAny(gls []github.Label, cls map[int][]string) (int, bool) {
var keys []int
for k := range cls {
keys = append(keys, k)
}
sort.Ints(keys)
for _, gl := range gls {
for _, k := range keys {
for _, l := range cls[k] {
if match(gl.GetName(), l) {
return k, true
}
}
}
}
return 0, false
}
func match(a, rx string) bool {
if a == rx {
return true
}
re, err := regexp.Compile(fmt.Sprintf("^%s$", rx))
if err != nil {
return false
}
return re.MatchString(a)
}

7
tools/github-changelog-generator/src/GithubChangelogGenerator/main.go

@ -0,0 +1,7 @@
package main
import "GithubChangelogGenerator/cmd"
func main() {
cmd.Execute()
}

329
tools/github-changelog-generator/src/GithubChangelogGenerator/pkg/repo.go

@ -0,0 +1,329 @@
package pkg
import (
"context"
"fmt"
"os"
"strconv"
"strings"
"time"
"github.com/google/go-github/github"
"golang.org/x/oauth2"
)
type GitHubRepo struct {
repoOwner string
repoName string
token string
repo *github.Repository
milestone *github.Milestone
}
func NewRepo(repo string, token ...string) (*GitHubRepo, error) {
gr := &GitHubRepo{}
sx := strings.Split(repo, "/")
if len(sx) != 2 {
return nil, fmt.Errorf("repo must be in organization/repository format")
}
gr.repoOwner = sx[0]
gr.repoName = sx[1]
client := github.NewClient(nil)
if token != nil && token[0] != "" {
gr.token = token[0]
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: token[0]},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var err error
gr.repo, _, err = client.Repositories.Get(context.Background(), sx[0], sx[1])
if err != nil {
return nil, err
}
return gr, nil
}
func FilterMilestone(issues []*github.Issue, milestone string) []*github.Issue {
var fi []*github.Issue
if milestone != "" {
for _, issue := range issues {
if issue.Milestone.GetTitle() == milestone {
fi = append(fi, issue)
}
}
}
return fi
}
func FilterSince(issues []*github.Issue, since time.Time) []*github.Issue {
var fi []*github.Issue
if !since.IsZero() {
for _, issue := range issues {
if issue.GetClosedAt().After(since) {
fi = append(fi, issue)
}
}
}
return fi
}
func FilterUntil(issues []*github.Issue, until time.Time) []*github.Issue {
var fi []*github.Issue
if !until.IsZero() {
for _, issue := range issues {
if issue.GetClosedAt().Before(until) {
fi = append(fi, issue)
}
}
}
return fi
}
func (gr *GitHubRepo) AllIssues(state ...string) ([]*github.Issue, error) {
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var allIssues []*github.Issue
opt := &github.IssueListByRepoOptions{State: "closed", ListOptions: github.ListOptions{PerPage: 100}}
if len(state) == 1 {
if state[0] == "open" || state[0] == "closed" || state[0] == "all" {
opt.State = state[0]
}
}
for {
issues, resp, err := client.Issues.ListByRepo(context.Background(), gr.repoOwner, gr.repoName, opt)
if err != nil {
return nil, err
}
allIssues = append(allIssues, issues...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allIssues, nil
}
func (gr *GitHubRepo) IssuesByMilestone(milestone string, state ...string) ([]*github.Issue, error) {
if milestone == "" {
return nil, nil
}
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var allIssues []*github.Issue
mil, err := gr.Milestone(milestone)
if err != nil {
return nil, err
}
opt := &github.IssueListByRepoOptions{Milestone: strconv.Itoa(mil.GetNumber()), State: "closed", ListOptions: github.ListOptions{PerPage: 100}}
if len(state) == 1 {
if state[0] == "open" || state[0] == "closed" || state[0] == "all" {
opt.State = state[0]
}
}
for {
issues, resp, err := client.Issues.ListByRepo(context.Background(), gr.repoOwner, gr.repoName, opt)
if err != nil {
return nil, err
}
allIssues = append(allIssues, issues...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allIssues, nil
}
func (gr *GitHubRepo) IssuesSince(time time.Time, state ...string) ([]*github.Issue, error) {
if time.IsZero() {
return nil, nil
}
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var allIssues []*github.Issue
opt := &github.IssueListByRepoOptions{Since: time, State: "closed", ListOptions: github.ListOptions{PerPage: 100}}
if len(state) == 1 {
if state[0] == "open" || state[0] == "closed" || state[0] == "all" {
opt.State = state[0]
}
}
for {
issues, resp, err := client.Issues.ListByRepo(context.Background(), gr.repoOwner, gr.repoName, opt)
if err != nil {
return nil, err
}
allIssues = append(allIssues, issues...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allIssues, nil
}
func (gr *GitHubRepo) Tags() ([]*github.RepositoryTag, error) {
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var allTags []*github.RepositoryTag
opt := &github.ListOptions{PerPage: 100}
for {
tags, resp, err := client.Repositories.ListTags(
context.Background(),
gr.repoOwner, gr.repoName,
opt,
)
if err != nil {
return nil, err
}
allTags = append(allTags, tags...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allTags, nil
}
func (gr *GitHubRepo) Milestones() ([]*github.Milestone, error) {
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
var allMilestones []*github.Milestone
opt := &github.MilestoneListOptions{State: "all", ListOptions: github.ListOptions{PerPage: 100}}
for {
milestones, resp, err := client.Issues.ListMilestones(
context.Background(),
gr.repoOwner, gr.repoName,
opt,
)
if err != nil {
return nil, err
}
allMilestones = append(allMilestones, milestones...)
if resp.NextPage == 0 {
break
}
opt.Page = resp.NextPage
}
return allMilestones, nil
}
func (gr *GitHubRepo) Milestone(title string) (*github.Milestone, error) {
if title == "" {
return nil, nil
}
if gr.milestone.GetTitle() == title {
return gr.milestone, nil
}
milestones, err := gr.Milestones()
if err != nil {
return nil, err
}
for _, milestone := range milestones {
if milestone.GetTitle() == title {
gr.milestone = milestone
return milestone, nil
}
}
return nil, fmt.Errorf("you didn't pass a valid milestone title")
}
func (gr *GitHubRepo) TagCommit(name string) (*github.Commit, error) {
if name == "" {
return nil, nil
}
client := github.NewClient(nil)
if gr.token != "" {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: gr.token},
)
tc := oauth2.NewClient(context.Background(), ts)
client = github.NewClient(tc)
}
refs, _, err := client.Git.GetRefs(context.Background(), gr.repoOwner, gr.repoName, "tags")
if err != nil {
return nil, err
}
sha := ""
var tags []string
refName := fmt.Sprintf("refs/tags/%s", name)
for _, ref := range refs {
if ref.GetRef() == refName {
sha = ref.Object.GetSHA()
}
tag := strings.Split(ref.GetRef(), "/")
tags = append(tags, tag[len(tag)-1])
}
if sha == "" {
return nil, fmt.Errorf("you didn't pass a valid tag name. the available tags are: %s", tags)
}
commit, _, err := client.Git.GetCommit(context.Background(), gr.repoOwner, gr.repoName, sha)
if err != nil {
return nil, err
}
return commit, err
}
func (gr *GitHubRepo) Repository() *github.Repository {
return gr.repo
}
func checkrate() error {
client := github.NewClient(nil)
rl, _, err := client.RateLimits(context.Background())
if err != nil {
return err
}
if rl.Core.Remaining == 0 {
fmt.Println("GitHub API rate limit exceeded!")
fmt.Printf("GitHub API rate limit resets on %s\n", rl.Core.Reset.Format("2-Jan-2006 15:04:05"))
os.Exit(1)
}
return nil
}
Loading…
Cancel
Save