support semver checkpoint formats · Entire
support semver checkpoint formats
1f1d021·
pfleidi·3w ago·3 files·+88 added/-28 removed
Parse checkpoint policy versions with golang.org/x/mod/semver so downgrade checks can compare minor, patch, and prerelease versions.
Keep read/write support explicit while canonicalizing equivalent branch-v1 forms through the format key.
Sessions
5b9a51224543View transcript
Changes
3
cmd/entire/cli/checkpointpolicy
Mformat.go+19/-13
Mformat_test.go+52/-15
Mupdate_test.go+17
2 unmodified lines
3
4
5
6
6
7
8
9
10
11
4 unmodified lines
16
17
18
18
19
19
20
21
22
23
23
24
24
25
26
27
28
28
29
30
29
30
31
32
33
34
35
36
37
38
33
39
40
41
42
37
43
44
45
40
46
47
48
49
5 unmodified lines
55
56
57
52
58
59
60
61
16 unmodified lines
78
79
80
75
81
82
83
84
2 unmodified lines
import (
"cmp"
"fmt"
"strconv"
"strings"
"golang.org/x/mod/semver"
)
type CheckpointFamily string
4 unmodified lines
)
type CheckpointFormat struct {
Family CheckpointFamily
Major int
Family CheckpointFamily
Version string
}
func ParseFormat(raw string) (CheckpointFormat, error) {
familyRaw, majorRaw, ok := strings.Cut(raw, "-v")
if !ok || familyRaw == "" || majorRaw == "" {
familyRaw, versionRaw, ok := strings.Cut(raw, "-")
if !ok || familyRaw == "" || versionRaw == "" {
return CheckpointFormat{}, fmt.Errorf("invalid checkpoint format %q", raw)
}
major, err := strconv.Atoi(majorRaw)
if err != nil || major <= 0 {
return CheckpointFormat{}, fmt.Errorf("invalid checkpoint major %q", majorRaw)
}
if !semver.IsValid(versionRaw) {
return CheckpointFormat{}, fmt.Errorf("invalid checkpoint version %q", versionRaw)
}
if semver.Major(versionRaw) == "v0" {
return CheckpointFormat{}, fmt.Errorf("invalid checkpoint version %q", versionRaw)
}
if semver.Build(versionRaw) != "" {
return CheckpointFormat{}, fmt.Errorf("checkpoint version %q must not include build metadata", versionRaw)
}
return CheckpointFormat{Family: CheckpointFamily(familyRaw), Major: major}, nil
}
return CheckpointFormat{Family: CheckpointFamily(familyRaw), Version: semver.Canonical(versionRaw)}, nil
}
func (f CheckpointFormat) String() string {
if f.Family == "" || f.Major == 0 {
if f.Family == "" || f.Version == "" {
return ""
}
return fmt.Sprintf("%s-v%d", f.Family, f.Major)
}
return fmt.Sprintf("%s-%s", f.Family, f.Version)
}
func Compare(a, b CheckpointFormat) int {
5 unmodified lines
if a.Family != b.Family {
return cmp.Compare(string(a.Family), string(b.Family))
}
return cmp.Compare(a.Major, b.Major)
return semver.Compare(a.Version, b.Version)
}
func CanRead(format CheckpointFormat) bool {
CheckpointFamilyRefs: 1,
}
var branchV1Format = CheckpointFormat{Family: CheckpointFamilyBranch, Major: 1}
var branchV1Format = CheckpointFormat{Family: CheckpointFamilyBranch, Version: semver.Canonical("v1")}
var (
readFormats = map[CheckpointFormat]bool{
Mcmd/entire/cli/checkpointpolicy/format.go+19/-13
10 unmodified lines
11
12
13
14
15
16
17
14
15
16
17
18
19
19
20
21
22
23
24
20
21
22
23
24
25
26
27
28
29
30
31
4 unmodified lines
36
37
38
35
36
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
53
88
89
90
91
92
93
94
95
57
96
97
98
61
99
10 unmodified lines
func TestParseFormat(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
want checkpointpolicy.CheckpointFormat
wantErr string
name string
input string
wantFamily checkpointpolicy.CheckpointFamily
wantString string
wantErr string
}{
{name: "branch v1", input: "branch-v1", want: checkpointpolicy.CheckpointFormat{Family: checkpointpolicy.CheckpointFamilyBranch, Major: 1}},
{name: "refs v2", input: "refs-v2", want: checkpointpolicy.CheckpointFormat{Family: checkpointpolicy.CheckpointFamilyRefs, Major: 2}},
{name: "unknown family parses", input: "unknown-v1", want: checkpointpolicy.CheckpointFormat{Family: "unknown", Major: 1}},
{name: "missing v", input: "branch-1", wantErr: "invalid checkpoint format"},
{name: "zero major", input: "branch-v0", wantErr: "invalid checkpoint major"},
{name: "non numeric major", input: "branch-vx", wantErr: "invalid checkpoint major"},
{name: "branch v1", input: "branch-v1", wantFamily: checkpointpolicy.CheckpointFamilyBranch, wantString: "branch-v1.0.0"},
{name: "branch v1 minor", input: "branch-v1.2", wantFamily: checkpointpolicy.CheckpointFamilyBranch, wantString: "branch-v1.2.0"},
{name: "branch v1 patch", input: "branch-v1.2.3", wantFamily: checkpointpolicy.CheckpointFamilyBranch, wantString: "branch-v1.2.3"},
{name: "refs prerelease", input: "refs-v2.0.0-rc.1", wantFamily: checkpointpolicy.CheckpointFamilyRefs, wantString: "refs-v2.0.0-rc.1"},
{name: "unknown family parses", input: "unknown-v1", wantFamily: "unknown", wantString: "unknown-v1.0.0"},
{name: "missing v", input: "branch-1", wantErr: "invalid checkpoint version"},
{name: "zero major", input: "branch-v0", wantErr: "invalid checkpoint version"},
{name: "non numeric major", input: "branch-vx", wantErr: "invalid checkpoint version"},
{name: "build metadata", input: "branch-v1.2.3+build.1", wantErr: "must not include build metadata"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
4 unmodified lines
return
}
require.NoError(t, err)
require.Equal(t, tt.want, got)
require.Equal(t, tt.input, got.String())
require.Equal(t, tt.wantFamily, got.Family)
require.Equal(t, tt.wantString, got.String())
})
}
}
func TestCompareFormats(t *testing.T) {
t.Parallel()
branchV1, err := checkpointpolicy.ParseFormat(checkpoint.CheckpointVersionBranchV1)
require.NoError(t, err)
branchV1Canonical, err := checkpointpolicy.ParseFormat("branch-v1.0.0")
require.NoError(t, err)
branchV1Minor, err := checkpointpolicy.ParseFormat("branch-v1.1.0")
require.NoError(t, err)
branchV1Patch, err := checkpointpolicy.ParseFormat("branch-v1.1.1")
require.NoError(t, err)
branchV1Prerelease, err := checkpointpolicy.ParseFormat("branch-v1.1.1-rc.1")
require.NoError(t, err)
refsV1, err := checkpointpolicy.ParseFormat("refs-v1")
require.NoError(t, err)
unknownV1, err := checkpointpolicy.ParseFormat("unknown-v1")
require.NoError(t, err)
require.Zero(t, checkpointpolicy.Compare(branchV1, branchV1Canonical))
require.Negative(t, checkpointpolicy.Compare(branchV1, branchV1Minor))
require.Negative(t, checkpointpolicy.Compare(branchV1Minor, branchV1Patch))
require.Negative(t, checkpointpolicy.Compare(branchV1Prerelease, branchV1Patch))
require.Negative(t, checkpointpolicy.Compare(branchV1Patch, refsV1))
require.Negative(t, checkpointpolicy.Compare(refsV1, unknownV1))
}
func TestSupportedFormats(t *testing.T) {
t.Parallel()
branchV1, err := checkpointpolicy.ParseFormat(checkpoint.CheckpointVersionBranchV1)
require.NoError(t, err)
branchV1Canonical, err := checkpointpolicy.ParseFormat("branch-v1.0.0")
require.NoError(t, err)
branchPrerelease, err := checkpointpolicy.ParseFormat("branch-v1.0.0-rc.1")
require.NoError(t, err)
refsV1, err := checkpointpolicy.ParseFormat("refs-v1")
require.NoError(t, err)
unknownV1, err := checkpointpolicy.ParseFormat("unknown-v1")
require.NoError(t, err)
require.True(t, checkpointpolicy.CanRead(branchV1))
require.True(t, checkpointpolicy.CanRead(branchV1Canonical))
require.True(t, checkpointpolicy.CanWrite(branchV1))
require.Equal(t, checkpoint.CheckpointVersionBranchV1, branchV1.String())
require.True(t, checkpointpolicy.CanWrite(branchV1Canonical))
require.Equal(t, "branch-v1.0.0", branchV1.String())
require.False(t, checkpointpolicy.CanRead(branchPrerelease))
require.False(t, checkpointpolicy.CanWrite(branchPrerelease))
require.False(t, checkpointpolicy.CanRead(refsV1))
require.False(t, checkpointpolicy.CanWrite(refsV1))
require.Negative(t, checkpointpolicy.Compare(branchV1, refsV1))
require.False(t, checkpointpolicy.CanRead(unknownV1))
require.False(t, checkpointpolicy.CanWrite(unknownV1))
require.Negative(t, checkpointpolicy.Compare(refsV1, unknownV1))
}