Try reproducing failures on Windows
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/CleanArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/CleanArchiveIntegrationTest.groovy
new file mode 100644
index 0000000..7ac3f69
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/CleanArchiveIntegrationTest.groovy
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.bundling
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import spock.lang.Issue
+
+@Issue("https://github.com/gradle/gradle/issues/25752")
+class CleanArchiveIntegrationTest extends AbstractIntegrationSpec {
+
+ def setup() {
+ file("contents/hello.txt") << "hello"
+ file("contents").zipTo(file("hello.zip"))
+ }
+
+ def "clean after unzipping file to cache in task"() {
+ buildFile << """
+ plugins {
+ id 'lifecycle-base'
+ }
+ System.out.println("Executing build.gradle")
+ def helloArchive = zipTree(file("hello.zip"))
+ tasks.create("makeArchive", Zip) {
+ System.out.println("Files in the archive: " + helloArchive.files)
+ archiveFileName = "archive.zip"
+ destinationDirectory = layout.buildDirectory
+ from helloArchive
+ }
+ """
+
+ expect:
+ succeeds "clean"
+ }
+
+ def "clean after unzipping file to cache during configuration phase"() {
+ buildFile << """
+ plugins {
+ id 'lifecycle-base'
+ }
+ zipTree(file("hello.zip")).files
+ """
+
+ expect:
+ succeeds "clean"
+ }
+
+ def "clean after unzipping during configuration, then unzip again in a different task"() {
+ buildFile << """
+ plugins {
+ id 'lifecycle-base'
+ }
+ System.out.println("Files in the archive: " + zipTree(file("hello.zip")).files)
+ def helloArchive = zipTree(file("hello.zip"))
+ tasks.create("makeArchive", Zip) {
+ archiveFileName = "archive.zip"
+ destinationDirectory = layout.buildDirectory
+ from helloArchive
+ doLast {
+ System.out.println("Files in the archive: " + helloArchive.files)
+ }
+ }
+ """
+
+ expect:
+ succeeds "clean", "makeArchive"
+ }
+}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
index 765cb1e..6c79099 100644
--- a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ConcurrentArchiveIntegrationTest.groovy
@@ -146,6 +146,59 @@
result.assertTasksExecutedAndNotSkipped(':project1:update', ':project2:update', ':project1:verify', ':project2:verify')
}
+
+ @Issue("https://github.com/gradle/gradle/issues/22685")
+ def "can visit and edit zip archive differently from two different projects with the same name in different directories in a multiproject build"() {
+ given: "an archive in the root of a multiproject build"
+ createZip('test.zip') {
+ subdir1 {
+ file ('file1.txt').text = 'original text 1'
+ }
+ subdir2 {
+ file('file2.txt').text = 'original text 2'
+ file ('file3.txt').text = 'original text 3'
+ }
+ }
+ settingsFile << """include ':lib', ':utils:lib'"""
+
+ and: "where each project edits that same archive differently via a visitor"
+ file('lib/build.gradle') << """
+ ${defineUpdateTask('zip')}
+ ${defineVerifyTask('zip')}
+ def theArchive = rootProject.file('test.zip')
+ tasks.register('update', UpdateTask) {
+ archive = theArchive
+ replacementText = 'modified by project1'
+ }
+ tasks.register('verify', VerifyTask) {
+ dependsOn tasks.named('update')
+ archive = theArchive
+ beginsWith = 'modified by project1'
+ }
+ """
+
+ file('utils/lib/build.gradle') << """
+ ${defineUpdateTask('zip')}
+ ${defineVerifyTask('zip')}
+ def theArchive = rootProject.file('test.zip')
+ tasks.register('update', UpdateTask) {
+ archive = theArchive
+ replacementText = 'edited by project2'
+ }
+ tasks.register('verify', VerifyTask) {
+ dependsOn tasks.named('update')
+ archive = theArchive
+ beginsWith = 'edited by project2'
+ }
+ """
+
+ when:
+ run 'verify'
+
+ then:
+ result.assertTasksExecutedAndNotSkipped(':lib:update', ':utils:lib:update', ':lib:verify', ':utils:lib:verify')
+ }
+
@Requires(IntegTestPreconditions.NotEmbeddedExecutor)
@Issue("https://github.com/gradle/gradle/issues/22685")
def "can visit and edit zip archive differently from settings script when gradle is run in two simultaneous processes"() {
@@ -469,8 +522,7 @@
dependsOn tasks.named('update1'), tasks.named('update2')
doLast {
def cacheDir = file("build/tmp/.cache/expanded")
- assert cacheDir.list().size() == 2 // There should only be 2 files here, the .lock file and the single unzipped cache entry
- assert cacheDir.list().contains('expanded.lock')
+ assert cacheDir.list().size() == 1 // There should only be 1 file here, the single unzipped cache entry
cacheDir.eachFile(groovy.io.FileType.DIRECTORIES) { File f ->
assert f.name.startsWith('tar_')
}
diff --git a/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ReuseArchiveIntegrationTest.groovy b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ReuseArchiveIntegrationTest.groovy
new file mode 100644
index 0000000..149bf2c
--- /dev/null
+++ b/subprojects/core/src/integTest/groovy/org/gradle/api/tasks/bundling/ReuseArchiveIntegrationTest.groovy
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2023 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.gradle.api.tasks.bundling
+
+import org.gradle.integtests.fixtures.AbstractIntegrationSpec
+import org.gradle.internal.hash.DefaultFileHasher
+import org.gradle.internal.hash.DefaultStreamHasher
+import org.gradle.internal.hash.FileHasher
+
+class ReuseArchiveIntegrationTest extends AbstractIntegrationSpec {
+
+ /*
+ * This is a pre-existing issue: What do you do if creating a cache using a pre-existing directory
+ * happens to contain content with the same name as content you're trying to cache? The content used
+ * by the cache no longer agrees with the content coming from the zip itself.
+ */
+ def "pre-existing content in cache dir with same hash is okay"() {
+ file("contents/hello.txt") << "hello"
+ file("contents").zipTo(file("hello.zip"))
+ FileHasher hasher = new DefaultFileHasher(new DefaultStreamHasher())
+ def hash = hasher.hash(file("hello.zip"))
+ def cachedFile = file("build/tmp/.cache/expanded/zip_${hash}/hello.txt")
+ def otherFile = file("build/tmp/.cache/expanded/zip_${hash}/other.txt")
+
+ buildFile << """
+ abstract class CopyAndList extends DefaultTask {
+ @Inject
+ abstract FileSystemOperations getFileSystemOperations()
+
+ @InputFiles
+ abstract ConfigurableFileCollection getUnzipped()
+
+ @Inject
+ abstract ProjectLayout getLayout()
+
+ @TaskAction
+ void copyAndList() {
+ getUnzipped().getFiles().each {
+ println it.absolutePath + ": " + it.text
+ }
+ getFileSystemOperations().copy {
+ from getUnzipped()
+ into layout.buildDirectory.dir("extract")
+ }
+ }
+ }
+ task extract2(type: CopyAndList) {
+ unzipped.from(zipTree(file("hello.zip")))
+ }
+ """
+ when:
+ succeeds("extract")
+ then:
+ file("build/extract/hello.txt").text == "hello"
+ cachedFile.assertExists()
+ otherFile.assertDoesNotExist()
+
+ when:
+ // write into the directory used by the extracted zip file
+ cachedFile.text = "some incorrect pre-existing pre-expanded content"
+ otherFile.touch()
+
+ and:
+ succeeds "extract"
+ then:
+ // the file is not extracted again and retains the modified content
+ cachedFile.text == "some incorrect pre-existing pre-expanded content"
+ otherFile.assertExists()
+ // users of the zipTree still see the zip's contents
+ file("build/extract/hello.txt").text == "hello"
+ }
+}