Updates and priority changes #62

Merged
lunny merged 3 commits from jolheiser/changelog:priority into master 2022-06-22 01:27:24 +00:00
14 changed files with 169 additions and 266 deletions

@ -13,18 +13,18 @@ trigger:
steps:
- name: build
pull: always
image: golang:1.17
image: golang:1.18
jolheiser marked this conversation as resolved Outdated
Outdated
Review

1.18

1.18
environment:
GOPROXY: https://goproxy.cn,direct
GOPROXY: https://goproxy.io,direct
commands:
- go test -race ./...
- go build
- name: check
pull: always
image: golang:1.17
image: golang:1.18
environment:
GOPROXY: https://goproxy.cn,direct
GOPROXY: https://goproxy.io,direct
commands:
- make lint
@ -70,7 +70,7 @@ steps:
pull: always
image: techknowlogick/xgo:latest
environment:
GOPROXY: https://goproxy.cn,direct
GOPROXY: https://goproxy.io,direct
commands:
- export PATH=$PATH:$GOPATH/bin
- make release

@ -10,12 +10,11 @@ linters:
- gocritic
- gocyclo
- gofmt
- golint
- gosimple
- govet
- maligned
- misspell
- prealloc
- revive
- staticcheck
- structcheck
- typecheck

@ -15,27 +15,23 @@ else
LONG_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
LDFLAGS := $(LDFLAGS) -X "main.Version=$(LONG_VERSION)"
LDFLAGS := $(LDFLAGS) -X "code.gitea.io/changelog/cmd.Version=$(LONG_VERSION)"
.PHONY: build
build: generate
build:
$(GO) build -ldflags '-s -w $(LDFLAGS)'
.PHONY: generate
generate:
$(GO) generate ./...
.PHONY: test
test:
$(GO) test -race -v ./...
.PHONY: lint
lint:
jolheiser marked this conversation as resolved Outdated
Outdated
Review

I think we should switch to use go install ... here

I think we should switch to use `go install ...` here

Or go run, now that Go supports versioned running

Or `go run`, now that Go supports versioned running
@hash golangci-lint > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
export BINARY="golangci-lint"; \
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.37.0; \
fi
golangci-lint run --timeout 5m
$(GO) run github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2 run --timeout 5m
.PHONY: fmt
fmt:
go fmt ./...
$(GO) fmt ./...
.PHONY: release
release: release-dirs check-xgo release-windows release-linux release-darwin release-copy release-compress release-check

