Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce spurious invalidations of the up-to-date index in Maven plugin #1461

Merged
merged 3 commits into from
Jan 8, 2023
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
2 changes: 2 additions & 0 deletions plugin-maven/CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Fixed
* Reduce spurious invalidations of the up-to-date index file ([#1461](https://github.com/diffplug/spotless/pull/1461))

## [2.29.0] - 2023-01-02
### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 DiffPlug
* Copyright 2021-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -76,7 +76,7 @@ static FileIndex read(FileIndexConfig config, Log log) {
PluginFingerprint computedFingerprint = config.getPluginFingerprint();
PluginFingerprint storedFingerprint = PluginFingerprint.from(firstLine);
if (!computedFingerprint.equals(storedFingerprint)) {
log.info("Fingerprint mismatch in the index file. Fallback to an empty index");
log.info("Index file corresponds to a different configuration of the plugin. Either the plugin version or its configuration has changed. Fallback to an empty index");
return emptyIndexFallback(config);
} else {
Content content = readIndexContent(reader, config.getProjectDir(), log);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 DiffPlug
* Copyright 2021-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -17,17 +17,21 @@

import java.io.IOException;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.Objects;

import org.apache.maven.model.Dependency;
import org.apache.maven.model.Plugin;
import org.apache.maven.project.MavenProject;

import com.diffplug.spotless.Formatter;

/**
* Represents a particular Spotless Maven plugin setup using a Base64-encoded serialized form of:
* <ol>
* <li>Plugin version as configured in the POM</li>
* <li>Formatter instances created according to the POM configuration</li>
* </ol>
*/
class PluginFingerprint {

private static final String SPOTLESS_PLUGIN_KEY = "com.diffplug.spotless:spotless-maven-plugin";
Expand Down Expand Up @@ -83,22 +87,15 @@ public String toString() {
}

private static byte[] digest(Plugin plugin, Iterable<Formatter> formatters) {
// dependencies can be an unserializable org.apache.maven.model.merge.ModelMerger$MergingList
// replace it with a serializable ArrayList
List<Dependency> dependencies = plugin.getDependencies();
plugin.setDependencies(new ArrayList<>(dependencies));
try (ObjectDigestOutputStream out = ObjectDigestOutputStream.create()) {
out.writeObject(plugin);
out.writeObject(plugin.getVersion());
for (Formatter formatter : formatters) {
out.writeObject(formatter);
}
out.flush();
return out.digest();
} catch (IOException e) {
throw new UncheckedIOException("Unable to serialize plugin " + plugin, e);
} finally {
// reset the original list
plugin.setDependencies(dependencies);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021 DiffPlug
* Copyright 2021-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -60,7 +60,7 @@ void readFallsBackToEmptyIndexOnFingerprintMismatch() throws Exception {
FileIndex index = FileIndex.read(config, log);

assertThat(index.size()).isZero();
verify(log).info("Fingerprint mismatch in the index file. Fallback to an empty index");
verify(log).info("Index file corresponds to a different configuration of the plugin. Either the plugin version or its configuration has changed. Fallback to an empty index");
}

@Test
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2021-2022 DiffPlug
* Copyright 2021-2023 DiffPlug
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,126 +43,22 @@ class PluginFingerprintTest extends MavenIntegrationHarness {
private static final String VERSION_1 = "1.0.0";
private static final String VERSION_2 = "2.0.0";

private static final String[] EXECUTION_1 = {
"<execution>",
" <id>check</id>",
" <goals>",
" <goal>check</goal>",
" </goals>",
"</execution>"
};
private static final String[] EXECUTION_2 = {};

private static final String[] CONFIGURATION_1 = {
"<googleJavaFormat>",
" <version>1.2</version>",
"</googleJavaFormat>"
};
private static final String[] CONFIGURATION_2 = {
"<googleJavaFormat>",
" <version>1.8</version>",
" <reflowLongStrings>true</reflowLongStrings>",
"</googleJavaFormat>"
};

private static final String[] DEPENDENCIES_1 = {
"<dependencies>",
" <dependency>",
" <groupId>unknown</groupId>",
" <artifactId>unknown</artifactId>",
" <version>1.0</version>",
" </dependency>",
"</dependencies>"
};
private static final String[] DEPENDENCIES_2 = {
"<dependencies>",
" <dependency>",
" <groupId>unknown</groupId>",
" <artifactId>unknown</artifactId>",
" <version>2.0</version>",
" </dependency>",
"</dependencies>"
};

private static final List<Formatter> FORMATTERS = singletonList(formatter(formatterStep("default")));

@Test
void sameFingerprint() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);
void sameFingerprintWhenVersionAndFormattersAreTheSame() throws Exception {
MavenProject project = mavenProject(VERSION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);
PluginFingerprint fingerprint1 = PluginFingerprint.from(project, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project, FORMATTERS);

assertThat(fingerprint1).isEqualTo(fingerprint2);
}

@Test
void sameFingerprintWithDependencies() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1, DEPENDENCIES_1);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1, DEPENDENCIES_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);

assertThat(fingerprint1).isEqualTo(fingerprint2);
}

@Test
void differentFingerprintForDifferentDependencies() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1, DEPENDENCIES_1);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1, DEPENDENCIES_2);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);

assertThat(fingerprint1).isNotEqualTo(fingerprint2);
}

