9
9
"net/http"
10
10
"os"
11
11
"regexp"
12
- "time"
13
12
14
13
"golang.org/x/exp/maps"
15
14
"oras.land/oras-go/pkg/registry/remote"
@@ -21,24 +20,19 @@ const (
21
20
)
22
21
23
22
var (
24
- repo * string
25
- baseURL * string
26
- dryRun * bool
27
23
accessToken = os .Getenv ("ACCESS_TOKEN" )
28
24
preserveSubstrings = []string {
29
25
"latest" ,
30
- // Preserve release branch images
31
- "release-v*" ,
32
- // Semver regex - vX.Y.Z(-rc1)
33
- "^v(?P<major>0|[1-9]\\ d*)\\ .(?P<minor>0|[1-9]\\ d*)\\ .(?P<patch>0|[1-9]\\ d*)(?:-(?P<prerelease>(?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\ .(?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\ +(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\ .[0-9a-zA-Z-]+)*))?$" ,
26
+ // Preserve semver release branch or semver tag regex - release-vX.Y.Z(-rc1) or vX.Y.Z(-rc1)
27
+ // Based on https://semver.org/#is-there-a-suggested-regular-expression-regex-to-check-a-semver-string
28
+ "^(v|release-v)(?P<major>0|[1-9]\\ d*)\\ .(?P<minor>0|[1-9]\\ d*)\\ .(?P<patch>0|[1-9]\\ d*)(?:-(?P<prerelease>(?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\ .(?:0|[1-9]\\ d*|\\ d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\ +(?P<buildmetadata>[0-9a-zA-Z-]+(?:\\ .[0-9a-zA-Z-]+)*))?$" ,
34
29
}
35
30
)
36
31
37
32
// Tag represents a tag in the repository.
38
33
type Tag struct {
39
- Name string `json:"name"`
40
- LastModified string `json:"last_modified"`
41
- Expiration string `json:"expiration"`
34
+ Name string `json:"name"`
35
+ Expiration string `json:"expiration"`
42
36
}
43
37
44
38
// TagsResponse represents the structure of the API response that contains tags.
@@ -49,9 +43,9 @@ type TagsResponse struct {
49
43
}
50
44
51
45
func main () {
52
- repo = flag .String ("repo" , "kuadrant/kuadrant-operator" , "Repository name" )
53
- baseURL = flag .String ("base-url" , "https://quay.io/api/v1/repository/" , "Base API URL" )
54
- dryRun = flag .Bool ("dry-run" , true , "Dry run" )
46
+ repo : = flag .String ("repo" , "kuadrant/kuadrant-operator" , "Repository name" )
47
+ baseURL : = flag .String ("base-url" , "https://quay.io/api/v1/repository/" , "Base API URL" )
48
+ dryRun : = flag .Bool ("dry-run" , true , "Dry run" )
55
49
flag .Parse ()
56
50
57
51
client := & http.Client {}
@@ -63,7 +57,7 @@ func main() {
63
57
}
64
58
65
59
// Fetch tags from the API
66
- tags , err := fetchTags (client )
60
+ tags , err := fetchTags (client , baseURL , repo )
67
61
if err != nil {
68
62
logger .Fatalln ("Error fetching tags:" , err )
69
63
}
@@ -81,7 +75,7 @@ func main() {
81
75
if dryRun != nil && * dryRun {
82
76
logger .Printf ("DRY RUN - Successfully deleted tag: %s\n " , tagName )
83
77
} else {
84
- if err := deleteTag (client , accessToken , tagName ); err != nil {
78
+ if err := deleteTag (client , baseURL , repo , accessToken , tagName ); err != nil {
85
79
logger .Println (err )
86
80
continue
87
81
}
@@ -99,13 +93,13 @@ func main() {
99
93
100
94
// fetchTags retrieves the tags from the repository using the Quay.io API.
101
95
// https://docs.quay.io/api/swagger/#!/tag/listRepoTags
102
- func fetchTags (client remote.Client ) ([]Tag , error ) {
96
+ func fetchTags (client remote.Client , baseURL , repo * string ) ([]Tag , error ) {
103
97
allTags := make ([]Tag , 0 )
104
98
105
99
i := 1
106
100
107
101
for {
108
- req , err := http .NewRequest ("GET" , fmt .Sprintf ("%s%s /tag/?page=%d&limit=%d" , * baseURL , * repo , i , pageLimit ), nil )
102
+ req , err := http .NewRequest ("GET" , fmt .Sprintf ("%v%v /tag/?page=%d&limit=%d" , baseURL , repo , i , pageLimit ), nil )
109
103
if err != nil {
110
104
return nil , fmt .Errorf ("error creating request: %w" , err )
111
105
}
@@ -155,8 +149,8 @@ func fetchTags(client remote.Client) ([]Tag, error) {
155
149
// deleteTag sends a DELETE request to remove the specified tag from the repository
156
150
// Returns nil if successful, error otherwise
157
151
// https://docs.quay.io/api/swagger/#!/tag/deleteFullTag
158
- func deleteTag (client remote.Client , accessToken , tagName string ) error {
159
- req , err := http .NewRequest ("DELETE" , fmt .Sprintf ("%s%s /tag/%s" , * baseURL , * repo , tagName ), nil )
152
+ func deleteTag (client remote.Client , baseURL , repo * string , accessToken , tagName string ) error {
153
+ req , err := http .NewRequest ("DELETE" , fmt .Sprintf ("%v%v /tag/%s" , baseURL , repo , tagName ), nil )
160
154
if err != nil {
161
155
return fmt .Errorf ("error creating DELETE request: %s" , err )
162
156
}
@@ -178,11 +172,8 @@ func deleteTag(client remote.Client, accessToken, tagName string) error {
178
172
179
173
// filterTags takes a slice of tags and preserves string regex and returns two maps: one for tags to delete and one for remaining tags.
180
174
func filterTags (tags []Tag , preserveSubstrings []string ) (map [string ]struct {}, map [string ]struct {}, error ) {
181
- // Calculate the cutoff time
182
- cutOffTime := time .Now ().AddDate (0 , 0 , 0 ).Add (0 * time .Hour ).Add (- 1 * time .Minute )
183
-
184
175
tagsToDelete := make (map [string ]struct {})
185
- perservedTags := make (map [string ]struct {})
176
+ preservedTags := make (map [string ]struct {})
186
177
187
178
// Compile the regexes for each preserve substring
188
179
var preserveRegexes []* regexp.Regexp
@@ -211,17 +202,12 @@ func filterTags(tags []Tag, preserveSubstrings []string) (map[string]struct{}, m
211
202
}
212
203
}
213
204
214
- lastModified , err := time .Parse (time .RFC1123 , tag .LastModified )
215
- if err != nil {
216
- return nil , nil , err
217
- }
218
-
219
- if lastModified .Before (cutOffTime ) && ! preserve {
205
+ if ! preserve {
220
206
tagsToDelete [tag .Name ] = struct {}{}
221
207
} else {
222
- perservedTags [tag .Name ] = struct {}{}
208
+ preservedTags [tag .Name ] = struct {}{}
223
209
}
224
210
}
225
211
226
- return tagsToDelete , perservedTags , nil
212
+ return tagsToDelete , preservedTags , nil
227
213
}
0 commit comments