-
Notifications
You must be signed in to change notification settings - Fork 0
/
describe.go
114 lines (100 loc) · 3.18 KB
/
describe.go
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
113
114
// Copyright (C) 2021-2022 Ambassador Labs
// Copyright (C) 2023 Luke Shumaker <[email protected]>
//
// SPDX-License-Identifier: Apache-2.0
package main
import (
"context"
"os"
"os/exec"
"path"
"strings"
"golang.org/x/mod/module"
"golang.org/x/mod/semver"
)
func cmdOutput(ctx context.Context, args ...string) (string, error) {
cmd := exec.CommandContext(ctx, args[0], args[1:]...)
cmd.Stderr = os.Stderr
bs, err := cmd.Output()
return string(bs), err
}
// Describe looks at the Git `commitish` and Git `vSEMVER` tags and returns a list of
// Go-modules-compatible version strings that refers to that commitish.
//
// If dirPrefix is non-empty, then Describe considers tags named `path.Join(dirPrefix, "vSEMVER")`
// rather than `vSEMVER`. Unlike Go itself, Describe does not validate that a `{dirPrefix}/go.mod`
// file exist in the tree referred to by `commitish`.
//
// If dirtyMarker is non-empty, then Describe checks if the current Git tree is dirty and if so
// appends dirtyMarker to the returned version strings. Strictly speaking, this makes the version
// strings not Go-modules-compatible.
//
// maxDescriptions limits how many version strings to return; a value of <=0 is no limit. For each
// ancestral tag, there is a distinct way to refer to the same commit; usually the highest version
// number (semver comparison) is the one you want; but sometimes it is useful to have the others.
// The strings returned are ordered highest-version-first.
func Describe(ctx context.Context, commitish, dirPrefix, dirtyMarker string, maxDescriptions int) ([]string, error) {
if dirPrefix != "" {
dirPrefix = path.Clean(dirPrefix) + "/"
}
commitInfo, err := statLocal(ctx, commitish)
if err != nil {
return nil, err
}
var parentTags []string
if maxDescriptions == 1 {
var parentTag string
parentTag, err = mostRecentTag(ctx, commitInfo, dirPrefix)
if parentTag != "" {
parentTags = []string{parentTag}
}
} else {
parentTags, err = mostRecentTags(ctx, commitInfo, dirPrefix)
}
if err != nil {
return nil, err
}
isDirty := false
if dirtyMarker != "" {
out, err := cmdOutput(ctx, "git", "status", "--porcelain")
if err != nil {
return nil, err
}
isDirty = len(out) > 0
}
var descriptions []string
for _, parentTag := range parentTags {
parentTagInfo, err := statLocal(ctx, parentTag)
if err != nil {
return nil, err
}
goVersionStr := strings.TrimPrefix(parentTag, dirPrefix)
// The '|| isDirty' here is important so that the dirtyMarker parses as a *post*-release
// rather than as a pre-release.
if parentTagInfo.Hash != commitInfo.Hash || isDirty {
goVersionStr = module.PseudoVersion(
semver.Major(goVersionStr),
goVersionStr,
commitInfo.Time,
ShortenSHA1(commitInfo.Hash))
}
if isDirty {
goVersionStr += dirtyMarker
}
descriptions = append(descriptions, goVersionStr)
if maxDescriptions > 0 && len(descriptions) >= maxDescriptions {
return descriptions, nil
}
}
// v0.0.0 without a tag.
goVersionStr := module.PseudoVersion(
"v0",
"",
commitInfo.Time,
ShortenSHA1(commitInfo.Hash))
if isDirty {
goVersionStr += dirtyMarker
}
descriptions = append(descriptions, goVersionStr)
return descriptions, nil
}