-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpulls.go
More file actions
160 lines (141 loc) · 4.5 KB
/
Copy pathpulls.go
File metadata and controls
160 lines (141 loc) · 4.5 KB
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
package main
import (
"cmp"
"fmt"
"slices"
"sort"
"github.com/Masterminds/semver/v3"
)
type ghPull struct {
Number int `json:"number"`
LevelLabels []string `json:"labels,omitempty"`
ChangeLevel changeLevel `json:"change_level"`
HasPreLabel bool `json:"has_pre_label,omitempty"`
PreReleasePrefix string `json:"pre_release_prefix,omitempty"`
HasStableLabel bool `json:"has_stable_label,omitempty"`
}
func newPull(number int, aliases map[string]string, labels ...string) (*ghPull, error) {
p := ghPull{
Number: number,
ChangeLevel: changeLevelNone,
}
sort.Strings(labels)
for _, label := range labels {
resolvedLabel := ResolveLabel(label, aliases)
level, ok := labelLevel(resolvedLabel)
if ok {
p.LevelLabels = append(p.LevelLabels, label)
if level > p.ChangeLevel {
p.ChangeLevel = level
}
}
pre, prefix := checkPrereleaseLabel(label, nil)
if pre {
p.HasPreLabel = true
if prefix != "" {
if p.PreReleasePrefix != "" && p.PreReleasePrefix != prefix {
return nil, fmt.Errorf("pull #%d has conflicting prerelease prefixes: %s and %s", number, p.PreReleasePrefix, prefix)
}
p.PreReleasePrefix = prefix
}
}
if resolvedLabel == labelStable {
p.HasStableLabel = true
}
}
if p.HasPreLabel && p.HasStableLabel {
return nil, fmt.Errorf("pull #%d has both prerelease and stable labels", number)
}
return &p, nil
}
func (p ghPull) String() string {
return fmt.Sprintf("#%d", p.Number)
}
type ghPulls []ghPull
func (p ghPulls) filter(filter func(ghPull) bool) ghPulls {
var result ghPulls
for _, pull := range p {
if filter(pull) {
result = append(result, pull)
}
}
return result
}
// stable returns pulls that have the stable label.
func (p ghPulls) stable() ghPulls {
return p.filter(func(pull ghPull) bool {
return pull.HasStableLabel
})
}
// unstable returns pulls that do not have the stable label.
func (p ghPulls) unstable() ghPulls {
return p.filter(func(pull ghPull) bool {
return !pull.HasStableLabel
})
}
// prerelease returns pulls that have the prerelease label.
func (p ghPulls) prerelease() ghPulls {
return p.filter(func(pull ghPull) bool {
return pull.HasPreLabel
})
}
// nonPrerelease returns pulls that do not indicate a prerelease.
func (p ghPulls) nonPrerelease() ghPulls {
return p.filter(func(pull ghPull) bool {
return !pull.HasPreLabel && pull.ChangeLevel > changeLevelNone
})
}
// compact sorts and removes duplicate pulls based on their number.
func (p ghPulls) compact() ghPulls {
pulls := slices.Clone(p)
slices.SortFunc(pulls, func(a, b ghPull) int {
return cmp.Compare(a.Number, b.Number)
})
return slices.CompactFunc(pulls, func(a, b ghPull) bool {
return a.Number == b.Number
})
}
// prereleasePrefix returns the pre-release prefix if all pre-release pulls have the same prefix.
func (p ghPulls) prereleasePrefix() (string, error) {
prefixPulls := p.prerelease().filter(func(pull ghPull) bool { return pull.PreReleasePrefix != "" })
prefix := ""
for _, pull := range prefixPulls {
if prefix != "" && prefix != pull.PreReleasePrefix {
return "", fmt.Errorf(
"cannot have multiple pre-release prefixes in the same release. release contains both %q and %q",
prefix, pull.PreReleasePrefix,
)
}
prefix = pull.PreReleasePrefix
}
return prefix, nil
}
func (p ghPulls) validateForChange(version semver.Version, isPrerelease, forceStable, forcePrerelease bool) error {
if len(p.prerelease()) > 0 && len(p.nonPrerelease()) > 0 {
return fmt.Errorf("cannot have pre-release and non-pre-release PRs in the same release. pre-release PRs: %v, non-pre-release PRs: %v",
p.prerelease(), p.nonPrerelease())
}
stablePulls := p.stable()
if forcePrerelease && len(stablePulls) > 0 {
return fmt.Errorf("cannot force pre-release with stable PRs. stable PRs: %v", stablePulls)
}
// the rest of this only applies to transitioning from prerelease to stable
if isPrerelease || version.Prerelease() == "" || forceStable {
// Already stable, no need to validate labels
return nil
}
unstablePulls := p.unstable()
if len(unstablePulls) > 0 && len(stablePulls) == 0 {
return fmt.Errorf(
"cannot create a stable release from a pre-release unless all PRs are labeled semver:stable. unlabeled PRs: %v",
unstablePulls,
)
}
if len(stablePulls) > 0 && len(unstablePulls) > 0 {
return fmt.Errorf(
"in order to release a stable version, all PRs must be labeled as stable. stable PRs: %v, unstable PRs: %v",
stablePulls, unstablePulls,
)
}
return nil
}