Commit 839cdcc1 authored by Juhani Ränkimies's avatar Juhani Ränkimies
Browse files

feat: add configurable pre-release scheme

parent b9cd5f34
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -28,7 +28,7 @@ test:

version:
  stage: version
  image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.16.0-unstable.20180825105527
  image: registry.gitlab.com/juhani/go-semrel-gitlab:v0.18.0-alpha.2
  script:
    - release next-version --allow-current > .next-version
  artifacts:
+11 −6
Original line number Diff line number Diff line
@@ -147,15 +147,10 @@ func analyzeCommits(c *cli.Context) (*semrel.ReleaseData, error) {
		}
		if !isReleaseBranch {
			// Turn this release a pre-release
			prV, err := semver.NewPRVersion(currBranch)
			err = render.SetPreReleaseNumber(releaseData, c)
			if err != nil {
				return nil, err
			}
			ts, err := semver.NewPRVersion(releaseData.Time.UTC().Format("20060102150405"))
			if err != nil {
				return nil, err
			}
			releaseData.NextVersion.Pre = []semver.PRVersion{prV, ts}
		}
	}
	return releaseData, nil
@@ -511,6 +506,16 @@ func main() {
                             produce normal releases. WARNING: this is an experimental feature.`,
			EnvVar: "GSG_RELEASE_BRANCHES",
		},
		cli.StringFlag{
			Name:   "pre-tmpl",
			Usage:  `Pre-release version template. Comma separated list of ID templates.`,
			EnvVar: "GSG_PRE_TMPL",
		},
		cli.StringFlag{
			Name:   "build-tmpl",
			Usage:  `Build metadata template. Comma separated list of ID templates.`,
			EnvVar: "GSG_BUILD_TMPL",
		},
		cli.StringFlag{
			Name:   "ci-project-path",
			Usage:  "gitlab CI environment variable",
+172 −6
Original line number Diff line number Diff line
@@ -2,8 +2,19 @@ package render

import (
	"bytes"
	"fmt"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/blang/semver"
	"github.com/juranki/go-semrel/semrel"
	"github.com/pkg/errors"
	"github.com/urfave/cli"
	git "gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/plumbing"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

var (
@@ -42,12 +53,27 @@ var (
{{ range .Changes.fix }}
- {{ if ne "" .Scope }}**{{ .Scope }}:** {{ end}}{{ .Subject }} ({{ .Hash }}){{ end }}{{ end }}`
	funcs   = template.FuncMap{"date": func() string { return time.Now().Format("2006-01-02") }}
	preTmpl = []string{
		`{{ (env "CI_COMMIT_REF_SLUG") }}`,
		`{{ seq }}`,
	}
)

