This is an automated email from the ASF dual-hosted git repository.

cstamas pushed a commit to branch fix-the-build
in repository 
https://gitbox.apache.org/repos/asf/maven-build-cache-extension.git

commit e6fedaa385e0486e4a39328edd2423b9c15fc428
Author: Tamas Cservenak <[email protected]>
AuthorDate: Sun Jul 19 13:30:36 2026 +0200

    Fix: fix the build on master
---
 .../maven/buildcache/CacheControllerImpl.java      | 19 ++++-
 .../org/apache/maven/buildcache/CacheUtils.java    | 68 +++++++++++++++++
 .../buildcache/CacheUtilsPermissionsTest.java      | 16 ++--
 .../maven/buildcache/CacheUtilsTimestampTest.java  | 88 +++++++++++-----------
 .../maven/buildcache/ModuleInfoCachingTest.java    | 10 +--
 5 files changed, 140 insertions(+), 61 deletions(-)

diff --git a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java 
b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
index ba3f050..65f1566 100644
--- a/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
+++ b/src/main/java/org/apache/maven/buildcache/CacheControllerImpl.java
@@ -373,7 +373,11 @@ private void restoreDirectoryArtifact(File cachedZip, 
Artifact artifact, Path re
         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);
     }
 
@@ -708,7 +712,12 @@ private void saveDirectoryArtifact(
             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());
