podman

Форк
0
250 строк · 8.4 Кб
1
// Copyright 2018 The Go Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style
3
// license that can be found in the LICENSE file.
4

5
// Pseudo-versions
6
//
7
// Code authors are expected to tag the revisions they want users to use,
8
// including prereleases. However, not all authors tag versions at all,
9
// and not all commits a user might want to try will have tags.
10
// A pseudo-version is a version with a special form that allows us to
11
// address an untagged commit and order that version with respect to
12
// other versions we might encounter.
13
//
14
// A pseudo-version takes one of the general forms:
15
//
16
//	(1) vX.0.0-yyyymmddhhmmss-abcdef123456
17
//	(2) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456
18
//	(3) vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible
19
//	(4) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456
20
//	(5) vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible
21
//
22
// If there is no recently tagged version with the right major version vX,
23
// then form (1) is used, creating a space of pseudo-versions at the bottom
24
// of the vX version range, less than any tagged version, including the unlikely v0.0.0.
25
//
26
// If the most recent tagged version before the target commit is vX.Y.Z or vX.Y.Z+incompatible,
27
// then the pseudo-version uses form (2) or (3), making it a prerelease for the next
28
// possible semantic version after vX.Y.Z. The leading 0 segment in the prerelease string
29
// ensures that the pseudo-version compares less than possible future explicit prereleases
30
// like vX.Y.(Z+1)-rc1 or vX.Y.(Z+1)-1.
31
//
32
// If the most recent tagged version before the target commit is vX.Y.Z-pre or vX.Y.Z-pre+incompatible,
33
// then the pseudo-version uses form (4) or (5), making it a slightly later prerelease.
34

35
package module
36

37
import (
38
	"errors"
39
	"fmt"
40
	"strings"
41
	"time"
42

43
	"golang.org/x/mod/internal/lazyregexp"
44
	"golang.org/x/mod/semver"
45
)
46

47
var pseudoVersionRE = lazyregexp.New(`^v[0-9]+\.(0\.0-|\d+\.\d+-([^+]*\.)?0\.)\d{14}-[A-Za-z0-9]+(\+[0-9A-Za-z-]+(\.[0-9A-Za-z-]+)*)?$`)
48

49
const PseudoVersionTimestampFormat = "20060102150405"
50

51
// PseudoVersion returns a pseudo-version for the given major version ("v1")
52
// preexisting older tagged version ("" or "v1.2.3" or "v1.2.3-pre"), revision time,
53
// and revision identifier (usually a 12-byte commit hash prefix).
54
func PseudoVersion(major, older string, t time.Time, rev string) string {
55
	if major == "" {
56
		major = "v0"
57
	}
58
	segment := fmt.Sprintf("%s-%s", t.UTC().Format(PseudoVersionTimestampFormat), rev)
59
	build := semver.Build(older)
60
	older = semver.Canonical(older)
61
	if older == "" {
62
		return major + ".0.0-" + segment // form (1)
63
	}
64
	if semver.Prerelease(older) != "" {
65
		return older + ".0." + segment + build // form (4), (5)
66
	}
67

68
	// Form (2), (3).
69
	// Extract patch from vMAJOR.MINOR.PATCH
70
	i := strings.LastIndex(older, ".") + 1
71
	v, patch := older[:i], older[i:]
72

73
	// Reassemble.
74
	return v + incDecimal(patch) + "-0." + segment + build
75
}
76

77
// ZeroPseudoVersion returns a pseudo-version with a zero timestamp and
78
// revision, which may be used as a placeholder.
79
func ZeroPseudoVersion(major string) string {
80
	return PseudoVersion(major, "", time.Time{}, "000000000000")
81
}
82

83
// incDecimal returns the decimal string incremented by 1.
84
func incDecimal(decimal string) string {
85
	// Scan right to left turning 9s to 0s until you find a digit to increment.
86
	digits := []byte(decimal)
87
	i := len(digits) - 1
88
	for ; i >= 0 && digits[i] == '9'; i-- {
89
		digits[i] = '0'
90
	}
91
	if i >= 0 {
92
		digits[i]++
93
	} else {
94
		// digits is all zeros
95
		digits[0] = '1'
96
		digits = append(digits, '0')
97
	}
98
	return string(digits)
99
}
100

101
// decDecimal returns the decimal string decremented by 1, or the empty string
102
// if the decimal is all zeroes.
103
func decDecimal(decimal string) string {
104
	// Scan right to left turning 0s to 9s until you find a digit to decrement.
105
	digits := []byte(decimal)
106
	i := len(digits) - 1
107
	for ; i >= 0 && digits[i] == '0'; i-- {
108
		digits[i] = '9'
109
	}
110
	if i < 0 {
111
		// decimal is all zeros
112
		return ""
113
	}
114
	if i == 0 && digits[i] == '1' && len(digits) > 1 {
115
		digits = digits[1:]
116
	} else {
117
		digits[i]--
118
	}
119
	return string(digits)
120
}
121

122
// IsPseudoVersion reports whether v is a pseudo-version.
123
func IsPseudoVersion(v string) bool {
124
	return strings.Count(v, "-") >= 2 && semver.IsValid(v) && pseudoVersionRE.MatchString(v)
125
}
126