@ -15,7 +15,7 @@ Download a pre-built binary from our [downloads page](https://dl.gitea.io/change
## Configuration
See the [changelog.example.yml](changelog.example.yml) example file.
See the [changelog.example.yml](config/changelog.example.yml) example file.
## Usage
@ -31,7 +31,6 @@ changelog -m=1.11.0 -c=/path/to/my_config_file contributors
## Building
```
go generate ./...
go build
```

@ -1,44 +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.
//go:build ignore
// +build ignore
package main
import (
"fmt"
"io/ioutil"
"os"
)
const (
exampleFile = "changelog.example.yml"
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` + "`" + `)
}
`
)
func main() {
bytes, err := ioutil.ReadFile(exampleFile)
if err != nil {
fmt.Printf("Could not read from %s. Are you in the root directory of the project?", exampleFile)
os.Exit(1)
}
data := fmt.Sprintf(tmpl, string(bytes))
if err := ioutil.WriteFile(writeFile, []byte(data), os.ModePerm); err != nil {
fmt.Printf("Could not write to %s.", writeFile)
os.Exit(1)
}
}

@ -1,4 +1,4 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
jolheiser marked this conversation as resolved Outdated
Outdated
Review

intentional?!?

intentional?!?

Ah, nope. I had deleted the file and then renamed a different one to this, but forgot a new copyright.

Ah, nope. I had deleted the file and then renamed a different one to this, but forgot a new copyright.
// 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.
@ -7,18 +7,82 @@ package cmd
import (
"os"
"path/filepath"
"github.com/urfave/cli/v2"
)
var (
MilestoneFlag string
TagFlag string
ConfigPathFlag string
TokenFlag string
DetailsFlag bool
AfterFlag int64
IssuesFlag bool
// Version of changelog
Version = "development"
milestoneFlag string
tagFlag string
configPathFlag string
tokenFlag string
detailsFlag bool
afterFlag int64
issuesFlag bool
)
// New returns a new changelog App
func New() *cli.App {
app := &cli.App{
Name: "changelog",
Usage: "Changelog tools for Gitea",
Version: Version,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "milestone",
Aliases: []string{"m"},
Usage: "Targeted milestone",
Destination: &milestoneFlag,
},
&cli.StringFlag{
Name: "tag",
Aliases: []string{"T"},
Usage: "Git tag for milestone url, if not set milestone is used",
Destination: &tagFlag,
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Specify a config file",
Value: getDefaultConfigFile(),
Destination: &configPathFlag,
},
&cli.StringFlag{
Name: "token",
Aliases: []string{"t"},
Usage: "Access token for private repositories/instances",
Destination: &tokenFlag,
},
&cli.BoolFlag{
Name: "details",
Aliases: []string{"d"},
Usage: "Generate detail lists instead of long lists",
Destination: &detailsFlag,
},
&cli.Int64Flag{
Name: "after",
Aliases: []string{"a"},
Usage: "Only select PRs after a given index (continuing a previous changelog)",
Destination: &afterFlag,
},
&cli.BoolFlag{
Name: "issues",
Aliases: []string{"i"},
Usage: "Generate changelog from issues (otherwise from pulls)",
Destination: &issuesFlag,
},
},
Commands: []*cli.Command{
Generate,
Contributors,
Init,
},
}
return app
}
func getDefaultConfigFile() string {
pwd, err := os.Getwd()
if err != nil {

@ -20,17 +20,13 @@ var Contributors = &cli.Command{
Action: runContributors,
}
func runContributors(cmd *cli.Context) error {
if ConfigPathFlag == "" {
ConfigPathFlag = getDefaultConfigFile()
}
cfg, err := config.New(ConfigPathFlag)
func runContributors(_ *cli.Context) error {
cfg, err := config.New(configPathFlag)
if err != nil {
return err
}
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, MilestoneFlag, TagFlag, TokenFlag, IssuesFlag)
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, milestoneFlag, tagFlag, tokenFlag, issuesFlag)
if err != nil {
return err
}

@ -7,6 +7,7 @@ package cmd
import (
"fmt"
"regexp"
"strings"
"code.gitea.io/changelog/config"
"code.gitea.io/changelog/service"
@ -14,30 +15,19 @@ import (
"github.com/urfave/cli/v2"
)
var (
Generate = &cli.Command{
Name: "generate",
Usage: "Generates a changelog for a special milestone",
Action: runGenerate,
}
labels = make(map[string]string)
entries = make(map[string][]service.Entry)
defaultGroup string
)
var Generate = &cli.Command{
Name: "generate",
Usage: "Generates a changelog for a special milestone",
Action: runGenerate,
}
func runGenerate(cmd *cli.Context) error {
if ConfigPathFlag == "" {
ConfigPathFlag = getDefaultConfigFile()
}
cfg, err := config.New(ConfigPathFlag)
func runGenerate(_ *cli.Context) error {
cfg, err := config.New(configPathFlag)
if err != nil {
return err
}
processGroups(cfg.Groups)
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, MilestoneFlag, TagFlag, TokenFlag, IssuesFlag)
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, milestoneFlag, tagFlag, tokenFlag, issuesFlag)
if err != nil {
return err
}
@ -47,7 +37,17 @@ func runGenerate(cmd *cli.Context) error {
return err
}
processPRs(prs, cfg.SkipRegex)
var defaultGroup string
lunny marked this conversation as resolved Outdated
Outdated
Review

Shouldn't these code be invoked before service.New?

Shouldn't these code be invoked before `service.New`?
Outdated
Review

I dont see it in use before ... so more a code-convention thing when to init vars?

I'm ok as it's done atm by the way...

I dont see it in use before ... so more a code-convention thing when to init vars? I'm ok as it's done atm by the way...

I always init the var right before I need it. In terms of service.New, the service is only responsible for getting entries, not for grouping them. That happens further down.

I always init the var right before I need it. In terms of `service.New`, the service is only responsible for getting entries, not for grouping them. That happens further down.
for _, g := range cfg.Groups {
if g.Default {
defaultGroup = g.Name
}
}
if defaultGroup == "" {
defaultGroup = cfg.Groups[len(cfg.Groups)-1].Name
6543 marked this conversation as resolved Outdated
Outdated
Review

should we just drop if there is no default group and no group match?

should we just drop if there is no default group and no group match?

I'm ambivalent, but that would technically be a breaking change. This is how the old logic worked.

I'm ambivalent, but that would technically be a breaking change. This is how the old logic worked.
Outdated
Review

something for next pull ;)

I think we should break this but agree - this is out of scope for this pull

something for next pull ;) I think we should break this but agree - this is out of scope for this pull
}
entries := processPRs(prs, cfg.NameLabels(), defaultGroup, cfg.SkipRegex)
fmt.Println(title)
fmt.Println()
@ -56,7 +56,7 @@ func runGenerate(cmd *cli.Context) error {
continue
}
if DetailsFlag {
if detailsFlag {
fmt.Println("<details><summary>" + g.Name + "</summary>")
fmt.Println()
for _, entry := range entries[g.Name] {
@ -74,44 +74,32 @@ func runGenerate(cmd *cli.Context) error {
return nil
}
func processGroups(groups []config.Group) {
for _, g := range groups {
entries[g.Name] = []service.Entry{}
for _, l := range g.Labels {
labels[l] = g.Name
}
if g.Default {
defaultGroup = g.Name
}
}
if defaultGroup == "" {
defaultGroup = groups[len(groups)-1].Name
}
}
func processPRs(prs []service.Entry, skip *regexp.Regexp) {
func processPRs(prs []service.Entry, order []config.NameLabel, defaultGroup string, skip *regexp.Regexp) map[string][]service.Entry {
entries := make(map[string][]service.Entry)
PRLoop: // labels in Go, let's get old school
for _, pr := range prs {
if pr.Index < AfterFlag {
if pr.Index < afterFlag {
continue
}
var label string
for _, lb := range pr.Labels {
if skip != nil && skip.MatchString(lb.Name) {
continue PRLoop
}
}
section := processSection(pr, order, defaultGroup)
entries[section] = append(entries[section], pr)
}
return entries
}
if g, ok := labels[lb.Name]; ok && len(label) == 0 {
label = g
func processSection(pr service.Entry, order []config.NameLabel, defaultGroup string) string {
for _, o := range order {
for _, lb := range pr.Labels {
if strings.EqualFold(o.Label, lb.Name) {
return o.Name
}
}
if len(label) > 0 {
entries[label] = append(entries[label], pr)
} else {
entries[defaultGroup] = append(entries[defaultGroup], pr)
}
}
return defaultGroup
}

@ -32,7 +32,7 @@ var (
nameFlag string
)
func runInit(cmd *cli.Context) error {
func runInit(_ *cli.Context) error {
if _, err := os.Stat(nameFlag); err == nil {
return fmt.Errorf("file '%s' already exists", nameFlag)
}

@ -5,12 +5,14 @@
package config
import (
_ "embed"
"io/ioutil"
"regexp"
"gopkg.in/yaml.v2"
)
//go:embed changelog.example.yml
var DefaultConfig []byte
// Group is a grouping of PRs
@ -20,6 +22,24 @@ type Group struct {
Default bool `yaml:"default"`
}
// NameLabel is a Group mapping for a Label to a Name
type NameLabel struct {
Name string
Label string
}
// NameLabels returns a slice of NameLabel
func (g Group) NameLabels() []NameLabel {
nl := make([]NameLabel, 0)
for _, l := range g.Labels {
nl = append(nl, NameLabel{
Name: g.Name,
Label: l,
})
}
return nl
}
// Config is the changelog settings
type Config struct {
Repo string `yaml:"repo"`
@ -30,13 +50,20 @@ type Config struct {
SkipRegex *regexp.Regexp `yaml:"-"`
}
// NameLabels returns a slice of NameLabel for each Group, keeping them in order (priority)
func (c Config) NameLabels() []NameLabel {
nl := make([]NameLabel, 0)
for _, g := range c.Groups {
nl = append(nl, g.NameLabels()...)
}
return nl
}
// New 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 := DefaultConfig
if len(configPath) != 0 {
configContent, err = ioutil.ReadFile(configPath)
if err != nil {
return nil, err

@ -1,65 +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 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:
-
name: BREAKING
labels:
- kind/breaking
-
name: FEATURES
labels:
- kind/feature
-
name: BUGFIXES
labels:
- kind/bug
-
name: ENHANCEMENTS
labels:
- kind/enhancement
- kind/refactor
- kind/ui
-
name: SECURITY
labels:
- kind/security
-
name: TESTING
labels:
- kind/testing
-
name: TRANSLATION
labels:
- kind/translation
-
name: BUILD
labels:
- kind/build
- kind/lint
-
name: DOCS
labels:
- kind/docs
-
name: MISC
default: true
# regex indicating which labels to skip for the changelog
skip-labels: skip-changelog|backport\/.+`)
}

