Merge remote-tracking branch 'origin/main' into fix-51-set-flags-after-subcommand

This commit is contained in:
Andrew Thornton 2022-09-15 15:40:50 +01:00
commit 7f649f6174
No known key found for this signature in database
GPG Key ID: 3CDE74631F13A748
17 changed files with 221 additions and 302 deletions

View File

@ -13,18 +13,18 @@ trigger:
steps:
- name: build
pull: always
image: golang:1.13
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.13
image: golang:1.18
environment:
GOPROXY: https://goproxy.cn,direct
GOPROXY: https://goproxy.io,direct
commands:
- make lint
@ -54,7 +54,7 @@ platform:
trigger:
branch:
- master
- main
event:
- push
- tag
@ -70,22 +70,21 @@ 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
- name: bucket-master
- name: bucket-main
pull: always
image: plugins/s3:1
image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
bucket: gitea-artifacts
endpoint: https://ams3.digitaloceanspaces.com
source: "dist/release/*"
strip_prefix: dist/release/
target: /changelog-tool/master
target: /changelog-tool/main
environment:
AWS_ACCESS_KEY_ID:
from_secret: aws_access_key_id
@ -97,12 +96,11 @@ steps:
- name: bucket-tag
pull: always
image: plugins/s3:1
image: woodpeckerci/plugin-s3:latest
settings:
acl: public-read
bucket: releases
endpoint: https://storage.gitea.io
path_style: true
bucket: gitea-artifacts
endpoint: https://ams3.digitaloceanspaces.com
source: "dist/release/*"
strip_prefix: dist/release/
target: "/changelog-tool/${DRONE_TAG##v}"

View File

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

View File

@ -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.23.1; \
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
@ -82,6 +78,6 @@ release-check:
.PHONY: release-compress
release-compress:
@hash gxz > /dev/null 2>&1; if [ $$? -ne 0 ]; then \
$(GO) get -u github.com/ulikunitz/xz/cmd/gxz; \
$(GO) install github.com/ulikunitz/xz/cmd/gxz@latest; \
fi
cd $(DIST)/release/; for file in `find . -type f -name "*"`; do echo "compressing $${file}" && gxz -k -9 $${file}; done;

View File

@ -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
```

View File

@ -1,43 +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.
// +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)
}
}

View File

@ -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,17 +7,83 @@ package cmd
import (
"os"
"path/filepath"
"github.com/urfave/cli/v2"
)
var (
MilestoneFlag 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,
},
}
copyGlobalFlags(app)
return app
}
func getDefaultConfigFile() string {
pwd, err := os.Getwd()
if err != nil {
@ -30,3 +96,28 @@ func getDefaultConfigFile() string {
}
return ""
}
func copyGlobalFlags(app *cli.App) {
for _, command := range app.Commands {
originalFlagNames := make([]string, 0, len(command.Flags))
for _, flag := range command.Flags {
originalFlagNames = append(originalFlagNames, flag.Names()...)
}
for _, flag := range app.Flags {
found := false
flagNameLoop:
for _, name := range flag.Names() {
for _, originalName := range originalFlagNames {
if name == originalName {
found = true
break flagNameLoop
}
}
}
if !found {
command.Flags = append(command.Flags, flag)
}
}
}
}

View File

@ -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, TokenFlag, IssuesFlag)
s, err := service.New(cfg.Service, cfg.Repo, cfg.BaseURL, milestoneFlag, tagFlag, tokenFlag, issuesFlag)
if err != nil {
return err
}

View File

@ -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, 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 == "" {
fmt.Println("<!-- WARNING - no default group found -->")
}
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,35 @@ 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)
if section == "" {
continue
}
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
}

View File

@ -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)
}

View File

@ -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

View File

@ -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
View File

@ -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
)

87
main.go
View File

@ -4,102 +4,17 @@
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: "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,
},
}
copyGlobalFlags(app)
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)
}
}
func copyGlobalFlags(app *cli.App) {
for _, command := range app.Commands {
originalFlagNames := make([]string, 0, len(command.Flags))
for _, flag := range command.Flags {
originalFlagNames = append(originalFlagNames, flag.Names()...)
}
for _, flag := range app.Flags {
found := false
flagNameLoop:
for _, name := range flag.Names() {
for _, originalName := range originalFlagNames {
if name == originalName {
found = true
break flagNameLoop
}
}
}
if !found {
command.Flags = append(command.Flags, flag)
}
}
}
}

View File

@ -14,6 +14,7 @@ import (
// Gitea defines a Gitea service
type Gitea struct {
Milestone string
GitTag string
Token string
BaseURL string
Owner string
@ -40,7 +41,7 @@ func (ge *Gitea) Generate() (string, []Entry, error) {
from = "issues"
}
tagURL := getGiteaTagURL(client, ge.BaseURL, ge.Owner, ge.Repo, ge.Milestone, from, milestone.ID)
tagURL := getGiteaTagURL(client, ge.BaseURL, ge.Owner, ge.Repo, ge.Milestone, ge.GitTag, from, milestone.ID)
perPage := ge.perPage(client)
for p := 1; ; p++ {
@ -81,11 +82,11 @@ func (ge *Gitea) Generate() (string, []Entry, error) {
return tagURL, entries, nil
}
func getGiteaTagURL(c *gitea.Client, baseURL, owner, repo, mileName, from string, mileID int64) string {
func getGiteaTagURL(c *gitea.Client, baseURL, owner, repo, mileName, gitTag, from string, mileID int64) string {
if err := c.CheckServerVersionConstraint(">=1.12"); err != nil {
return fmt.Sprintf("## [%s](%s/%s/%s/%s?q=&type=all&state=closed&milestone=%d) - %s", mileName, baseURL, owner, repo, from, mileID, time.Now().Format("2006-01-02"))
}
return fmt.Sprintf("## [%s](%s/%s/%s/releases/tag/%s) - %s", mileName, baseURL, owner, repo, mileName, time.Now().Format("2006-01-02"))
return fmt.Sprintf("## [%s](%s/%s/%s/releases/tag/%s) - %s", mileName, baseURL, owner, repo, gitTag, time.Now().Format("2006-01-02"))
}
func convertToEntry(issue gitea.Issue) Entry {

View File

@ -15,6 +15,7 @@ import (
// GitHub defines a GitHub service
type GitHub struct {
Milestone string
GitTag string
Token string
Repo string
Issues bool
@ -22,7 +23,7 @@ type GitHub struct {
// Generate returns a GitHub changelog
func (gh *GitHub) Generate() (string, []Entry, 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"))
tagURL := fmt.Sprintf("## [%s](https://github.com/%s/releases/tag/%s) - %s", gh.Milestone, gh.Repo, gh.GitTag, time.Now().Format("2006-01-02"))
client := github.NewClient(nil)
ctx := context.Background()

View File

@ -12,11 +12,16 @@ import (
const defaultGitea = "https://gitea.com"
// New returns a service from a string
func New(serviceType, repo, baseURL, milestone, token string, issues bool) (Service, error) {
func New(serviceType, repo, baseURL, milestone, tag, token string, issues bool) (Service, error) {
if len(tag) == 0 {
tag = milestone
}
switch strings.ToLower(serviceType) {
case "github":
return &GitHub{
Milestone: milestone,
GitTag: tag,
Token: token,
Repo: repo,
Issues: issues,
@ -28,6 +33,7 @@ func New(serviceType, repo, baseURL, milestone, token string, issues bool) (Serv
}
return &Gitea{
Milestone: milestone,
GitTag: tag,
Token: token,
BaseURL: baseURL,
Owner: ownerRepo[0],