From 8c1813068809a1f9177a95c3491cff657ad85c5c Mon Sep 17 00:00:00 2001 From: Maksim Avilov Date: Sat, 22 Nov 2025 12:31:23 +0100 Subject: [PATCH] Fixes #220 --- .../BuildCacheMojosExecutionStrategy.java | 49 +++++++++++---- .../its/SkipBuildExtensionTest.java | 62 +++++++++++++++++++ 2 files changed, 98 insertions(+), 13 deletions(-) create mode 100644 src/test/java/org/apache/maven/buildcache/its/SkipBuildExtensionTest.java diff --git a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java index 0a2d4d73..d5c5a264 100644 --- a/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java +++ b/src/main/java/org/apache/maven/buildcache/BuildCacheMojosExecutionStrategy.java @@ -100,6 +100,7 @@ public BuildCacheMojosExecutionStrategy( public void execute( List mojoExecutions, MavenSession session, MojoExecutionRunner mojoExecutionRunner) throws LifecycleExecutionException { + try { final MavenProject project = session.getCurrentProject(); final Source source = getSource(mojoExecutions); @@ -107,10 +108,13 @@ public void execute( // execute clean bound goals before restoring to not interfere/slowdown clean CacheState cacheState = DISABLED; CacheResult result = CacheResult.empty(); - boolean skipCache = cacheConfig.isSkipCache() || MavenProjectInput.isSkipCache(project); + boolean skipCache = + cacheConfig.isSkipCache() || MavenProjectInput.isSkipCache(project) || isGoalClean(mojoExecutions); boolean cacheIsDisabled = MavenProjectInput.isCacheDisabled(project); - // Forked execution should be thought as a part of originating mojo internal implementation - // If forkedExecution is detected, it means that originating mojo is not cached so forks should rerun too + // Forked execution should be thought as a part of originating mojo internal + // implementation + // If forkedExecution is detected, it means that originating mojo is not cached + // so forks should rerun too boolean forkedExecution = lifecyclePhasesHelper.isForkedProject(project); String projectName = getVersionlessProjectKey(project); List cleanPhase = null; @@ -178,13 +182,30 @@ public void execute( } /** - * Cache configuration could demand to restore some files in the project directory (generated sources or even arbitrary content) - * If an error occurs during or after this kind of restoration AND a clean phase was required in the build : - * we execute an extra clean phase to remove any potential partially restored files + * Check if the current mojo execution is for the clean goal + * + * @param mojoExecutions the mojo executions + * @return true if the goal is clean and it is the only goal, false otherwise + */ + private boolean isGoalClean(List mojoExecutions) { + if (mojoExecutions.stream().allMatch(mojoExecution -> "clean".equals(mojoExecution.getLifecyclePhase()))) { + LOGGER.warn("Build cache is disabled for 'clean' goal."); + return true; + } + return false; + } + + /** + * Cache configuration could demand to restore some files in the project + * directory (generated sources or even arbitrary content) + * If an error occurs during or after this kind of restoration AND a clean phase + * was required in the build : + * we execute an extra clean phase to remove any potential partially restored + * files * * @param cacheRestorationStatus the restoration status - * @param cleanPhase clean phase mojos - * @param mojoExecutionRunner mojo runner + * @param cleanPhase clean phase mojos + * @param mojoExecutionRunner mojo runner * @throws LifecycleExecutionException */ private void executeExtraCleanPhaseIfNeeded( @@ -260,13 +281,14 @@ private CacheRestorationStatus restoreProject( cacheCandidate.getMojoDescriptor().getFullGoalName()); // need maven 4 as minumum // mojoExecutionScope.seed( - // org.apache.maven.api.plugin.Log.class, - // new DefaultLog(LoggerFactory.getLogger( - // cacheCandidate.getMojoDescriptor().getFullGoalName()))); + // org.apache.maven.api.plugin.Log.class, + // new DefaultLog(LoggerFactory.getLogger( + // cacheCandidate.getMojoDescriptor().getFullGoalName()))); // mojoExecutionScope.seed(Project.class, ((DefaultSession) // session.getSession()).getProject(project)); // mojoExecutionScope.seed( - // org.apache.maven.api.MojoExecution.class, new DefaultMojoExecution(cacheCandidate)); + // org.apache.maven.api.MojoExecution.class, new + // DefaultMojoExecution(cacheCandidate)); mojoExecutionRunner.run(cacheCandidate); } else { LOGGER.info( @@ -413,7 +435,8 @@ boolean isParamsMatched( * - all absolute paths under project root to be relativized for portability * - redundant '..' and '.' to be removed to have consistent views on all paths * - all relative paths are considered portable and should not be touched - * - absolute paths outside of project directory could not be deterministically relativized and not touched + * - absolute paths outside of project directory could not be deterministically + * relativized and not touched */ private static String normalizedPath(Path path, Path baseDirPath) { boolean isProjectSubdir = path.isAbsolute() && path.startsWith(baseDirPath); diff --git a/src/test/java/org/apache/maven/buildcache/its/SkipBuildExtensionTest.java b/src/test/java/org/apache/maven/buildcache/its/SkipBuildExtensionTest.java new file mode 100644 index 00000000..68f2da15 --- /dev/null +++ b/src/test/java/org/apache/maven/buildcache/its/SkipBuildExtensionTest.java @@ -0,0 +1,62 @@ +/* + * 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.its; + +import java.util.List; + +import org.apache.maven.buildcache.its.junit.IntegrationTest; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.collect.Lists; + +import static org.apache.maven.buildcache.util.LogFileUtils.findFirstLineContainingTextsInLogs; + +@IntegrationTest("src/test/projects/build-extension") +class SkipBuildExtensionTest { + + @Test + void simple(Verifier verifier) throws VerificationException { + verifier.setAutoclean(false); + + verifier.setLogFileName("../log-1.txt"); + verifier.executeGoal("clean"); + verifier.verifyErrorFreeLog(); + + verifier.verifyTextInLog("Build cache is disabled for 'clean' goal."); + } + + @Test + void multipleGoals(Verifier verifier) throws VerificationException { + verifier.setAutoclean(false); + + verifier.setLogFileName("../log-2.txt"); + String[] goals = {"clean", "install"}; + List goalsList = Lists.newArrayList(goals); + verifier.executeGoals(goalsList); + verifier.verifyErrorFreeLog(); + + verifyNoTextInLog(verifier, "Build cache is disabled for 'clean' goal."); + } + + private static void verifyNoTextInLog(Verifier verifier, String text) throws VerificationException { + Assertions.assertNull(findFirstLineContainingTextsInLogs(verifier, text)); + } +}