@@ -1059,7 +1068,8 @@ private boolean zipAndAttachArtifact(MavenProject 
project, Path dir, String clas
             throws IOException {
         Path temp = Files.createTempFile("maven-incremental-", 
project.getArtifactId());
         temp.toFile().deleteOnExit();
-        boolean hasFile = CacheUtils.zip(dir, temp, glob, 
cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
+        boolean hasFile = CacheUtils.zip(
+                dir, temp, glob, cacheConfig.isPreservePermissions(), 
cacheConfig.isPreserveTimestamps());
         if (hasFile) {
             projectHelper.attachArtifact(project, "zip", classifier, 
temp.toFile());
         }
@@ -1074,7 +1084,8 @@ private void restoreGeneratedSources(Artifact artifact, 
Path artifactFilePath, M
         if (!Files.exists(outputDir)) {
             Files.createDirectories(outputDir);
         }
-        CacheUtils.unzip(artifactFilePath, outputDir, 
cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
+        CacheUtils.unzip(
+                artifactFilePath, outputDir, 
cacheConfig.isPreservePermissions(), cacheConfig.isPreserveTimestamps());
     }
 
     // TODO: move to config
diff --git a/src/main/java/org/apache/maven/buildcache/CacheUtils.java 
b/src/main/java/org/apache/maven/buildcache/CacheUtils.java
index e1e574e..fc0832a 100644
--- a/src/main/java/org/apache/maven/buildcache/CacheUtils.java
+++ b/src/main/java/org/apache/maven/buildcache/CacheUtils.java
@@ -46,17 +46,23 @@
 import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
 import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
 import org.apache.commons.compress.archivers.zip.ZipFile;
+import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.Strings;
 import org.apache.commons.lang3.mutable.MutableBoolean;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.buildcache.xml.build.CompletedExecution;
+import org.apache.maven.buildcache.xml.build.PropertyValue;
 import org.apache.maven.buildcache.xml.build.Scm;
 import org.apache.maven.execution.MavenSession;
 import org.apache.maven.model.Dependency;
 import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
 import org.apache.maven.project.MavenProject;
+import 
org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
 import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 import static org.apache.maven.artifact.Artifact.LATEST_VERSION;
 import static org.apache.maven.artifact.Artifact.SNAPSHOT_VERSION;
@@ -65,6 +71,7 @@
  * Cache Utils
  */
 public class CacheUtils {
+    private static final Logger LOGGER = 
LoggerFactory.getLogger(CacheUtils.class);
 
     public static boolean isPom(MavenProject project) {
         return project.getPackaging().equals("pom");
@@ -405,4 +412,65 @@ private static Set<PosixFilePermission> 
modeToPermissions(int mode) {
         }
         return permissions;
     }
+
+    static Object interpolateExpression(String expression, MavenSession 
session, MojoExecution execution) {
+        try {
+            PluginParameterExpressionEvaluator evaluator = new 
PluginParameterExpressionEvaluator(session, execution);
+            return evaluator.evaluate(expression);
+        } catch (ExpressionEvaluationException e) {
+            LOGGER.warn("Cannot interpolate expression '{}': {}", expression, 
e.getMessage(), e);
+            return expression; // return the expression as is when 
interpolation fails
+        }
+    }
+
+    static String normalizeValue(Object value, Path baseDirPath) {
+        if (value instanceof File) {
+            Path path = ((File) value).toPath();
+            return normalizedPath(path, baseDirPath);
+        } else if (value instanceof Path) {
+            return normalizedPath(((Path) value), baseDirPath);
+        } else if (value != null && value.getClass().isArray()) {
+            return ArrayUtils.toString(value);
+        } else {
+            return String.valueOf(value);
+        }
+    }
+
+    /*
+     Best effort to normalize paths from Mojo fields.
+     - all absolute paths under project root are relativized for portability
+     - redundant '..' and '.' are removed to have consistent views on all paths
+     - all relative paths are considered portable and are not be touched
+     - absolute paths outside of project directory cannot be deterministically
+       relativized and are not touched
+    */
+    private static String normalizedPath(Path path, Path baseDirPath) {
+        boolean isProjectSubdir = path.isAbsolute() && 
path.startsWith(baseDirPath);
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug(
+                    "normalizedPath isProjectSubdir {} path '{}' - baseDirPath 
'{}', path.isAbsolute() {},"
+                            + " path.startsWith(baseDirPath) {}",
+                    isProjectSubdir,
+                    path,
+                    baseDirPath,
+                    path.isAbsolute(),
+                    path.startsWith(baseDirPath));
+        }
+        Path preparedPath = isProjectSubdir ? baseDirPath.relativize(path) : 
path;
+        String normalizedPath = preparedPath.normalize().toString();
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("normalizedPath '{}' - {} return {}", path, 
baseDirPath, normalizedPath);
+        }
+        return normalizedPath;
+    }
+
+    static void addProperty(
+            CompletedExecution execution, String propertyName, Object value, 
Path baseDirPath, boolean tracked) {
+        final PropertyValue valueType = new PropertyValue();
+        valueType.setName(propertyName);
+        final String valueText = normalizeValue(value, baseDirPath);
+        valueType.setValue(valueText);
+        valueType.setTracked(tracked);
+        execution.addProperty(valueType);
+    }
 }
diff --git 
a/src/test/java/org/apache/maven/buildcache/CacheUtilsPermissionsTest.java 
b/src/test/java/org/apache/maven/buildcache/CacheUtilsPermissionsTest.java
index 0ce8685..0edaabc 100644
--- a/src/test/java/org/apache/maven/buildcache/CacheUtilsPermissionsTest.java
+++ b/src/test/java/org/apache/maven/buildcache/CacheUtilsPermissionsTest.java
@@ -78,8 +78,8 @@ void testPermissionsAffectFileHashWhenEnabled() throws 
IOException {
         // When: Create ZIP files with preservePermissions=true
         Path zip1 = tempDir.resolve("cache1.zip");
         Path zip2 = tempDir.resolve("cache2.zip");
-        CacheUtils.zip(sourceDir1, zip1, "*", true);
-        CacheUtils.zip(sourceDir2, zip2, "*", true);
+        CacheUtils.zip(sourceDir1, zip1, "*", true, false);
+        CacheUtils.zip(sourceDir2, zip2, "*", true, false);
 
         // Then: ZIP files should have different hashes despite identical 
content
         byte[] hash1 = Files.readAllBytes(zip1);
@@ -124,16 +124,16 @@ void testPermissionsDoNotAffectHashWhenDisabled() throws 
IOException {
         // When: Create ZIP files with preservePermissions=false
         Path zip1 = tempDir.resolve("cache1.zip");
         Path zip2 = tempDir.resolve("cache2.zip");
-        CacheUtils.zip(sourceDir1, zip1, "*", false);
-        CacheUtils.zip(sourceDir2, zip2, "*", false);
+        CacheUtils.zip(sourceDir1, zip1, "*", false, false);
+        CacheUtils.zip(sourceDir2, zip2, "*", false, false);
 
         // Unzip and verify permissions are NOT preserved
         Path extractDir1 = tempDir.resolve("extracted1");
         Path extractDir2 = tempDir.resolve("extracted2");
         Files.createDirectories(extractDir1);
         Files.createDirectories(extractDir2);
-        CacheUtils.unzip(zip1, extractDir1, false);
-        CacheUtils.unzip(zip2, extractDir2, false);
+        CacheUtils.unzip(zip1, extractDir1, false, false);
+        CacheUtils.unzip(zip2, extractDir2, false, false);
 
         Path extractedFile1 = extractDir1.resolve("script.sh");
         Path extractedFile2 = extractDir2.resolve("script.sh");
@@ -163,12 +163,12 @@ void testPermissionsAreRestoredOnUnzip() throws Exception 
{
 
         // When: ZIP file is created from that directory with 
"preservePermissions=true"
         Path zippedFile = tempDir.resolve("target.zip");
-        CacheUtils.zip(source, zippedFile, "*", true);
+        CacheUtils.zip(source, zippedFile, "*", true, false);
 
         // And: ZIP file is extracted with "preservePermissions=true"
         Path unzippedDir = tempDir.resolve("target-unzipped");
         Files.createDirectories(unzippedDir);
-        CacheUtils.unzip(zippedFile, unzippedDir, true);
+        CacheUtils.unzip(zippedFile, unzippedDir, true, false);
 
         // Then: extracted file should have executable permissions
         Path extractedScript = unzippedDir.resolve("script.sh");
diff --git 
a/src/test/java/org/apache/maven/buildcache/CacheUtilsTimestampTest.java 
b/src/test/java/org/apache/maven/buildcache/CacheUtilsTimestampTest.java
index 238e5c4..b30c962 100644
--- a/src/test/java/org/apache/maven/buildcache/CacheUtilsTimestampTest.java
+++ b/src/test/java/org/apache/maven/buildcache/CacheUtilsTimestampTest.java
@@ -18,9 +18,6 @@
  */
 package org.apache.maven.buildcache;
 
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
 import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.charset.StandardCharsets;
@@ -38,7 +35,9 @@
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
@@ -78,11 +77,11 @@ void testFileTimestampPreservation() throws IOException {
 
         // When: Zip and unzip using CacheUtils
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", true);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, true);
 
         Path extractDir = tempDir.resolve("extracted");
         Files.createDirectories(extractDir);
-        CacheUtils.unzip(zipFile, extractDir, true);
+        CacheUtils.unzip(zipFile, extractDir, false, true);
 
         // Then: File timestamps should be preserved
         Path extractedFile1 = 
extractDir.resolve("com").resolve("example").resolve("Service.class");
@@ -124,11 +123,11 @@ void testDirectoryTimestampPreservation() throws 
IOException {
 
         // When: Zip and unzip using CacheUtils
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", true);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, true);
 
         Path extractDir = tempDir.resolve("extracted");
         Files.createDirectories(extractDir);
-        CacheUtils.unzip(zipFile, extractDir, true);
+        CacheUtils.unzip(zipFile, extractDir, false, true);
 
         // Then: Directory timestamps should be preserved
         Path extractedDir = extractDir.resolve("com").resolve("example");
@@ -154,7 +153,7 @@ void testDirectoryEntriesStoredInZip() throws IOException {
 
         // When: Create zip
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", true);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, true);
 
         // Then: Zip should contain directory entries
         List<String> entries = new ArrayList<>();
@@ -192,7 +191,7 @@ void testTimestampsInZipEntries() throws IOException {
 
         // When: Create zip
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", true);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, true);
 
         // Then: Zip entries should have correct timestamps
         try (ZipInputStream zis = new 
ZipInputStream(Files.newInputStream(zipFile))) {
@@ -250,7 +249,7 @@ void testMavenWarningScenario() throws IOException {
         Instant packageTime = compileTime.plus(5, ChronoUnit.SECONDS);
         Path jarFile = tempDir.resolve("target").resolve("my-module-1.0.jar");
         Files.createDirectories(jarFile.getParent());
-        CacheUtils.zip(classesDir, jarFile, "*", true);
+        CacheUtils.zip(classesDir, jarFile, "*", false, true);
         Files.setLastModifiedTime(jarFile, FileTime.from(packageTime));
 
         long jarTimestamp = Files.getLastModifiedTime(jarFile).toMillis();
@@ -259,7 +258,7 @@ void testMavenWarningScenario() throws IOException {
         deleteRecursively(classesDir);
 
         // When: Simulate cache restoration - restore JAR contents back to 
target/classes
-        CacheUtils.unzip(jarFile, classesDir, true);
+        CacheUtils.unzip(jarFile, classesDir, false, true);
 
         // Then: Restored file should NOT be newer than JAR
         Path restoredClass = 
classesDir.resolve("com").resolve("example").resolve("Service.class");
@@ -270,11 +269,11 @@ void testMavenWarningScenario() throws IOException {
         if (restoredTimestamp > jarTimestamp) {
             long diffSeconds = (restoredTimestamp - jarTimestamp) / 1000;
             fail(String.format(
-                    "[WARNING] File 'target/classes/com/example/Service.class' 
is more recent%n" +
-                    "          than the packaged artifact 
'my-module-1.0.jar'%n" +
-                    "          (difference: %d seconds)%n" +
-                    "          Please run a full 'mvn clean package' 
build%n%n" +
-                    "This indicates timestamps are not being preserved 
correctly during cache restoration.",
+                    "[WARNING] File 'target/classes/com/example/Service.class' 
is more recent%n"
+                            + "          than the packaged artifact 
'my-module-1.0.jar'%n"
+                            + "          (difference: %d seconds)%n"
+                            + "          Please run a full 'mvn clean package' 
build%n%n"
+                            + "This indicates timestamps are not being 
preserved correctly during cache restoration.",
                     diffSeconds));
         }
 
@@ -299,8 +298,7 @@ void testMultipleFilesTimestampConsistency() throws 
IOException {
                 packageDir.resolve("Service.class"),
                 packageDir.resolve("Repository.class"),
                 packageDir.resolve("Controller.class"),
-                packageDir.resolve("Model.class")
-        );
+                packageDir.resolve("Model.class"));
 
         for (Path file : files) {
             writeString(file, "// " + file.getFileName() + " content");
@@ -311,11 +309,11 @@ void testMultipleFilesTimestampConsistency() throws 
IOException {
 
         // When: Zip and unzip
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", true);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, true);
 
         Path extractDir = tempDir.resolve("extracted");
         Files.createDirectories(extractDir);
-        CacheUtils.unzip(zipFile, extractDir, true);
+        CacheUtils.unzip(zipFile, extractDir, false, true);
 
         // Then: All files should have consistent timestamps
         for (Path originalFile : files) {
@@ -348,11 +346,11 @@ void testPreserveTimestampsFalse() throws IOException {
 
         // When: Zip and unzip with preserveTimestamps=false
         Path zipFile = tempDir.resolve("cache.zip");
-        CacheUtils.zip(sourceDir, zipFile, "*", false);
+        CacheUtils.zip(sourceDir, zipFile, "*", false, false);
 
         Path extractDir = tempDir.resolve("extracted");
         Files.createDirectories(extractDir);
-        CacheUtils.unzip(zipFile, extractDir, false);
+        CacheUtils.unzip(zipFile, extractDir, false, false);
 
         // Then: Extracted file should NOT have the original timestamp
         // (it should have a timestamp close to now, not 1 hour ago)
@@ -365,17 +363,22 @@ void testPreserveTimestampsFalse() throws IOException {
         long diffFromCurrent = Math.abs(extractedTimestamp - currentTime);
 
         // The extracted file should be much closer to current time than to 
the old timestamp
-        assertTrue(diffFromCurrent < diffFromOriginal,
-                String.format("When preserveTimestamps=false, extracted file 
timestamp should be close to current time.%n" +
-                        "Original timestamp (1 hour ago): %s (%d)%n" +
-                        "Extracted timestamp: %s (%d)%n" +
-                        "Current time: %s (%d)%n" +
-                        "Diff from original: %d seconds%n" +
-                        "Diff from current: %d seconds%n" +
-                        "Expected: diff from current < diff from original",
-                        Instant.ofEpochMilli(originalTimestamp), 
originalTimestamp,
-                        Instant.ofEpochMilli(extractedTimestamp), 
extractedTimestamp,
-                        Instant.ofEpochMilli(currentTime), currentTime,
+        assertTrue(
+                diffFromCurrent < diffFromOriginal,
+                String.format(
+                        "When preserveTimestamps=false, extracted file 
timestamp should be close to current time.%n"
+                                + "Original timestamp (1 hour ago): %s (%d)%n"
+                                + "Extracted timestamp: %s (%d)%n"
+                                + "Current time: %s (%d)%n"
+                                + "Diff from original: %d seconds%n"
+                                + "Diff from current: %d seconds%n"
+                                + "Expected: diff from current < diff from 
original",
+                        Instant.ofEpochMilli(originalTimestamp),
+                        originalTimestamp,
+                        Instant.ofEpochMilli(extractedTimestamp),
+                        extractedTimestamp,
+                        Instant.ofEpochMilli(currentTime),
+                        currentTime,
                         diffFromOriginal / 1000,
                         diffFromCurrent / 1000));
     }
@@ -390,15 +393,14 @@ private void assertTimestampPreserved(String fileName, 
long expectedMs, long act
 
         if (diffMs > TIMESTAMP_TOLERANCE_MS) {
             String errorMessage = String.format(
-                    "%s%n" +
-                    "File: %s%n" +
-                    "Expected timestamp: %s (%d)%n" +
-                    "Actual timestamp:   %s (%d)%n" +
-                    "Difference:         %d seconds (%.2f hours)%n" +
-                    "%n" +
-                    "Timestamps must be preserved within %d ms tolerance.%n" +
-                    "This failure indicates CacheUtils.zip() or 
CacheUtils.unzip() is not%n" +
-                    "correctly preserving file/directory timestamps.",
+                    "%s%n" + "File: %s%n"
+                            + "Expected timestamp: %s (%d)%n"
+                            + "Actual timestamp:   %s (%d)%n"
+                            + "Difference:         %d seconds (%.2f hours)%n"
+                            + "%n"
+                            + "Timestamps must be preserved within %d ms 
tolerance.%n"
+                            + "This failure indicates CacheUtils.zip() or 
CacheUtils.unzip() is not%n"
+                            + "correctly preserving file/directory 
timestamps.",
                     message,
                     fileName,
                     Instant.ofEpochMilli(expectedMs),
diff --git 
a/src/test/java/org/apache/maven/buildcache/ModuleInfoCachingTest.java 
b/src/test/java/org/apache/maven/buildcache/ModuleInfoCachingTest.java
index 063b650..c6ddcf9 100644
--- a/src/test/java/org/apache/maven/buildcache/ModuleInfoCachingTest.java
+++ b/src/test/java/org/apache/maven/buildcache/ModuleInfoCachingTest.java
@@ -51,21 +51,19 @@ public void testModuleInfoClassIsIncludedInZip(@TempDir 
Path testDir) throws IOE
 
         // Zip using the default glob pattern "*" (same as attachedOutputs 
uses)
         Path zipFile = testDir.resolve("test.zip");
-        boolean hasFiles = CacheUtils.zip(classesDir, zipFile, "*", true);
+        boolean hasFiles = CacheUtils.zip(classesDir, zipFile, "*", true, 
true);
 
         assertTrue(hasFiles, "Zip should contain files");
         assertTrue(Files.exists(zipFile), "Zip file should be created");
 
         // Extract and verify module-info.class is present
         Path extractDir = testDir.resolve("extracted");
-        CacheUtils.unzip(zipFile, extractDir, true);
+        CacheUtils.unzip(zipFile, extractDir, true, true);
 
         Path extractedModuleInfo = extractDir.resolve("module-info.class");
-        assertTrue(Files.exists(extractedModuleInfo),
-            "module-info.class should be present after extraction");
+        assertTrue(Files.exists(extractedModuleInfo), "module-info.class 
should be present after extraction");
 
         Path extractedRegularClass = 
extractDir.resolve("com/example/MyClass.class");
-        assertTrue(Files.exists(extractedRegularClass),
-            "Regular .class file should be present after extraction");
+        assertTrue(Files.exists(extractedRegularClass), "Regular .class file 
should be present after extraction");
     }
 }

Reply via email to