You've already forked go-semantic-release
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,7 +4,9 @@
|
|||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
go-semantic-release
|
||||||
|
|
||||||
|
!cmd/go-semantic-release
|
||||||
# Test binary, build with `go test -c`
|
# Test binary, build with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
|
||||||
@@ -12,3 +14,5 @@
|
|||||||
*.out
|
*.out
|
||||||
.version
|
.version
|
||||||
.vscode/settings.json
|
.vscode/settings.json
|
||||||
|
CHANGELOG.md
|
||||||
|
cover.html
|
||||||
|
|||||||
11
.golangci.yml
Normal file
11
.golangci.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
run:
|
||||||
|
tests: true
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
golint:
|
||||||
|
min-confidence: 0
|
||||||
|
issues:
|
||||||
|
exclude-use-default: true
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- golint
|
||||||
22
.release.yml
Normal file
22
.release.yml
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
commitFormat: angular
|
||||||
|
title: "go-semantic-release release"
|
||||||
|
branch:
|
||||||
|
master: beta
|
||||||
|
rc: rc
|
||||||
|
beta: beta
|
||||||
|
alpha: alpha
|
||||||
|
add_git_releases: alpha
|
||||||
|
changelog:
|
||||||
|
printAll: false
|
||||||
|
template: ''
|
||||||
|
templatePath: ''
|
||||||
|
release: 'github'
|
||||||
|
assets:
|
||||||
|
- name: ./build/go-semantic-release
|
||||||
|
compress: false
|
||||||
|
- name: ./build/go-semantic-release.exe
|
||||||
|
compress: false
|
||||||
|
github:
|
||||||
|
repo: "go-semantic-release"
|
||||||
|
user: "nightapes"
|
||||||
|
customUrl: ""
|
||||||
40
.travis.yml
Normal file
40
.travis.yml
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
dist: xenial
|
||||||
|
|
||||||
|
git:
|
||||||
|
depth: false
|
||||||
|
|
||||||
|
language: go
|
||||||
|
go:
|
||||||
|
- 1.12.x
|
||||||
|
go_import_path: github.com/nightapes/go-semantic-release
|
||||||
|
cache:
|
||||||
|
directories:
|
||||||
|
- $HOME/.cache/go-build
|
||||||
|
- $HOME/gopath/pkg/mod
|
||||||
|
|
||||||
|
services:
|
||||||
|
- docker
|
||||||
|
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b $(go env GOPATH)/bin v1.16.0
|
||||||
|
|
||||||
|
script:
|
||||||
|
- golangci-lint run ./...
|
||||||
|
- go test -v ./...
|
||||||
|
- go build -o build/go-semantic-release-temp ./cmd/go-semantic-release/
|
||||||
|
- echo "Building version `./build/go-semantic-release-temp next --loglevel debug --no-cache`"
|
||||||
|
- go build -o build/go-semantic-release -ldflags "-X main.version=`./build/go-semantic-release-temp next`" ./cmd/go-semantic-release/
|
||||||
|
- GOOS=windows GOARCH=386 go build -o build/go-semantic-release.exe -ldflags "-X main.version=`./build/go-semantic-release-temp next`" ./cmd/go-semantic-release/
|
||||||
|
|
||||||
|
after_success:
|
||||||
|
- ./build/go-semantic-release-temp release --loglevel debug
|
||||||
|
|
||||||
|
branches:
|
||||||
|
except:
|
||||||
|
- /^v\d+\.\d+\.\d+$/
|
||||||
10
README.md
10
README.md
@@ -1,5 +1,15 @@
|
|||||||
# go-semantic-release
|
# go-semantic-release
|
||||||
|
|
||||||
|
## Release Types
|
||||||
|
|
||||||
|
| Type | Git tag | Changelog | Release | Write access git | Api token |
|
||||||
|
|--- |:---: |:---: |:---: |:---: |:---: |
|
||||||
|
| `git` | :white_check_mark: | | | :white_check_mark:| |
|
||||||
|
| `github` | :white_check_mark: | :white_check_mark: | :white_check_mark:| | :white_check_mark: |
|
||||||
|
| `gitlab` | :white_check_mark: | :white_check_mark: | :white_check_mark:| | :white_check_mark: |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
`go build ./cmd/go-semantic-release/`
|
`go build ./cmd/go-semantic-release/`
|
||||||
|
|||||||
2
README2.md
Normal file
2
README2.md
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
go build ./cmd/main.go && ./main.exe version next --path /f/Repro/ambassador/
|
||||||
|
go build ./cmd/main.go && ./main.exe --loglevel debug version set v1.1.1 --path /f/Repro/ambassador/
|
||||||
65
cmd/go-semantic-release/commands/changelog.go
Normal file
65
cmd/go-semantic-release/commands/changelog.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
changelogCmd.Flags().StringP("out", "o", "CHANGELOG.md", "Name of the file")
|
||||||
|
rootCmd.AddCommand(changelogCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var changelogCmd = &cobra.Command{
|
||||||
|
Use: "changelog",
|
||||||
|
Short: "Generate changelog and save to file",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
config, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repository, err := cmd.Flags().GetString("repository")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
force, err := cmd.Flags().GetBool("no-cache")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := cmd.Flags().GetString("out")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := semanticrelease.New(readConfig(config), repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := s.GetCIProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseVersion, commits, err := s.GetNextVersion(provider, force)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
log.Debugf("Found %d commits till last release", len(commits))
|
||||||
|
|
||||||
|
generatedChangelog, err := s.GetChangelog(commits, releaseVersion)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = s.WriteChangeLog(generatedChangelog.Content, file); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
53
cmd/go-semantic-release/commands/next.go
Normal file
53
cmd/go-semantic-release/commands/next.go
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(nextCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var nextCmd = &cobra.Command{
|
||||||
|
Use: "next",
|
||||||
|
Short: "Get next release version",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
config, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repository, err := cmd.Flags().GetString("repository")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
force, err := cmd.Flags().GetBool("no-cache")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := semanticrelease.New(readConfig(config), repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := s.GetCIProvider()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Infof("Will not calculate version, set fake version. Could not find CI Provider, if running locally, set env CI=true")
|
||||||
|
fmt.Println("0.0.0-fake.0")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseVersion, _, err := s.GetNextVersion(provider, force)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(releaseVersion.Next.Version.String())
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
43
cmd/go-semantic-release/commands/release.go
Normal file
43
cmd/go-semantic-release/commands/release.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(releaseCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var releaseCmd = &cobra.Command{
|
||||||
|
Use: "release",
|
||||||
|
Short: "Make a release",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
config, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repository, err := cmd.Flags().GetString("repository")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
force, err := cmd.Flags().GetBool("no-cache")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := semanticrelease.New(readConfig(config), repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := s.GetCIProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.Release(provider, force)
|
||||||
|
},
|
||||||
|
}
|
||||||
60
cmd/go-semantic-release/commands/root.go
Normal file
60
cmd/go-semantic-release/commands/root.go
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "go-semantic-release",
|
||||||
|
Short: "Make simple releases",
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
level, err := cmd.Flags().GetString("loglevel")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
setLoglevel(level)
|
||||||
|
cmd.SilenceUsage = true
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
//Execute rootCmd
|
||||||
|
func Execute(version string) {
|
||||||
|
rootCmd.Version = version
|
||||||
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
currentDir, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
rootCmd.PersistentFlags().StringP("repository", "r", currentDir, "Path to repository")
|
||||||
|
rootCmd.PersistentFlags().StringP("loglevel", "l", "error", "Set loglevel")
|
||||||
|
rootCmd.PersistentFlags().StringP("config", "c", ".release.yml", "Path to config file")
|
||||||
|
rootCmd.PersistentFlags().Bool("no-cache", false, "Ignore cache, don't use in ci build")
|
||||||
|
}
|
||||||
|
|
||||||
|
func readConfig(file string) *config.ReleaseConfig {
|
||||||
|
releaseConfig, err := config.Read(file)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return releaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLoglevel(level string) {
|
||||||
|
parsed, err := log.ParseLevel(level)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("Invalid loglevel %s", level)
|
||||||
|
} else {
|
||||||
|
log.SetLevel(parsed)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
41
cmd/go-semantic-release/commands/set.go
Normal file
41
cmd/go-semantic-release/commands/set.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(setCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var setCmd = &cobra.Command{
|
||||||
|
Use: "set [version]",
|
||||||
|
Args: cobra.ExactArgs(1),
|
||||||
|
Short: "Set next release version",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
|
||||||
|
config, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repository, err := cmd.Flags().GetString("repository")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := semanticrelease.New(readConfig(config), repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
provider, err := s.GetCIProvider()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return s.SetVersion(provider, args[0])
|
||||||
|
},
|
||||||
|
}
|
||||||
37
cmd/go-semantic-release/commands/zip.go
Normal file
37
cmd/go-semantic-release/commands/zip.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(zipCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
var zipCmd = &cobra.Command{
|
||||||
|
Use: "zip",
|
||||||
|
Short: "Zip configured artifact from release config",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
config, err := cmd.Flags().GetString("config")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
repository, err := cmd.Flags().GetString("repository")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
s, err := semanticrelease.New(readConfig(config), repository)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = s.ZipFiles(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
@@ -1,112 +1,11 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
"github.com/Nightapes/go-semantic-release/cmd/go-semantic-release/commands"
|
||||||
|
|
||||||
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
"gopkg.in/urfave/cli.v1" // imports as package "cli"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var version string
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app := cli.NewApp()
|
commands.Execute(version)
|
||||||
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "loglevel",
|
|
||||||
Value: "error",
|
|
||||||
Usage: "Set loglevel 'LEVEL",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
app.Commands = []cli.Command{
|
|
||||||
{
|
|
||||||
Name: "version",
|
|
||||||
Aliases: []string{"v"},
|
|
||||||
Usage: "version commands",
|
|
||||||
Subcommands: []cli.Command{
|
|
||||||
{
|
|
||||||
Name: "set",
|
|
||||||
Usage: "set version `VERSION`",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
setLoglevel(c.GlobalString("loglevel"))
|
|
||||||
path := c.String("path")
|
|
||||||
version := c.Args().First()
|
|
||||||
log.Infof("Version %s", version)
|
|
||||||
return semanticrelease.SetVersion(version, path)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config, c",
|
|
||||||
Value: "release-config.json",
|
|
||||||
Usage: "Load release configuration from `FILE`",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "path, p",
|
|
||||||
Usage: "`PATH` to repro ",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "next",
|
|
||||||
Usage: "get next `VERSION` or the set one ",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
setLoglevel(c.GlobalString("loglevel"))
|
|
||||||
path := c.String("path")
|
|
||||||
return semanticrelease.GetNextVersion(path)
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config, c",
|
|
||||||
Value: "release-config.json",
|
|
||||||
Usage: "Load release configuration from `FILE`",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "path, p",
|
|
||||||
Usage: "`PATH` to repro ",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "release",
|
|
||||||
Aliases: []string{},
|
|
||||||
Usage: "make release",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
Flags: []cli.Flag{
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "config, c",
|
|
||||||
Value: "release-config.json",
|
|
||||||
Usage: "Load release configuration from `FILE`",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
Name: "init",
|
|
||||||
Aliases: []string{},
|
|
||||||
Usage: "create config",
|
|
||||||
Action: func(c *cli.Context) error {
|
|
||||||
return nil
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
//gitutil.GetCommits(folder)
|
|
||||||
err := app.Run(os.Args)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func setLoglevel(level string) {
|
|
||||||
parsed, err := log.ParseLevel(level)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("Invalid loglevel %s", level)
|
|
||||||
} else {
|
|
||||||
log.SetLevel(parsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
20
go.mod
20
go.mod
@@ -4,10 +4,20 @@ go 1.12
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/Masterminds/semver v1.4.2
|
github.com/Masterminds/semver v1.4.2
|
||||||
github.com/sirupsen/logrus v1.4.1
|
github.com/coreos/etcd v3.3.10+incompatible
|
||||||
github.com/urfave/cli v1.20.0 // indirect
|
github.com/gliderlabs/ssh v0.2.2 // indirect
|
||||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f // indirect
|
github.com/google/go-cmp v0.3.0 // indirect
|
||||||
gopkg.in/src-d/go-git.v4 v4.11.0
|
github.com/google/go-github/v25 v25.1.3
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0
|
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c // indirect
|
||||||
|
github.com/sirupsen/logrus v1.4.2
|
||||||
|
github.com/spf13/cobra v0.0.5
|
||||||
|
github.com/stretchr/testify v1.3.0
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
|
||||||
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||||
|
google.golang.org/appengine v1.6.1 // indirect
|
||||||
|
gopkg.in/src-d/go-billy.v4 v4.3.1
|
||||||
|
gopkg.in/src-d/go-git.v4 v4.12.0
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
)
|
)
|
||||||
|
|||||||
135
go.sum
135
go.sum
@@ -1,77 +1,154 @@
|
|||||||
|
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc=
|
||||||
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7 h1:uSoVVbwJiQipAclBbw+8quDsfcvFjOpI5iCf4p/cqCs=
|
||||||
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs=
|
||||||
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
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/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
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/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
|
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||||
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568 h1:BHsljHzVlRcyQhjrss6TZTdY2VfCqZPbv5k3iBFa2ZQ=
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
||||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
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/gliderlabs/ssh v0.1.3/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0=
|
||||||
|
github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
||||||
|
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/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
|
||||||
|
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||||
|
github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ=
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||||
|
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||||
|
github.com/google/go-github/v25 v25.1.3 h1:Ht4YIQgUh4l4lc80fvGnw60khXysXvlgPxPP8uJG3EA=
|
||||||
|
github.com/google/go-github/v25 v25.1.3/go.mod h1:6z5pC69qHtrPJ0sXPsj4BLnd82b+r6sLB7qcBoRZqpw=
|
||||||
|
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/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e h1:RgQk53JHp/Cjunrr1WlsXSZpqXn+uREuHvUVcK82CV8=
|
||||||
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
github.com/kevinburke/ssh_config v0.0.0-20180830205328-81db2a75821e/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c h1:VAx3LRNjVNvjtgO7KFRuT/3aye/0zJvwn01rHSfoolo=
|
||||||
|
github.com/kevinburke/ssh_config v0.0.0-20190630040420-2e50c441276c/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||||
github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0=
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
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-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
|
github.com/pelletier/go-buffruneio v0.2.0 h1:U4t4R6YkofJ5xHm3dJzuRpPZ0mr5MMCoAWooScCR7aA=
|
||||||
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||||
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
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/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
github.com/sergi/go-diff v1.0.0 h1:Kpca3qRNrduNnOQeazBd0ysaKrUJiIuISHxogkT9RPQ=
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||||
|
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.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||||
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
|
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/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4=
|
||||||
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
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/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 h1:HuIa8hRrWRSrqYzx1qI49NNxhdi2PrY7gxVSq1JjLDc=
|
||||||
|
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190502183928-7f726cade0ab/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 h1:rTIdg5QFRR7XCaK4LCjBiPbx8j4DQRpdYMnGn/bJUEU=
|
||||||
|
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0=
|
||||||
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI=
|
||||||
|
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas=
|
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9 h1:lkiLiLBHGoH3XnqSLUIaBsilGMUjI+Uy2Xu2JLUtTas=
|
||||||
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20180903190138-2b024373dcd9/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
|
||||||
|
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f h1:sjxqKRXfnzgJFg/igBXeLZoBVAKXuAAljgr+PcNr7u8=
|
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||||
|
google.golang.org/appengine v1.1.0 h1:igQkv0AAhEIvTEpD5LIpAfav2eeVO9HBTjvKHVJPRSs=
|
||||||
|
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||||
|
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||||
|
google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I=
|
||||||
|
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
gopkg.in/src-d/go-billy.v4 v4.3.1 h1:OkK1DmefDy1Z6Veu82wdNj/cLpYORhdX4qdaYCPwc7s=
|
||||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
|
gopkg.in/src-d/go-billy.v4 v4.3.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||||
gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU=
|
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||||
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
|
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
|
||||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
|
||||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
|
|||||||
@@ -1,63 +1,102 @@
|
|||||||
|
// Package analyzer provides different commit analyzer
|
||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//Analyzer struct
|
||||||
type Analyzer struct {
|
type Analyzer struct {
|
||||||
CommitFormat string
|
analyzeCommit analyzeCommit
|
||||||
|
Config config.ChangelogConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type Rules struct {
|
//Release types, like major
|
||||||
Tag string
|
type Release string
|
||||||
Release string
|
|
||||||
|
//Scope of the commit, like feat, fix,..
|
||||||
|
type Scope string
|
||||||
|
|
||||||
|
//Rule for commits
|
||||||
|
type Rule struct {
|
||||||
|
Tag string
|
||||||
|
TagString string
|
||||||
|
Release Release
|
||||||
|
Changelog bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type AnalyzeCommit interface {
|
type analyzeCommit interface {
|
||||||
Analyze(commit gitutil.Commit, tag string) (AnalyzedCommit, bool)
|
analyze(commit gitutil.Commit, tag Rule) (AnalyzedCommit, bool, error)
|
||||||
GetRules() []Rules
|
getRules() []Rule
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//AnalyzedCommit struct
|
||||||
type AnalyzedCommit struct {
|
type AnalyzedCommit struct {
|
||||||
Commit gitutil.Commit
|
Commit gitutil.Commit
|
||||||
ParsedMessage string
|
ParsedMessage string
|
||||||
Scope string
|
Scope Scope
|
||||||
ParsedBreakingChangeMessage string
|
ParsedBreakingChangeMessage string
|
||||||
|
Tag string
|
||||||
|
TagString string
|
||||||
|
Print bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(format string) *Analyzer {
|
//New Analyzer struct for given commit format
|
||||||
return &Analyzer{
|
func New(format string, config config.ChangelogConfig) (*Analyzer, error) {
|
||||||
CommitFormat: format,
|
analyzer := &Analyzer{
|
||||||
|
Config: config,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
switch format {
|
||||||
|
|
||||||
func (a *Analyzer) Analyze(commits []gitutil.Commit) map[string][]AnalyzedCommit {
|
|
||||||
|
|
||||||
var commitAnalayzer AnalyzeCommit
|
|
||||||
switch a.CommitFormat {
|
|
||||||
case "angular":
|
case "angular":
|
||||||
log.Infof("analyze angular format")
|
log.Debugf("Commit format set to angular")
|
||||||
commitAnalayzer = NewAngular()
|
analyzer.analyzeCommit = newAngular()
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid commit format: %s", format)
|
||||||
}
|
}
|
||||||
|
return analyzer, nil
|
||||||
|
|
||||||
analyzedCommits := make(map[string][]AnalyzedCommit)
|
}
|
||||||
|
|
||||||
|
// GetRules from current mode
|
||||||
|
func (a *Analyzer) GetRules() []Rule {
|
||||||
|
return a.analyzeCommit.getRules()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Analyze commits and return commits splitted by major,minor,patch
|
||||||
|
func (a *Analyzer) Analyze(commits []gitutil.Commit) map[Release][]AnalyzedCommit {
|
||||||
|
|
||||||
|
analyzedCommits := make(map[Release][]AnalyzedCommit)
|
||||||
analyzedCommits["major"] = make([]AnalyzedCommit, 0)
|
analyzedCommits["major"] = make([]AnalyzedCommit, 0)
|
||||||
analyzedCommits["minor"] = make([]AnalyzedCommit, 0)
|
analyzedCommits["minor"] = make([]AnalyzedCommit, 0)
|
||||||
analyzedCommits["patch"] = make([]AnalyzedCommit, 0)
|
analyzedCommits["patch"] = make([]AnalyzedCommit, 0)
|
||||||
|
analyzedCommits["none"] = make([]AnalyzedCommit, 0)
|
||||||
|
|
||||||
for _, commit := range commits {
|
for _, commit := range commits {
|
||||||
for _, rule := range commitAnalayzer.GetRules() {
|
for _, rule := range a.analyzeCommit.getRules() {
|
||||||
analyzedCommit, hasBreakingChange := commitAnalayzer.Analyze(commit, rule.Tag)
|
analyzedCommit, hasBreakingChange, err := a.analyzeCommit.analyze(commit, rule)
|
||||||
if hasBreakingChange {
|
if err == nil {
|
||||||
analyzedCommits["major"] = append(analyzedCommits["major"], analyzedCommit)
|
if a.Config.PrintAll {
|
||||||
} else {
|
analyzedCommit.Print = true
|
||||||
analyzedCommits[rule.Release] = append(analyzedCommits[rule.Release], analyzedCommit)
|
} else {
|
||||||
|
analyzedCommit.Print = rule.Changelog
|
||||||
|
}
|
||||||
|
if hasBreakingChange {
|
||||||
|
analyzedCommits["major"] = append(analyzedCommits["major"], analyzedCommit)
|
||||||
|
} else {
|
||||||
|
analyzedCommits[rule.Release] = append(analyzedCommits[rule.Release], analyzedCommit)
|
||||||
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.Debugf("Analyzed commits: major=%d minor=%d patch=%d none=%d", len(analyzedCommits["major"]), len(analyzedCommits["minor"]), len(analyzedCommits["patch"]), len(analyzedCommits["none"]))
|
||||||
|
|
||||||
return analyzedCommits
|
return analyzedCommits
|
||||||
}
|
}
|
||||||
|
|||||||
16
internal/analyzer/analyzer_test.go
Normal file
16
internal/analyzer/analyzer_test.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package analyzer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAnalyzer(t *testing.T) {
|
||||||
|
|
||||||
|
_, err := analyzer.New("unknown", config.ChangelogConfig{})
|
||||||
|
assert.Error(t, err)
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,65 +1,113 @@
|
|||||||
|
// Package analyzer provides different commit analyzer
|
||||||
package analyzer
|
package analyzer
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Angular struct {
|
type angular struct {
|
||||||
rules []Rules
|
rules []Rule
|
||||||
regex string
|
regex string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAngular() *Angular {
|
func newAngular() *angular {
|
||||||
return &Angular{
|
return &angular{
|
||||||
regex: `(TAG)(?:\((.*)\))?: (.*)`,
|
regex: `(TAG)(?:\((.*)\))?: (.*)`,
|
||||||
rules: []Rules{
|
rules: []Rule{
|
||||||
{
|
{
|
||||||
Tag: "feat",
|
Tag: "feat",
|
||||||
Release: "minor",
|
TagString: "Features",
|
||||||
|
Release: "minor",
|
||||||
|
Changelog: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Tag: "fix",
|
Tag: "fix",
|
||||||
Release: "patch",
|
TagString: "Bug fixes",
|
||||||
|
Release: "patch",
|
||||||
|
Changelog: true,
|
||||||
}, {
|
}, {
|
||||||
Tag: "perf",
|
Tag: "perf",
|
||||||
Release: "patch",
|
TagString: "Performance improvments",
|
||||||
|
Release: "patch",
|
||||||
|
Changelog: true,
|
||||||
|
}, {
|
||||||
|
Tag: "docs",
|
||||||
|
TagString: "Documentation changes",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tag: "style",
|
||||||
|
TagString: "Style",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
}, {
|
||||||
|
Tag: "refactor",
|
||||||
|
TagString: "Code refactor",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
}, {
|
||||||
|
Tag: "test",
|
||||||
|
TagString: "Testing",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
}, {
|
||||||
|
Tag: "chore",
|
||||||
|
TagString: "Changes to the build process or auxiliary tools and libraries such as documentation generation",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
}, {
|
||||||
|
Tag: "build",
|
||||||
|
TagString: "Changes to CI/CD",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Angular) GetRules() []Rules {
|
func (a *angular) getRules() []Rule {
|
||||||
return a.rules
|
return a.rules
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Angular) Analyze(commit gitutil.Commit, tag string) (AnalyzedCommit, bool) {
|
func (a *angular) analyze(commit gitutil.Commit, rule Rule) (AnalyzedCommit, bool, error) {
|
||||||
|
|
||||||
analyzed := AnalyzedCommit{
|
analyzed := AnalyzedCommit{
|
||||||
Commit: commit,
|
Commit: commit,
|
||||||
|
Tag: rule.Tag,
|
||||||
|
TagString: rule.TagString,
|
||||||
}
|
}
|
||||||
|
|
||||||
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", tag, -1))
|
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1))
|
||||||
matches := re.FindAllStringSubmatch(commit.Message+" "+commit.Message, -1)
|
matches := re.FindAllStringSubmatch(commit.Message, -1)
|
||||||
if len(matches) >= 1 {
|
if len(matches) >= 1 {
|
||||||
if len(matches[0]) >= 3 {
|
if len(matches[0]) >= 3 {
|
||||||
analyzed.Scope = matches[0][2]
|
|
||||||
|
analyzed.Scope = Scope(matches[0][2])
|
||||||
|
|
||||||
message := strings.Join(matches[0][3:], "")
|
message := strings.Join(matches[0][3:], "")
|
||||||
splitted := strings.SplitN(message, "BREAKING CHANGE:", 1)
|
if !strings.Contains(message, "BREAKING CHANGE:") {
|
||||||
|
analyzed.ParsedMessage = strings.Trim(message, " ")
|
||||||
|
|
||||||
if len(splitted) == 1 {
|
log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
||||||
analyzed.ParsedMessage = splitted[0]
|
return analyzed, false, nil
|
||||||
return analyzed, false
|
|
||||||
}
|
}
|
||||||
analyzed.ParsedMessage = splitted[0]
|
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
||||||
analyzed.ParsedBreakingChangeMessage = splitted[1]
|
|
||||||
return analyzed, true
|
|
||||||
|
|
||||||
|
analyzed.ParsedMessage = strings.Trim(breakingChange[0], " ")
|
||||||
|
analyzed.ParsedBreakingChangeMessage = strings.Trim(breakingChange[1], " ")
|
||||||
|
|
||||||
|
log.Tracef(" %s, BREAKING CHANGE found", commit.Message)
|
||||||
|
return analyzed, true, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return analyzed, false
|
log.Tracef("%s does not match %s, skip", commit.Message, rule.Tag)
|
||||||
|
return analyzed, false, fmt.Errorf("not found")
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
173
internal/analyzer/angular_test.go
Normal file
173
internal/analyzer/angular_test.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package analyzer_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAngular(t *testing.T) {
|
||||||
|
|
||||||
|
testConfigs := []struct {
|
||||||
|
testCase string
|
||||||
|
commits []gitutil.Commit
|
||||||
|
analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testCase: "feat",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first commit",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
commits: []gitutil.Commit{
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "feat breaking change",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first commit",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"major": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first break BREAKING CHANGE: change api to v2",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345668",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first break",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
commits: []gitutil.Commit{
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first break BREAKING CHANGE: change api to v2",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345668",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "invalid",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{},
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
commits: []gitutil.Commit{
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "internal/changelog: my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "feat and build",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first commit",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"none": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "build(internal/changelog): my first build",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345668",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first build",
|
||||||
|
Tag: "build",
|
||||||
|
TagString: "Changes to CI/CD",
|
||||||
|
Print: false,
|
||||||
|
ParsedBreakingChangeMessage: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
commits: []gitutil.Commit{
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "feat(internal/changelog): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
gitutil.Commit{
|
||||||
|
Message: "build(internal/changelog): my first build",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345668",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
angular, err := analyzer.New("angular", config.ChangelogConfig{})
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
for _, test := range testConfigs {
|
||||||
|
analyzedCommits := angular.Analyze(test.commits)
|
||||||
|
assert.Equalf(t, test.analyzedCommits["major"], analyzedCommits["major"], "Testcase %s should have major commits", test.testCase)
|
||||||
|
assert.Equalf(t, test.analyzedCommits["minor"], analyzedCommits["minor"], "Testcase %s should have minor commits", test.testCase)
|
||||||
|
assert.Equalf(t, test.analyzedCommits["patch"], analyzedCommits["patch"], "Testcase %s should have patch commits", test.testCase)
|
||||||
|
assert.Equalf(t, test.analyzedCommits["none"], analyzedCommits["none"], "Testcase %s should have none commits", test.testCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
93
internal/cache/cache.go
vendored
Normal file
93
internal/cache/cache.go
vendored
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
// Package cache helper for cache version
|
||||||
|
package cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"io/ioutil"
|
||||||
|
"path"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ReleaseVersion struct
|
||||||
|
type ReleaseVersion struct {
|
||||||
|
Last ReleaseVersionEntry `yaml:"last"`
|
||||||
|
Next ReleaseVersionEntry `yaml:"next"`
|
||||||
|
Branch string `yaml:"branch"`
|
||||||
|
Draft bool `yaml:"draft"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//ReleaseVersionEntry struct
|
||||||
|
type ReleaseVersionEntry struct {
|
||||||
|
Commit string `yaml:"commit"`
|
||||||
|
Version string `yaml:"version"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write version into .version
|
||||||
|
func Write(repository string, releaseVersion shared.ReleaseVersion) error {
|
||||||
|
completePath := path.Join(path.Dir(repository), ".version")
|
||||||
|
|
||||||
|
toCache := &ReleaseVersion{
|
||||||
|
Next: ReleaseVersionEntry{
|
||||||
|
Commit: releaseVersion.Next.Commit,
|
||||||
|
Version: releaseVersion.Next.Version.String(),
|
||||||
|
},
|
||||||
|
Last: ReleaseVersionEntry{
|
||||||
|
Commit: releaseVersion.Last.Commit,
|
||||||
|
Version: releaseVersion.Last.Version.String(),
|
||||||
|
},
|
||||||
|
Branch: releaseVersion.Branch,
|
||||||
|
Draft: releaseVersion.Draft,
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := yaml.Marshal(toCache)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Infof("Save %s with hash %s to cache", releaseVersion.Next.Version.String(), releaseVersion.Next.Commit)
|
||||||
|
return ioutil.WriteFile(completePath, data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read version into .version
|
||||||
|
func Read(repository string) (*shared.ReleaseVersion, error) {
|
||||||
|
completePath := path.Join(path.Dir(repository), ".version")
|
||||||
|
|
||||||
|
content, err := ioutil.ReadFile(completePath)
|
||||||
|
if err != nil {
|
||||||
|
return &shared.ReleaseVersion{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsedContent ReleaseVersion
|
||||||
|
err = yaml.Unmarshal(content, &parsedContent)
|
||||||
|
if err != nil {
|
||||||
|
return &shared.ReleaseVersion{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nextVersion, err := semver.NewVersion(parsedContent.Next.Version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
lastVersion, err := semver.NewVersion(parsedContent.Last.Version)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseVersion := &shared.ReleaseVersion{
|
||||||
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Commit: parsedContent.Next.Commit,
|
||||||
|
Version: nextVersion,
|
||||||
|
},
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Commit: parsedContent.Last.Commit,
|
||||||
|
Version: lastVersion,
|
||||||
|
},
|
||||||
|
Branch: parsedContent.Branch,
|
||||||
|
Draft: parsedContent.Draft,
|
||||||
|
}
|
||||||
|
log.Infof("Found cache, will return cached version %s", parsedContent.Next.Version)
|
||||||
|
return releaseVersion, nil
|
||||||
|
}
|
||||||
89
internal/cache/cache_test.go
vendored
Normal file
89
internal/cache/cache_test.go
vendored
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package cache_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/cache"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadCacheNotFound(t *testing.T) {
|
||||||
|
|
||||||
|
_, err := cache.Read("notfound/dir")
|
||||||
|
assert.Errorf(t, err, "Read non exsiting file")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadCacheInvalidContent(t *testing.T) {
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "prefix")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
completePath := path.Join(path.Dir(dir), ".version")
|
||||||
|
brokenContent := []byte("hello broken\ngo: lang\n")
|
||||||
|
err = ioutil.WriteFile(completePath, brokenContent, 0644)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, readError := cache.Read(dir)
|
||||||
|
assert.Errorf(t, readError, "Should give error, when broken content")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAndReadCache(t *testing.T) {
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "prefix")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
content := shared.ReleaseVersion{
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Commit: "12345",
|
||||||
|
Version: createVersion("1.0.0"),
|
||||||
|
},
|
||||||
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Commit: "12346",
|
||||||
|
Version: createVersion("1.1.0"),
|
||||||
|
},
|
||||||
|
Branch: "master",
|
||||||
|
Draft: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
writeError := cache.Write(dir, content)
|
||||||
|
assert.NoErrorf(t, writeError, "Should write file")
|
||||||
|
result, readError := cache.Read(dir)
|
||||||
|
assert.NoErrorf(t, readError, "Should read file")
|
||||||
|
|
||||||
|
assert.EqualValues(t, &content, result)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteNotFound(t *testing.T) {
|
||||||
|
|
||||||
|
err := cache.Write("notfound/dir", shared.ReleaseVersion{
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Commit: "12345",
|
||||||
|
Version: createVersion("1.0.0"),
|
||||||
|
},
|
||||||
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Commit: "12346",
|
||||||
|
Version: createVersion("1.1.0"),
|
||||||
|
},
|
||||||
|
Branch: "master",
|
||||||
|
Draft: true,
|
||||||
|
})
|
||||||
|
assert.Errorf(t, err, "Write non exsiting file")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func createVersion(version string) *semver.Version {
|
||||||
|
ver, _ := semver.NewVersion(version)
|
||||||
|
return ver
|
||||||
|
}
|
||||||
68
internal/calculator/calculator.go
Normal file
68
internal/calculator/calculator.go
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package calculator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Calculator struct
|
||||||
|
type Calculator struct{}
|
||||||
|
|
||||||
|
// New Calculator struct
|
||||||
|
func New() *Calculator {
|
||||||
|
return &Calculator{}
|
||||||
|
}
|
||||||
|
|
||||||
|
//IncPrerelease increase prerelease by one
|
||||||
|
func (c *Calculator) IncPrerelease(preReleaseType string, version *semver.Version) (semver.Version, error) {
|
||||||
|
defaultPrerelease := preReleaseType + ".0"
|
||||||
|
if version.Prerelease() == "" || !strings.HasPrefix(version.Prerelease(), preReleaseType) {
|
||||||
|
return version.SetPrerelease(defaultPrerelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
parts := strings.Split(version.Prerelease(), ".")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
i, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("Could not parse release tag %s, use version %s", version.Prerelease(), version.String())
|
||||||
|
return version.SetPrerelease(defaultPrerelease)
|
||||||
|
}
|
||||||
|
return version.SetPrerelease(preReleaseType + "." + strconv.Itoa((i + 1)))
|
||||||
|
|
||||||
|
}
|
||||||
|
log.Warnf("Could not parse release tag %s, use version %s", version.Prerelease(), version.String())
|
||||||
|
return version.SetPrerelease(defaultPrerelease)
|
||||||
|
}
|
||||||
|
|
||||||
|
//CalculateNewVersion from given commits and lastversion
|
||||||
|
func (c *Calculator) CalculateNewVersion(commits map[analyzer.Release][]analyzer.AnalyzedCommit, lastVersion *semver.Version, releaseType string, firstRelease bool) (semver.Version, bool) {
|
||||||
|
switch releaseType {
|
||||||
|
case "beta", "alpha":
|
||||||
|
if len(commits["major"]) > 0 || len(commits["minor"]) > 0 || len(commits["patch"]) > 0 {
|
||||||
|
version, _ := c.IncPrerelease(releaseType, lastVersion)
|
||||||
|
return version, true
|
||||||
|
}
|
||||||
|
case "rc":
|
||||||
|
if len(commits["major"]) > 0 || len(commits["minor"]) > 0 || len(commits["patch"]) > 0 {
|
||||||
|
version, _ := c.IncPrerelease(releaseType, lastVersion)
|
||||||
|
return version, false
|
||||||
|
}
|
||||||
|
case "release":
|
||||||
|
if !firstRelease {
|
||||||
|
if len(commits["major"]) > 0 {
|
||||||
|
return lastVersion.IncMajor(), false
|
||||||
|
} else if len(commits["minor"]) > 0 {
|
||||||
|
return lastVersion.IncMinor(), false
|
||||||
|
} else if len(commits["patch"]) > 0 {
|
||||||
|
return lastVersion.IncPatch(), false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return *lastVersion, false
|
||||||
|
}
|
||||||
219
internal/calculator/calculator_test.go
Normal file
219
internal/calculator/calculator_test.go
Normal file
@@ -0,0 +1,219 @@
|
|||||||
|
package calculator_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/calculator"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func createVersion(version string) *semver.Version {
|
||||||
|
ver, _ := semver.NewVersion(version)
|
||||||
|
return ver
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculator_IncPrerelease(t *testing.T) {
|
||||||
|
|
||||||
|
testConfigs := []struct {
|
||||||
|
testCase string
|
||||||
|
preReleaseType string
|
||||||
|
lastVersion *semver.Version
|
||||||
|
nextVersion string
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testCase: "version without preRelease",
|
||||||
|
preReleaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0-alpha.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease",
|
||||||
|
preReleaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0-alpha.0"),
|
||||||
|
nextVersion: "1.0.0-alpha.1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease, change type",
|
||||||
|
preReleaseType: "beta",
|
||||||
|
lastVersion: createVersion("1.0.0-alpha.0"),
|
||||||
|
nextVersion: "1.0.0-beta.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease but broken",
|
||||||
|
preReleaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0-alpha.br0ken"),
|
||||||
|
nextVersion: "1.0.0-alpha.0",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease but broken 2",
|
||||||
|
preReleaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0-alphabr0ken"),
|
||||||
|
nextVersion: "1.0.0-alpha.0",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := calculator.New()
|
||||||
|
|
||||||
|
for _, test := range testConfigs {
|
||||||
|
next, err := c.IncPrerelease(test.preReleaseType, test.lastVersion)
|
||||||
|
assert.Equalf(t, test.hasError, err != nil, "Testcase %s should have error: %t -> %s", test.testCase, test.hasError, err)
|
||||||
|
assert.Equal(t, test.nextVersion, next.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCalculator_CalculateNewVersion(t *testing.T) {
|
||||||
|
|
||||||
|
testConfigs := []struct {
|
||||||
|
testCase string
|
||||||
|
releaseType string
|
||||||
|
lastVersion *semver.Version
|
||||||
|
nextVersion string
|
||||||
|
isDraft bool
|
||||||
|
isFirst bool
|
||||||
|
analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease alpha",
|
||||||
|
releaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0-alpha.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with preRelease beta",
|
||||||
|
releaseType: "beta",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0-beta.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version without commits",
|
||||||
|
releaseType: "alpha",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and first release",
|
||||||
|
releaseType: "release",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: true,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and rc release",
|
||||||
|
releaseType: "rc",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.0-rc.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and rc release",
|
||||||
|
releaseType: "rc",
|
||||||
|
lastVersion: createVersion("1.0.0-rc.0"),
|
||||||
|
nextVersion: "1.0.0-rc.1",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and major release",
|
||||||
|
releaseType: "release",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "2.0.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and minor release",
|
||||||
|
releaseType: "release",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.1.0",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "version with commits and minor patch",
|
||||||
|
releaseType: "release",
|
||||||
|
lastVersion: createVersion("1.0.0"),
|
||||||
|
nextVersion: "1.0.1",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"major": []analyzer.AnalyzedCommit{},
|
||||||
|
"minor": []analyzer.AnalyzedCommit{},
|
||||||
|
"patch": []analyzer.AnalyzedCommit{analyzer.AnalyzedCommit{}},
|
||||||
|
"none": []analyzer.AnalyzedCommit{},
|
||||||
|
},
|
||||||
|
isFirst: false,
|
||||||
|
isDraft: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
c := calculator.New()
|
||||||
|
|
||||||
|
for _, test := range testConfigs {
|
||||||
|
next, draft := c.CalculateNewVersion(test.analyzedCommits, test.lastVersion, test.releaseType, test.isFirst)
|
||||||
|
assert.Equalf(t, test.isDraft, draft, "Should have draft %t for testcase %s", test.isDraft, test.testCase)
|
||||||
|
assert.Equalf(t, test.nextVersion, next.String(), "Should have version %s for testcase %s", test.nextVersion, test.testCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
132
internal/changelog/changelog.go
Normal file
132
internal/changelog/changelog.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package changelog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"strings"
|
||||||
|
"text/template"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const defaultChangelogTitle string = `v{{.Version}} ({{.Now.Format "2006-01-02"}})`
|
||||||
|
const defaultChangelog string = `# v{{$.Version}} ({{.Now.Format "2006-01-02"}})
|
||||||
|
{{ range $index,$commit := .BreakingChanges -}}
|
||||||
|
{{ if eq $index 0 }}
|
||||||
|
## BREAKING CHANGES
|
||||||
|
{{ end}}
|
||||||
|
* **{{$.Backtick}}{{$commit.Scope}}{{$.Backtick}}** {{$commit.ParsedBreakingChangeMessage}}
|
||||||
|
introduced by commit:
|
||||||
|
{{$commit.ParsedMessage}} {{if $.HasURL}} ([{{ printf "%.7s" $commit.Commit.Hash}}]({{ replace $.URL "{{hash}}" $commit.Commit.Hash}})) {{end}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ range $key := .Order }}
|
||||||
|
{{ $commits := index $.Commits $key}} {{if $commits -}}
|
||||||
|
### {{ $key }}
|
||||||
|
{{ range $index,$commit := $commits -}}
|
||||||
|
* **{{$.Backtick}}{{$commit.Scope}}{{$.Backtick}}** {{$commit.ParsedMessage}} {{if $.HasURL}} ([{{ printf "%.7s" $commit.Commit.Hash}}]({{ replace $.URL "{{hash}}" $commit.Commit.Hash}})) {{end}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
{{ end -}}
|
||||||
|
`
|
||||||
|
|
||||||
|
type changelogContent struct {
|
||||||
|
Commits map[string][]analyzer.AnalyzedCommit
|
||||||
|
BreakingChanges []analyzer.AnalyzedCommit
|
||||||
|
Order []string
|
||||||
|
Version string
|
||||||
|
Now time.Time
|
||||||
|
Backtick string
|
||||||
|
HasURL bool
|
||||||
|
URL string
|
||||||
|
}
|
||||||
|
|
||||||
|
//Changelog struct
|
||||||
|
type Changelog struct {
|
||||||
|
config *config.ReleaseConfig
|
||||||
|
rules []analyzer.Rule
|
||||||
|
releaseTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
//New Changelog struct for generating changelog from commits
|
||||||
|
func New(config *config.ReleaseConfig, rules []analyzer.Rule, releaseTime time.Time) *Changelog {
|
||||||
|
return &Changelog{
|
||||||
|
config: config,
|
||||||
|
rules: rules,
|
||||||
|
releaseTime: releaseTime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateChanglog from given commits
|
||||||
|
func (c *Changelog) GenerateChanglog(templateConfig shared.ChangelogTemplateConfig, analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit) (*shared.GeneratedChangelog, error) {
|
||||||
|
|
||||||
|
commitsPerScope := map[string][]analyzer.AnalyzedCommit{}
|
||||||
|
commitsBreakingChange := []analyzer.AnalyzedCommit{}
|
||||||
|
order := make([]string, 0)
|
||||||
|
|
||||||
|
for _, rule := range c.rules {
|
||||||
|
log.Debugf("Add %s to list", rule.TagString)
|
||||||
|
if rule.Changelog || c.config.Changelog.PrintAll {
|
||||||
|
order = append(order, rule.TagString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, commits := range analyzedCommits {
|
||||||
|
for _, commit := range commits {
|
||||||
|
if commit.Print {
|
||||||
|
if commit.ParsedBreakingChangeMessage != "" {
|
||||||
|
commitsBreakingChange = append(commitsBreakingChange, commit)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, ok := commitsPerScope[commit.TagString]; !ok {
|
||||||
|
commitsPerScope[commit.TagString] = make([]analyzer.AnalyzedCommit, 0)
|
||||||
|
}
|
||||||
|
commitsPerScope[commit.TagString] = append(commitsPerScope[commit.TagString], commit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
changelogContent := changelogContent{
|
||||||
|
Version: templateConfig.Version,
|
||||||
|
Commits: commitsPerScope,
|
||||||
|
Now: c.releaseTime,
|
||||||
|
BreakingChanges: commitsBreakingChange,
|
||||||
|
Backtick: "`",
|
||||||
|
Order: order,
|
||||||
|
HasURL: templateConfig.CommitURL != "",
|
||||||
|
URL: templateConfig.CommitURL,
|
||||||
|
}
|
||||||
|
|
||||||
|
title, err := generateTemplate(defaultChangelogTitle, changelogContent)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
content, err := generateTemplate(defaultChangelog, changelogContent)
|
||||||
|
|
||||||
|
return &shared.GeneratedChangelog{Title: title, Content: content}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateTemplate(text string, values changelogContent) (string, error) {
|
||||||
|
|
||||||
|
funcMap := template.FuncMap{
|
||||||
|
"replace": replace,
|
||||||
|
}
|
||||||
|
|
||||||
|
var tpl bytes.Buffer
|
||||||
|
tmpl, err := template.New("template").Funcs(funcMap).Parse(text)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
err = tmpl.Execute(&tpl, values)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tpl.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func replace(input, from, to string) string {
|
||||||
|
return strings.Replace(input, from, to, -1)
|
||||||
|
}
|
||||||
120
internal/changelog/changelog_test.go
Normal file
120
internal/changelog/changelog_test.go
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package changelog_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/changelog"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestChangelog(t *testing.T) {
|
||||||
|
|
||||||
|
templateConfig := shared.ChangelogTemplateConfig{
|
||||||
|
CommitURL: "https://commit.url",
|
||||||
|
CompareURL: "https://compare.url",
|
||||||
|
Hash: "hash",
|
||||||
|
Version: "1.0.0",
|
||||||
|
}
|
||||||
|
|
||||||
|
testConfigs := []struct {
|
||||||
|
testCase string
|
||||||
|
analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit
|
||||||
|
result *shared.GeneratedChangelog
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
testCase: "feat",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(test): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first commit",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &shared.GeneratedChangelog{
|
||||||
|
Title: "v1.0.0 (2019-07-19)",
|
||||||
|
Content: "# v1.0.0 (2019-07-19)\n\n ### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url)) \n\n ",
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testCase: "feat breaking change",
|
||||||
|
analyzedCommits: map[analyzer.Release][]analyzer.AnalyzedCommit{
|
||||||
|
"minor": []analyzer.AnalyzedCommit{
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(test): my first commit",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345667",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first commit",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
},
|
||||||
|
analyzer.AnalyzedCommit{
|
||||||
|
Commit: gitutil.Commit{
|
||||||
|
Message: "feat(test): my first break: BREAKING CHANGE: change api to v2",
|
||||||
|
Author: "me",
|
||||||
|
Hash: "12345668",
|
||||||
|
},
|
||||||
|
Scope: "internal/changelog",
|
||||||
|
ParsedMessage: "my first break",
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Print: true,
|
||||||
|
ParsedBreakingChangeMessage: "change api to v2",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
result: &shared.GeneratedChangelog{
|
||||||
|
Title: "v1.0.0 (2019-07-19)",
|
||||||
|
Content: "# v1.0.0 (2019-07-19)\n\n## BREAKING CHANGES\n\n* **`internal/changelog`** change api to v2 \nintroduced by commit: \nmy first break ([1234566](https://commit.url)) \n\n ### Features\n* **`internal/changelog`** my first commit ([1234566](https://commit.url)) \n\n ",
|
||||||
|
},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
cl := changelog.New(&config.ReleaseConfig{}, []analyzer.Rule{
|
||||||
|
{
|
||||||
|
Tag: "feat",
|
||||||
|
TagString: "Features",
|
||||||
|
Release: "minor",
|
||||||
|
Changelog: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tag: "fix",
|
||||||
|
TagString: "Bug fixes",
|
||||||
|
Release: "patch",
|
||||||
|
Changelog: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Tag: "build",
|
||||||
|
TagString: "Build",
|
||||||
|
Release: "none",
|
||||||
|
Changelog: false,
|
||||||
|
},
|
||||||
|
}, time.Date(2019, 7, 19, 0, 0, 0, 0, time.UTC))
|
||||||
|
|
||||||
|
for _, config := range testConfigs {
|
||||||
|
generatedChangelog, err := cl.GenerateChanglog(templateConfig, config.analyzedCommits)
|
||||||
|
assert.Equalf(t, config.hasError, err != nil, "Testcase %s should have error: %t -> %s", config.testCase, config.hasError, err)
|
||||||
|
assert.Equalf(t, config.result, generatedChangelog, "Testcase %s should have generated changelog", config.testCase)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
57
internal/ci/ci.go
Normal file
57
internal/ci/ci.go
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
package ci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ProviderConfig struct
|
||||||
|
type ProviderConfig struct {
|
||||||
|
IsPR bool
|
||||||
|
PR string
|
||||||
|
PRBranch string
|
||||||
|
Branch string
|
||||||
|
Tag string
|
||||||
|
Commit string
|
||||||
|
BuildURL string
|
||||||
|
Service string
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
//Service interface
|
||||||
|
type Service interface {
|
||||||
|
detect(envs map[string]string) (*ProviderConfig, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
//ReadAllEnvs as a map
|
||||||
|
func ReadAllEnvs() map[string]string {
|
||||||
|
envs := map[string]string{}
|
||||||
|
for _, pair := range os.Environ() {
|
||||||
|
splitted := strings.SplitN(pair, "=", 2)
|
||||||
|
envs[splitted[0]] = splitted[1]
|
||||||
|
}
|
||||||
|
return envs
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetCIProvider get provider
|
||||||
|
func GetCIProvider(gitUtil *gitutil.GitUtil, envs map[string]string) (*ProviderConfig, error) {
|
||||||
|
|
||||||
|
services := []Service{
|
||||||
|
Travis{},
|
||||||
|
Git{gitUtil: gitUtil}, // GIt must be the last option to check
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range services {
|
||||||
|
config, err := service.detect(envs)
|
||||||
|
if err == nil {
|
||||||
|
log.Infof("Found CI: %s", config.Name)
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
|
log.Debugf("%s", err.Error())
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not find any CI, if running locally set env CI=true")
|
||||||
|
}
|
||||||
100
internal/ci/ci_test.go
Normal file
100
internal/ci/ci_test.go
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
package ci_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/ci"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"gopkg.in/src-d/go-billy.v4/memfs"
|
||||||
|
"gopkg.in/src-d/go-git.v4"
|
||||||
|
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||||
|
"gopkg.in/src-d/go-git.v4/storage/memory"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCi(t *testing.T) {
|
||||||
|
|
||||||
|
fs := memfs.New()
|
||||||
|
|
||||||
|
repository, err := git.Init(memory.NewStorage(), fs)
|
||||||
|
assert.NoError(t, err, "should open git repository")
|
||||||
|
|
||||||
|
file, err := fs.Create("README.md")
|
||||||
|
assert.NoError(t, err, "should create file")
|
||||||
|
|
||||||
|
w, err := repository.Worktree()
|
||||||
|
assert.NoError(t, err, "should get worktree")
|
||||||
|
|
||||||
|
_, err = w.Add(file.Name())
|
||||||
|
assert.NoError(t, err, "should add file")
|
||||||
|
|
||||||
|
gitUtilInMemory := &gitutil.GitUtil{
|
||||||
|
Repository: repository,
|
||||||
|
}
|
||||||
|
|
||||||
|
newCommit, err := w.Commit("fix(test): add a commit", &git.CommitOptions{
|
||||||
|
Author: &object.Signature{
|
||||||
|
Name: "John Doe",
|
||||||
|
Email: "john@doe.org",
|
||||||
|
When: time.Now(),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assert.NoError(t, err, "should commit")
|
||||||
|
|
||||||
|
testConfigs := []struct {
|
||||||
|
service string
|
||||||
|
envs map[string]string
|
||||||
|
result *ci.ProviderConfig
|
||||||
|
hasError bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
service: "none",
|
||||||
|
envs: map[string]string{},
|
||||||
|
result: nil,
|
||||||
|
hasError: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: "Git",
|
||||||
|
envs: map[string]string{
|
||||||
|
"CI": "true",
|
||||||
|
},
|
||||||
|
result: &ci.ProviderConfig{IsPR: false, PR: "", PRBranch: "", Branch: "master", Tag: "", Commit: newCommit.String(), BuildURL: "", Service: "git", Name: "Git only"},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: "Travis PR",
|
||||||
|
envs: map[string]string{
|
||||||
|
"TRAVIS": "true",
|
||||||
|
"TRAVIS_PULL_REQUEST": "10",
|
||||||
|
"TRAVIS_COMMIT": "190bfd6aa60022afd0ef830342cfb07e33c45f37",
|
||||||
|
"TRAVIS_TAG": "TAG",
|
||||||
|
"TRAVIS_BUILD_WEB_URL": "https://travis-ci.com/owner/repo/builds/1234",
|
||||||
|
"TRAVIS_BRANCH": "master",
|
||||||
|
"TRAVIS_PULL_REQUEST_BRANCH": "pr",
|
||||||
|
},
|
||||||
|
result: &ci.ProviderConfig{IsPR: true, PR: "10", PRBranch: "pr", Branch: "master", Tag: "TAG", Commit: "190bfd6aa60022afd0ef830342cfb07e33c45f37", BuildURL: "https://travis-ci.com/owner/repo/builds/1234", Service: "travis", Name: "Travis CI"},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
service: "Travis Push",
|
||||||
|
envs: map[string]string{
|
||||||
|
"TRAVIS": "true",
|
||||||
|
"TRAVIS_PULL_REQUEST": "false",
|
||||||
|
"TRAVIS_COMMIT": "190bfd6aa60022afd0ef830342cfb07e33c45f37",
|
||||||
|
"TRAVIS_TAG": "TAG",
|
||||||
|
"TRAVIS_BUILD_WEB_URL": "https://travis-ci.com/owner/repo/builds/1234",
|
||||||
|
"TRAVIS_BRANCH": "master",
|
||||||
|
},
|
||||||
|
result: &ci.ProviderConfig{IsPR: false, PR: "", PRBranch: "", Branch: "master", Tag: "TAG", Commit: "190bfd6aa60022afd0ef830342cfb07e33c45f37", BuildURL: "https://travis-ci.com/owner/repo/builds/1234", Service: "travis", Name: "Travis CI"},
|
||||||
|
hasError: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, config := range testConfigs {
|
||||||
|
provider, err := ci.GetCIProvider(gitUtilInMemory, config.envs)
|
||||||
|
assert.Equalf(t, config.hasError, err != nil, "Service %s should have error: %t -> %s", config.service, config.hasError, err)
|
||||||
|
assert.Equalf(t, config.result, provider, "Service %s should have provider", config.service)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
37
internal/ci/git.go
Normal file
37
internal/ci/git.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package ci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Git struct
|
||||||
|
type Git struct {
|
||||||
|
gitUtil *gitutil.GitUtil
|
||||||
|
}
|
||||||
|
|
||||||
|
//Detect if on Git
|
||||||
|
func (t Git) detect(envs map[string]string) (*ProviderConfig, error) {
|
||||||
|
|
||||||
|
if _, exists := envs["CI"]; !exists {
|
||||||
|
return nil, fmt.Errorf("running not git only")
|
||||||
|
}
|
||||||
|
|
||||||
|
hash, err := t.gitUtil.GetHash()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
currentBranch, err := t.gitUtil.GetBranch()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ProviderConfig{
|
||||||
|
Service: "git",
|
||||||
|
Name: "Git only",
|
||||||
|
Commit: hash,
|
||||||
|
Branch: currentBranch,
|
||||||
|
IsPR: false,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
41
internal/ci/travis.go
Normal file
41
internal/ci/travis.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package ci
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
//Travis struct
|
||||||
|
type Travis struct{}
|
||||||
|
|
||||||
|
//Detect if on travis
|
||||||
|
func (t Travis) detect(envs map[string]string) (*ProviderConfig, error) {
|
||||||
|
|
||||||
|
if _, exists := envs["TRAVIS"]; !exists {
|
||||||
|
return nil, fmt.Errorf("not running on travis")
|
||||||
|
}
|
||||||
|
|
||||||
|
isPR := false
|
||||||
|
|
||||||
|
value := envs["TRAVIS_PULL_REQUEST"]
|
||||||
|
pr := ""
|
||||||
|
|
||||||
|
if value == "false" {
|
||||||
|
log.Debugf("TRAVIS_PULL_REQUEST=%s, not running on pr", value)
|
||||||
|
} else {
|
||||||
|
isPR = true
|
||||||
|
pr = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ProviderConfig{
|
||||||
|
Service: "travis",
|
||||||
|
Name: "Travis CI",
|
||||||
|
Commit: envs["TRAVIS_COMMIT"],
|
||||||
|
Tag: envs["TRAVIS_TAG"],
|
||||||
|
BuildURL: envs["TRAVIS_BUILD_WEB_URL"],
|
||||||
|
Branch: envs["TRAVIS_BRANCH"],
|
||||||
|
IsPR: isPR,
|
||||||
|
PR: pr,
|
||||||
|
PRBranch: envs["TRAVIS_PULL_REQUEST_BRANCH"],
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// Package gitutil provides helper methods for git
|
||||||
package gitutil
|
package gitutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
@@ -7,32 +8,37 @@ import (
|
|||||||
"github.com/Masterminds/semver"
|
"github.com/Masterminds/semver"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/src-d/go-git.v4"
|
"gopkg.in/src-d/go-git.v4"
|
||||||
|
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Commit struct
|
||||||
type Commit struct {
|
type Commit struct {
|
||||||
Message string
|
Message string
|
||||||
Author string
|
Author string
|
||||||
Hash string
|
Hash string
|
||||||
}
|
}
|
||||||
|
|
||||||
type GitUtils struct {
|
// GitUtil struct
|
||||||
|
type GitUtil struct {
|
||||||
Repository *git.Repository
|
Repository *git.Repository
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(folder string) (*GitUtils, error) {
|
// New GitUtil struct and open git repository
|
||||||
|
func New(folder string) (*GitUtil, error) {
|
||||||
r, err := git.PlainOpen(folder)
|
r, err := git.PlainOpen(folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
utils := &GitUtils{
|
utils := &GitUtil{
|
||||||
Repository: r,
|
Repository: r,
|
||||||
}
|
}
|
||||||
return utils, nil
|
return utils, nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitUtils) GetHash() (string, error) {
|
// GetHash from git HEAD
|
||||||
|
func (g *GitUtil) GetHash() (string, error) {
|
||||||
ref, err := g.Repository.Head()
|
ref, err := g.Repository.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
@@ -40,38 +46,59 @@ func (g *GitUtils) GetHash() (string, error) {
|
|||||||
return ref.Hash().String(), nil
|
return ref.Hash().String(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitUtils) GetBranch() (string, error) {
|
// GetBranch from git HEAD
|
||||||
|
func (g *GitUtil) GetBranch() (string, error) {
|
||||||
ref, err := g.Repository.Head()
|
ref, err := g.Repository.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !ref.Name().IsBranch() {
|
if !ref.Name().IsBranch() {
|
||||||
return "", fmt.Errorf("No branch found, found %s, please checkout a branch (git checkout <BRANCH>)", ref.Name().String())
|
branches, err := g.Repository.Branches()
|
||||||
}
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentBranch string
|
||||||
|
found := branches.ForEach(func(p *plumbing.Reference) error {
|
||||||
|
|
||||||
|
if p.Name().IsBranch() && p.Name().Short() != "origin" {
|
||||||
|
currentBranch = p.Name().Short()
|
||||||
|
return fmt.Errorf("break")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if found != nil {
|
||||||
|
log.Debugf("Found branch from HEAD %s", currentBranch)
|
||||||
|
return currentBranch, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("no branch found, found %s, please checkout a branch (git checkout -b <BRANCH>)", ref.Name().String())
|
||||||
|
}
|
||||||
|
log.Debugf("Found branch %s", ref.Name().Short())
|
||||||
return ref.Name().Short(), nil
|
return ref.Name().Short(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitUtils) GetLastVersion() (*semver.Version, string, error) {
|
// GetLastVersion from git tags
|
||||||
|
func (g *GitUtil) GetLastVersion() (*semver.Version, string, error) {
|
||||||
|
|
||||||
log.Debugf("GetLastVersion")
|
var tags []*semver.Version
|
||||||
|
|
||||||
|
gitTags, err := g.Repository.Tags()
|
||||||
|
|
||||||
tagObjects, err := g.Repository.TagObjects()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var tags []*semver.Version
|
err = gitTags.ForEach(func(p *plumbing.Reference) error {
|
||||||
|
v, err := semver.NewVersion(p.Name().Short())
|
||||||
|
log.Tracef("%+v with hash: %s", p.Target(), p.Hash())
|
||||||
|
|
||||||
err = tagObjects.ForEach(func(t *object.Tag) error {
|
if err == nil {
|
||||||
v, err := semver.NewVersion(t.Name)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
log.Debugf("Tag %s is not a valid version, skip", t.Name)
|
|
||||||
} else {
|
|
||||||
log.Debugf("Add tag %s", t.Name)
|
|
||||||
tags = append(tags, v)
|
tags = append(tags, v)
|
||||||
|
} else {
|
||||||
|
log.Debugf("Tag %s is not a valid version, skip", p.Name().Short())
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
@@ -94,18 +121,13 @@ func (g *GitUtils) GetLastVersion() (*semver.Version, string, error) {
|
|||||||
return nil, "", err
|
return nil, "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
tagObject, err := g.Repository.TagObject(tag.Hash())
|
log.Debugf("Found old hash %s", tag.Hash().String())
|
||||||
if err != nil {
|
return tags[0], tag.Hash().String(), nil
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Found old hash %s", tagObject.Target.String())
|
|
||||||
return tags[0], tagObject.Target.String(), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
// GetCommits from git hash to HEAD
|
||||||
|
func (g *GitUtil) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||||
|
|
||||||
log.Printf("Read head")
|
|
||||||
ref, err := g.Repository.Head()
|
ref, err := g.Repository.Head()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -121,11 +143,12 @@ func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
|||||||
|
|
||||||
err = cIter.ForEach(func(c *object.Commit) error {
|
err = cIter.ForEach(func(c *object.Commit) error {
|
||||||
if c.Hash.String() == lastTagHash {
|
if c.Hash.String() == lastTagHash {
|
||||||
log.Infof("%s == %s", c.Hash.String(), lastTagHash)
|
log.Debugf("Found commit with hash %s, will stop here", c.Hash.String())
|
||||||
foundEnd = true
|
foundEnd = true
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
if !foundEnd {
|
if !foundEnd {
|
||||||
|
log.Tracef("Found commit with hash %s", c.Hash.String())
|
||||||
commit := Commit{
|
commit := Commit{
|
||||||
Message: c.Message,
|
Message: c.Message,
|
||||||
Author: c.Committer.Name,
|
Author: c.Committer.Name,
|
||||||
@@ -136,5 +159,5 @@ func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
return commits, nil
|
return commits, err
|
||||||
}
|
}
|
||||||
|
|||||||
141
internal/releaser/github/github.go
Normal file
141
internal/releaser/github/github.go
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
package github
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/releaser/util"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
|
||||||
|
"github.com/google/go-github/v25/github"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GITHUB identifer for github interface
|
||||||
|
const GITHUB = "github"
|
||||||
|
|
||||||
|
// Client type struct
|
||||||
|
type Client struct {
|
||||||
|
config *config.GitHubProvider
|
||||||
|
client *github.Client
|
||||||
|
context context.Context
|
||||||
|
release *github.RepositoryRelease
|
||||||
|
baseURL string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initialize a new GitHubRelease
|
||||||
|
func New(c *config.GitHubProvider) (*Client, error) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if c.AccessToken, err = util.GetAccessToken(GITHUB); err != nil {
|
||||||
|
return &Client{}, err
|
||||||
|
}
|
||||||
|
ctx := context.Background()
|
||||||
|
httpClient := util.CreateBearerHTTPClient(ctx, c.AccessToken)
|
||||||
|
|
||||||
|
var client *github.Client
|
||||||
|
baseURL := "https://github.com"
|
||||||
|
if c.CustomURL == "" {
|
||||||
|
client = github.NewClient(httpClient)
|
||||||
|
} else {
|
||||||
|
if client, err = github.NewEnterpriseClient(c.CustomURL, c.CustomURL+"/api/v3/", httpClient); err != nil {
|
||||||
|
return &Client{}, err
|
||||||
|
}
|
||||||
|
baseURL = c.CustomURL
|
||||||
|
}
|
||||||
|
return &Client{
|
||||||
|
config: c,
|
||||||
|
client: client,
|
||||||
|
context: ctx,
|
||||||
|
baseURL: baseURL,
|
||||||
|
}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetCommitURL for github
|
||||||
|
func (g *Client) GetCommitURL() string {
|
||||||
|
return fmt.Sprintf("%s/%s/%s/commit/{{hash}}", g.baseURL, g.config.User, g.config.Repo)
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetCompareURL for github
|
||||||
|
func (g *Client) GetCompareURL(oldVersion, newVersion string) string {
|
||||||
|
return fmt.Sprintf("%s/%s/%s/compare/%s...%s", g.baseURL, g.config.User, g.config.Repo, oldVersion, newVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
//ValidateConfig for github
|
||||||
|
func (g *Client) ValidateConfig() error {
|
||||||
|
log.Debugf("validate GitHub provider config")
|
||||||
|
|
||||||
|
if g.config.Repo == "" {
|
||||||
|
return fmt.Errorf("github Repro is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.config.User == "" {
|
||||||
|
return fmt.Errorf("github User is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateRelease creates release on remote
|
||||||
|
func (g *Client) CreateRelease(releaseVersion *shared.ReleaseVersion, generatedChangelog *shared.GeneratedChangelog) error {
|
||||||
|
|
||||||
|
tag := releaseVersion.Next.Version.String()
|
||||||
|
log.Debugf("create release with version %s", tag)
|
||||||
|
|
||||||
|
prerelease := releaseVersion.Next.Version.Prerelease() != ""
|
||||||
|
|
||||||
|
release, resp, err := g.client.Repositories.CreateRelease(g.context, g.config.User, g.config.Repo, &github.RepositoryRelease{
|
||||||
|
TagName: &tag,
|
||||||
|
TargetCommitish: &releaseVersion.Branch,
|
||||||
|
Name: &generatedChangelog.Title,
|
||||||
|
Body: &generatedChangelog.Content,
|
||||||
|
Draft: &releaseVersion.Draft,
|
||||||
|
Prerelease: &prerelease,
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if !strings.Contains(err.Error(), "already_exists") && resp.StatusCode >= http.StatusUnprocessableEntity {
|
||||||
|
return fmt.Errorf("could not create release: %v", err)
|
||||||
|
}
|
||||||
|
log.Infof("A release with tag %s already exits, will not perform a release or update", tag)
|
||||||
|
} else {
|
||||||
|
g.release = release
|
||||||
|
log.Debugf("Release repsone: %+v", *release)
|
||||||
|
log.Infof("Crated release")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UploadAssets uploads specified assets
|
||||||
|
func (g *Client) UploadAssets(repoDir string, assets []config.Asset) error {
|
||||||
|
if g.release != nil {
|
||||||
|
filesToUpload, err := util.PrepareAssets(repoDir, assets)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, f := range filesToUpload {
|
||||||
|
|
||||||
|
file, err := os.Open(*f)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fileInfo, _ := file.Stat()
|
||||||
|
|
||||||
|
_, resp, err := g.client.Repositories.UploadReleaseAsset(g.context, g.config.User, g.config.Repo, g.release.GetID(), &github.UploadOptions{Name: fileInfo.Name()}, file)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode >= http.StatusBadRequest {
|
||||||
|
return fmt.Errorf("releaser: github: Could not upload asset %s: %s", file.Name(), resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
42
internal/releaser/releaser.go
Normal file
42
internal/releaser/releaser.go
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
package releaser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/releaser/github"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Releasers struct type
|
||||||
|
type Releasers struct {
|
||||||
|
config *config.ReleaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
// Releaser interface for providers
|
||||||
|
type Releaser interface {
|
||||||
|
ValidateConfig() error
|
||||||
|
CreateRelease(*shared.ReleaseVersion, *shared.GeneratedChangelog) error
|
||||||
|
UploadAssets(repoDir string, assets []config.Asset) error
|
||||||
|
GetCommitURL() string
|
||||||
|
GetCompareURL(oldVersion, newVersion string) string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New initialize a Relerser
|
||||||
|
func New(c *config.ReleaseConfig) *Releasers {
|
||||||
|
return &Releasers{
|
||||||
|
config: c,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetReleaser returns an initialized releaser
|
||||||
|
func (r *Releasers) GetReleaser() (Releaser, error) {
|
||||||
|
switch r.config.Release {
|
||||||
|
case github.GITHUB:
|
||||||
|
log.Debugf("initialize new %s-provider", github.GITHUB)
|
||||||
|
return github.New(&r.config.GitHubProvider)
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("could not initialize a releaser from this type: %s", r.config.Release)
|
||||||
|
}
|
||||||
111
internal/releaser/util/util.go
Normal file
111
internal/releaser/util/util.go
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package util
|
||||||
|
|
||||||
|
import (
|
||||||
|
"archive/zip"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
|
)
|
||||||
|
|
||||||
|
//CreateBearerHTTPClient with given token
|
||||||
|
func CreateBearerHTTPClient(ctx context.Context, token string) *http.Client {
|
||||||
|
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{
|
||||||
|
AccessToken: token},
|
||||||
|
)
|
||||||
|
|
||||||
|
client := oauth2.NewClient(ctx, tokenSource)
|
||||||
|
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAccessToken lookup for the providers accesstoken
|
||||||
|
func GetAccessToken(providerName string) (string, error) {
|
||||||
|
var token string
|
||||||
|
var exists bool
|
||||||
|
envName := fmt.Sprintf("%s_ACCESS_TOKEN", strings.ToUpper(providerName))
|
||||||
|
|
||||||
|
log.Debugf("check if %s environment variable is set", envName)
|
||||||
|
|
||||||
|
if token, exists = os.LookupEnv(envName); !exists {
|
||||||
|
return "", fmt.Errorf("could not find %s in the enviroment variables. Please check if it is set", envName)
|
||||||
|
} else if token == "" {
|
||||||
|
return "", fmt.Errorf("token %s is set in environment variables but is empty", envName)
|
||||||
|
}
|
||||||
|
return token, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrepareAssets prepare all files before uploading
|
||||||
|
func PrepareAssets(repository string, assets []config.Asset) ([]*string, error) {
|
||||||
|
filesToUpload := []*string{}
|
||||||
|
for _, asset := range assets {
|
||||||
|
if asset.Name == "" {
|
||||||
|
return nil, fmt.Errorf("asset name declaration is empty, please check your configuration file")
|
||||||
|
} else if asset.Compress {
|
||||||
|
log.Debugf("Asset %s will now be compressed", asset.Name)
|
||||||
|
log.Debugf("Repo url %s", repository)
|
||||||
|
zipNameWithPath, err := zipFile(repository, asset.Name)
|
||||||
|
if err != nil {
|
||||||
|
return filesToUpload, err
|
||||||
|
}
|
||||||
|
filesToUpload = append(filesToUpload, &zipNameWithPath)
|
||||||
|
} else {
|
||||||
|
tmpFileName := fmt.Sprintf("%s/%s", repository, asset.Name)
|
||||||
|
filesToUpload = append(filesToUpload, &tmpFileName)
|
||||||
|
}
|
||||||
|
log.Debugf("Add asset %s to files to upload", asset.Name)
|
||||||
|
}
|
||||||
|
return filesToUpload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZipFile compress given file in zip format
|
||||||
|
func zipFile(repository string, file string) (string, error) {
|
||||||
|
|
||||||
|
fileToZip, err := os.Open(repository + "/" + file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer fileToZip.Close()
|
||||||
|
|
||||||
|
zipFileName := fmt.Sprintf("%s/%s.zip", strings.TrimSuffix(repository, "/"), file)
|
||||||
|
zipFile, err := os.Create(zipFileName)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
log.Debugf("Created zipfile %s", zipFile.Name())
|
||||||
|
|
||||||
|
defer zipFile.Close()
|
||||||
|
|
||||||
|
fileToZipInfo, err := fileToZip.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
zipWriter := zip.NewWriter(zipFile)
|
||||||
|
defer zipWriter.Close()
|
||||||
|
|
||||||
|
fileToZipHeader, err := zip.FileInfoHeader(fileToZipInfo)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fileToZipHeader.Name = fileToZipInfo.Name()
|
||||||
|
|
||||||
|
fileToZipWriter, err := zipWriter.CreateHeader(fileToZipHeader)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err = io.Copy(fileToZipWriter, fileToZip); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return zipFileName, nil
|
||||||
|
}
|
||||||
119
internal/releaser/util/util_test.go
Normal file
119
internal/releaser/util/util_test.go
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
package util_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/releaser/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateBearerHTTPClient(t *testing.T) {
|
||||||
|
client := util.CreateBearerHTTPClient(context.Background(), "")
|
||||||
|
|
||||||
|
assert.True(t, client != nil, "Client is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
type testDoubleToken struct {
|
||||||
|
providerName, token string
|
||||||
|
valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var testDoubles = []testDoubleToken{
|
||||||
|
testDoubleToken{providerName: "test0", token: "foo", valid: true},
|
||||||
|
testDoubleToken{providerName: "test1", token: "", valid: false},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetAccessToken(t *testing.T) {
|
||||||
|
for _, testObject := range testDoubles {
|
||||||
|
envName := fmt.Sprintf("%s_ACCESS_TOKEN", strings.ToUpper(testObject.providerName))
|
||||||
|
if err := os.Setenv(envName, testObject.token); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := util.GetAccessToken(testObject.providerName)
|
||||||
|
|
||||||
|
assert.Equal(t, testObject.valid, err == nil)
|
||||||
|
os.Unsetenv(envName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testDoubleFiles struct {
|
||||||
|
testFiles []config.Asset
|
||||||
|
valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var files = []testDoubleFiles{
|
||||||
|
testDoubleFiles{
|
||||||
|
testFiles: []config.Asset{
|
||||||
|
config.Asset{
|
||||||
|
Name: "file0",
|
||||||
|
Compress: true,
|
||||||
|
},
|
||||||
|
config.Asset{
|
||||||
|
Name: "file1",
|
||||||
|
Compress: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
valid: true,
|
||||||
|
},
|
||||||
|
testDoubleFiles{
|
||||||
|
testFiles: []config.Asset{
|
||||||
|
config.Asset{
|
||||||
|
Name: "",
|
||||||
|
Compress: true,
|
||||||
|
},
|
||||||
|
config.Asset{
|
||||||
|
Name: "",
|
||||||
|
Compress: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
valid: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPrepareAssets(t *testing.T) {
|
||||||
|
for _, testObject := range files {
|
||||||
|
workDir, _ := os.Getwd()
|
||||||
|
filesToDelete := []string{}
|
||||||
|
|
||||||
|
for _, testFile := range testObject.testFiles {
|
||||||
|
|
||||||
|
if testFile.Name != "" {
|
||||||
|
filesToDelete = append(filesToDelete, testFile.Name)
|
||||||
|
|
||||||
|
file, err := os.Create(testFile.Name)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err.Error())
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
if testFile.Compress {
|
||||||
|
filesToDelete = append(filesToDelete, testFile.Name+".zip")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
preparedFiles, err := util.PrepareAssets(workDir, testObject.testFiles)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
assert.Equal(t, 2, len(preparedFiles))
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, testObject.valid, err == nil)
|
||||||
|
|
||||||
|
for _, file := range filesToDelete {
|
||||||
|
if err := os.Remove(file); err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
33
internal/shared/shared.go
Normal file
33
internal/shared/shared.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package shared
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/Masterminds/semver"
|
||||||
|
)
|
||||||
|
|
||||||
|
//ReleaseVersion struct
|
||||||
|
type ReleaseVersion struct {
|
||||||
|
Last ReleaseVersionEntry
|
||||||
|
Next ReleaseVersionEntry
|
||||||
|
Branch string
|
||||||
|
Draft bool
|
||||||
|
}
|
||||||
|
|
||||||
|
//ReleaseVersionEntry struct
|
||||||
|
type ReleaseVersionEntry struct {
|
||||||
|
Commit string
|
||||||
|
Version *semver.Version
|
||||||
|
}
|
||||||
|
|
||||||
|
//GeneratedChangelog struct
|
||||||
|
type GeneratedChangelog struct {
|
||||||
|
Title string
|
||||||
|
Content string
|
||||||
|
}
|
||||||
|
|
||||||
|
//ChangelogTemplateConfig struct
|
||||||
|
type ChangelogTemplateConfig struct {
|
||||||
|
CommitURL string
|
||||||
|
CompareURL string
|
||||||
|
Hash string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io/ioutil"
|
|
||||||
|
|
||||||
"gopkg.in/yaml.v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VersionFileContent struct
|
|
||||||
type VersionFileContent struct {
|
|
||||||
Version string `yaml:"version"`
|
|
||||||
NextVersion string `yaml:"nextVersion"`
|
|
||||||
Commit string `yaml:"commit"`
|
|
||||||
Branch string `yaml:"branch"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Write version into .version
|
|
||||||
func Write(versionFileContent VersionFileContent) error {
|
|
||||||
data, err := yaml.Marshal(&versionFileContent)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ioutil.WriteFile(".version", data, 0644)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read version into .version
|
|
||||||
func Read() (*VersionFileContent, error) {
|
|
||||||
|
|
||||||
content, err := ioutil.ReadFile(".version")
|
|
||||||
if err != nil {
|
|
||||||
return &VersionFileContent{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var versionFileContent VersionFileContent
|
|
||||||
err = yaml.Unmarshal(content, &versionFileContent)
|
|
||||||
if err != nil {
|
|
||||||
return &VersionFileContent{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &versionFileContent, nil
|
|
||||||
}
|
|
||||||
61
pkg/config/config.go
Normal file
61
pkg/config/config.go
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
// Package config provides defimition of .release.yml and read method
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ChangelogConfig struct
|
||||||
|
type ChangelogConfig struct {
|
||||||
|
PrintAll bool `yaml:"printAll,omitempty"`
|
||||||
|
Template string `yaml:"template,omitempty"`
|
||||||
|
TemplatePath string `yaml:"templatePath,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
//Asset type struct
|
||||||
|
type Asset struct {
|
||||||
|
Name string `yaml:"name"`
|
||||||
|
Compress bool `yaml:"compress"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// GitHubProvider struct
|
||||||
|
type GitHubProvider struct {
|
||||||
|
Repo string `yaml:"repo"`
|
||||||
|
User string `yaml:"user"`
|
||||||
|
CustomURL string `yaml:"customUrl,omitempty"`
|
||||||
|
AccessToken string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReleaseConfig struct
|
||||||
|
type ReleaseConfig struct {
|
||||||
|
CommitFormat string `yaml:"commitFormat"`
|
||||||
|
Branch map[string]string `yaml:"branch"`
|
||||||
|
Changelog ChangelogConfig `yaml:"changelog,omitempty"`
|
||||||
|
Release string `yaml:"release,omitempty"`
|
||||||
|
GitHubProvider GitHubProvider `yaml:"github,omitempty"`
|
||||||
|
Assets []Asset `yaml:"assets"`
|
||||||
|
ReleaseTitle string `yaml:"title"`
|
||||||
|
IsPreRelease, IsDraft bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read ReleaseConfig
|
||||||
|
func Read(configPath string) (*ReleaseConfig, error) {
|
||||||
|
|
||||||
|
content, err := ioutil.ReadFile(configPath)
|
||||||
|
if err != nil {
|
||||||
|
return &ReleaseConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var releaseConfig ReleaseConfig
|
||||||
|
err = yaml.Unmarshal(content, &releaseConfig)
|
||||||
|
if err != nil {
|
||||||
|
return &ReleaseConfig{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Tracef("Found config %+v", releaseConfig)
|
||||||
|
|
||||||
|
return &releaseConfig, nil
|
||||||
|
}
|
||||||
112
pkg/config/config_test.go
Normal file
112
pkg/config/config_test.go
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
package config_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReadCacheNotFound(t *testing.T) {
|
||||||
|
|
||||||
|
_, err := config.Read("notfound/dir")
|
||||||
|
assert.Errorf(t, err, "Read non exsiting file")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestReadCacheInvalidContent(t *testing.T) {
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "prefix")
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
completePath := path.Join(path.Dir(dir), ".release.yml")
|
||||||
|
brokenContent := []byte("hello broken\ngo: lang\n")
|
||||||
|
err = ioutil.WriteFile(completePath, brokenContent, 0644)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
_, readError := config.Read(completePath)
|
||||||
|
assert.Errorf(t, readError, "Should give error, when broken content")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestWriteAndReadCache(t *testing.T) {
|
||||||
|
|
||||||
|
dir, err := ioutil.TempDir("", "prefix")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
defer os.RemoveAll(dir)
|
||||||
|
|
||||||
|
completePath := path.Join(path.Dir(dir), ".release.yml")
|
||||||
|
content := []byte(`
|
||||||
|
commitFormat: angular
|
||||||
|
title: "go-semantic-release release"
|
||||||
|
branch:
|
||||||
|
master: release
|
||||||
|
rc: rc
|
||||||
|
beta: beta
|
||||||
|
alpha: alpha
|
||||||
|
add_git_releases: alpha
|
||||||
|
changelog:
|
||||||
|
printAll: false
|
||||||
|
template: ''
|
||||||
|
templatePath: ''
|
||||||
|
release: 'github'
|
||||||
|
assets:
|
||||||
|
- name: ./build/go-semantic-release
|
||||||
|
compress: false
|
||||||
|
github:
|
||||||
|
repo: "go-semantic-release"
|
||||||
|
user: "nightapes"
|
||||||
|
customUrl: ""
|
||||||
|
`)
|
||||||
|
err = ioutil.WriteFile(completePath, content, 0644)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
result, readError := config.Read(completePath)
|
||||||
|
assert.NoErrorf(t, readError, "Should read file")
|
||||||
|
|
||||||
|
assert.EqualValues(t, &config.ReleaseConfig{
|
||||||
|
CommitFormat: "angular",
|
||||||
|
Branch: map[string]string{"add_git_releases": "alpha", "alpha": "alpha", "beta": "beta", "master": "release", "rc": "rc"},
|
||||||
|
Changelog: config.ChangelogConfig{
|
||||||
|
PrintAll: false,
|
||||||
|
Template: "",
|
||||||
|
TemplatePath: ""},
|
||||||
|
Release: "github",
|
||||||
|
GitHubProvider: config.GitHubProvider{
|
||||||
|
Repo: "go-semantic-release",
|
||||||
|
User: "nightapes",
|
||||||
|
CustomURL: "",
|
||||||
|
AccessToken: ""},
|
||||||
|
Assets: []config.Asset{
|
||||||
|
config.Asset{
|
||||||
|
Name: "./build/go-semantic-release",
|
||||||
|
Compress: false}},
|
||||||
|
ReleaseTitle: "go-semantic-release release",
|
||||||
|
IsPreRelease: false,
|
||||||
|
IsDraft: false,
|
||||||
|
}, result)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// func TestWriteNotFound(t *testing.T) {
|
||||||
|
|
||||||
|
// err := cache.Write("notfound/dir", shared.ReleaseVersion{
|
||||||
|
// Last: shared.ReleaseVersionEntry{
|
||||||
|
// Commit: "12345",
|
||||||
|
// Version: createVersion("1.0.0"),
|
||||||
|
// },
|
||||||
|
// Next: shared.ReleaseVersionEntry{
|
||||||
|
// Commit: "12346",
|
||||||
|
// Version: createVersion("1.1.0"),
|
||||||
|
// },
|
||||||
|
// Branch: "master",
|
||||||
|
// Draft: true,
|
||||||
|
// })
|
||||||
|
// assert.Errorf(t, err, "Write non exsiting file")
|
||||||
|
|
||||||
|
// }
|
||||||
@@ -1,114 +1,238 @@
|
|||||||
package semanticrelease
|
package semanticrelease
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"io/ioutil"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/Masterminds/semver"
|
"github.com/Masterminds/semver"
|
||||||
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
"github.com/Nightapes/go-semantic-release/internal/analyzer"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/cache"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/calculator"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/changelog"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/ci"
|
||||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||||
"github.com/Nightapes/go-semantic-release/internal/storage"
|
"github.com/Nightapes/go-semantic-release/internal/releaser"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/releaser/util"
|
||||||
|
"github.com/Nightapes/go-semantic-release/internal/shared"
|
||||||
|
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetNextVersion from .version or calculate new
|
// SemanticRelease struct
|
||||||
func GetNextVersion(repro string) error {
|
type SemanticRelease struct {
|
||||||
util, err := gitutil.New(repro)
|
config *config.ReleaseConfig
|
||||||
|
gitutil *gitutil.GitUtil
|
||||||
|
analyzer *analyzer.Analyzer
|
||||||
|
calculator *calculator.Calculator
|
||||||
|
releaser releaser.Releaser
|
||||||
|
repository string
|
||||||
|
}
|
||||||
|
|
||||||
|
// New SemanticRelease struct
|
||||||
|
func New(c *config.ReleaseConfig, repository string) (*SemanticRelease, error) {
|
||||||
|
util, err := gitutil.New(repository)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := util.GetHash()
|
analyzer, err := analyzer.New(c.CommitFormat, c.Changelog)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := storage.Read()
|
releaser, err := releaser.New(c).GetReleaser()
|
||||||
|
|
||||||
if err == nil && content.Commit == hash {
|
|
||||||
fmt.Printf(content.NextVersion)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Debugf("Mismatch git and version file %s - %s", content.Commit, hash)
|
|
||||||
|
|
||||||
lastVersion, lastVersionHash, err := util.GetLastVersion()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return &SemanticRelease{
|
||||||
|
config: c,
|
||||||
|
gitutil: util,
|
||||||
|
releaser: releaser,
|
||||||
|
analyzer: analyzer,
|
||||||
|
repository: repository,
|
||||||
|
calculator: calculator.New(),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//GetCIProvider result with ci config
|
||||||
|
func (s *SemanticRelease) GetCIProvider() (*ci.ProviderConfig, error) {
|
||||||
|
return ci.GetCIProvider(s.gitutil, ci.ReadAllEnvs())
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNextVersion from .version or calculate new from commits
|
||||||
|
func (s *SemanticRelease) GetNextVersion(provider *ci.ProviderConfig, force bool) (*shared.ReleaseVersion, map[analyzer.Release][]analyzer.AnalyzedCommit, error) {
|
||||||
|
log.Debugf("Ignore .version file if exits, %t", force)
|
||||||
|
if !force {
|
||||||
|
releaseVersion, err := cache.Read(s.repository)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if releaseVersion.Next.Commit == provider.Commit && releaseVersion != nil {
|
||||||
|
return releaseVersion, nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lastVersion, lastVersionHash, err := s.gitutil.GetLastVersion()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
firstRelease := false
|
||||||
|
|
||||||
if lastVersion == nil {
|
if lastVersion == nil {
|
||||||
defaultVersion, _ := semver.NewVersion("1.0.0")
|
defaultVersion, _ := semver.NewVersion("1.0.0")
|
||||||
SetVersion(defaultVersion.String(), repro)
|
lastVersion = defaultVersion
|
||||||
fmt.Printf(defaultVersion.String())
|
firstRelease = true
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
commits, err := util.GetCommits(lastVersionHash)
|
commits, err := s.gitutil.GetCommits(lastVersionHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Debugf("Found %d commits till last release", len(commits))
|
log.Debugf("Found %d commits till last release", len(commits))
|
||||||
|
|
||||||
a := analyzer.New("angular")
|
analyzedCommits := s.analyzer.Analyze(commits)
|
||||||
result := a.Analyze(commits)
|
|
||||||
|
|
||||||
|
isDraft := false
|
||||||
var newVersion semver.Version
|
var newVersion semver.Version
|
||||||
|
for branch, releaseType := range s.config.Branch {
|
||||||
if len(result["major"]) > 0 {
|
if provider.Branch == branch || strings.HasPrefix(provider.Branch, branch) {
|
||||||
newVersion = lastVersion.IncMajor()
|
log.Debugf("Found branch config for branch %s with release type %s", provider.Branch, releaseType)
|
||||||
return nil
|
newVersion, isDraft = s.calculator.CalculateNewVersion(analyzedCommits, lastVersion, releaseType, firstRelease)
|
||||||
} else if len(result["minor"]) > 0 {
|
break
|
||||||
newVersion = lastVersion.IncMinor()
|
}
|
||||||
} else if len(result["patch"]) > 0 {
|
|
||||||
newVersion = lastVersion.IncPatch()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SetVersion(newVersion.String(), repro)
|
releaseVersion := shared.ReleaseVersion{
|
||||||
fmt.Printf(newVersion.String())
|
Next: shared.ReleaseVersionEntry{
|
||||||
|
Commit: provider.Commit,
|
||||||
|
Version: &newVersion,
|
||||||
|
},
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
|
Commit: lastVersionHash,
|
||||||
|
Version: lastVersion,
|
||||||
|
},
|
||||||
|
Branch: provider.Branch,
|
||||||
|
Draft: isDraft,
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
log.Infof("New version %s -> %s", lastVersion.String(), newVersion.String())
|
||||||
|
err = cache.Write(s.repository, releaseVersion)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
return &releaseVersion, analyzedCommits, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetVersion(version string, repro string) error {
|
//SetVersion for git repository
|
||||||
|
func (s *SemanticRelease) SetVersion(provider *ci.ProviderConfig, version string) error {
|
||||||
util, err := gitutil.New(repro)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
newVersion, err := semver.NewVersion(version)
|
newVersion, err := semver.NewVersion(version)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
hash, err := util.GetHash()
|
lastVersion, lastVersionHash, err := s.gitutil.GetLastVersion()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if lastVersion == nil {
|
||||||
branch, err := util.GetBranch()
|
lastVersion, _ = semver.NewVersion("1.0.0")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newVersionContent := storage.VersionFileContent{
|
return cache.Write(s.repository, shared.ReleaseVersion{
|
||||||
Commit: hash,
|
Next: shared.ReleaseVersionEntry{
|
||||||
NextVersion: newVersion.String(),
|
Commit: provider.Commit,
|
||||||
Branch: branch,
|
Version: newVersion,
|
||||||
}
|
},
|
||||||
|
Last: shared.ReleaseVersionEntry{
|
||||||
lastVersion, _, err := util.GetLastVersion()
|
Commit: lastVersionHash,
|
||||||
if err != nil {
|
Version: lastVersion,
|
||||||
return err
|
},
|
||||||
}
|
Branch: provider.Branch,
|
||||||
|
})
|
||||||
if lastVersion != nil {
|
|
||||||
newVersionContent.Version = lastVersion.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
return storage.Write(newVersionContent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Release() {
|
// GetChangelog from last version till now
|
||||||
|
func (s *SemanticRelease) GetChangelog(analyzedCommits map[analyzer.Release][]analyzer.AnalyzedCommit, releaseVersion *shared.ReleaseVersion) (*shared.GeneratedChangelog, error) {
|
||||||
|
c := changelog.New(s.config, s.analyzer.GetRules(), time.Now())
|
||||||
|
return c.GenerateChanglog(shared.ChangelogTemplateConfig{
|
||||||
|
Version: releaseVersion.Next.Version.String(),
|
||||||
|
Hash: releaseVersion.Last.Commit,
|
||||||
|
CommitURL: s.releaser.GetCommitURL(),
|
||||||
|
CompareURL: s.releaser.GetCompareURL(releaseVersion.Last.Version.String(), releaseVersion.Next.Version.String()),
|
||||||
|
}, analyzedCommits)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteChangeLog wirtes changelog content to the given file
|
||||||
|
func (s *SemanticRelease) WriteChangeLog(changelogContent, file string) error {
|
||||||
|
return ioutil.WriteFile(file, []byte(changelogContent), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Release pusblish release to provider
|
||||||
|
func (s *SemanticRelease) Release(provider *ci.ProviderConfig, force bool) error {
|
||||||
|
|
||||||
|
if provider.IsPR {
|
||||||
|
log.Debugf("Will not perform a new release. This is a pull request")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, ok := s.config.Branch[provider.Branch]; !ok {
|
||||||
|
log.Debugf("Will not perform a new release. Current %s branch is not configured in release config", provider.Branch)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
releaseVersion, analyzedCommits, err := s.GetNextVersion(provider, force)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Could not get next version")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if releaseVersion.Next.Version.Equal(releaseVersion.Next.Version) {
|
||||||
|
log.Infof("No new version, no release needed")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
generatedChanglog, err := s.GetChangelog(analyzedCommits, releaseVersion)
|
||||||
|
if err != nil {
|
||||||
|
log.Debugf("Could not get changelog")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
releaser, err := releaser.New(s.config).GetReleaser()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = releaser.ValidateConfig()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = releaser.CreateRelease(releaseVersion, generatedChanglog); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = releaser.UploadAssets(s.repository, s.config.Assets); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ZipFiles zip files configured in release config
|
||||||
|
func (s *SemanticRelease) ZipFiles() error {
|
||||||
|
for _, file := range s.config.Assets {
|
||||||
|
if file.Compress {
|
||||||
|
if _, err := util.PrepareAssets(s.repository, s.config.Assets); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user