Skip to content

Commit 10eb2c4

Browse files
committed
Fix timestamp preservation when extracting cached files
The build cache extension was not properly preserving file and directory timestamps when restoring attachedOutputs from cache. This caused Maven to warn about files being 'more recent than the packaged artifact' even after a successful build. Root Causes: 1. The zip() method did not store directory entries with timestamps 2. The unzip() method set directory timestamps immediately, but they were later modified by Files.copy() operations for files within Changes: - Modified CacheUtils.zip() to store directory entries with timestamps via preVisitDirectory() callback - Modified CacheUtils.unzip() to defer directory timestamp updates until after all files are extracted, preventing them from being overwritten - Added Map<Path, Long> to track directory timestamps during extraction This ensures that cached build outputs maintain their original timestamps, preventing spurious warnings and improving build cache consistency.
1 parent 5990c9f commit 10eb2c4

File tree

1 file changed

+30
-3
lines changed

1 file changed

+30
-3
lines changed

src/main/java/org/apache/maven/buildcache/CacheUtils.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,9 @@
3131
import java.nio.file.attribute.FileTime;
3232
import java.util.Arrays;
3333
import java.util.Collection;
34+
import java.util.HashMap;
3435
import java.util.List;
36+
import java.util.Map;
3537
import java.util.NoSuchElementException;
3638
import java.util.stream.Stream;
3739
import java.util.zip.ZipEntry;
@@ -165,13 +167,26 @@ public static boolean zip(final Path dir, final Path zip, final String glob) thr
165167
"*".equals(glob) ? null : FileSystems.getDefault().getPathMatcher("glob:" + glob);
166168
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
167169

170+
@Override
171+
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) throws IOException {
172+
if (!path.equals(dir)) {
173+
String relativePath = dir.relativize(path).toString() + "/";
174+
ZipEntry zipEntry = new ZipEntry(relativePath);
175+
zipEntry.setTime(attrs.lastModifiedTime().toMillis());
176+
zipOutputStream.putNextEntry(zipEntry);
177+
zipOutputStream.closeEntry();
178+
}
179+
return FileVisitResult.CONTINUE;
180+
}
181+
168182
@Override
169183
public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
170184
throws IOException {
171185

172186
if (matcher == null || matcher.matches(path.getFileName())) {
173187
final ZipEntry zipEntry =
174188
new ZipEntry(dir.relativize(path).toString());
189+
zipEntry.setTime(basicFileAttributes.lastModifiedTime().toMillis());
175190
zipOutputStream.putNextEntry(zipEntry);
176191
Files.copy(path, zipOutputStream);
177192
hasFiles.setTrue();
@@ -185,6 +200,7 @@ public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttribu
185200
}
186201

187202
public static void unzip(Path zip, Path out) throws IOException {
203+
Map<Path, Long> directoryTimestamps = new HashMap<>();
188204
try (ZipInputStream zis = new ZipInputStream(Files.newInputStream(zip))) {
189205
ZipEntry entry = zis.getNextEntry();
190206
while (entry != null) {
@@ -193,16 +209,27 @@ public static void unzip(Path zip, Path out) throws IOException {
193209
throw new RuntimeException("Bad zip entry");
194210
}
195211
if (entry.isDirectory()) {
196-
Files.createDirectory(file);
212+
if (!Files.exists(file)) {
213+
Files.createDirectories(file);
214+
}
215+
directoryTimestamps.put(file, entry.getTime());
197216
} else {
198217
Path parent = file.getParent();
199-
Files.createDirectories(parent);
218+
if (!Files.exists(parent)) {
219+
Files.createDirectories(parent);
220+
}
200221
Files.copy(zis, file, StandardCopyOption.REPLACE_EXISTING);
222+
Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
201223
}
202-
Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
203224
entry = zis.getNextEntry();
204225
}
205226
}
227+
228+
// Set directory timestamps after all files have been extracted to avoid them being
229+
// updated by file creation operations
230+
for (Map.Entry<Path, Long> dirEntry : directoryTimestamps.entrySet()) {
231+
Files.setLastModifiedTime(dirEntry.getKey(), FileTime.fromMillis(dirEntry.getValue()));
232+
}
206233
}
207234

208235
public static <T> void debugPrintCollection(

0 commit comments

Comments
 (0)