Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import javax.inject.Named;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.HashSet;
import java.util.List;
Expand Down Expand Up @@ -138,33 +139,58 @@ public void execute(

boolean restorable = result.isSuccess() || result.isPartialSuccess();
boolean restored = false; // if partially restored need to save increment

if (restorable) {
CacheRestorationStatus cacheRestorationStatus =
restoreProject(result, mojoExecutions, mojoExecutionRunner, cacheConfig);
restored = CacheRestorationStatus.SUCCESS == cacheRestorationStatus;
executeExtraCleanPhaseIfNeeded(cacheRestorationStatus, cleanPhase, mojoExecutionRunner);
}
if (!restored) {
for (MojoExecution mojoExecution : mojoExecutions) {
if (source == Source.CLI
|| mojoExecution.getLifecyclePhase() == null
|| lifecyclePhasesHelper.isLaterPhaseThanClean(mojoExecution.getLifecyclePhase())) {
mojoExecutionRunner.run(mojoExecution);

try {
if (!restored && !forkedExecution) {
// Move pre-existing artifacts to staging directory to prevent caching stale files
// from previous builds (e.g., after git branch switch, or from cache restored
// with clock skew). This ensures save() only sees fresh files built during this session.
// Skip for forked executions since they don't cache and shouldn't modify artifacts.
try {
cacheController.stagePreExistingArtifacts(session, project);
} catch (IOException e) {
LOGGER.warn("Failed to stage pre-existing artifacts: {}", e.getMessage());
// Continue build - if staging fails, we'll just cache what exists
}
}
}

if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) {
if (cacheConfig.isSkipSave()) {
LOGGER.info("Cache saving is disabled.");
} else if (cacheConfig.isMandatoryClean()
&& lifecyclePhasesHelper
.getCleanSegment(project, mojoExecutions)
.isEmpty()) {
LOGGER.info("Cache storing is skipped since there was no \"clean\" phase.");
} else {
final Map<String, MojoExecutionEvent> executionEvents = mojoListener.getProjectExecutions(project);
cacheController.save(result, mojoExecutions, executionEvents);
if (!restored) {
for (MojoExecution mojoExecution : mojoExecutions) {
if (source == Source.CLI
|| mojoExecution.getLifecyclePhase() == null
|| lifecyclePhasesHelper.isLaterPhaseThanClean(mojoExecution.getLifecyclePhase())) {
mojoExecutionRunner.run(mojoExecution);
}
}
}

if (cacheState == INITIALIZED && (!result.isSuccess() || !restored)) {
if (cacheConfig.isSkipSave()) {
LOGGER.info("Cache saving is disabled.");
} else if (cacheConfig.isMandatoryClean()
&& lifecyclePhasesHelper
.getCleanSegment(project, mojoExecutions)
.isEmpty()) {
LOGGER.info("Cache storing is skipped since there was no \"clean\" phase.");
} else {
final Map<String, MojoExecutionEvent> executionEvents =
mojoListener.getProjectExecutions(project);
cacheController.save(result, mojoExecutions, executionEvents);
}
}
} finally {
// Always restore staged files after build completes (whether save ran or not).
// Files that were rebuilt are discarded; files that weren't rebuilt are restored.
// Skip for forked executions since they don't stage artifacts.
if (!restored && !forkedExecution) {
cacheController.restoreStagedArtifacts(session, project);
}
}

Expand Down
20 changes: 20 additions & 0 deletions src/main/java/org/apache/maven/buildcache/CacheController.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.apache.maven.buildcache;

import java.io.IOException;
import java.util.List;
import java.util.Map;

Expand Down Expand Up @@ -45,4 +46,23 @@ void save(
boolean isForcedExecution(MavenProject project, MojoExecution execution);

void saveCacheReport(MavenSession session);

/**
* Move pre-existing artifacts to staging directory to prevent caching stale files.
* Called before mojos run to ensure save() only sees fresh files.
*
* @param session the Maven session
* @param project the Maven project
* @throws IOException if file operations fail
*/
void stagePreExistingArtifacts(MavenSession session, MavenProject project) throws IOException;

/**
* Restore staged artifacts after save() completes.
* Files that were rebuilt are discarded; files that weren't rebuilt are restored.
*
* @param session the Maven session
* @param project the Maven project
*/
void restoreStagedArtifacts(MavenSession session, MavenProject project);
}
Loading