You've already forked go-semantic-release
4
.gitignore
vendored
4
.gitignore
vendored
@@ -4,7 +4,9 @@
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
go-semantic-release
|
||||
|
||||
!cmd/go-semantic-release
|
||||
# Test binary, build with `go test -c`
|
||||
*.test
|
||||
|
||||
@@ -12,3 +14,5 @@
|
||||
*.out
|
||||
.version
|
||||
.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
|
||||
|
||||
## 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
|
||||
|
||||
`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
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/pkg/semanticrelease"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/urfave/cli.v1" // imports as package "cli"
|
||||
"github.com/Nightapes/go-semantic-release/cmd/go-semantic-release/commands"
|
||||
)
|
||||
|
||||
var version string
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
commands.Execute(version)
|
||||
}
|
||||
|
||||
20
go.mod
20
go.mod
@@ -4,10 +4,20 @@ go 1.12
|
||||
|
||||
require (
|
||||
github.com/Masterminds/semver v1.4.2
|
||||
github.com/sirupsen/logrus v1.4.1
|
||||
github.com/urfave/cli v1.20.0 // indirect
|
||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f // indirect
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0
|
||||
gopkg.in/urfave/cli.v1 v1.20.0
|
||||
github.com/coreos/etcd v3.3.10+incompatible
|
||||
github.com/gliderlabs/ssh v0.2.2 // indirect
|
||||
github.com/google/go-cmp v0.3.0 // indirect
|
||||
github.com/google/go-github/v25 v25.1.3
|
||||
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
|
||||
)
|
||||
|
||||
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/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/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/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/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emirpasic/gods v1.9.0 h1:rUF4PuzEjMChMiNsVjdI+SyLu7rEqpQ5reNFnhC7oFo=
|
||||
github.com/emirpasic/gods v1.9.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o=
|
||||
github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg=
|
||||
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/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.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/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
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/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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
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/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
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/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
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/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/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/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||
github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k=
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
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/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/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
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/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/xanzy/ssh-agent v0.2.0 h1:Adglfbi5p9Z0BmK2oKU9nTG+zKfniSfnaMYB+ULd+Ro=
|
||||
github.com/xanzy/ssh-agent v0.2.0/go.mod h1:0NyE30eGUDliuLEHJgYte/zncp2zdTStcOnWhgSqHD8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793 h1:u+LnwYTOOW7Ukr/fppxEb1Nwz0AtPflrblfvUudpo+I=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xanzy/ssh-agent v0.2.1 h1:TCbipTQL2JiiCprBWx9frJ2eJlCYT00NmctrHxVAr70=
|
||||
github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4=
|
||||
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/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/crypto v0.0.0-20190422183909-d864b10871cd/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
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/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/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-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/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/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f h1:sjxqKRXfnzgJFg/igBXeLZoBVAKXuAAljgr+PcNr7u8=
|
||||
golang.org/x/tools v0.0.0-20190508150211-cf84161cff3f/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
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 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/src-d/go-billy.v4 v4.2.1 h1:omN5CrMrMcQ+4I8bJ0wEhOBPanIRWzFC953IiXKdYzo=
|
||||
gopkg.in/src-d/go-billy.v4 v4.2.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1 h1:XWW/s5W18RaJpmo1l0IYGqXKuJITWRFuA45iOf1dKJs=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.1.1/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0 h1:cJwWgJ0DXifrNrXM6RGN1Y2yR60Rr1zQ9Q5DX5S9qgU=
|
||||
gopkg.in/src-d/go-git.v4 v4.11.0/go.mod h1:Vtut8izDyrM8BUVQnzJ+YvmNcem2J89EmfZYCkLokZk=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0 h1:NdAVW6RYxDif9DhDHaAortIu956m2c0v+09AZBPTbE0=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.0/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1 h1:OkK1DmefDy1Z6Veu82wdNj/cLpYORhdX4qdaYCPwc7s=
|
||||
gopkg.in/src-d/go-billy.v4 v4.3.1/go.mod h1:tm33zBoOwxjYHZIE+OV8bxTWFMJLrconzFMd38aARFk=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0 h1:ivZFOIltbce2Mo8IjzUHAFoq/IylO9WHhNOAJK+LsJg=
|
||||
gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0 h1:CKgvBCJCcdfNnyXPYI4Cp8PaDDAmAPEN0CtfEdEAbd8=
|
||||
gopkg.in/src-d/go-git.v4 v4.12.0/go.mod h1:zjlNnzc1Wjn43v3Mtii7RVxiReNP0fIu9npcXKzuNp4=
|
||||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
|
||||
@@ -1,63 +1,102 @@
|
||||
// Package analyzer provides different commit analyzer
|
||||
package analyzer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
"github.com/Nightapes/go-semantic-release/pkg/config"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
//Analyzer struct
|
||||
type Analyzer struct {
|
||||
CommitFormat string
|
||||
analyzeCommit analyzeCommit
|
||||
Config config.ChangelogConfig
|
||||
}
|
||||
|
||||
type Rules struct {
|
||||
//Release types, like major
|
||||
type Release string
|
||||
|
||||
//Scope of the commit, like feat, fix,..
|
||||
type Scope string
|
||||
|
||||
//Rule for commits
|
||||
type Rule struct {
|
||||
Tag string
|
||||
Release string
|
||||
TagString string
|
||||
Release Release
|
||||
Changelog bool
|
||||
}
|
||||
|
||||
type AnalyzeCommit interface {
|
||||
Analyze(commit gitutil.Commit, tag string) (AnalyzedCommit, bool)
|
||||
GetRules() []Rules
|
||||
type analyzeCommit interface {
|
||||
analyze(commit gitutil.Commit, tag Rule) (AnalyzedCommit, bool, error)
|
||||
getRules() []Rule
|
||||
}
|
||||
|
||||
//AnalyzedCommit struct
|
||||
type AnalyzedCommit struct {
|
||||
Commit gitutil.Commit
|
||||
ParsedMessage string
|
||||
Scope string
|
||||
Scope Scope
|
||||
ParsedBreakingChangeMessage string
|
||||
Tag string
|
||||
TagString string
|
||||
Print bool
|
||||
}
|
||||
|
||||
func New(format string) *Analyzer {
|
||||
return &Analyzer{
|
||||
CommitFormat: format,
|
||||
//New Analyzer struct for given commit format
|
||||
func New(format string, config config.ChangelogConfig) (*Analyzer, error) {
|
||||
analyzer := &Analyzer{
|
||||
Config: config,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (a *Analyzer) Analyze(commits []gitutil.Commit) map[string][]AnalyzedCommit {
|
||||
|
||||
var commitAnalayzer AnalyzeCommit
|
||||
switch a.CommitFormat {
|
||||
switch format {
|
||||
case "angular":
|
||||
log.Infof("analyze angular format")
|
||||
commitAnalayzer = NewAngular()
|
||||
log.Debugf("Commit format set to angular")
|
||||
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["minor"] = make([]AnalyzedCommit, 0)
|
||||
analyzedCommits["patch"] = make([]AnalyzedCommit, 0)
|
||||
analyzedCommits["none"] = make([]AnalyzedCommit, 0)
|
||||
|
||||
for _, commit := range commits {
|
||||
for _, rule := range commitAnalayzer.GetRules() {
|
||||
analyzedCommit, hasBreakingChange := commitAnalayzer.Analyze(commit, rule.Tag)
|
||||
for _, rule := range a.analyzeCommit.getRules() {
|
||||
analyzedCommit, hasBreakingChange, err := a.analyzeCommit.analyze(commit, rule)
|
||||
if err == nil {
|
||||
if a.Config.PrintAll {
|
||||
analyzedCommit.Print = true
|
||||
} 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
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/Nightapes/go-semantic-release/internal/gitutil"
|
||||
)
|
||||
|
||||
type Angular struct {
|
||||
rules []Rules
|
||||
type angular struct {
|
||||
rules []Rule
|
||||
regex string
|
||||
}
|
||||
|
||||
func NewAngular() *Angular {
|
||||
return &Angular{
|
||||
func newAngular() *angular {
|
||||
return &angular{
|
||||
regex: `(TAG)(?:\((.*)\))?: (.*)`,
|
||||
rules: []Rules{
|
||||
rules: []Rule{
|
||||
{
|
||||
Tag: "feat",
|
||||
TagString: "Features",
|
||||
Release: "minor",
|
||||
Changelog: true,
|
||||
},
|
||||
{
|
||||
Tag: "fix",
|
||||
TagString: "Bug fixes",
|
||||
Release: "patch",
|
||||
Changelog: true,
|
||||
}, {
|
||||
Tag: "perf",
|
||||
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
|
||||
}
|
||||
|
||||
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{
|
||||
Commit: commit,
|
||||
Tag: rule.Tag,
|
||||
TagString: rule.TagString,
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", tag, -1))
|
||||
matches := re.FindAllStringSubmatch(commit.Message+" "+commit.Message, -1)
|
||||
re := regexp.MustCompile(strings.Replace(a.regex, "TAG", rule.Tag, -1))
|
||||
matches := re.FindAllStringSubmatch(commit.Message, -1)
|
||||
if len(matches) >= 1 {
|
||||
if len(matches[0]) >= 3 {
|
||||
analyzed.Scope = matches[0][2]
|
||||
|
||||
analyzed.Scope = Scope(matches[0][2])
|
||||
|
||||
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 {
|
||||
analyzed.ParsedMessage = splitted[0]
|
||||
return analyzed, false
|
||||
log.Tracef("%s: found %s", commit.Message, rule.Tag)
|
||||
return analyzed, false, nil
|
||||
}
|
||||
analyzed.ParsedMessage = splitted[0]
|
||||
analyzed.ParsedBreakingChangeMessage = splitted[1]
|
||||
return analyzed, true
|
||||
|
||||
}
|
||||
}
|
||||
return analyzed, false
|
||||
breakingChange := strings.SplitN(message, "BREAKING CHANGE:", 2)
|
||||
|
||||
analyzed.ParsedMessage = strings.Trim(breakingChange[0], " ")
|
||||
analyzed.ParsedBreakingChangeMessage = strings.Trim(breakingChange[1], " ")
|
||||
|
||||
log.Tracef(" %s, BREAKING CHANGE found", commit.Message)
|
||||
return analyzed, true, nil
|
||||
}
|
||||
}
|
||||
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
|
||||
|
||||
import (
|
||||
@@ -7,32 +8,37 @@ import (
|
||||
"github.com/Masterminds/semver"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/src-d/go-git.v4"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing"
|
||||
"gopkg.in/src-d/go-git.v4/plumbing/object"
|
||||
)
|
||||
|
||||
// Commit struct
|
||||
type Commit struct {
|
||||
Message string
|
||||
Author string
|
||||
Hash string
|
||||
}
|
||||
|
||||
type GitUtils struct {
|
||||
// GitUtil struct
|
||||
type GitUtil struct {
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
utils := &GitUtils{
|
||||
utils := &GitUtil{
|
||||
Repository: r,
|
||||
}
|
||||
return utils, nil
|
||||
|
||||
}
|
||||
|
||||
func (g *GitUtils) GetHash() (string, error) {
|
||||
// GetHash from git HEAD
|
||||
func (g *GitUtil) GetHash() (string, error) {
|
||||
ref, err := g.Repository.Head()
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -40,38 +46,59 @@ func (g *GitUtils) GetHash() (string, error) {
|
||||
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()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
if err == nil {
|
||||
tags = append(tags, v)
|
||||
} else {
|
||||
log.Debugf("Tag %s is not a valid version, skip", p.Name().Short())
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@@ -94,18 +121,13 @@ func (g *GitUtils) GetLastVersion() (*semver.Version, string, error) {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
tagObject, err := g.Repository.TagObject(tag.Hash())
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
log.Debugf("Found old hash %s", tag.Hash().String())
|
||||
return tags[0], tag.Hash().String(), nil
|
||||
}
|
||||
|
||||
log.Debugf("Found old hash %s", tagObject.Target.String())
|
||||
return tags[0], tagObject.Target.String(), nil
|
||||
}
|
||||
// GetCommits from git hash to HEAD
|
||||
func (g *GitUtil) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||
|
||||
func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||
|
||||
log.Printf("Read head")
|
||||
ref, err := g.Repository.Head()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -121,11 +143,12 @@ func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||
|
||||
err = cIter.ForEach(func(c *object.Commit) error {
|
||||
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
|
||||
}
|
||||
|
||||
}
|
||||
if !foundEnd {
|
||||
log.Tracef("Found commit with hash %s", c.Hash.String())
|
||||
commit := Commit{
|
||||
Message: c.Message,
|
||||
Author: c.Committer.Name,
|
||||
@@ -136,5 +159,5 @@ func (g *GitUtils) GetCommits(lastTagHash string) ([]Commit, error) {
|
||||
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
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
"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/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"
|
||||
)
|
||||
|
||||
// GetNextVersion from .version or calculate new
|
||||
func GetNextVersion(repro string) error {
|
||||
util, err := gitutil.New(repro)
|
||||
// SemanticRelease struct
|
||||
type SemanticRelease struct {
|
||||
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 {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash, err := util.GetHash()
|
||||
analyzer, err := analyzer.New(c.CommitFormat, c.Changelog)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := storage.Read()
|
||||
|
||||
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()
|
||||
releaser, err := releaser.New(c).GetReleaser()
|
||||
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 {
|
||||
defaultVersion, _ := semver.NewVersion("1.0.0")
|
||||
SetVersion(defaultVersion.String(), repro)
|
||||
fmt.Printf(defaultVersion.String())
|
||||
return nil
|
||||
lastVersion = defaultVersion
|
||||
firstRelease = true
|
||||
}
|
||||
|
||||
commits, err := util.GetCommits(lastVersionHash)
|
||||
commits, err := s.gitutil.GetCommits(lastVersionHash)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
log.Debugf("Found %d commits till last release", len(commits))
|
||||
|
||||
a := analyzer.New("angular")
|
||||
result := a.Analyze(commits)
|
||||
analyzedCommits := s.analyzer.Analyze(commits)
|
||||
|
||||
isDraft := false
|
||||
var newVersion semver.Version
|
||||
|
||||
if len(result["major"]) > 0 {
|
||||
newVersion = lastVersion.IncMajor()
|
||||
return nil
|
||||
} else if len(result["minor"]) > 0 {
|
||||
newVersion = lastVersion.IncMinor()
|
||||
} else if len(result["patch"]) > 0 {
|
||||
newVersion = lastVersion.IncPatch()
|
||||
for branch, releaseType := range s.config.Branch {
|
||||
if provider.Branch == branch || strings.HasPrefix(provider.Branch, branch) {
|
||||
log.Debugf("Found branch config for branch %s with release type %s", provider.Branch, releaseType)
|
||||
newVersion, isDraft = s.calculator.CalculateNewVersion(analyzedCommits, lastVersion, releaseType, firstRelease)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
SetVersion(newVersion.String(), repro)
|
||||
fmt.Printf(newVersion.String())
|
||||
|
||||
return err
|
||||
releaseVersion := shared.ReleaseVersion{
|
||||
Next: shared.ReleaseVersionEntry{
|
||||
Commit: provider.Commit,
|
||||
Version: &newVersion,
|
||||
},
|
||||
Last: shared.ReleaseVersionEntry{
|
||||
Commit: lastVersionHash,
|
||||
Version: lastVersion,
|
||||
},
|
||||
Branch: provider.Branch,
|
||||
Draft: isDraft,
|
||||
}
|
||||
|
||||
func SetVersion(version string, repro string) error {
|
||||
|
||||
util, err := gitutil.New(repro)
|
||||
log.Infof("New version %s -> %s", lastVersion.String(), newVersion.String())
|
||||
err = cache.Write(s.repository, releaseVersion)
|
||||
if err != nil {
|
||||
return err
|
||||
return nil, nil, err
|
||||
}
|
||||
return &releaseVersion, analyzedCommits, err
|
||||
}
|
||||
|
||||
//SetVersion for git repository
|
||||
func (s *SemanticRelease) SetVersion(provider *ci.ProviderConfig, version string) error {
|
||||
|
||||
newVersion, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hash, err := util.GetHash()
|
||||
lastVersion, lastVersionHash, err := s.gitutil.GetLastVersion()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if lastVersion == nil {
|
||||
lastVersion, _ = semver.NewVersion("1.0.0")
|
||||
}
|
||||
|
||||
return cache.Write(s.repository, shared.ReleaseVersion{
|
||||
Next: shared.ReleaseVersionEntry{
|
||||
Commit: provider.Commit,
|
||||
Version: newVersion,
|
||||
},
|
||||
Last: shared.ReleaseVersionEntry{
|
||||
Commit: lastVersionHash,
|
||||
Version: lastVersion,
|
||||
},
|
||||
Branch: provider.Branch,
|
||||
})
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
branch, err := util.GetBranch()
|
||||
err = releaser.ValidateConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newVersionContent := storage.VersionFileContent{
|
||||
Commit: hash,
|
||||
NextVersion: newVersion.String(),
|
||||
Branch: branch,
|
||||
}
|
||||
|
||||
lastVersion, _, err := util.GetLastVersion()
|
||||
if err != nil {
|
||||
if err = releaser.CreateRelease(releaseVersion, generatedChanglog); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if lastVersion != nil {
|
||||
newVersionContent.Version = lastVersion.String()
|
||||
if err = releaser.UploadAssets(s.repository, s.config.Assets); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return storage.Write(newVersionContent)
|
||||
return nil
|
||||
}
|
||||
|
||||
func Release() {
|
||||
// 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