summaryrefslogtreecommitdiff
path: root/version/gen/vgen.go
blob: 8fd2a00d6365cbf4a8db065f5cf70456ec500c98 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// run as 'go run ./version/gen/vgen.go'

package main

import (
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/Masterminds/semver/v3"
)

func main() {
	descVer, err := describedVersion()
	if err != nil {
		log.Fatal(err)
	}

	latest, err := latestTag()
	if err != nil {
		log.Fatal(err)
	}

	ver := version(descVer, latest)

	fmt.Println(ver.String())
}

func describedVersion() (*semver.Version, error) {
	desc, err := runCmd("git describe --always")
	if err != nil {
		return nil, fmt.Errorf("git describe failed: %w", err)
	}

	ver, err := semver.NewVersion(desc)
	if err != nil {
		return nil, err
	}

	return ver, nil
}

func version(descVer, latest *semver.Version) *semver.Version {
	ver := *descVer
	if ver.Prerelease() != "" {
		ver = ver.IncPatch().IncPatch()
		ver, _ = ver.SetPrerelease(descVer.Prerelease())
		ver, _ = ver.SetMetadata(descVer.Metadata())
	} else if ver.Metadata() != "" {
		ver = ver.IncPatch()
		ver, _ = ver.SetMetadata(descVer.Metadata())
	}

	// if we're on a release tag already, we're done
	if descVer.Equal(&ver) {
		return descVer
	}

	// If 'latest' is greater than 'ver', we need to skip to the next patch.
	// If 'latest' is already a prerelease (i.e. if v5.0.0-pre was tagged),
	// we should use
	// If 'latest' is a prerelease, it's the same logic, except that we don't
	// want to increment the patch version.
	if latest.GreaterThan(&ver) || latest.Prerelease() != "" {
		v := *latest
		if v.Prerelease() == "" {
			v = v.IncPatch()
		}
		v, _ = v.SetPrerelease(ver.Prerelease())
		v, _ = v.SetMetadata(ver.Metadata())

		ver = v
	}

	return &ver
}

func latestTag() (*semver.Version, error) {
	// get the latest tag
	tags, err := runCmd("git tag --list v*")
	if err != nil {
		return nil, fmt.Errorf("git tag failed: %w", err)
	}

	// find the latest tag
	var latest *semver.Version
	for tag := range strings.SplitSeq(tags, "\n") {
		ver, err := semver.NewVersion(tag)
		if err != nil {
			return nil, fmt.Errorf("parsing tag %q failed: %w", tag, err)
		}

		if latest == nil || ver.GreaterThan(latest) {
			latest = ver
		}
	}

	if latest == nil {
		return nil, fmt.Errorf("no tags found")
	}

	return latest, nil
}

func runCmd(c string) (string, error) {
	parts := strings.Split(c, " ")
	//nolint:gosec
	cmd := exec.Command(parts[0], parts[1:]...)
	out, err := cmd.CombinedOutput()
	return strings.TrimSpace(string(out)), err
}