@@ -43,19 +43,24 @@ impl std::ops::Deref for TagList {
43
43
}
44
44
}
45
45
46
+ #[ derive( Deserialize , Debug ) ]
47
+ struct ArtifactReference {
48
+ child_digest : String ,
49
+ }
46
50
#[ derive( Deserialize , Debug ) ]
47
51
struct Artifact {
48
52
digest : String ,
49
53
manifest_media_type : String ,
50
54
media_type : String ,
51
55
tags : Option < TagList > ,
56
+ references : Option < Vec < ArtifactReference > > ,
52
57
}
53
58
54
59
#[ tokio:: main]
55
60
async fn main ( ) -> Result < ( ) , Box < dyn std:: error:: Error > > {
56
61
let registry_hostname = "oci.stackable.tech" ;
57
62
let base_url = format ! ( "https://{}/api/v2.0" , registry_hostname) ;
58
- let page_size = 10 ;
63
+ let page_size = 20 ;
59
64
let mut page = 1 ;
60
65
let attestation_tag_regex = Regex :: new ( r"^sha256-[0-9a-f]{64}.att$" ) . unwrap ( ) ;
61
66
@@ -73,15 +78,33 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
73
78
if project_name == "sandbox" {
74
79
continue ;
75
80
}
76
- let artifacts: Vec < Artifact > = reqwest:: get ( format ! (
77
- "{}/projects/{}/repositories/{}/artifacts" ,
78
- base_url,
79
- encode( project_name) ,
80
- encode( repository_name)
81
- ) )
82
- . await ?
83
- . json ( )
84
- . await ?;
81
+
82
+ let mut attestations: Vec < String > = Vec :: with_capacity ( 32 ) ;
83
+ let mut potentially_attested_artifacts: Vec < & str > = Vec :: with_capacity ( 32 ) ;
84
+ let mut artifacts: Vec < Artifact > = Vec :: with_capacity ( 64 ) ;
85
+ let mut page = 1 ;
86
+ let page_size = 20 ;
87
+ loop {
88
+ let artifacts_page: Vec < Artifact > = reqwest:: get ( format ! (
89
+ "{}/projects/{}/repositories/{}/artifacts?page_size={}&page={}" ,
90
+ base_url,
91
+ encode( project_name) ,
92
+ encode( repository_name) ,
93
+ page_size,
94
+ page
95
+ ) )
96
+ . await ?
97
+ . json ( )
98
+ . await ?;
99
+
100
+ let number_of_returned_artifacts = artifacts_page. len ( ) ;
101
+ artifacts. extend ( artifacts_page) ;
102
+ if number_of_returned_artifacts < page_size {
103
+ break ;
104
+ }
105
+ page += 1 ;
106
+ }
107
+
85
108
for artifact in & artifacts {
86
109
if artifact
87
110
. tags
@@ -99,15 +122,20 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
99
122
{
100
123
// it's an attestation, attestations artifacts themselves are not signed
101
124
println ! (
102
- "skipping attestation {}{} ({})" ,
125
+ "skipping attestation {} {} ({})" ,
103
126
repository_name,
104
127
artifact. digest,
105
128
artifact. tags. as_ref( ) . unwrap( )
106
129
) ;
130
+ attestations. push ( artifact. tags . as_ref ( ) . unwrap ( ) [ 0 ] . name . clone ( ) ) ;
107
131
continue ;
132
+ } else {
133
+ potentially_attested_artifacts. push ( & artifact. digest [ 7 ..71 ] ) ;
108
134
}
109
135
110
- if project_name == "sdp" && ( repository_name != "git-sync" && repository_name != "ubi8-rust-builder" ) {
136
+ if project_name == "sdp"
137
+ && ( repository_name != "git-sync" && repository_name != "ubi8-rust-builder" )
138
+ {
111
139
if artifact. manifest_media_type
112
140
!= "application/vnd.docker.distribution.manifest.v2+json"
113
141
{
@@ -159,6 +187,24 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
159
187
exit ( cmd_output. status . code ( ) . unwrap_or ( 1 ) ) ;
160
188
}
161
189
}
190
+
191
+ // remove dangling attestations
192
+ for attestation in attestations {
193
+ if !potentially_attested_artifacts. contains ( & & attestation[ 7 ..71 ] ) {
194
+ println ! ( "removing dangling attestation {}" , attestation) ;
195
+ reqwest:: Client :: new ( )
196
+ . delete ( format ! (
197
+ "{}/projects/{}/repositories/{}/artifacts/{}" ,
198
+ base_url,
199
+ encode( project_name) ,
200
+ encode( repository_name) ,
201
+ attestation
202
+ ) )
203
+ . basic_auth ( "robot$stackable-cleanup" , Some ( "XX" ) )
204
+ . send ( )
205
+ . await ?;
206
+ }
207
+ }
162
208
}
163
209
164
210
if repositories. len ( ) < page_size {
@@ -168,5 +214,51 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
168
214
page += 1 ;
169
215
}
170
216
217
+ let latest_rust_builder_artifact: Artifact = reqwest:: Client :: new ( )
218
+ . get ( format ! (
219
+ "{}/projects/sdp/repositories/ubi8-rust-builder/artifacts/latest" ,
220
+ base_url,
221
+ ) )
222
+ . send ( )
223
+ . await ?
224
+ . json ( )
225
+ . await ?;
226
+
227
+ let referenced_digests = latest_rust_builder_artifact
228
+ . references
229
+ . unwrap ( )
230
+ . into_iter ( )
231
+ . map ( |reference| reference. child_digest )
232
+ . collect :: < Vec < String > > ( ) ;
233
+
234
+ let rust_builder_artifacts: Vec < Artifact > = reqwest:: get ( format ! (
235
+ "{}/projects/sdp/repositories/ubi8-rust-builder/artifacts?page_size=100" ,
236
+ base_url
237
+ ) )
238
+ . await ?
239
+ . json ( )
240
+ . await ?;
241
+
242
+ // keep "latest" and its referenced artifacts
243
+ for artifact in rust_builder_artifacts {
244
+ if artifact. digest != latest_rust_builder_artifact. digest
245
+ && !referenced_digests. contains ( & artifact. digest )
246
+ {
247
+ println ! (
248
+ "removing dangling rust builder artifact {}" ,
249
+ artifact. digest
250
+ ) ;
251
+ reqwest:: Client :: new ( )
252
+ . delete ( format ! (
253
+ "{}/projects/sdp/repositories/ubi8-rust-builder/artifacts/{}" ,
254
+ base_url, artifact. digest
255
+ ) )
256
+ . basic_auth ( "robot$stackable-cleanup" , Some ( "XX" ) )
257
+ . send ( )
258
+ . await ?;
259
+ }
260
+ }
261
+
171
262
Ok ( ( ) )
172
263
}
264
+ // cachebust
0 commit comments