Why eachFile on a filtered FileTree still seem to run on all files

Hello,

I have a multiproject with a frontend and a backend.
The frontend produces a zip.
In backend I want to expand this zip in the resources public folder.

(Of course with sourceSets I didn’t manage. :slight_smile: )

So I tryied with bootJar, in my backend project. And I almost did it. Just that I don’t understand how it works and I still have a isuue, but it works.

I have the following code backend:

configurations {
    frontEnd
}

dependencies {
    frontEnd project(path: ':user-login-ui', configuration: 'archives')
...
}

bootJar {
    dependsOn(":user-login-ui:build")
    from {
        configurations.frontEnd.filter( { it.toString().endsWith("user-login-ui.zip") } ).collect {
            println "it: "+ it
            zipTree(it).matching {
                include 'user-login/**'
                include 'user-login-ui/**'
                eachFile {  FileCopyDetails file ->
                    println 'file: ' + file
                    file.path = file.path.replaceFirst('^user-login-ui/', 'public/user-login-ui/')
                    file.path = file.path.replaceFirst('^user-login/', 'public/user-login/')
                }
            }
        }
    }
}

So I want to select only the zip from the frontend output:

configurations.frontEnd.filter( { it.toString().endsWith("user-login-ui.zip") } ).collect {

I print it so I see it is only the zip:

            println "it: "+ it

I expand it and for each file I’m changing the path to put the file in ‘public’ folder.
Surprise is in eachFile where I print again the file: I see there the list of all zip entries in my zip + the class files in my crt backend project + all the 3th party jars in my backend project, etc.

So I filtered the files but still eachFiles goes through all possible files it founds. (from mainSpec ?)
Can someone please explain me the whole mechanism and principle because it is missing in documentation ?
I want to change the path on all files that I filtered. If eachFile is not the appropriate method, which is the good one ?

And the issue I still can not resolve: the files from the zip are placed where I expected but additionally the folders appear (empty) in the root jar folder and I don’t want this. How can I solve this ?

Thanks

I’d separate the unzipping into its own task which can be skipped if it’s up to date. Eg:

task unzip {
   inputs.files configurations.frontEnd
   outputs.dir "$buildDir/unzip" 
   doLast {
      def zips = configurations.frontEnd.findAll {
         it.name.endsWith("user-login-ui.zip") 
      } 
      copy {
         from zips.collect { zipTree(it) } 
         into "$buildDir/unzip/public" 
         include 'user-login/**'
         include 'user-login-ui/**'
     } 
  } 
} 
bootJar {
   from unzip 
} 

See also Feature request: UnzipTask

1 Like

Thank you for the solution.

Still, besides, I want to understand why eachFile seems to iterate over all possible files - maybe inputs - of bootJar task instead of iterating only over the files I filtered.
Something I don’t get it right and I want to correct it.
If eachFile can not be used for this purpose, which is the correct api/approach.
Thank you

eachFile is a method from the Copy task, so it’s not being applied where you think it is. It’s not a method on the FileTree. So basically

bootJar {
    dependsOn(":user-login-ui:build")
    from {
        configurations.frontEnd.filter( { it.toString().endsWith("user-login-ui.zip") } ).collect {
            println "it: "+ it
            zipTree(it).matching {
                include 'user-login/**'
                include 'user-login-ui/**'
                eachFile {  FileCopyDetails file ->
                    println 'file: ' + file
                    file.path = file.path.replaceFirst('^user-login-ui/', 'public/user-login-ui/')
                    file.path = file.path.replaceFirst('^user-login/', 'public/user-login/')
                }
            }
        }

Could be rewritten as

bootJar {
    dependsOn(":user-login-ui:build")
    from {
        configurations.frontEnd.filter( { it.toString().endsWith("user-login-ui.zip") } ).collect {
            println "it: "+ it
            zipTree(it).matching {
                include 'user-login/**'
                include 'user-login-ui/**'
            }
        }
    } 
    eachFile {  FileCopyDetails file ->
        println 'file: ' + file
        file.path = file.path.replaceFirst('^user-login-ui/', 'public/user-login-ui/')
        file.path = file.path.replaceFirst('^user-login/', 'public/user-login/')
    }
1 Like

Note: I think you can achieve your original requirement using Copy.with(CopySpec). Eg:

bootJar {
   with copySpec({
      from ... 
      eachFile { ... } 
   }) 
}