204 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			204 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
/*
 | 
						|
   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"
 | 
						|
	"errors"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"io/ioutil"
 | 
						|
	"os"
 | 
						|
	"os/exec"
 | 
						|
	"path/filepath"
 | 
						|
	"sort"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/BurntSushi/toml"
 | 
						|
	"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)
 | 
						|
		}
 | 
						|
		deps = append(deps, dependency{
 | 
						|
			Name:   parts[0],
 | 
						|
			Commit: parts[1],
 | 
						|
		})
 | 
						|
	}
 | 
						|
	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 getChangelog(previous, commit string) ([]byte, error) {
 | 
						|
	return git("log", "--oneline", fmt.Sprintf("%s..%s", previous, commit))
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
func git(args ...string) ([]byte, error) {
 | 
						|
	o, err := exec.Command("git", args...).CombinedOutput()
 | 
						|
	if err != nil {
 | 
						|
		return nil, fmt.Errorf("%s: %s", err, o)
 | 
						|
	}
 | 
						|
	return o, nil
 | 
						|
}
 | 
						|
 | 
						|
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
 | 
						|
}
 | 
						|
 | 
						|
func getContributors(previous, commit string) ([]string, error) {
 | 
						|
	raw, err := git("log", "--format=%aN", fmt.Sprintf("%s..%s", previous, commit))
 | 
						|
	if err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	var (
 | 
						|
		set = make(map[string]struct{})
 | 
						|
		s   = bufio.NewScanner(bytes.NewReader(raw))
 | 
						|
		out []string
 | 
						|
	)
 | 
						|
	for s.Scan() {
 | 
						|
		set[s.Text()] = struct{}{}
 | 
						|
	}
 | 
						|
	if err := s.Err(); err != nil {
 | 
						|
		return nil, err
 | 
						|
	}
 | 
						|
	for name := range set {
 | 
						|
		out = append(out, name)
 | 
						|
	}
 | 
						|
	sort.Strings(out)
 | 
						|
	return out, nil
 | 
						|
}
 | 
						|
 | 
						|
// 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
 | 
						|
}
 |