Skip to content

Commit 5968076

Browse files
authored
make npmInstallCache more robust (#2096)
2 parents 7cd08cd + 1d515e7 commit 5968076

File tree

7 files changed

+74
-15
lines changed

7 files changed

+74
-15
lines changed

CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
2626
### Fixed
2727
* Ignore system git config when running tests ([#1990](https://github.com/diffplug/spotless/issues/1990))
2828
* Correctly provide EditorConfig property types for Ktlint ([#2052](https://github.com/diffplug/spotless/issues/2052))
29+
* Made ShadowCopy (`npmInstallCache`) more robust by re-creating the cache dir if it goes missing ([#1984](https://github.com/diffplug/spotless/issues/1984),[2096](https://github.com/diffplug/spotless/pull/2096))
2930

3031
## [2.45.0] - 2024-01-23
3132
### Added

lib/src/main/java/com/diffplug/spotless/npm/NodeModulesCachingNpmProcessFactory.java

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 DiffPlug
2+
* Copyright 2023-2024 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -38,11 +38,11 @@ public class NodeModulesCachingNpmProcessFactory implements NpmProcessFactory {
3838

3939
private NodeModulesCachingNpmProcessFactory(@Nonnull File cacheDir) {
4040
this.cacheDir = Objects.requireNonNull(cacheDir);
41-
assertDir(cacheDir);
42-
this.shadowCopy = new ShadowCopy(cacheDir);
41+
assertDir(); // throws if cacheDir is not a directory
42+
this.shadowCopy = new ShadowCopy(this::assertDir);
4343
}
4444

45-
private void assertDir(File cacheDir) {
45+
private synchronized File assertDir() {
4646
if (cacheDir.exists() && !cacheDir.isDirectory()) {
4747
throw new IllegalArgumentException("Cache dir must be a directory");
4848
}
@@ -51,6 +51,7 @@ private void assertDir(File cacheDir) {
5151
throw new IllegalArgumentException("Cache dir could not be created.");
5252
}
5353
}
54+
return cacheDir;
5455
}
5556

5657
public static NodeModulesCachingNpmProcessFactory create(@Nonnull File cacheDir) {

lib/src/main/java/com/diffplug/spotless/npm/ShadowCopy.java

+14-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 DiffPlug
2+
* Copyright 2023-2024 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import java.nio.file.attribute.BasicFileAttributes;
2828
import java.time.Duration;
2929
import java.util.concurrent.TimeoutException;
30+
import java.util.function.Supplier;
3031

3132
import javax.annotation.Nonnull;
3233

@@ -39,13 +40,18 @@ class ShadowCopy {
3940

4041
private static final Logger logger = LoggerFactory.getLogger(ShadowCopy.class);
4142

42-
private final File shadowCopyRoot;
43+
private final Supplier<File> shadowCopyRootSupplier;
4344

44-
public ShadowCopy(@Nonnull File shadowCopyRoot) {
45-
this.shadowCopyRoot = shadowCopyRoot;
45+
public ShadowCopy(@Nonnull Supplier<File> shadowCopyRootSupplier) {
46+
this.shadowCopyRootSupplier = shadowCopyRootSupplier;
47+
}
48+
49+
private File shadowCopyRoot() {
50+
File shadowCopyRoot = shadowCopyRootSupplier.get();
4651
if (!shadowCopyRoot.isDirectory()) {
47-
throw new IllegalArgumentException("Shadow copy root must be a directory: " + shadowCopyRoot);
52+
throw new IllegalStateException("Shadow copy root must be a directory: " + shadowCopyRoot);
4853
}
54+
return shadowCopyRoot;
4955
}
5056

5157
public void addEntry(String key, File orig) {
@@ -86,17 +92,17 @@ private void cleanupReservation(String key) {
8692
}
8793

8894
private Path markerFilePath(String key) {
89-
return Paths.get(shadowCopyRoot.getAbsolutePath(), key + ".marker");
95+
return Paths.get(shadowCopyRoot().getAbsolutePath(), key + ".marker");
9096
}
9197

9298
private File entry(String key, String origName) {
93-
return Paths.get(shadowCopyRoot.getAbsolutePath(), key, origName).toFile();
99+
return Paths.get(shadowCopyRoot().getAbsolutePath(), key, origName).toFile();
94100
}
95101

96102
private boolean reserveSubFolder(String key) {
97103
// put a marker file named "key".marker in "shadowCopyRoot" to make sure no other process is using it or return false if it already exists
98104
try {
99-
Files.createFile(Paths.get(shadowCopyRoot.getAbsolutePath(), key + ".marker"));
105+
Files.createFile(Paths.get(shadowCopyRoot().getAbsolutePath(), key + ".marker"));
100106
return true;
101107
} catch (FileAlreadyExistsException e) {
102108
return false;

plugin-gradle/CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
88
* Ignore system git config when running tests ([#1990](https://github.com/diffplug/spotless/issues/1990))
99
* Correctly provide EditorConfig property types for Ktlint ([#2052](https://github.com/diffplug/spotless/issues/2052))
1010
* Fixed memory leak introduced in 6.21.0 ([#2067](https://github.com/diffplug/spotless/issues/2067))
11+
* Made ShadowCopy (`npmInstallCache`) more robust by re-creating the cache dir if it goes missing ([#1984](https://github.com/diffplug/spotless/issues/1984),[2096](https://github.com/diffplug/spotless/pull/2096))
1112
### Changes
1213
* Bump default `ktfmt` version to latest `0.46` -> `0.47`. ([#2045](https://github.com/diffplug/spotless/pull/2045))
1314
* Bump default `sortpom` version to latest `3.2.1` -> `3.4.0`. ([#2049](https://github.com/diffplug/spotless/pull/2049))

plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PrettierIntegrationTest.java

+49-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2016-2023 DiffPlug
2+
* Copyright 2016-2024 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -358,6 +358,54 @@ void autodetectNpmrcFileConfig(String prettierVersion) throws IOException {
358358
Assertions.assertThat(spotlessApply.getOutput()).containsPattern("Running npm command.*npm install.* failed with exit code: 1");
359359
}
360360

361+
@ParameterizedTest(name = "{index}: verifyCleanAndSpotlessWorks with prettier {0}")
362+
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
363+
void verifyCleanAndSpotlessWorks(String prettierVersion) throws IOException {
364+
setFile("build.gradle").toLines(
365+
"plugins {",
366+
" id 'com.diffplug.spotless'",
367+
"}",
368+
"repositories { mavenCentral() }",
369+
"def prettierConfig = [:]",
370+
"prettierConfig['printWidth'] = 20",
371+
"prettierConfig['parser'] = 'typescript'",
372+
"spotless {",
373+
" format 'mytypescript', {",
374+
" target 'test.ts'",
375+
" prettier('" + prettierVersion + "').config(prettierConfig)",
376+
" }",
377+
"}");
378+
setFile("test.ts").toResource("npm/prettier/config/typescript.dirty");
379+
final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "clean", "spotlessApply").build();
380+
Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL");
381+
final BuildResult spotlessApply2 = gradleRunner().withArguments("--stacktrace", "clean", "spotlessApply").build();
382+
Assertions.assertThat(spotlessApply2.getOutput()).contains("BUILD SUCCESSFUL");
383+
}
384+
385+
@ParameterizedTest(name = "{index}: verifyCleanAndSpotlessWithNpmInstallCacheWorks with prettier {0}")
386+
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
387+
void verifyCleanAndSpotlessWithNpmInstallCacheWorks(String prettierVersion) throws IOException {
388+
setFile("build.gradle").toLines(
389+
"plugins {",
390+
" id 'com.diffplug.spotless'",
391+
"}",
392+
"repositories { mavenCentral() }",
393+
"def prettierConfig = [:]",
394+
"prettierConfig['printWidth'] = 20",
395+
"prettierConfig['parser'] = 'typescript'",
396+
"spotless {",
397+
" format 'mytypescript', {",
398+
" target 'test.ts'",
399+
" prettier('" + prettierVersion + "').npmInstallCache().config(prettierConfig)",
400+
" }",
401+
"}");
402+
setFile("test.ts").toResource("npm/prettier/config/typescript.dirty");
403+
final BuildResult spotlessApply = gradleRunner().withArguments("--stacktrace", "clean", "spotlessApply").build();
404+
Assertions.assertThat(spotlessApply.getOutput()).contains("BUILD SUCCESSFUL");
405+
final BuildResult spotlessApply2 = gradleRunner().withArguments("--stacktrace", "clean", "spotlessApply").build();
406+
Assertions.assertThat(spotlessApply2.getOutput()).contains("BUILD SUCCESSFUL");
407+
}
408+
361409
@ParameterizedTest(name = "{index}: autodetectNpmrcFileConfig with prettier {0}")
362410
@ValueSource(strings = {PRETTIER_VERSION_2, PRETTIER_VERSION_3})
363411
void pickupNpmrcFileConfig(String prettierVersion) throws IOException {

plugin-maven/CHANGES.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
66
### Fixed
77
* Ignore system git config when running tests ([#1990](https://github.com/diffplug/spotless/issues/1990))
88
* Correctly provide EditorConfig property types for Ktlint ([#2052](https://github.com/diffplug/spotless/issues/2052))
9+
* Made ShadowCopy (`npmInstallCache`) more robust by re-creating the cache dir if it goes missing ([#1984](https://github.com/diffplug/spotless/issues/1984),[2096](https://github.com/diffplug/spotless/pull/2096))
910
### Changes
1011
* Bump default `ktfmt` version to latest `0.46` -> `0.47`. ([#2045](https://github.com/diffplug/spotless/pull/2045))
1112
* Bump default `sortpom` version to latest `3.2.1` -> `3.4.0`. ([#2049](https://github.com/diffplug/spotless/pull/2049))

testlib/src/test/java/com/diffplug/spotless/npm/ShadowCopyTest.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2023 DiffPlug
2+
* Copyright 2023-2024 DiffPlug
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,6 +29,7 @@
2929
import org.junit.jupiter.api.BeforeEach;
3030
import org.junit.jupiter.api.Test;
3131

32+
import com.diffplug.common.base.Suppliers;
3233
import com.diffplug.spotless.ResourceHarness;
3334

3435
class ShadowCopyTest extends ResourceHarness {
@@ -43,7 +44,7 @@ class ShadowCopyTest extends ResourceHarness {
4344
@BeforeEach
4445
void setUp() throws IOException {
4546
shadowCopyRoot = newFolder("shadowCopyRoot");
46-
shadowCopy = new ShadowCopy(shadowCopyRoot);
47+
shadowCopy = new ShadowCopy(Suppliers.ofInstance(shadowCopyRoot));
4748
}
4849

4950
@Test

0 commit comments

Comments
 (0)