127
// IsZeroPseudoVersion returns whether v is a pseudo-version with a zero base,
128
// timestamp, and revision, as returned by [ZeroPseudoVersion].
129
func IsZeroPseudoVersion(v string) bool {
130
	return v == ZeroPseudoVersion(semver.Major(v))
131
}
132

133
// PseudoVersionTime returns the time stamp of the pseudo-version v.
134
// It returns an error if v is not a pseudo-version or if the time stamp
135
// embedded in the pseudo-version is not a valid time.
136
func PseudoVersionTime(v string) (time.Time, error) {
137
	_, timestamp, _, _, err := parsePseudoVersion(v)
138
	if err != nil {
139
		return time.Time{}, err
140
	}
141
	t, err := time.Parse("20060102150405", timestamp)
142
	if err != nil {
143
		return time.Time{}, &InvalidVersionError{
144
			Version: v,
145
			Pseudo:  true,
146
			Err:     fmt.Errorf("malformed time %q", timestamp),
147
		}
148
	}
149
	return t, nil
150
}
151

152
// PseudoVersionRev returns the revision identifier of the pseudo-version v.
153
// It returns an error if v is not a pseudo-version.
154
func PseudoVersionRev(v string) (rev string, err error) {
155
	_, _, rev, _, err = parsePseudoVersion(v)
156
	return
157
}
158

159
// PseudoVersionBase returns the canonical parent version, if any, upon which
160
// the pseudo-version v is based.
161
//
162
// If v has no parent version (that is, if it is "vX.0.0-[…]"),
163
// PseudoVersionBase returns the empty string and a nil error.
164
func PseudoVersionBase(v string) (string, error) {
165
	base, _, _, build, err := parsePseudoVersion(v)
166
	if err != nil {
167
		return "", err
168
	}
169

170
	switch pre := semver.Prerelease(base); pre {
171
	case "":
172
		// vX.0.0-yyyymmddhhmmss-abcdef123456 → ""
173
		if build != "" {
174
			// Pseudo-versions of the form vX.0.0-yyyymmddhhmmss-abcdef123456+incompatible
175
			// are nonsensical: the "vX.0.0-" prefix implies that there is no parent tag,
176
			// but the "+incompatible" suffix implies that the major version of
177
			// the parent tag is not compatible with the module's import path.
178
			//
179
			// There are a few such entries in the index generated by proxy.golang.org,
180
			// but we believe those entries were generated by the proxy itself.
181
			return "", &InvalidVersionError{
182
				Version: v,
183
				Pseudo:  true,
184
				Err:     fmt.Errorf("lacks base version, but has build metadata %q", build),
185
			}
186
		}
187
		return "", nil
188

189
	case "-0":
190
		// vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z
191
		// vX.Y.(Z+1)-0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z+incompatible
192
		base = strings.TrimSuffix(base, pre)
193
		i := strings.LastIndexByte(base, '.')
194
		if i < 0 {
195
			panic("base from parsePseudoVersion missing patch number: " + base)
196
		}
197
		patch := decDecimal(base[i+1:])
198
		if patch == "" {
199
			// vX.0.0-0 is invalid, but has been observed in the wild in the index
200
			// generated by requests to proxy.golang.org.
201
			//
202
			// NOTE(bcmills): I cannot find a historical bug that accounts for
203
			// pseudo-versions of this form, nor have I seen such versions in any
204
			// actual go.mod files. If we find actual examples of this form and a
205
			// reasonable theory of how they came into existence, it seems fine to
206
			// treat them as equivalent to vX.0.0 (especially since the invalid
207
			// pseudo-versions have lower precedence than the real ones). For now, we
208
			// reject them.
209
			return "", &InvalidVersionError{
210
				Version: v,
211
				Pseudo:  true,
212
				Err:     fmt.Errorf("version before %s would have negative patch number", base),
213
			}
214
		}
215
		return base[:i+1] + patch + build, nil
216

217
	default:
218
		// vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456 → vX.Y.Z-pre
219
		// vX.Y.Z-pre.0.yyyymmddhhmmss-abcdef123456+incompatible → vX.Y.Z-pre+incompatible
220
		if !strings.HasSuffix(base, ".0") {
221
			panic(`base from parsePseudoVersion missing ".0" before date: ` + base)
222
		}
223
		return strings.TrimSuffix(base, ".0") + build, nil
224
	}
225
}
226

227
var errPseudoSyntax = errors.New("syntax error")
228

229
func parsePseudoVersion(v string) (base, timestamp, rev, build string, err error) {
230
	if !IsPseudoVersion(v) {
231
		return "", "", "", "", &InvalidVersionError{
232
			Version: v,
233
			Pseudo:  true,
234
			Err:     errPseudoSyntax,
235
		}
236
	}
237
	build = semver.Build(v)
238
	v = strings.TrimSuffix(v, build)
239
	j := strings.LastIndex(v, "-")
240
	v, rev = v[:j], v[j+1:]
241
	i := strings.LastIndex(v, "-")
242
	if j := strings.LastIndex(v, "."); j > i {
243
		base = v[:j] // "vX.Y.Z-pre.0" or "vX.Y.(Z+1)-0"
244
		timestamp = v[j+1:]
245
	} else {
246
		base = v[:i] // "vX.0.0"
247
		timestamp = v[i+1:]
248
	}
249
	return base, timestamp, rev, build, nil
250
}
251

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.