@Test
void differentFingerprintForDifferentPluginVersion() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);
String xml2 = createPomXmlContent(VERSION_2, EXECUTION_1, CONFIGURATION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);

assertThat(fingerprint1).isNotEqualTo(fingerprint2);
}

@Test
void differentFingerprintForDifferentExecution() throws Exception {
String xml1 = createPomXmlContent(VERSION_2, EXECUTION_1, CONFIGURATION_1);
String xml2 = createPomXmlContent(VERSION_2, EXECUTION_2, CONFIGURATION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);

assertThat(fingerprint1).isNotEqualTo(fingerprint2);
}

@Test
void differentFingerprintForDifferentConfiguration() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_2, CONFIGURATION_2);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_2, CONFIGURATION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);
void differentFingerprintForDifferentPluginVersions() throws Exception {
MavenProject project1 = mavenProject(VERSION_1);
MavenProject project2 = mavenProject(VERSION_2);

PluginFingerprint fingerprint1 = PluginFingerprint.from(project1, FORMATTERS);
PluginFingerprint fingerprint2 = PluginFingerprint.from(project2, FORMATTERS);
Expand All @@ -172,11 +68,8 @@ void differentFingerprintForDifferentConfiguration() throws Exception {

@Test
void differentFingerprintForFormattersWithDifferentSteps() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);
MavenProject project1 = mavenProject(VERSION_1);
MavenProject project2 = mavenProject(VERSION_1);

FormatterStep step1 = formatterStep("step1");
FormatterStep step2 = formatterStep("step2");
Expand All @@ -192,11 +85,8 @@ void differentFingerprintForFormattersWithDifferentSteps() throws Exception {

@Test
void differentFingerprintForFormattersWithDifferentLineEndings() throws Exception {
String xml1 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);
String xml2 = createPomXmlContent(VERSION_1, EXECUTION_1, CONFIGURATION_1);

MavenProject project1 = mavenProject(xml1);
MavenProject project2 = mavenProject(xml2);
MavenProject project1 = mavenProject(VERSION_1);
MavenProject project2 = mavenProject(VERSION_1);

FormatterStep step = formatterStep("step");
List<Formatter> formatters1 = singletonList(formatter(LineEnding.UNIX, step));
Expand Down Expand Up @@ -224,7 +114,8 @@ void failsWhenProjectDoesNotContainSpotlessPlugin() {
.hasMessageContaining("Spotless plugin absent from the project");
}

private static MavenProject mavenProject(String xml) throws Exception {
private MavenProject mavenProject(String spotlessVersion) throws Exception {
String xml = createPomXmlContent(spotlessVersion, new String[0], new String[0]);
return new MavenProject(readPom(xml));
}

Expand Down