13
go.mod

@ -1,12 +1,19 @@
module code.gitea.io/changelog
go 1.13
go 1.18
require (
code.gitea.io/sdk/gitea v0.14.0
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/google/go-github/v32 v32.1.0
github.com/urfave/cli/v2 v2.2.0
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
gopkg.in/yaml.v2 v2.3.0
)
require (
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/google/go-querystring v1.0.0 // indirect
github.com/hashicorp/go-version v1.2.1 // indirect
github.com/russross/blackfriday/v2 v2.0.1 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de // indirect
)

66
main.go

@ -4,79 +4,15 @@
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"
)
var (
// Version of changelog
Version = "development"
)
func main() {
app := &cli.App{
Name: "changelog",
Usage: "Changelog tools for Gitea",
Version: Version,
Flags: []cli.Flag{
&cli.StringFlag{
Name: "milestone",
Aliases: []string{"m"},
Usage: "Targeted milestone",
Destination: &cmd.MilestoneFlag,
},
&cli.StringFlag{
Name: "tag",
Aliases: []string{"T"},
Usage: "Git tag for milestone url, if not set milestone is used",
Destination: &cmd.TagFlag,
},
&cli.StringFlag{
Name: "config",
Aliases: []string{"c"},
Usage: "Specify a config file",
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,
},
&cli.BoolFlag{
Name: "issues",
Aliases: []string{"i"},
Usage: "Generate changelog from issues (otherwise from pulls)",
Destination: &cmd.IssuesFlag,
},
},
Commands: []*cli.Command{
cmd.Generate,
cmd.Contributors,
cmd.Init,
},
}
app := cmd.New()
if err := app.Run(os.Args); err != nil {
fmt.Printf("Failed to run app with %s: %v\n", os.Args[1:], err)
}