Copilot commented on code in PR #3964:
URL: https://github.com/apache/solr/pull/3964#discussion_r2628917383


##########
solr/docker/build.gradle:
##########
@@ -234,19 +294,64 @@ task dockerPush(dependsOn: tasks.dockerTag) {
   // Ensure that the docker image is re-pushed if the image ID or tag changes
   inputs.properties([
           dockerImageName: dockerImageName,
+          dockerImagePlatforms: dockerImagePlatforms
   ])
   inputs.file(imageIdFile)
 
   // We don't want to push a docker image unless the tests have passed
   mustRunAfter tasks.testDocker
 
   doLast {
-    exec {
-      commandLine "docker", "push", dockerImageName
-    }
+    def platformValue = inputs.properties.dockerImagePlatforms
+
+    if (isMultiPlatform()) {
+      // Multi-platform push requires buildx and rebuilding with --push
+
+      // Get builder info and validate it exists
+      def builderInfo = getBuildxBuilderInfo()
+
+      // Verify the builder supports the requested platforms (fail fast)
+      def requestedPlatforms = platformValue.split(',').collect { it.trim() }
+      def missingPlatforms = []
+      requestedPlatforms.each { platform ->
+        // Check for exact platform match - use delimiters instead of word 
boundaries
+        // Match platform at start/end or surrounded by commas/spaces
+        def platformPattern = "(?:^|[,\\s])" + 
java.util.regex.Pattern.quote(platform) + "(?:[,\\s]|\$)"
+        if (!(builderInfo =~ platformPattern).find()) {
+          missingPlatforms.add(platform)
+        }
+      }
 
-    // Print information on the image after it has been created
-    project.logger.lifecycle("Solr Docker Image Pushed: \t$dockerImageName")
+      if (!missingPlatforms.isEmpty()) {
+        // Extract supported platforms from builder info using matcher
+        def supportedPlatforms = (builderInfo =~ 
/linux\/\w+(?:\/\w+)?/).collect().join(', ')
+        throw new GradleException("Current builder does not support all 
requested platforms: ${missingPlatforms}\n" +
+                "Supported platforms: ${supportedPlatforms}\n" +
+                "To add platform support, create a builder with QEMU:\n" +
+                "  docker run --privileged --rm tonistiigi/binfmt --install 
all\n" +
+                "  docker buildx create --name solr-builder --use --platform 
${platformValue}")
+      }
+
+      def solrTgzConfiguration = isImageSlim() ? configurations.solrSlimTgz : 
configurations.solrFullTgz

Review Comment:
   The solr tarball configuration is retrieved again here, but it was already 
computed at the task level (line 192). This duplicates logic and could lead to 
inconsistencies if the configuration selection logic changes. Consider 
accessing this value from the task inputs or storing it in a variable that can 
be reused in both places.
   ```suggestion
   
   ```



##########
solr/docker/build.gradle:
##########
@@ -234,19 +294,64 @@ task dockerPush(dependsOn: tasks.dockerTag) {
   // Ensure that the docker image is re-pushed if the image ID or tag changes
   inputs.properties([
           dockerImageName: dockerImageName,
+          dockerImagePlatforms: dockerImagePlatforms
   ])
   inputs.file(imageIdFile)
 
   // We don't want to push a docker image unless the tests have passed
   mustRunAfter tasks.testDocker
 
   doLast {
-    exec {
-      commandLine "docker", "push", dockerImageName
-    }
+    def platformValue = inputs.properties.dockerImagePlatforms
+
+    if (isMultiPlatform()) {
+      // Multi-platform push requires buildx and rebuilding with --push
+
+      // Get builder info and validate it exists
+      def builderInfo = getBuildxBuilderInfo()
+
+      // Verify the builder supports the requested platforms (fail fast)
+      def requestedPlatforms = platformValue.split(',').collect { it.trim() }
+      def missingPlatforms = []
+      requestedPlatforms.each { platform ->
+        // Check for exact platform match - use delimiters instead of word 
boundaries
+        // Match platform at start/end or surrounded by commas/spaces
+        def platformPattern = "(?:^|[,\\s])" + 
java.util.regex.Pattern.quote(platform) + "(?:[,\\s]|\$)"
+        if (!(builderInfo =~ platformPattern).find()) {
+          missingPlatforms.add(platform)
+        }
+      }
 
-    // Print information on the image after it has been created
-    project.logger.lifecycle("Solr Docker Image Pushed: \t$dockerImageName")
+      if (!missingPlatforms.isEmpty()) {
+        // Extract supported platforms from builder info using matcher
+        def supportedPlatforms = (builderInfo =~ 
/linux\/\w+(?:\/\w+)?/).collect().join(', ')
+        throw new GradleException("Current builder does not support all 
requested platforms: ${missingPlatforms}\n" +
+                "Supported platforms: ${supportedPlatforms}\n" +
+                "To add platform support, create a builder with QEMU:\n" +
+                "  docker run --privileged --rm tonistiigi/binfmt --install 
all\n" +
+                "  docker buildx create --name solr-builder --use --platform 
${platformValue}")
+      }
+
+      def solrTgzConfiguration = isImageSlim() ? configurations.solrSlimTgz : 
configurations.solrFullTgz
+      exec {
+        standardInput = solrTgzConfiguration.singleFile.newDataInputStream()
+        commandLine "docker", "buildx", "build",
+                "-f", "solr-${ -> project.version 
}${dockerImageDistSuffix}/docker/Dockerfile",
+                "--platform", platformValue,

Review Comment:
   The platform values from user input are not validated before being passed to 
shell commands. While the Pattern.quote() method is used for regex matching on 
line 319, the raw platformValue is passed directly to the docker command on 
line 340. A malicious or malformed platform string could potentially inject 
additional command arguments. Consider validating that platform values match 
the expected format (e.g., `linux/[a-z0-9]+(/[a-z0-9]+)?`) before use.



##########
solr/docker/build.gradle:
##########
@@ -134,6 +136,32 @@ def checksum = { file ->
   return new DigestUtils(DigestUtils.sha512Digest).digestAsHex(file).trim()
 }
 
+// Helper method to get Docker Buildx builder info, throws exception if not 
available
+def getBuildxBuilderInfo = {
+  def builderCheckOutput = new ByteArrayOutputStream()
+  def builderCheckResult = exec {
+    ignoreExitValue = true
+    standardOutput = builderCheckOutput
+    errorOutput = new ByteArrayOutputStream()
+    commandLine "docker", "buildx", "inspect"
+  }
+
+  if (builderCheckResult.exitValue != 0) {
+    throw new GradleException("Docker Buildx is not available or no builder is 
configured.\n" +
+            "Please ensure Docker Buildx is installed and create a builder:\n" 
+
+            "  docker buildx create --name solr-builder --use\n" +
+            "Or use an existing builder:\n" +
+            "  docker buildx use <builder-name>")
+  }
+
+  return builderCheckOutput.toString()
+}
+
+// Helper method to validate Docker Buildx builder is available

Review Comment:
   The `validateBuildxBuilder` function is a thin wrapper that just calls 
`getBuildxBuilderInfo()` and discards the result. This adds an unnecessary 
layer of indirection. Consider either removing this wrapper and calling 
`getBuildxBuilderInfo()` directly where validation is needed, or if the wrapper 
serves a semantic purpose, add a comment explaining why it exists separately 
from `getBuildxBuilderInfo()`.
   ```suggestion
   // Helper method to validate Docker Buildx builder is available.
   // This is a semantic wrapper for use in Gradle tasks that only need to 
assert
   // the presence of a configured Buildx builder (and do not require its info).
   ```



##########
solr/docker/build.gradle:
##########
@@ -234,19 +294,64 @@ task dockerPush(dependsOn: tasks.dockerTag) {
   // Ensure that the docker image is re-pushed if the image ID or tag changes
   inputs.properties([
           dockerImageName: dockerImageName,
+          dockerImagePlatforms: dockerImagePlatforms
   ])
   inputs.file(imageIdFile)
 
   // We don't want to push a docker image unless the tests have passed
   mustRunAfter tasks.testDocker
 
   doLast {
-    exec {
-      commandLine "docker", "push", dockerImageName
-    }
+    def platformValue = inputs.properties.dockerImagePlatforms
+
+    if (isMultiPlatform()) {
+      // Multi-platform push requires buildx and rebuilding with --push
+
+      // Get builder info and validate it exists
+      def builderInfo = getBuildxBuilderInfo()
+
+      // Verify the builder supports the requested platforms (fail fast)
+      def requestedPlatforms = platformValue.split(',').collect { it.trim() }
+      def missingPlatforms = []
+      requestedPlatforms.each { platform ->
+        // Check for exact platform match - use delimiters instead of word 
boundaries
+        // Match platform at start/end or surrounded by commas/spaces
+        def platformPattern = "(?:^|[,\\s])" + 
java.util.regex.Pattern.quote(platform) + "(?:[,\\s]|\$)"

Review Comment:
   The regex pattern uses `\w+` which only matches word characters (letters, 
digits, underscore). However, Docker platform names can include hyphens (e.g., 
`linux/arm/v7`). The current pattern would fail to match platforms with hyphens 
in their variants. Consider using a more permissive character class like 
`[^,\s]+` or `[\w\-]+` to handle all valid platform identifiers.



##########
solr/docker/build.gradle:
##########
@@ -158,32 +186,64 @@ task dockerBuild() {
 
   // Ensure that the docker image is rebuilt on build-arg changes or changes 
in the docker context
   inputs.properties([
-          baseDockerImage: baseDockerImage
+          baseDockerImage: baseDockerImage,
+          dockerImagePlatforms: dockerImagePlatforms
   ])
   var solrTgzConfiguration = isImageSlim() ? configurations.solrSlimTgz : 
configurations.solrFullTgz
   inputs.files(solrTgzConfiguration)
   inputs.property("isSlimImage", isImageSlim())
+  inputs.property("isMultiPlatform", isMultiPlatform())
   dependsOn(solrTgzConfiguration)
 
   doLast {
-    exec {
-      standardInput = solrTgzConfiguration.singleFile.newDataInputStream()
-      commandLine "docker", "build",
-              "-f", "solr-${ -> project.version 
}${dockerImageDistSuffix}/docker/Dockerfile",
-              "--iidfile", imageIdFile,
-              "--build-arg", "BASE_IMAGE=${ -> 
inputs.properties.baseDockerImage}",
-              "-"
+    def platformValue = inputs.properties.dockerImagePlatforms

Review Comment:
   The local variable `platformValue` duplicates information already available 
in `inputs.properties.dockerImagePlatforms`. Since this value is accessed 
multiple times in the method, using the local variable improves readability, 
but it creates inconsistency with the closure-based approach used elsewhere 
(like line 244). Consider either consistently using the local variable 
throughout the doLast block, or accessing it from inputs.properties 
consistently to maintain uniform code style.



##########
solr/docker/build.gradle:
##########
@@ -234,19 +294,64 @@ task dockerPush(dependsOn: tasks.dockerTag) {
   // Ensure that the docker image is re-pushed if the image ID or tag changes
   inputs.properties([
           dockerImageName: dockerImageName,
+          dockerImagePlatforms: dockerImagePlatforms
   ])
   inputs.file(imageIdFile)
 
   // We don't want to push a docker image unless the tests have passed
   mustRunAfter tasks.testDocker
 
   doLast {
-    exec {
-      commandLine "docker", "push", dockerImageName
-    }
+    def platformValue = inputs.properties.dockerImagePlatforms
+
+    if (isMultiPlatform()) {
+      // Multi-platform push requires buildx and rebuilding with --push
+
+      // Get builder info and validate it exists
+      def builderInfo = getBuildxBuilderInfo()
+
+      // Verify the builder supports the requested platforms (fail fast)
+      def requestedPlatforms = platformValue.split(',').collect { it.trim() }

Review Comment:
   The platform string is split and trimmed for validation, but the original 
`platformValue` (which may contain spaces around commas) is passed to the 
docker buildx command on line 340. While Docker likely tolerates extra 
whitespace, it would be more robust to use the trimmed, normalized platform 
list consistently. Consider storing the cleaned platform list and using it for 
both validation and the command invocation.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to