Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletions src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,11 @@
if (!Files.exists(restorationPath)) {
Files.createDirectories(restorationPath);
}
CacheUtils.unzip(cachedZip.toPath(), restorationPath, cacheConfig.isPreservePermissions());
CacheUtils.unzip(
cachedZip.toPath(),
restorationPath,
cacheConfig.isPreservePermissions(),
cacheConfig.isPreserveTimestamps());
LOGGER.debug("Restored directory artifact by unzipping: {} -> {}", artifact.getFileName(), restorationPath);
}

Expand Down Expand Up @@ -482,7 +486,7 @@
project.addLifecyclePhase("package");
}
}
restoredAttachedArtifacts.forEach(project::addAttachedArtifact);

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated

Check warning on line 489 in src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

addAttachedArtifact(org.apache.maven.artifact.Artifact) in org.apache.maven.project.MavenProject has been deprecated
restorationReport.setSuccess(true);
} catch (Exception e) {
LOGGER.debug("Cannot restore cache, continuing with normal build.", e);
Expand Down Expand Up @@ -708,7 +712,12 @@
File originalFile)
throws IOException {
Path tempZip = Files.createTempFile("maven-cache-", "-" + project.getArtifactId() + ".zip");
boolean hasFiles = CacheUtils.zip(originalFile.toPath(), tempZip, "*", cacheConfig.isPreservePermissions());
boolean hasFiles = CacheUtils.zip(
originalFile.toPath(),
tempZip,
"*",
cacheConfig.isPreservePermissions(),
cacheConfig.isPreserveTimestamps());
if (hasFiles) {
// Temporarily replace artifact file with zip for saving
projectArtifact.setFile(tempZip.toFile());
Expand Down Expand Up @@ -1059,7 +1068,8 @@
throws IOException {
Path temp = Files.createTempFile("maven-incremental-", project.getArtifactId());
temp.toFile().deleteOnExit();
boolean hasFile = CacheUtils.zip(dir, temp, glob, cacheConfig.isPreservePermissions());
boolean hasFile = CacheUtils.zip(
dir, temp, glob, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
if (hasFile) {
projectHelper.attachArtifact(project, "zip", classifier, temp.toFile());
}
Expand All @@ -1074,7 +1084,8 @@
if (!Files.exists(outputDir)) {
Files.createDirectories(outputDir);
}
CacheUtils.unzip(artifactFilePath, outputDir, cacheConfig.isPreservePermissions());
CacheUtils.unzip(
artifactFilePath, outputDir, cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
}

// TODO: move to config
Expand Down
89 changes: 85 additions & 4 deletions src/main/java/org/apache/maven/buildcache/CacheUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,10 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Stream;
Expand Down Expand Up @@ -112,7 +114,7 @@
}

public static Path getMultimoduleRoot(MavenSession session) {
return session.getRequest().getMultiModuleProjectDirectory().toPath();

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated

Check warning on line 117 in src/main/java/org/apache/maven/buildcache/CacheUtils.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

getMultiModuleProjectDirectory() in org.apache.maven.execution.MavenExecutionRequest has been deprecated
}

public static Scm readGitInfo(MavenSession session) throws IOException {
Expand Down Expand Up @@ -161,6 +163,11 @@
return Strings.CS.endsWithAny(fileName, ".jar", ".zip", ".war", ".ear");
}

public static boolean zip(final Path dir, final Path zip, final String glob, boolean preservePermissions)
throws IOException {
return zip(dir, zip, glob, preservePermissions, false);
}

/**
* Put every matching files of a directory in a zip.
* @param dir directory to zip
Expand All @@ -172,10 +179,12 @@
* the ZIP file (e.g., for cache keys) will include permission information, ensuring
* cache invalidation when file permissions change. This behavior is similar to how Git
* includes file mode in tree hashes.</p>
* @param preserveTimestamps whether to preserve file and directory timestamps in the zip
* @return true if at least one file has been included in the zip.
* @throws IOException
*/
public static boolean zip(final Path dir, final Path zip, final String glob, boolean preservePermissions)
public static boolean zip(
final Path dir, final Path zip, final String glob, boolean preservePermissions, boolean preserveTimestamps)
throws IOException {
final MutableBoolean hasFiles = new MutableBoolean();
// Check once if filesystem supports POSIX permissions instead of catching exceptions for every file
Expand All @@ -186,16 +195,45 @@

PathMatcher matcher =
"*".equals(glob) ? null : FileSystems.getDefault().getPathMatcher("glob:" + glob);
final Map<Path, FileTime> directoryTimestamps = new HashMap<>();
final Set<Path> directoriesWithMatchingFiles = new HashSet<>();
if (preserveTimestamps) {
ZipArchiveEntry zipEntry = new ZipArchiveEntry("./");
zipEntry.setTime(Files.getLastModifiedTime(dir).toMillis());
zipOutputStream.putArchiveEntry(zipEntry);
zipOutputStream.closeArchiveEntry();
}
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {

@Override
public FileVisitResult preVisitDirectory(Path path, BasicFileAttributes attrs) {
if (preserveTimestamps) {
directoryTimestamps.put(path, attrs.lastModifiedTime());
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult visitFile(Path path, BasicFileAttributes basicFileAttributes)
throws IOException {

if (matcher == null || matcher.matches(path.getFileName())) {
if (preserveTimestamps) {
Path parent = path.getParent();
while (parent != null && !parent.equals(dir)) {
directoriesWithMatchingFiles.add(parent);
parent = parent.getParent();
}
}

final ZipArchiveEntry zipEntry =
new ZipArchiveEntry(dir.relativize(path).toString());

if (preserveTimestamps) {
zipEntry.setTime(
basicFileAttributes.lastModifiedTime().toMillis());
}

// Preserve Unix permissions if requested and filesystem supports it
if (supportsPosix) {
Set<PosixFilePermission> permissions = Files.getPosixFilePermissions(path);
Expand All @@ -209,16 +247,39 @@
}
return FileVisitResult.CONTINUE;
}

@Override
public FileVisitResult postVisitDirectory(Path path, IOException exc) throws IOException {
if (exc != null) {
throw exc;
}
if (preserveTimestamps
&& !path.equals(dir)
&& (matcher == null || directoriesWithMatchingFiles.contains(path))) {
ZipArchiveEntry zipEntry =
new ZipArchiveEntry(dir.relativize(path).toString() + "/");
zipEntry.setTime(directoryTimestamps.get(path).toMillis());
zipOutputStream.putArchiveEntry(zipEntry);
zipOutputStream.closeArchiveEntry();
}
return FileVisitResult.CONTINUE;
}
});
}
return hasFiles.booleanValue();
}

public static void unzip(Path zip, Path out, boolean preservePermissions) throws IOException {
unzip(zip, out, preservePermissions, true);
}

public static void unzip(Path zip, Path out, boolean preservePermissions, boolean preserveTimestamps)
throws IOException {
// Check once if filesystem supports POSIX permissions instead of catching exceptions for every file
final boolean supportsPosix = preservePermissions
&& out.getFileSystem().supportedFileAttributeViews().contains("posix");

final Map<Path, Long> directoryTimestamps = new HashMap<>();
try (ZipFile zipFile = ZipFile.builder().setFile(zip.toFile()).get()) {
Enumeration<ZipArchiveEntry> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
Expand All @@ -228,15 +289,26 @@
throw new RuntimeException("Bad zip entry");
}
if (entry.isDirectory()) {
Files.createDirectory(file);
Files.createDirectories(file);
if (preserveTimestamps) {
directoryTimestamps.put(file, entry.getTime());
}
} else {
Path parent = file.getParent();
Files.createDirectories(parent);
if (parent != null) {
Files.createDirectories(parent);
}
try (InputStream is = zipFile.getInputStream(entry)) {
Files.copy(is, file, StandardCopyOption.REPLACE_EXISTING);
}
if (preserveTimestamps) {
try {
Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));
} catch (IOException e) {
LOGGER.debug("Could not preserve timestamp for {}", file, e);
}
}
}
Files.setLastModifiedTime(file, FileTime.fromMillis(entry.getTime()));

// Restore Unix permissions if requested and filesystem supports it
if (supportsPosix) {
Expand All @@ -248,6 +320,15 @@
}
}
}
if (preserveTimestamps) {
for (Map.Entry<Path, Long> entry : directoryTimestamps.entrySet()) {
try {
Files.setLastModifiedTime(entry.getKey(), FileTime.fromMillis(entry.getValue()));
} catch (IOException e) {
LOGGER.debug("Could not preserve timestamp for {}", entry.getKey(), e);
}
}
}
}

