-
Notifications
You must be signed in to change notification settings - Fork 0
/
upgrade.go
178 lines (142 loc) · 4.19 KB
/
upgrade.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
package catalog
import (
"fmt"
"golang.org/x/mod/semver"
"strings"
)
type UpgradeStep struct {
Info UpgradeInfo
Release Release
Criticality Criticality
}
type UpgradePath struct {
Info UpgradeInfo
Criticality Criticality
Steps []UpgradeStep
}
type UpgradeInfo struct {
ShortInfo string
Description string
Explanation string
ReferenceUrl string
}
// FindInstallVersion retrieves the latest (stable) version
func FindInstallVersion(availableReleases []*Release, withUnstable bool) (*UpgradeStep, error) {
if len(availableReleases) < 1 {
return nil, nil
}
releaseMap, availableVersions := buildReleaseMap(availableReleases)
// assume availableReleases to be presorted
for i := len(availableVersions) - 1; i > 0; i-- {
if !withUnstable && releaseMap[availableVersions[i]].Unstable {
continue
}
return &UpgradeStep{
Criticality: None,
Release: *releaseMap[availableVersions[i]],
}, nil
}
// everything unstable or nothing released yet
return nil, nil
}
func FindUpgradePath(availableReleases []*Release, currentVersion string) (*UpgradePath, error) {
path := &UpgradePath{
Steps: make([]UpgradeStep, 0, 4), // reserve spots for patch, minor and major
}
stepVersion := currentVersion
for {
step, err := FindNextUpgrade(availableReleases, stepVersion)
if err != nil {
return nil, err
}
if step == nil {
// nothing new in town?
if len(path.Steps) < 1 {
return nil, nil
}
// summary = whatever the first steps dictates
path.Criticality = path.Steps[0].Criticality
path.Info = path.Steps[0].Info
return path, nil
}
path.Steps = append(path.Steps, *step)
stepVersion = step.Release.Version
}
}
func FindNextUpgrade(availableReleases []*Release, currentVersion string) (*UpgradeStep, error) {
releaseMap, availableVersions := buildReleaseMap(availableReleases)
currentVersion = completeVersion(currentVersion)
currentVersionObj, ok := releaseMap[currentVersion]
if !ok {
return nil, fmt.Errorf("current version not found in available releases")
}
target := currentVersionObj.UpgradeTarget
// Check if we should search for patches if no further upgrade version has been found
searchPatches := true
if strings.HasPrefix(string(target), "nopatches:") {
searchPatches = false
target = target[len("nopatches:"):]
}
if target == "" {
target = DefaultUpgradeTarget
}
targetVersion, err := FindTargetVersion(availableVersions, currentVersion, target)
if err != nil {
return nil, err
}
// nothing new in town
if targetVersion == "" {
if !searchPatches {
return nil, nil
}
targetVersion, err = FindTargetVersion(availableVersions, currentVersion, "#.#.*")
if err != nil {
return nil, err
}
if targetVersion == "" {
return nil, nil
}
}
targetVersionObj, ok := releaseMap[targetVersion]
if !ok {
return nil, fmt.Errorf("target version not found in available releases")
}
criticality := calculateDefaultCriticality(currentVersion, targetVersion)
if currentVersionObj.ShouldUpgrade > criticality {
criticality = currentVersionObj.ShouldUpgrade
}
return &UpgradeStep{
// TODO: Gather info and description from release store
Release: *targetVersionObj,
Criticality: criticality,
}, nil
}
func buildReleaseMap(releases []*Release) (releaseMap map[string]*Release, availableVersions []string) {
// We can allocate the exact size needed here
releaseMap = make(map[string]*Release, len(releases))
availableVersions = make([]string, len(releases))
for i, release := range releases {
version := completeVersion(release.Version)
availableVersions[i] = version
releaseMap[version] = release
}
semver.Sort(availableVersions)
return releaseMap, availableVersions
}
func calculateDefaultCriticality(currentVersion string, targetVersion string) Criticality {
criticality := Possible
if semver.MajorMinor(currentVersion) != semver.MajorMinor(targetVersion) {
criticality = Recommended
}
if semver.Major(currentVersion) != semver.Major(targetVersion) {
criticality = StronglyRecommended
}
return criticality
}
func (step *UpgradeStep) ToPath() *UpgradePath {
return &UpgradePath{
Steps: []UpgradeStep{*step},
Info: step.Info,
Criticality: step.Criticality,
}
}