diff --git a/.drone.yml b/.drone.yml index a7fc43a..99b6073 100644 --- a/.drone.yml +++ b/.drone.yml @@ -13,18 +13,18 @@ trigger: steps: - name: build pull: always - image: golang:1.17 + image: golang: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 diff --git a/.golangci.yml b/.golangci.yml index 6561c3f..2b6a8ba 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -10,12 +10,11 @@ linters: - gocritic - gocyclo - gofmt - - golint - gosimple - govet - - maligned - misspell - prealloc + - revive - staticcheck - structcheck - typecheck diff --git a/Makefile b/Makefile index a97118f..113dbf1 100644 --- a/Makefile +++ b/Makefile @@ -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: - @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 diff --git a/README.md b/README.md index faf8c97..cc94d7c 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/changelog.example.go b/changelog.example.go deleted file mode 100644 index b370761..0000000 --- a/changelog.example.go +++ /dev/null @@ -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) - } -} diff --git a/cmd/cmd.go b/cmd/cmd.go index 434cb41..25284c8 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -1,4 +1,4 @@ -// Copyright 2020 The Gitea Authors. All rights reserved. +// 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 { diff --git a/cmd/contributors.go b/cmd/contributors.go index d0b09f6..fbed4d9 100644 --- a/cmd/contributors.go +++ b/cmd/contributors.go @@ -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 } diff --git a/cmd/generate.go b/cmd/generate.go index 94d264f..ee77b63 100644 --- a/cmd/generate.go +++ b/cmd/generate.go @@ -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 + for _, g := range cfg.Groups { + if g.Default { + defaultGroup = g.Name + } + } + if defaultGroup == "" { + defaultGroup = cfg.Groups[len(cfg.Groups)-1].Name + } + + 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("
" + g.Name + "") 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 } diff --git a/cmd/init.go b/cmd/init.go index 0666cfc..629a155 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -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) } diff --git a/changelog.example.yml b/config/changelog.example.yml similarity index 100% rename from changelog.example.yml rename to config/changelog.example.yml diff --git a/config/config.go b/config/config.go index cf040ed..08a32ae 100644 --- a/config/config.go +++ b/config/config.go @@ -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 diff --git a/config/config_default.go b/config/config_default.go deleted file mode 100644 index b224d64..0000000 --- a/config/config_default.go +++ /dev/null @@ -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\/.+`) -} diff --git a/go.mod b/go.mod index a85f863..2705659 100644 --- a/go.mod +++ b/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 +) diff --git a/main.go b/main.go index dab4a04..2f5326e 100644 --- a/main.go +++ b/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) }