Skip to content

Commit e5a521f

Browse files
refactor(#5179): simplify Janitor to wipe cache on every SNAPSHOT build
1 parent 70ddb1d commit e5a521f

5 files changed

Lines changed: 88 additions & 60 deletions

File tree

eo-maven-plugin/src/main/java/org/eolang/maven/Cache.java

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
package org.eolang.maven;
66

77
import java.io.IOException;
8-
import java.io.InputStream;
98
import java.nio.charset.StandardCharsets;
109
import java.nio.file.Files;
1110
import java.nio.file.Path;
@@ -164,6 +163,4 @@ private String dirSha(final Path dir) {
164163
);
165164
}
166165
}
167-
168-
169166
}

eo-maven-plugin/src/main/java/org/eolang/maven/Janitor.java

Lines changed: 48 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77
import com.jcabi.log.Logger;
88
import java.io.IOException;
99
import java.net.URISyntaxException;
10-
import java.nio.charset.StandardCharsets;
1110
import java.nio.file.Files;
1211
import java.nio.file.Path;
1312
import java.nio.file.Paths;
1413
import java.util.Comparator;
1514
import java.util.stream.Stream;
1615

1716
/**
18-
* Invalidates the local EO cache when the plugin code changes.
19-
* Only applies to SNAPSHOT versions — released versions use version-keyed
20-
* cache directories that never collide.
17+
* Invalidates the local EO cache on every SNAPSHOT build.
18+
* Released versions use version-keyed cache directories that never collide,
19+
* so no invalidation is needed for them.
20+
* @see <a href="https://github.com/objectionary/eo/issues/5179">issue #5179</a>
2121
* @since 0.62.0
2222
*/
2323
final class Janitor {
@@ -53,41 +53,57 @@ final class Janitor {
5353
}
5454

5555
/**
56-
* Wipes all SNAPSHOT stage caches if the plugin code has changed since last build.
56+
* Wipes all SNAPSHOT stage caches on every build.
57+
* Does nothing for released versions.
5758
*/
58-
void exec() {
59-
if (!this.version.contains("SNAPSHOT")) {
60-
return;
61-
}
62-
try {
63-
final String current = String.valueOf(Janitor.mtime());
64-
final Path fingerprint = this.cache.resolve(Janitor.FINGERPRINT);
65-
if (Files.exists(fingerprint)
66-
&& new String(Files.readAllBytes(fingerprint), StandardCharsets.UTF_8)
67-
.equals(current)) {
68-
return;
69-
}
70-
for (final String stage : Janitor.STAGES) {
71-
final Path dir = this.cache.resolve(stage).resolve(this.version);
72-
if (Files.exists(dir)) {
73-
Logger.info(
74-
this,
75-
"Plugin code changed, invalidating SNAPSHOT cache at %[file]s",
76-
dir
77-
);
78-
Janitor.wipe(dir);
79-
}
59+
void clean() {
60+
if (this.version.contains("SNAPSHOT")) {
61+
try {
62+
this.invalidate();
63+
} catch (final IOException | URISyntaxException ex) {
64+
throw new IllegalStateException("Failed to clean SNAPSHOT cache", ex);
8065
}
66+
}
67+
}
68+
69+
/**
70+
* Checks the fingerprint and wipes caches if it has changed.
71+
* @throws IOException If an IO operation fails
72+
* @throws URISyntaxException If the code source location URI is malformed
73+
*/
74+
private void invalidate() throws IOException, URISyntaxException {
75+
final String current = String.valueOf(Janitor.mtime());
76+
final Path fingerprint = this.cache.resolve(Janitor.FINGERPRINT);
77+
if (!Files.exists(fingerprint)
78+
|| !Files.readString(fingerprint).equals(current)) {
79+
this.wipeCaches();
8180
new Saved(current, fingerprint).value();
82-
} catch (final IOException | URISyntaxException ex) {
83-
throw new IllegalStateException("Failed to clean SNAPSHOT cache", ex);
8481
}
8582
}
8683

8784
/**
88-
* Returns the newest modification time across all files at the plugin code source location.
89-
* Works for both a JAR file (production) and a classes directory (development).
90-
* @return Newest last-modified timestamp in milliseconds
85+
* Deletes the versioned cache directory for each stage if it exists.
86+
* @throws IOException If deletion fails
87+
*/
88+
private void wipeCaches() throws IOException {
89+
for (final String stage : Janitor.STAGES) {
90+
final Path dir = this.cache.resolve(stage).resolve(this.version);
91+
if (Files.exists(dir)) {
92+
Logger.info(
93+
this,
94+
"Plugin code changed, invalidating SNAPSHOT cache at %[file]s",
95+
dir
96+
);
97+
Janitor.wipe(dir);
98+
}
99+
}
100+
}
101+
102+
/**
103+
* Returns the modification time of the plugin JAR or classes directory.
104+
* Used as a build fingerprint: changes on every build, so comparing it
105+
* to the stored value always detects a new build.
106+
* @return Last-modified timestamp in milliseconds
91107
* @throws URISyntaxException If the code source location URI is malformed
92108
* @throws IOException If reading the directory fails
93109
*/

eo-maven-plugin/src/main/java/org/eolang/maven/MjSafe.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,7 +487,7 @@ public final void execute() throws MojoFailureException {
487487
} else {
488488
try {
489489
final long start = System.nanoTime();
490-
new Janitor(this.cache.toPath(), this.plugin.getVersion()).exec();
490+
this.invalidate();
491491
this.execWithTimeout();
492492
if (Logger.isDebugEnabled(this)) {
493493
Logger.debug(
@@ -622,6 +622,16 @@ Proxy[] proxies() {
622622
).toArray(Proxy[]::new);
623623
}
624624

625+
/**
626+
* Invalidates the SNAPSHOT cache if the plugin descriptor is available.
627+
* The descriptor is absent in some test scenarios that use minimal setup.
628+
*/
629+
private void invalidate() {
630+
if (this.plugin != null) {
631+
new Janitor(this.cache.toPath(), this.plugin.getVersion()).clean();
632+
}
633+
}
634+
625635
/**
626636
* Runs exec command with timeout if needed.
627637
* @throws ExecutionException If unexpected exception happened during execution

eo-maven-plugin/src/main/java/org/eolang/maven/Sha.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ final class Sha {
3434
this.path = path;
3535
}
3636

37-
/**
38-
* Returns Base64-encoded SHA-256 hash.
39-
* @return Base64-encoded SHA-256 hash
40-
*/
4137
@Override
4238
public String toString() {
4339
try {
@@ -59,8 +55,7 @@ private static String hash(final Path path) throws IOException, NoSuchAlgorithmE
5955
final MessageDigest digest = MessageDigest.getInstance("SHA-256");
6056
try (Stream<Path> walk = Files.walk(path)) {
6157
walk.filter(Files::isRegularFile)
62-
.sorted(Comparator.comparing(Path::toString))
63-
.forEach(
58+
.sorted(Comparator.comparing(Path::toString)).forEach(
6459
file -> {
6560
try (InputStream input = Files.newInputStream(file)) {
6661
final byte[] buffer = new byte[8192];

eo-maven-plugin/src/test/java/org/eolang/maven/JanitorTest.java

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ final class JanitorTest {
2323

2424
@Test
2525
void doesNothingForReleasedVersion(@Mktmp final Path temp) {
26-
new Janitor(temp, "1.0").exec();
26+
new Janitor(temp, "1.0").clean();
2727
MatcherAssert.assertThat(
2828
"Fingerprint must not be created for released versions",
2929
Files.exists(temp.resolve(".snapshot-fingerprint")),
@@ -32,18 +32,22 @@ void doesNothingForReleasedVersion(@Mktmp final Path temp) {
3232
}
3333

3434
@Test
35-
void wipesStageDirsAndWritesFingerprintOnFirstRun(@Mktmp final Path temp) throws Exception {
35+
void wipesStageDirsOnFirstRun(@Mktmp final Path temp) throws Exception {
3636
final String version = "1.0-SNAPSHOT";
37-
Files.createDirectories(temp.resolve("transpiled").resolve(version));
38-
Files.createFile(
39-
temp.resolve("transpiled").resolve(version).resolve("stale.xmir")
40-
);
41-
new Janitor(temp, version).exec();
37+
new Saved("", JanitorTest.staleFile(temp, version)).value();
38+
new Janitor(temp, version).clean();
4239
MatcherAssert.assertThat(
4340
"Stale SNAPSHOT cache must be wiped on first run",
44-
Files.exists(temp.resolve("transpiled").resolve(version).resolve("stale.xmir")),
41+
Files.exists(JanitorTest.staleFile(temp, version)),
4542
Matchers.is(false)
4643
);
44+
}
45+
46+
@Test
47+
void writesFingerprintOnFirstRun(@Mktmp final Path temp) throws Exception {
48+
final String version = "1.0-SNAPSHOT";
49+
new Saved("", JanitorTest.staleFile(temp, version)).value();
50+
new Janitor(temp, version).clean();
4751
MatcherAssert.assertThat(
4852
"Fingerprint file must be written after wipe",
4953
Files.exists(temp.resolve(".snapshot-fingerprint")),
@@ -54,11 +58,10 @@ void wipesStageDirsAndWritesFingerprintOnFirstRun(@Mktmp final Path temp) throws
5458
@Test
5559
void doesNothingWhenFingerprintMatches(@Mktmp final Path temp) throws Exception {
5660
final String version = "1.0-SNAPSHOT";
57-
new Janitor(temp, version).exec();
58-
Files.createDirectories(temp.resolve("transpiled").resolve(version));
61+
new Janitor(temp, version).clean();
5962
final Path kept = temp.resolve("transpiled").resolve(version).resolve("valid.xmir");
60-
Files.createFile(kept);
61-
new Janitor(temp, version).exec();
63+
new Saved("", kept).value();
64+
new Janitor(temp, version).clean();
6265
MatcherAssert.assertThat(
6366
"Cache must not be wiped when fingerprint matches",
6467
Files.exists(kept),
@@ -73,15 +76,22 @@ void wipesStageDirsWhenFingerprintDiffers(@Mktmp final Path temp) throws Excepti
7376
temp.resolve(".snapshot-fingerprint"),
7477
"outdated-hash".getBytes(StandardCharsets.UTF_8)
7578
);
76-
Files.createDirectories(temp.resolve("transpiled").resolve(version));
77-
Files.createFile(
78-
temp.resolve("transpiled").resolve(version).resolve("stale.xmir")
79-
);
80-
new Janitor(temp, version).exec();
79+
new Saved("", JanitorTest.staleFile(temp, version)).value();
80+
new Janitor(temp, version).clean();
8181
MatcherAssert.assertThat(
8282
"Stale cache must be wiped when fingerprint differs",
83-
Files.exists(temp.resolve("transpiled").resolve(version).resolve("stale.xmir")),
83+
Files.exists(JanitorTest.staleFile(temp, version)),
8484
Matchers.is(false)
8585
);
8686
}
87+
88+
/**
89+
* Builds the path to the stale transpiled file used in cache-wipe tests.
90+
* @param temp Temporary directory
91+
* @param version Plugin version
92+
* @return Path to the stale file inside the transpiled stage cache
93+
*/
94+
private static Path staleFile(final Path temp, final String version) {
95+
return temp.resolve("transpiled").resolve(version).resolve("stale.xmir");
96+
}
8797
}

0 commit comments

Comments
 (0)