Skip to content

Commit 24ce31e

Browse files
committed
respect retracted tag, make tag match less greedy, change dir ignore from prefix to regex
1 parent 15722ca commit 24ce31e

File tree

4 files changed

+120
-26
lines changed

4 files changed

+120
-26
lines changed

.github/workflows/rc-breaking-changes.yaml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ on:
44
push:
55
branches:
66
- main
7+
workflow_dispatch:
78

89
jobs:
910
breaking-changes:
@@ -16,13 +17,15 @@ jobs:
1617
with:
1718
fetch-depth: 0
1819
fetch-tags: true
19-
- name: Set up Go 1.22.6
20+
- name: Set up Go 1.23.3
2021
uses: actions/setup-go@v5
2122
with:
22-
go-version: '1.22.6'
23+
go-version: '1.23.3'
2324
- name: Install gorelease tool
2425
run: |
2526
go install golang.org/x/exp/cmd/gorelease@latest
2627
- name: Run Breaking Changes Script
2728
run: |
28-
go run ./tools/breakingchanges/cmd/main.go --ignore tools
29+
cd ./tools/breakingchanges
30+
go mod tidy
31+
go run cmd/main.go --ignore tools --root ../../

tools/breakingchanges/cmd/main.go

Lines changed: 104 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ import (
1010
"os/exec"
1111
"path/filepath"
1212
"regexp"
13+
"sort"
1314
"strings"
15+
16+
"github.com/Masterminds/semver/v3"
17+
"golang.org/x/mod/modfile"
1418
)
1519

