Merge pull request #2735 from estesp/remove-release-tool
Remove containerd-release tool from main repo
This commit is contained in:
commit
d4529076f7
2
Makefile
2
Makefile
@ -71,7 +71,7 @@ TEST_REQUIRES_ROOT_PACKAGES=$(filter \
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Project binaries.
|
# Project binaries.
|
||||||
COMMANDS=ctr containerd containerd-stress containerd-release
|
COMMANDS=ctr containerd containerd-stress
|
||||||
MANPAGES=ctr.1 containerd.1 containerd-config.1 containerd-config.toml.5
|
MANPAGES=ctr.1 containerd.1 containerd-config.1 containerd-config.toml.5
|
||||||
|
|
||||||
# Build tags seccomp and apparmor are needed by CRI plugin.
|
# Build tags seccomp and apparmor are needed by CRI plugin.
|
||||||
|
@ -1,290 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"text/tabwriter"
|
|
||||||
"text/template"
|
|
||||||
"unicode"
|
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
const vendorConf = "vendor.conf"
|
|
||||||
|
|
||||||
type note struct {
|
|
||||||
Title string `toml:"title"`
|
|
||||||
Description string `toml:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type change struct {
|
|
||||||
Commit string `toml:"commit"`
|
|
||||||
Description string `toml:"description"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type dependency struct {
|
|
||||||
Name string
|
|
||||||
Commit string
|
|
||||||
Previous string
|
|
||||||
CloneURL string
|
|
||||||
}
|
|
||||||
|
|
||||||
type download struct {
|
|
||||||
Filename string
|
|
||||||
Hash string
|
|
||||||
}
|
|
||||||
|
|
||||||
type projectChange struct {
|
|
||||||
Name string
|
|
||||||
Changes []change
|
|
||||||
}
|
|
||||||
|
|
||||||
type projectRename struct {
|
|
||||||
Old string `toml:"old"`
|
|
||||||
New string `toml:"new"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type release struct {
|
|
||||||
ProjectName string `toml:"project_name"`
|
|
||||||
GithubRepo string `toml:"github_repo"`
|
|
||||||
Commit string `toml:"commit"`
|
|
||||||
Previous string `toml:"previous"`
|
|
||||||
PreRelease bool `toml:"pre_release"`
|
|
||||||
Preface string `toml:"preface"`
|
|
||||||
Notes map[string]note `toml:"notes"`
|
|
||||||
BreakingChanges map[string]change `toml:"breaking"`
|
|
||||||
|
|
||||||
// dependency options
|
|
||||||
MatchDeps string `toml:"match_deps"`
|
|
||||||
RenameDeps map[string]projectRename `toml:"rename_deps"`
|
|
||||||
|
|
||||||
// generated fields
|
|
||||||
Changes []projectChange
|
|
||||||
Contributors []string
|
|
||||||
Dependencies []dependency
|
|
||||||
Tag string
|
|
||||||
Version string
|
|
||||||
Downloads []download
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
app := cli.NewApp()
|
|
||||||
app.Name = "release"
|
|
||||||
app.Description = `release tooling.
|
|
||||||
|
|
||||||
This tool should be ran from the root of the project repository for a new release.
|
|
||||||
`
|
|
||||||
app.Flags = []cli.Flag{
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "dry,n",
|
|
||||||
Usage: "run the release tooling as a dry run to print the release notes to stdout",
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "debug,d",
|
|
||||||
Usage: "show debug output",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "tag,t",
|
|
||||||
Usage: "tag name for the release, defaults to release file name",
|
|
||||||
},
|
|
||||||
cli.StringFlag{
|
|
||||||
Name: "template",
|
|
||||||
Usage: "template filepath to use in place of the default",
|
|
||||||
Value: defaultTemplateFile,
|
|
||||||
},
|
|
||||||
cli.BoolFlag{
|
|
||||||
Name: "linkify,l",
|
|
||||||
Usage: "add links to changelog",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
app.Action = func(context *cli.Context) error {
|
|
||||||
var (
|
|
||||||
releasePath = context.Args().First()
|
|
||||||
tag = context.String("tag")
|
|
||||||
linkify = context.Bool("linkify")
|
|
||||||
)
|
|
||||||
if tag == "" {
|
|
||||||
tag = parseTag(releasePath)
|
|
||||||
}
|
|
||||||
version := strings.TrimLeft(tag, "v")
|
|
||||||
if context.Bool("debug") {
|
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
|
||||||
}
|
|
||||||
r, err := loadRelease(releasePath)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
logrus.Infof("Welcome to the %s release tool...", r.ProjectName)
|
|
||||||
|
|
||||||
mailmapPath, err := filepath.Abs(".mailmap")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "failed to resolve mailmap")
|
|
||||||
}
|
|
||||||
gitConfigs["mailmap.file"] = mailmapPath
|
|
||||||
|
|
||||||
var (
|
|
||||||
contributors = map[contributor]int{}
|
|
||||||
projectChanges = []projectChange{}
|
|
||||||
)
|
|
||||||
|
|
||||||
changes, err := changelog(r.Previous, r.Commit)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if linkify {
|
|
||||||
if err := linkifyChanges(changes, githubCommitLink(r.GithubRepo), githubPRLink(r.GithubRepo)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err := addContributors(r.Previous, r.Commit, contributors); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
projectChanges = append(projectChanges, projectChange{
|
|
||||||
Name: "",
|
|
||||||
Changes: changes,
|
|
||||||
})
|
|
||||||
|
|
||||||
logrus.Infof("creating new release %s with %d new changes...", tag, len(changes))
|
|
||||||
rd, err := fileFromRev(r.Commit, vendorConf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
previous, err := getPreviousDeps(r.Previous)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
deps, err := parseDependencies(rd)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
renameDependencies(previous, r.RenameDeps)
|
|
||||||
updatedDeps := updatedDeps(previous, deps)
|
|
||||||
|
|
||||||
sort.Slice(updatedDeps, func(i, j int) bool {
|
|
||||||
return updatedDeps[i].Name < updatedDeps[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
if r.MatchDeps != "" && len(updatedDeps) > 0 {
|
|
||||||
re, err := regexp.Compile(r.MatchDeps)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "unable to compile 'match_deps' regexp")
|
|
||||||
}
|
|
||||||
td, err := ioutil.TempDir("", "tmp-clone-")
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "unable to create temp clone directory")
|
|
||||||
}
|
|
||||||
defer os.RemoveAll(td)
|
|
||||||
|
|
||||||
cwd, err := os.Getwd()
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrap(err, "unable to get cwd")
|
|
||||||
}
|
|
||||||
for _, dep := range updatedDeps {
|
|
||||||
matches := re.FindStringSubmatch(dep.Name)
|
|
||||||
if matches == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
logrus.Debugf("Matched dependency %s with %s", dep.Name, r.MatchDeps)
|
|
||||||
var name string
|
|
||||||
if len(matches) < 2 {
|
|
||||||
name = path.Base(dep.Name)
|
|
||||||
} else {
|
|
||||||
name = matches[1]
|
|
||||||
}
|
|
||||||
if err := os.Chdir(td); err != nil {
|
|
||||||
return errors.Wrap(err, "unable to chdir to temp clone directory")
|
|
||||||
}
|
|
||||||
git("clone", dep.CloneURL, name)
|
|
||||||
|
|
||||||
if err := os.Chdir(name); err != nil {
|
|
||||||
return errors.Wrapf(err, "unable to chdir to cloned %s directory", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
changes, err := changelog(dep.Previous, dep.Commit)
|
|
||||||
if err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to get changelog for %s", name)
|
|
||||||
}
|
|
||||||
if err := addContributors(dep.Previous, dep.Commit, contributors); err != nil {
|
|
||||||
return errors.Wrapf(err, "failed to get authors for %s", name)
|
|
||||||
}
|
|
||||||
if linkify {
|
|
||||||
if !strings.HasPrefix(dep.Name, "github.com/") {
|
|
||||||
logrus.Debugf("linkify only supported for Github, skipping %s", dep.Name)
|
|
||||||
} else {
|
|
||||||
ghname := dep.Name[11:]
|
|
||||||
if err := linkifyChanges(changes, githubCommitLink(ghname), githubPRLink(ghname)); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
projectChanges = append(projectChanges, projectChange{
|
|
||||||
Name: name,
|
|
||||||
Changes: changes,
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
if err := os.Chdir(cwd); err != nil {
|
|
||||||
return errors.Wrap(err, "unable to chdir to previous cwd")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// update the release fields with generated data
|
|
||||||
r.Contributors = orderContributors(contributors)
|
|
||||||
r.Dependencies = updatedDeps
|
|
||||||
r.Changes = projectChanges
|
|
||||||
r.Tag = tag
|
|
||||||
r.Version = version
|
|
||||||
|
|
||||||
// Remove trailing new lines
|
|
||||||
r.Preface = strings.TrimRightFunc(r.Preface, unicode.IsSpace)
|
|
||||||
|
|
||||||
tmpl, err := getTemplate(context)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if context.Bool("dry") {
|
|
||||||
t, err := template.New("release-notes").Parse(tmpl)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
w := tabwriter.NewWriter(os.Stdout, 8, 8, 2, ' ', 0)
|
|
||||||
if err := t.Execute(w, r); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return w.Flush()
|
|
||||||
}
|
|
||||||
logrus.Info("release complete!")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if err := app.Run(os.Args); err != nil {
|
|
||||||
fmt.Fprintln(os.Stderr, err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
const (
|
|
||||||
defaultTemplateFile = "TEMPLATE"
|
|
||||||
releaseNotes = `{{.ProjectName}} {{.Version}}
|
|
||||||
|
|
||||||
Welcome to the {{.Tag}} release of {{.ProjectName}}!
|
|
||||||
{{- if .PreRelease }}
|
|
||||||
*This is a pre-release of {{.ProjectName}}*
|
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
{{.Preface}}
|
|
||||||
|
|
||||||
Please try out the release binaries and report any issues at
|
|
||||||
https://github.com/{{.GithubRepo}}/issues.
|
|
||||||
|
|
||||||
{{- range $note := .Notes}}
|
|
||||||
|
|
||||||
### {{$note.Title}}
|
|
||||||
|
|
||||||
{{$note.Description}}
|
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
### Contributors
|
|
||||||
{{range $contributor := .Contributors}}
|
|
||||||
* {{$contributor}}
|
|
||||||
{{- end -}}
|
|
||||||
|
|
||||||
{{range $project := .Changes}}
|
|
||||||
|
|
||||||
### Changes{{if $project.Name}} from {{$project.Name}}{{end}}
|
|
||||||
{{range $change := $project.Changes }}
|
|
||||||
* {{$change.Commit}} {{$change.Description}}
|
|
||||||
{{- end}}
|
|
||||||
{{- end}}
|
|
||||||
|
|
||||||
### Dependency Changes
|
|
||||||
|
|
||||||
Previous release can be found at [{{.Previous}}](https://github.com/{{.GithubRepo}}/releases/tag/{{.Previous}})
|
|
||||||
{{range $dep := .Dependencies}}
|
|
||||||
* **{{$dep.Name}}** {{if $dep.Previous}}{{$dep.Previous}} -> {{$dep.Commit}}{{else}}{{$dep.Commit}} **_new_**{{end}}
|
|
||||||
{{- end}}
|
|
||||||
`
|
|
||||||
)
|
|
@ -1,338 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright The containerd Authors.
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"regexp"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/BurntSushi/toml"
|
|
||||||
"github.com/pkg/errors"
|
|
||||||
"github.com/sirupsen/logrus"
|
|
||||||
"github.com/urfave/cli"
|
|
||||||
)
|
|
||||||
|
|
||||||
func loadRelease(path string) (*release, error) {
|
|
||||||
var r release
|
|
||||||
if _, err := toml.DecodeFile(path, &r); err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil, errors.New("please specify the release file as the first argument")
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return &r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseTag(path string) string {
|
|
||||||
return strings.TrimSuffix(filepath.Base(path), ".toml")
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDependencies(r io.Reader) ([]dependency, error) {
|
|
||||||
var deps []dependency
|
|
||||||
s := bufio.NewScanner(r)
|
|
||||||
for s.Scan() {
|
|
||||||
ln := strings.TrimSpace(s.Text())
|
|
||||||
if strings.HasPrefix(ln, "#") || ln == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
cidx := strings.Index(ln, "#")
|
|
||||||
if cidx > 0 {
|
|
||||||
ln = ln[:cidx]
|
|
||||||
}
|
|
||||||
ln = strings.TrimSpace(ln)
|
|
||||||
parts := strings.Fields(ln)
|
|
||||||
if len(parts) != 2 && len(parts) != 3 {
|
|
||||||
return nil, fmt.Errorf("invalid config format: %s", ln)
|
|
||||||
}
|
|
||||||
|
|
||||||
var cloneURL string
|
|
||||||
if len(parts) == 3 {
|
|
||||||
cloneURL = parts[2]
|
|
||||||
} else {
|
|
||||||
cloneURL = "git://" + parts[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
deps = append(deps, dependency{
|
|
||||||
Name: parts[0],
|
|
||||||
Commit: parts[1],
|
|
||||||
CloneURL: cloneURL,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return deps, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getPreviousDeps(previous string) ([]dependency, error) {
|
|
||||||
r, err := fileFromRev(previous, vendorConf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return parseDependencies(r)
|
|
||||||
}
|
|
||||||
|
|
||||||
func changelog(previous, commit string) ([]change, error) {
|
|
||||||
raw, err := getChangelog(previous, commit)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return parseChangelog(raw)
|
|
||||||
}
|
|
||||||
|
|
||||||
func gitChangeDiff(previous, commit string) string {
|
|
||||||
if previous != "" {
|
|
||||||
return fmt.Sprintf("%s..%s", previous, commit)
|
|
||||||
}
|
|
||||||
return commit
|
|
||||||
}
|
|
||||||
|
|
||||||
func getChangelog(previous, commit string) ([]byte, error) {
|
|
||||||
return git("log", "--oneline", gitChangeDiff(previous, commit))
|
|
||||||
}
|
|
||||||
|
|
||||||
func linkifyChanges(c []change, commit, msg func(change) (string, error)) error {
|
|
||||||
for i := range c {
|
|
||||||
commitLink, err := commit(c[i])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
description, err := msg(c[i])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c[i].Commit = fmt.Sprintf("[`%s`](%s)", c[i].Commit, commitLink)
|
|
||||||
c[i].Description = description
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseChangelog(changelog []byte) ([]change, error) {
|
|
||||||
var (
|
|
||||||
changes []change
|
|
||||||
s = bufio.NewScanner(bytes.NewReader(changelog))
|
|
||||||
)
|
|
||||||
for s.Scan() {
|
|
||||||
fields := strings.Fields(s.Text())
|
|
||||||
changes = append(changes, change{
|
|
||||||
Commit: fields[0],
|
|
||||||
Description: strings.Join(fields[1:], " "),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return changes, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileFromRev(rev, file string) (io.Reader, error) {
|
|
||||||
p, err := git("show", fmt.Sprintf("%s:%s", rev, file))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes.NewReader(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var gitConfigs = map[string]string{}
|
|
||||||
|
|
||||||
func git(args ...string) ([]byte, error) {
|
|
||||||
var gitArgs []string
|
|
||||||
for k, v := range gitConfigs {
|
|
||||||
gitArgs = append(gitArgs, "-c", fmt.Sprintf("%s=%s", k, v))
|
|
||||||
}
|
|
||||||
gitArgs = append(gitArgs, args...)
|
|
||||||
o, err := exec.Command("git", gitArgs...).CombinedOutput()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("%s: %s", err, o)
|
|
||||||
}
|
|
||||||
return o, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func renameDependencies(deps []dependency, renames map[string]projectRename) {
|
|
||||||
if len(renames) == 0 {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
type dep struct {
|
|
||||||
shortname string
|
|
||||||
name string
|
|
||||||
}
|
|
||||||
renameMap := map[string]dep{}
|
|
||||||
for shortname, rename := range renames {
|
|
||||||
renameMap[rename.Old] = dep{
|
|
||||||
shortname: shortname,
|
|
||||||
name: rename.New,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for i := range deps {
|
|
||||||
if updated, ok := renameMap[deps[i].Name]; ok {
|
|
||||||
logrus.Debugf("Renamed %s from %s to %s", updated.shortname, deps[i].Name, updated.name)
|
|
||||||
deps[i].Name = updated.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func updatedDeps(previous, deps []dependency) []dependency {
|
|
||||||
var updated []dependency
|
|
||||||
pm, cm := toDepMap(previous), toDepMap(deps)
|
|
||||||
for name, c := range cm {
|
|
||||||
d, ok := pm[name]
|
|
||||||
if !ok {
|
|
||||||
// it is a new dep and should be noted
|
|
||||||
updated = append(updated, c)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// it exists, see if its updated
|
|
||||||
if d.Commit != c.Commit {
|
|
||||||
// set the previous commit
|
|
||||||
c.Previous = d.Commit
|
|
||||||
updated = append(updated, c)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return updated
|
|
||||||
}
|
|
||||||
|
|
||||||
func toDepMap(deps []dependency) map[string]dependency {
|
|
||||||
out := make(map[string]dependency)
|
|
||||||
for _, d := range deps {
|
|
||||||
out[d.Name] = d
|
|
||||||
}
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
type contributor struct {
|
|
||||||
name string
|
|
||||||
email string
|
|
||||||
}
|
|
||||||
|
|
||||||
func addContributors(previous, commit string, contributors map[contributor]int) error {
|
|
||||||
raw, err := git("log", `--format=%aE %aN`, gitChangeDiff(previous, commit))
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
s := bufio.NewScanner(bytes.NewReader(raw))
|
|
||||||
for s.Scan() {
|
|
||||||
p := strings.SplitN(s.Text(), " ", 2)
|
|
||||||
if len(p) != 2 {
|
|
||||||
return errors.Errorf("invalid author line: %q", s.Text())
|
|
||||||
}
|
|
||||||
c := contributor{
|
|
||||||
name: p[1],
|
|
||||||
email: p[0],
|
|
||||||
}
|
|
||||||
contributors[c] = contributors[c] + 1
|
|
||||||
}
|
|
||||||
if err := s.Err(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func orderContributors(contributors map[contributor]int) []string {
|
|
||||||
type contribstat struct {
|
|
||||||
name string
|
|
||||||
email string
|
|
||||||
count int
|
|
||||||
}
|
|
||||||
all := make([]contribstat, 0, len(contributors))
|
|
||||||
for c, count := range contributors {
|
|
||||||
all = append(all, contribstat{
|
|
||||||
name: c.name,
|
|
||||||
email: c.email,
|
|
||||||
count: count,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
sort.Slice(all, func(i, j int) bool {
|
|
||||||
if all[i].count == all[j].count {
|
|
||||||
return all[i].name < all[j].name
|
|
||||||
}
|
|
||||||
return all[i].count > all[j].count
|
|
||||||
})
|
|
||||||
names := make([]string, len(all))
|
|
||||||
for i := range names {
|
|
||||||
logrus.Debugf("Contributor: %s <%s> with %d commits", all[i].name, all[i].email, all[i].count)
|
|
||||||
names[i] = all[i].name
|
|
||||||
}
|
|
||||||
|
|
||||||
return names
|
|
||||||
}
|
|
||||||
|
|
||||||
// getTemplate will use a builtin template if the template is not specified on the cli
|
|
||||||
func getTemplate(context *cli.Context) (string, error) {
|
|
||||||
path := context.GlobalString("template")
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
// if the template file does not exist and the path is for the default template then
|
|
||||||
// return the compiled in template
|
|
||||||
if os.IsNotExist(err) && path == defaultTemplateFile {
|
|
||||||
return releaseNotes, nil
|
|
||||||
}
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
data, err := ioutil.ReadAll(f)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(data), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func githubCommitLink(repo string) func(change) (string, error) {
|
|
||||||
return func(c change) (string, error) {
|
|
||||||
full, err := git("rev-parse", c.Commit)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
commit := strings.TrimSpace(string(full))
|
|
||||||
|
|
||||||
return fmt.Sprintf("https://github.com/%s/commit/%s", repo, commit), nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func githubPRLink(repo string) func(change) (string, error) {
|
|
||||||
r := regexp.MustCompile("^Merge pull request #[0-9]+")
|
|
||||||
return func(c change) (string, error) {
|
|
||||||
var err error
|
|
||||||
message := r.ReplaceAllStringFunc(c.Description, func(m string) string {
|
|
||||||
idx := strings.Index(m, "#")
|
|
||||||
pr := m[idx+1:]
|
|
||||||
|
|
||||||
// TODO: Validate links using github API
|
|
||||||
// TODO: Validate PR merged as commit hash
|
|
||||||
link := fmt.Sprintf("https://github.com/%s/pull/%s", repo, pr)
|
|
||||||
|
|
||||||
return fmt.Sprintf("%s [#%s](%s)", m[:idx], pr, link)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return message, nil
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user