func render(releaseInfo interface{}, tmpl string) (string, error) {
	t := template.Must(template.New("release").Funcs(funcs).Parse(tmpl))
type preReleaseInfo struct {
	CommitTS time.Time
}

func render(releaseInfo interface{}, tmpl string, fns template.FuncMap) (string, error) {
	t := template.New("")
	if fns != nil {
		t = t.Funcs(fns)
	}
	t, err := t.Parse(tmpl)
	if err != nil {
		return "", err
	}
	buf := bytes.Buffer{}
	err := t.Execute(&buf, releaseInfo)
	err = t.Execute(&buf, releaseInfo)
	if err != nil {
		return "", err
	}
@@ -56,10 +82,150 @@ func render(releaseInfo interface{}, tmpl string) (string, error) {

// ReleaseNote takes release info object returned by semrel.Release and returns markdown for the release note
func ReleaseNote(releaseInfo interface{}) (string, error) {
	return render(releaseInfo, releaseNoteTmpl)
	return render(releaseInfo, releaseNoteTmpl, funcs)
}

// ChangelogEntry takes release info object returned by semrel.Release and returns markdown for the changelog entry
func ChangelogEntry(releaseInfo interface{}) (string, error) {
	return render(releaseInfo, changelogTmpl)
	return render(releaseInfo, changelogTmpl, funcs)
}

// SetPreReleaseNumber adds pre- and build parts to the next version of releaseData
func SetPreReleaseNumber(releaseData *semrel.ReleaseData, context *cli.Context) error {
	preTs := parseCommaSeparatedList(context.GlobalString("pre-tmpl"))
	buildTs := parseCommaSeparatedList(context.GlobalString("build-tmpl"))

	versions, err := getMatchingVersions(releaseData.NextVersion)
	if err != nil {
		return err
	}
	fns := template.FuncMap{
		"env":      os.Getenv,
		"commitTS": func() time.Time { return releaseData.Time.UTC() },
	}

	if len(preTs) == 0 {
		preTs = preTmpl
	}

	if len(buildTs) > 0 {
		buildIDs := make([]string, len(buildTs))
		for i, buildT := range buildTs {
			buildStr, err := render(nil, buildT, fns)
			if err != nil {
				return errors.Wrapf(err, "formatting %s", buildT)
			}
			buildID, err := semver.NewBuildVersion(buildStr)
			if err != nil {
				return errors.Wrap(err, "build version tmpl")
			}
			buildIDs[i] = buildID
		}
		releaseData.NextVersion.Build = buildIDs
	}

	if len(preTs) > 0 {
		preIDs := make([]semver.PRVersion, 0)
		for _, preT := range preTs {
			fns["seq"] = getVersionsFun(versions, preIDs)
			preStr, err := render(nil, preT, fns)
			if err != nil {
				return errors.Wrap(err, "pre version tmpl")
			}
			preID, err := semver.NewPRVersion(preStr)
			if err != nil {
				return errors.Wrap(err, "pre version tmpl")
			}
			preIDs = append(preIDs, preID)
		}
		releaseData.NextVersion.Pre = preIDs
	}
	return nil
}

func getMatchingVersions(v semver.Version) ([]semver.Version, error) {
	versions := make([]semver.Version, 0)

	r, err := git.PlainOpen(".")
	if err != nil {
		return nil, err
	}

	addIfSemVer := func(sha string, version string) {
		sv, err := semver.ParseTolerant(version)
		if err == nil {
			if v.Major == sv.Major && v.Minor == sv.Minor && v.Patch == sv.Patch {
				versions = append(versions, sv)
			}
		}
	}

	tagRefs, err := r.Tags()
	if err != nil {
		return nil, err
	}
	err = tagRefs.ForEach(func(t *plumbing.Reference) error {
		addIfSemVer(t.Hash().String(), t.Name().Short())
		return nil
	})
	if err != nil {
		return nil, err
	}

	tagObjects, err := r.TagObjects()
	if err != nil {
		return nil, err
	}
	err = tagObjects.ForEach(func(t *object.Tag) error {
		addIfSemVer(t.Target.String(), t.Name)
		return nil
	})
	if err != nil {
		return nil, err
	}

	return versions, nil
}

func getVersionsFun(versions []semver.Version, preIDs []semver.PRVersion) func() (string, error) {
	f := func() (string, error) {
		n := uint64(0)
		if len(preIDs) == 0 {
			return "", errors.New("seq cannot be used in first ID of pre number")
		}
		for _, v := range versions {
			if len(v.Pre) > len(preIDs) {
				if !v.Pre[len(preIDs)].IsNumeric() {
					continue
				}
				if v.Pre[len(preIDs)].VersionNum <= n {
					continue
				}
				match := true
				for i, p := range preIDs {
					if p.Compare(v.Pre[i]) != 0 {
						match = false
						break
					}
				}
				if match {
					n = v.Pre[len(preIDs)].VersionNum
				}
			}
		}
		return fmt.Sprintf("%d", n+1), nil
	}
	return f
}

func parseCommaSeparatedList(s string) []string {
	if len(strings.TrimSpace(s)) == 0 {
		return []string{}
	}
	ss := strings.Split(s, ",")
	rv := []string{}
	for _, t := range ss {
		rv = append(rv, strings.TrimSpace(t))
	}
	return rv
}
+3 −3
Original line number Diff line number Diff line
RELEASE_URL=https://gitlab.com/api/v4/projects/5767443/jobs/93566147/artifacts/release
RELEASE_URL=https://gitlab.com/api/v4/projects/5767443/jobs/95879253/artifacts/release
RELEASE_DESC="x86_64 GNU/Linux binary"
RELEASE_SHA=3ddda5ff04ca6ddb078c02abf1cc3c9b3b75c2b4
RELEASE_VERSION=0.16.1-unstable.20180831034951
RELEASE_SHA=0cb60e575c5771c19a2316522a5cce697374d219
RELEASE_VERSION=0.18.0-alpha.3