1620
const (
@@ -47,31 +51,101 @@ func findGoModDirs(rootFolder, subDir string) ([]string, error) {
4751
return goModDirs, nil
4852
}
4953

50-
func getLastTag(pathPrefix string) (string, error) {
54+
func getRetractedTags(goModPath string) ([]*semver.Constraints, error) {
55+
data, err := os.ReadFile(goModPath)
56+
if err != nil {
57+
return nil, fmt.Errorf("error reading go.mod file: %w", err)
58+
}
59+
60+
modFile, err := modfile.Parse("go.mod", data, nil)
61+
if err != nil {
62+
return nil, fmt.Errorf("error parsing go.mod file: %w", err)
63+
}
64+
65+
var retractedTags []*semver.Constraints
66+
for _, retract := range modFile.Retract {
67+
lowVersion, err := semver.NewVersion(retract.Low)
68+
if err != nil {
69+
return nil, fmt.Errorf("error parsing retracted version: %w", err)
70+
}
71+
highVersion, err := semver.NewVersion(retract.High)
72+
if err != nil {
73+
return nil, fmt.Errorf("error parsing retracted version: %w", err)
74+
}
75+
constraint, err := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", lowVersion.String(), highVersion.String()))
76+
if err != nil {
77+
return nil, fmt.Errorf("error parsing retracted version: %w", err)
78+
}
79+
retractedTags = append(retractedTags, constraint)
80+
fmt.Printf("Retracted version: %s\n", constraint)
81+
}
82+
83+
return retractedTags, nil
84+
}
85+
86+
func getLatestTag(pathPrefix string, retractedTags []*semver.Constraints) (string, error) {
87+
// use regex to find exact matches, as otherwise might include pre-release versions
88+
// or versions that partially match the path prefix, e.g. when seraching for 'lib'
89+
// we won't make sure we won't include tags like `lib/grafana/v1.0.0`
90+
grepRegex := fmt.Sprintf("^%s/v[0-9]+\\.[0-9]+\\.[0-9]+$", pathPrefix)
91+
5192
//nolint
52-
cmd := exec.Command("sh", "-c", fmt.Sprintf("git tag | grep '%s' | tail -1", pathPrefix))
93+
cmd := exec.Command("sh", "-c", fmt.Sprintf("git tag | grep -E '%s'", grepRegex))
5394
var out bytes.Buffer
5495
cmd.Stdout = &out
5596
err := cmd.Run()
5697
if err != nil {
5798
return "", fmt.Errorf("error fetching tags: %w", err)
5899
}
59100

60-
tag := strings.TrimSpace(out.String())
61-
if tag == "" {
62-
return "", nil
101+
tags := strings.Split(strings.TrimSpace(out.String()), "\n")
102+
if len(tags) == 0 {
103+
return "", fmt.Errorf("no tags found for regex: %s", grepRegex)
63104
}
64105

65-
// Use regex to find the version tag starting with 'v'
66-
re := regexp.MustCompile(`v\d+\.\d+\.\d+`)
67-
matches := re.FindStringSubmatch(tag)
68-
if len(matches) > 0 {
69-
tag = matches[0]
70-
} else {
71-
return "", fmt.Errorf("no valid version tag found in '%s'", tag)
106+
// Parse the tags into semver versions
107+
var allTags []*semver.Version
108+
for _, tag := range tags {
109+
v, err := semver.NewVersion(strings.TrimPrefix(tag, pathPrefix+"/"))
110+
if err != nil {
111+
return "", fmt.Errorf("error parsing version tag: %w", err)
112+
}
113+
allTags = append(allTags, v)
114+
}
115+
116+
// Sort the tags in descending order
117+
sort.Sort(sort.Reverse(semver.Collection(allTags)))
118+
119+
if len(retractedTags) == 0 {
120+
tag := fmt.Sprintf("v%s", allTags[0].String())
121+
return tag, nil
122+
}
123+
124+
// Find the latest tag that doesn't match any of the retracted tags
125+
for _, tag := range allTags {
126+
isRetracted := false
127+
for _, constraint := range retractedTags {
128+
if constraint.Check(tag) {
129+
isRetracted = true
130+
break
131+
}
132+
}
133+
134+
if !isRetracted {
135+
tag := fmt.Sprintf("v%s", tag.String())
136+
fmt.Printf("Found non-retracted tag: %s\n", tag)
137+
return tag, nil
138+
}
72139
}
73140

74-
return tag, nil
141+
fmt.Println("No non-retracted tags found")
142+
fmt.Printf("All tags: %s\n", strings.Join(tags, ", "))
143+
fmt.Println("Retracted tags:")
144+
for _, constraint := range retractedTags {
145+
fmt.Printf("%s\n", constraint)
146+
}
147+
148+
return "", fmt.Errorf("failed to find a non-retracted tag got path prefix: %s", pathPrefix)
75149
}
76150

77151
func checkBreakingChanges(tag string) (string, string, error) {
@@ -94,13 +168,15 @@ func getIgnoredDirs(flag *string) []string {
94168
return ignoredDirs
95169
}
96170

97-
func isIgnoredDirPrefix(pathPrefix string, ignoredDirs []string) bool {
171+
func isIgnoredDirRegex(pathPrefix string, ignoredDirs []string) bool {
98172
for _, d := range ignoredDirs {
99173
if d == "" {
100174
continue
101175
}
102-
fmt.Printf("Checking prefix: %s, path: %s\n", d, pathPrefix)
103-
if strings.HasPrefix(pathPrefix, d) {
176+
177+
fmt.Printf("Checking regex: %s, path: %s\n", d, pathPrefix)
178+
re := regexp.MustCompile(d)
179+
if re.MatchString(pathPrefix) {
104180
fmt.Printf("Path is ignored, skipping: %s\n", pathPrefix)
105181
return true
106182
}
@@ -111,7 +187,7 @@ func isIgnoredDirPrefix(pathPrefix string, ignoredDirs []string) bool {
111187
func main() {
112188
rootFolder := flag.String("root", ".", "The root folder to start scanning from")
113189
subDir := flag.String("subdir", "", "The subdirectory inside the root folder to scan for modules")
114-
ignoreDirs := flag.String("ignore", "", "Ignore directory paths starting with prefix")
190+
ignoreDirs := flag.String("ignore", "", "Ignore directory paths matching regex (comma-separated)")
115191
flag.Parse()
116192

117193
absRootFolder, err := filepath.Abs(*rootFolder)
@@ -133,24 +209,30 @@ func main() {
133209
// Convert the stripped path back to absolute
134210
pathPrefix := strings.TrimPrefix(dirPath, absRootFolder+string(os.PathSeparator))
135211

136-
if isIgnoredDirPrefix(pathPrefix, ignoredDirs) {
212+
if isIgnoredDirRegex(pathPrefix, ignoredDirs) {
213+
continue
214+
}
215+
216+
retractedVersions, err := getRetractedTags(filepath.Join(dirPath, "go.mod"))
217+
if err != nil {
218+
fmt.Printf("Error getting retracted versions: %v\n", err)
137219
continue
138220
}
139221

140-
lastTag, err := getLastTag(pathPrefix)
222+
latestTag, err := getLatestTag(pathPrefix, retractedVersions)
141223
if err != nil {
142-
fmt.Printf("Error finding last tag: %v\n", err)
224+
fmt.Printf("Error finding latest tag: %v\n", err)
143225
continue
144226
}
145227

146-
if lastTag != "" {
228+
if latestTag != "" {
147229
fmt.Printf("%sProcessing directory: %s%s\n", Yellow, dirPath, Reset)
148230
if err := os.Chdir(dirPath); err != nil {
149231
fmt.Printf("Error changing directory: %v\n", err)
150232
continue
151233
}
152234

153-
stdout, stderr, err := checkBreakingChanges(lastTag)
235+
stdout, stderr, err := checkBreakingChanges(latestTag)
154236
if err != nil {
155237
fmt.Printf("Error running gorelease: %v\n", err)
156238
breakingChanges = true

tools/breakingchanges/go.mod

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
module github.com/smartcontractkit/chainlink-testing-framework/tools/breakingchanges
22

3-
go 1.22.6
3+
go 1.23.3
44

55
retract [v1.999.0-test-release, v1.999.999-test-release]
6+
7+
require (
8+
github.com/Masterminds/semver/v3 v3.3.1
9+
golang.org/x/mod v0.22.0
10+
)

tools/breakingchanges/go.sum

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
2+
github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
3+
golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
4+
golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=

0 commit comments

Comments
 (0)