Is it a bug for Zip task in distribution plugin?

Distribution plugin in gradle version 4.7.

Test case 1:
script:

task archieveZip(type: Zip, dependsOn: 'jar') {
	baseName = archivesBaseName
	from (tasks.jar.archivePath)
	from fileTree('config')
}

output(expected):

my.zip
├── my.jar
└── my.properties

Test case 2:
script:

task archieveZip(type: Zip, dependsOn: 'jar') {
    baseName = archivesBaseName
    from (tasks.jar.archivePath){
    	into 'lib'
    }
    from fileTree('config')
}

output(expected):

my.zip
├── lib
│	    └── my.jar
└── my

Test case 3:
script:

task archieveZip(type: Zip, dependsOn: 'jar') {
    baseName = archivesBaseName
    from (tasks.jar.archivePath)
    from fileTree('config'){
    	into 'config'
    }
}

output(not expected):

my.zip
└── config
  	    ├── my.properties
 	    └── my.jar

For test case 3, what I expected output is:

my.zip
├── config
│	    └── my.properties
└── my.jar

Here’s what’s happening in test case 3, the closure containing into 'config' is actually being passed to the fileTree method, not the from method. Since ConfigurableFileTree's do not have an into method it is being found further up in scope. Therefore, you are effectively doing the following and setting the root level directory which all artifacts will be placed:

task archieveZip(type: Zip, dependsOn: 'jar') {
    baseName = archivesBaseName
    from (tasks.jar.archivePath)
    from fileTree('config')
    into 'config'
}

In order to fix this, you need to use parentheses with the from call:

task archieveZip(type: Zip, dependsOn: 'jar') {
    baseName = archivesBaseName
    from (tasks.jar.archivePath)
    from (fileTree('config')) {
        into 'config'
    }
}

Extra Tip:
Using tasks.jar.archivePath has a negative side effect. If you were to reconfigure the jar task to output to a different location after configuring your archieveZip task, the jar file would not be included in the zip. This is because you are taking a snapshot of the jar task’s configuration at a specific moment in time. Instead, you can simply say from( tasks.jar ) and the output file of the jar task will be included no matter where or how often the configuration is changed. Doing it this way will also create an implicit dependency on the jar task and you no longer need to declare it using dependsOn.

If you’re not familiar with Groovy, then the following describes what’s going on. Knowing how Groovy handles these details and also how property setters/getters work is very helpful if you want or need to start using Gradle’s javadoc (since you will need to know how to translate between Groovy and Java syntax).

http://groovy-lang.org/style-guide.html#_omitting_parentheses