@@ -10,9 +10,12 @@ plugins {
1010}
1111
1212import groovy.json.JsonOutput
13+ import groovy.json.JsonSlurper
1314import org.graalvm.internal.tck.ContributionTask
1415import org.graalvm.internal.tck.DockerTask
1516import org.graalvm.internal.tck.ConfigFilesChecker
17+ import org.graalvm.internal.tck.DockerUtils
18+ import org.graalvm.internal.tck.PullImagesFromFileTask
1619import org.graalvm.internal.tck.ScaffoldTask
1720import org.graalvm.internal.tck.GrypeTask
1821import org.graalvm.internal.tck.TestedVersionUpdaterTask
@@ -42,7 +45,47 @@ def writeGithubOutput(String key, String value) {
4245}
4346
4447String coordinateFilter = Objects . requireNonNullElse(project. findProperty(" coordinates" ), " " )
45- List<String > matchingCoordinates = tck. getMatchingCoordinates(coordinateFilter)
48+
49+ // Support fractional batching coordinates in the form "k/n" (e.g., "1/16")
50+ boolean isFractionalBatch (String s ) {
51+ return s != null && (s ==~ / \d +\/\d +/ )
52+ }
53+
54+ List<Integer > parseFraction (String s ) {
55+ def m = s =~ / (\d +)\/ (\d +)/
56+ if (! m. matches()) return null
57+ int k = (m[0 ][1 ] as int )
58+ int n = (m[0 ][2 ] as int )
59+ return [k, n]
60+ }
61+
62+ List<String > computeBatchedCoordinates (List<String > allCoords , int k , int n ) {
63+ if (n <= 0 ) throw new GradleException (" Invalid batches denominator: ${ n} " )
64+ if (k < 1 || k > n) throw new GradleException (" Invalid batch index: ${ k} /${ n} " )
65+ def sorted = new ArrayList<String > (allCoords)
66+ java.util.Collections . sort(sorted)
67+ int target = (k - 1 )
68+ def result = []
69+ int i = 0
70+ for (String c : sorted) {
71+ if ((i % n) == target) {
72+ result. add(c)
73+ }
74+ i++
75+ }
76+ return result
77+ }
78+
79+ List<String > matchingCoordinates
80+ if (isFractionalBatch(coordinateFilter)) {
81+ def frac = parseFraction(coordinateFilter)
82+ int k = frac[0 ]
83+ int n = frac[1 ]
84+ List<String > all = tck. getMatchingCoordinates(" all" )
85+ matchingCoordinates = computeBatchedCoordinates(all, k, n)
86+ } else {
87+ matchingCoordinates = tck. getMatchingCoordinates(coordinateFilter)
88+ }
4689
4790// gradle test -Pcoordinates=<maven-coordinates>
4891Provider<Task > test = tasks. register(" test" , DefaultTask ) { task ->
@@ -78,6 +121,79 @@ tasks.named("check").configure {
78121 dependsOn(checkstyle)
79122}
80123
124+ final String METADATA_GROUP = " Metadata"
125+
126+ Provider<Task > pullAllowedDockerImagesMatching = tasks. register(" pullAllowedDockerImagesMatching" , DefaultTask ) { task ->
127+ task. setDescription(" Pull allowed docker images for all matching coordinates" )
128+ task. setGroup(METADATA_GROUP )
129+ task. doFirst {
130+ if (matchingCoordinates == null || matchingCoordinates. isEmpty()) {
131+ throw new GradleException (" No matching coordinates found for property 'coordinates'. Provide -Pcoordinates=<filter> or a fractional batch 'k/n'." )
132+ }
133+ println " Pulling allowed docker images for ${ matchingCoordinates.size()} coordinate(s):"
134+ matchingCoordinates. each { c -> println (" - ${ c} " ) }
135+ }
136+ }
137+
138+ // Collect unique required images across matching coordinates and filter by allowed list
139+ tasks. register(" collectRequiredAllowedDockerImagesMatching" , DefaultTask ) { task ->
140+ task. setDescription(" Collect unique allowed docker images required by matching coordinates into a file" )
141+ task. setGroup(METADATA_GROUP )
142+ File out = new File (buildDir, " required-docker-images-union.txt" )
143+ // Declare the output so Gradle can track it
144+ task. outputs. file(out)
145+ task. doFirst {
146+ if (matchingCoordinates == null || matchingCoordinates. isEmpty()) {
147+ throw new GradleException (" No matching coordinates found for property 'coordinates'. Provide -Pcoordinates=<filter> or a fractional batch 'k/n'." )
148+ }
149+ Set<String > unionRequired = new LinkedHashSet<> ()
150+ matchingCoordinates. each { c ->
151+ def parts = c. split(" :" )
152+ if (parts. length < 3 ) {
153+ logger. warn(" Skipping invalid coordinates: ${ c} " )
154+ return
155+ }
156+ def group = parts[0 ]
157+ def artifact = parts[1 ]
158+ def version = parts[2 ]
159+ File f = project. file(" tests/src/${ group} /${ artifact} /${ version} /required-docker-images.txt" )
160+ if (f. exists()) {
161+ f. readLines()
162+ .collect { it?. trim() }
163+ .findAll { it && ! it. startsWith(" #" ) }
164+ .each { unionRequired. add(it) }
165+ }
166+ }
167+
168+ Set<String > allowed = DockerUtils . getAllAllowedImages()
169+ def notAllowed = unionRequired. findAll { ! allowed. contains(it) }
170+ if (! notAllowed. isEmpty()) {
171+ throw new GradleException (" The following images are not in the allowed list: ${ notAllowed} . " +
172+ " If you need them, add Dockerfiles under tests/tck-build-logic/src/main/resources/allowed-docker-images " +
173+ " per CONTRIBUTING.md, or adjust required-docker-images.txt files." )
174+ }
175+
176+ out. parentFile. mkdirs()
177+ def finalList = unionRequired. findAll { allowed. contains(it) }. toList()
178+ out. text = finalList. join(System . lineSeparator())
179+ println " Collected ${ finalList.size()} required allowed image(s):"
180+ finalList. each { println (" - ${ it} " ) }
181+ }
182+ }
183+
184+ // Pull the collected unique images (once)
185+ tasks. register(" pullRequiredAllowedDockerImagesMatching" , PullImagesFromFileTask . class) { task ->
186+ task. setDescription(" Pull unique allowed docker images required by matching coordinates" )
187+ task. setGroup(METADATA_GROUP )
188+ task. dependsOn(" collectRequiredAllowedDockerImagesMatching" )
189+ task. imagesFile. set(new File (buildDir, " required-docker-images-union.txt" ))
190+ }
191+
192+ // Rewire the aggregate to depend on the unique pull task
193+ pullAllowedDockerImagesMatching. configure {
194+ dependsOn(" pullRequiredAllowedDockerImagesMatching" )
195+ }
196+
81197// Here we want to configure all test and checkstyle tasks for all filtered subprojects
82198for (String coordinates in matchingCoordinates) {
83199 String testTaskName = generateTaskName(" test" , coordinates)
@@ -127,6 +243,13 @@ for (String coordinates in matchingCoordinates) {
127243 javaTest. configure {
128244 dependsOn(javaTestTaskName)
129245 }
246+
247+ String pullDockerTaskName = generateTaskName(" pullAllowedDockerImages" , coordinates)
248+ if ((! tasks. getNames(). contains(pullDockerTaskName))) {
249+ tasks. register(pullDockerTaskName, DockerTask . class) { t ->
250+ t. setCoordinates(coordinates)
251+ }
252+ }
130253}
131254
132255// gradle diff -PbaseCommit=<base-commit> -PnewCommit=<new-commit>
@@ -157,15 +280,23 @@ if (project.hasProperty("baseCommit")) {
157280 }
158281}
159282
160- def matrixDefault = [
161- " version " : [
162- " 17 " ,
163- " latest-ea "
164- ],
165- " os " : [ " ubuntu-latest " ]
166- ]
283+ Map< String , Object > loadCi () {
284+ File f = project . file( " ci.json " )
285+ if ( ! f . exists()) {
286+ throw new GradleException ( " Missing ci.json in repo root " )
287+ }
288+ new JsonSlurper () . parse(f) as Map< String , Object >
289+ }
167290
168- final String METADATA_GROUP = " Metadata"
291+ Map<String , List<String > > matrixDefaultsFor (String section ) {
292+ def ci = loadCi()
293+ def sec = ci[section]
294+ if (sec == null ) throw new GradleException (" Missing '${ section} ' in ci.json" )
295+ [
296+ " version" : (sec[" java" ] as List<String > ),
297+ " os" : (sec[" os" ] as List<String > )
298+ ]
299+ }
169300
170301// gradle generateMatrixMatchingCoordinates -Pcoordinates=<maven-coordinates>
171302Provider<Task > generateMatrixMatchingCoordinates = tasks. register(" generateMatrixMatchingCoordinates" , DefaultTask ) { task ->
@@ -175,7 +306,25 @@ Provider<Task> generateMatrixMatchingCoordinates = tasks.register("generateMatri
175306 def matrix = [
176307 " coordinates" : matchingCoordinates
177308 ]
178- matrix. putAll(matrixDefault)
309+ matrix. putAll(matrixDefaultsFor(" generateMatrixMatchingCoordinates" ))
310+ writeGithubOutput(" matrix" , JsonOutput . toJson(matrix))
311+ }
312+ }
313+
314+ // gradle generateMatrixBatchedCoordinates [-Pbatches=<n>]
315+ Provider<Task > generateMatrixBatchedCoordinates = tasks. register(" generateMatrixBatchedCoordinates" , DefaultTask ) { task ->
316+ task. setDescription(" Returns matrix definition populated with fractional batch coordinates (k/n)" )
317+ task. setGroup(METADATA_GROUP )
318+ task. doFirst {
319+ int batches = project. hasProperty(" batches" ) ? Integer . parseInt(project. findProperty(" batches" ). toString()) : 16
320+ if (batches <= 0 ) {
321+ throw new GradleException (" Invalid 'batches' value: ${ batches} " )
322+ }
323+ List<String > coords = (1 .. batches). collect { i -> " ${ i} /${ batches} " }
324+ def matrix = [
325+ " coordinates" : coords
326+ ]
327+ matrix. putAll(matrixDefaultsFor(" generateMatrixBatchedCoordinates" ))
179328 writeGithubOutput(" matrix" , JsonOutput . toJson(matrix))
180329 }
181330}
@@ -193,13 +342,11 @@ Provider<Task> generateChangedCoordinatesMatrix = tasks.register("generateChange
193342 def matrix = [
194343 " coordinates" : noneFound ? [] : diffCoordinates
195344 ]
196- matrix. putAll(matrixDefault )
345+ matrix. putAll(matrixDefaultsFor( " generateChangedCoordinatesMatrix " ) )
197346 if (noneFound) {
198347 println " No changed coordinates were found!"
199348 }
200349
201- matrix. version. add(" 21" )
202- matrix. version. add(" 25" )
203350
204351 writeGithubOutput(" matrix" , JsonOutput . toJson(matrix))
205352 writeGithubOutput(" none-found" , noneFound. toString())
@@ -215,7 +362,7 @@ Provider<Task> generateInfrastructureChangedCoordinatesMatrix = tasks.register("
215362 boolean infraChanged = true
216363 // Pre-selected modules; keep stable for infra verification
217364 List<String > preselectedModules = [
218- ' org.bouncycastle:bcpkix-jdk15on ' ,
365+ ' ch.qos.logback:logback-classic ' ,
219366 ' org.bouncycastle:bcpkix-jdk18on' ,
220367 ' io.netty:netty-common' ,
221368 ' io.grpc:grpc-core' ,
@@ -243,10 +390,8 @@ Provider<Task> generateInfrastructureChangedCoordinatesMatrix = tasks.register("
243390 def matrix = [
244391 " coordinates" : selected
245392 ]
246- matrix. putAll(matrixDefault )
393+ matrix. putAll(matrixDefaultsFor( " generateInfrastructureChangedCoordinatesMatrix " ) )
247394
248- matrix. version. add(" 21" )
249- matrix. version. add(" 25" )
250395
251396 writeGithubOutput(" matrix" , JsonOutput . toJson(matrix))
252397 writeGithubOutput(" none-found" , " false" )
0 commit comments