public static <T> void debugPrintCollection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.apache.maven.model.Resource;
import org.apache.maven.model.io.xpp3.MavenXpp3Writer;

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 85 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated
import org.apache.maven.project.MavenProject;
import org.eclipse.aether.RepositorySystem;
import org.eclipse.aether.artifact.DefaultArtifactType;
Expand Down Expand Up @@ -110,7 +110,7 @@
/**
* Version of cache implementation. It is recommended to change to simplify remote cache maintenance
*/
public static final String CACHE_IMPLEMENTATION_VERSION = "v1.1";
public static final String CACHE_IMPLEMENTATION_VERSION = "v1.2";

/**
* property name to pass glob value. The glob to be used to list directory files in plugins scanning
Expand Down Expand Up @@ -340,7 +340,7 @@
private String getEffectivePom(Model prototype) throws IOException {
ByteArrayOutputStream output = new ByteArrayOutputStream();
try (Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
new MavenXpp3Writer().write(output, prototype);

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-17-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-21-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / ubuntu-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 4.0.0-rc-5

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-25-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated

Check warning on line 343 in src/main/java/org/apache/maven/buildcache/checksum/MavenProjectInput.java

View workflow job for this annotation

GitHub Actions / Verify / macos-latest jdk-21-zulu 3.10.0-rc-1

org.apache.maven.model.io.xpp3.MavenXpp3Writer in org.apache.maven.model.io.xpp3 has been deprecated
}

// normalize env specifics
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ public interface CacheConfig {

boolean isPreservePermissions();

default boolean isPreserveTimestamps() {
return true;
}

boolean adjustMetaInfVersion();

boolean calculateProjectVersionChecksum();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,13 @@ public boolean isPreservePermissions() {
return attachedOutputs == null || attachedOutputs.isPreservePermissions();
}

@Override
public boolean isPreserveTimestamps() {
checkInitializedState();
final AttachedOutputs attachedOutputs = getConfiguration().getAttachedOutputs();
return attachedOutputs == null || attachedOutputs.isPreserveTimestamps();
}

@Override
public boolean adjustMetaInfVersion() {
if (isEnabled()) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/mdo/build-cache-config.mdo
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,12 @@ under the License.
<defaultValue>true</defaultValue>
<description>Preserve Unix file permissions when saving/restoring attached outputs. When enabled, permissions are stored in ZIP entry headers and become part of the cache key, ensuring cache invalidation when permissions change. This is similar to how Git includes file mode in tree hashes. Disabling this may improve portability across different systems but will not preserve executable bits.</description>
</field>
<field>
<name>preserveTimestamps</name>
<type>boolean</type>
<defaultValue>true</defaultValue>
<description>Preserve file and directory timestamps when saving/restoring attached outputs.</description>
</field>
<field>
<name>dirNames</name>
<association>
Expand Down
7 changes: 5 additions & 2 deletions src/site/markdown/parameters.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,17 +111,20 @@ The following elements are supported in `maven-build-cache-config.xml` but have
```xml

<configuration>
<attachedOutputs preservePermissions="true">
<attachedOutputs>
<preservePermissions>true</preservePermissions>
<preserveTimestamps>true</preserveTimestamps>
<dirNames>
<dirName>generated-sources/apt</dirName>
</dirNames>
</attachedOutputs>
</configuration>
```

| Element/Attribute | Default | Description |
| Element | Default | Description |
|-----------------------|---------|----------------------------------------------------------------------------------------------------------|
| `preservePermissions` | `true` | Preserve Unix file-system permissions when restoring artifacts from cache. Requires a POSIX file system. |
| `preserveTimestamps` | `true` | Preserve file and directory timestamps when saving and restoring cached outputs. |
| `dirNames/dirName` | — | Additional output directories to include in cached artifacts beyond standard Maven output directories. |

#### `<projectVersioning>` — version handling in cached artifacts
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.apache.maven.buildcache;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;

import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;

import static org.junit.jupiter.api.Assertions.assertTrue;

class CacheUtilsTimestampTest {
private static final long TIMESTAMP_TOLERANCE_MILLIS = 2_000;

@Test
void preservesFileAndDirectoryTimestamps(@TempDir Path tempDir) throws IOException {
Path source = tempDir.resolve("source");
Path nested = source.resolve("a/b/c");
Files.createDirectories(nested);
Path file = nested.resolve("output.txt");
Files.write(file, "cached output".getBytes(StandardCharsets.UTF_8));

FileTime timestamp = FileTime.fromMillis(System.currentTimeMillis() - 3_600_000);
Files.setLastModifiedTime(file, timestamp);
Files.setLastModifiedTime(nested, timestamp);
Files.setLastModifiedTime(nested.getParent(), timestamp);
Files.setLastModifiedTime(nested.getParent().getParent(), timestamp);
Files.setLastModifiedTime(source, timestamp);

Path archive = tempDir.resolve("output.zip");
CacheUtils.zip(source, archive, "*", true, true);

Path restored = tempDir.resolve("restored");
Files.createDirectories(restored);
CacheUtils.unzip(archive, restored, true, true);

assertTimestamp(timestamp, Files.getLastModifiedTime(restored));
assertTimestamp(timestamp, Files.getLastModifiedTime(restored.resolve("a/b/c/output.txt")));
assertTimestamp(timestamp, Files.getLastModifiedTime(restored.resolve("a/b/c")));
assertTimestamp(timestamp, Files.getLastModifiedTime(restored.resolve("a/b")));
assertTimestamp(timestamp, Files.getLastModifiedTime(restored.resolve("a")));
}

@Test
void doesNotPreserveTimestampsWhenDisabled(@TempDir Path tempDir) throws IOException {
Path source = tempDir.resolve("source");
Files.createDirectories(source);
Path file = source.resolve("output.txt");
Files.write(file, "cached output".getBytes(StandardCharsets.UTF_8));
FileTime timestamp = FileTime.fromMillis(System.currentTimeMillis() - 3_600_000);
Files.setLastModifiedTime(file, timestamp);

Path archive = tempDir.resolve("output.zip");
CacheUtils.zip(source, archive, "*", false, false);
Path restored = tempDir.resolve("restored");
CacheUtils.unzip(archive, restored, false, false);

long difference = Math.abs(
Files.getLastModifiedTime(restored.resolve("output.txt")).toMillis() - timestamp.toMillis());
assertTrue(difference > TIMESTAMP_TOLERANCE_MILLIS, "Timestamp was unexpectedly preserved");
}

private static void assertTimestamp(FileTime expected, FileTime actual) {
long difference = Math.abs(actual.toMillis() - expected.toMillis());
assertTrue(difference <= TIMESTAMP_TOLERANCE_MILLIS, "Timestamp difference: " + difference);
}
}
Loading