Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into removeUnusedImports_j…
Browse files Browse the repository at this point in the history
…dk17StringBlock
  • Loading branch information
blacelle committed Mar 1, 2023
2 parents 4565699 + 2f436db commit ab288a8
Show file tree
Hide file tree
Showing 56 changed files with 2,047 additions and 217 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ on:
pull_request:
push:
branches: [main]
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
Expand Down
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,19 @@ This document is intended for Spotless developers.
We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`).

## [Unreleased]
### Changes
* We are now opting in to Gradle's new stable configuration cache. ([#1591](https://github.com/diffplug/spotless/pull/1591))

## [2.36.0] - 2023-02-27
### Added
* `gradlew equoIde` opens a repeatable clean Spotless dev environment. ([#1523](https://github.com/diffplug/spotless/pull/1523))
* `cleanthat` added `includeDraft` option, to include draft mutators from composite mutators. ([#1574](https://github.com/diffplug/spotless/pull/1574))
* `npm`-based formatters now support caching of `node_modules` directory ([#1590](https://github.com/diffplug/spotless/pull/1590))
### Fixed
* `JacksonJsonFormatterFunc` handles json files with an Array as root. ([#1585](https://github.com/diffplug/spotless/pull/1585))
### Changes
* Bump default `cleanthat` version to latest `2.1` -> `2.6` ([#1569](https://github.com/diffplug/spotless/pull/1569) and [#1574](https://github.com/diffplug/spotless/pull/1574))
* Reduce logging-noise created by `npm`-based formatters ([#1590](https://github.com/diffplug/spotless/pull/1590) fixes [#1582](https://github.com/diffplug/spotless/issues/1582))

## [2.35.0] - 2023-02-10
### Added
Expand Down
12 changes: 8 additions & 4 deletions _ext/gradle/p2-fat-jar-setup.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ ext {
p2Dependencies = "p2Dependencies not defined in project"
}

// Some JARs may include JARs themselfs, which shall be unpacked and added to the embedded class folder.
// Some JARs may include JARs themselfs, which shall be unpacked and added to the embedded class folder.
if (!project.hasProperty('internalJars')) {
internalJars = []
}
Expand All @@ -35,13 +35,13 @@ ext {
'META-INF/*.SF', // ... so all signatures are filtered
]
}

// Map fat-JAR resources path if JAR file name does not correspond to plugin package name (e.g. required for internal plugins)
if (!project.hasProperty('fatJarResourcesMap')) {
fatJarResourcesMap = [:]
}


// The directory contains all external classes for the fat-jar
embeddedClassesDirName = 'build/embeddedClasses'
embeddedClassesDir = project.file(embeddedClassesDirName)
Expand Down Expand Up @@ -163,3 +163,7 @@ apply plugin: 'idea'
// Encure that the dependent classes are preovided for compilation if project is build via Eclipse instead of command line
tasks.idea.dependsOn(unjarEmbeddedClasses)

tasks.named('ideaModule') {
notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/13480")
}

6 changes: 5 additions & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# To fix metaspace errors
org.gradle.jvmargs=-Xmx2g -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8
org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1024m -Dfile.encoding=UTF-8
org.gradle.parallel=true
org.gradle.caching=true
org.gradle.unsafe.configuration-cache=true

name=spotless
description=Spotless - keep your code spotless with Gradle
org=diffplug
Expand Down
4 changes: 4 additions & 0 deletions gradle/java-publish.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,10 @@ if (System.env['JITPACK'] == 'true' || version.endsWith('-SNAPSHOT')) {
}
}

tasks.withType(Sign).configureEach {
notCompatibleWithConfigurationCache("https://github.com/gradle/gradle/issues/13470")
}

tasks.withType(AbstractArchiveTask).configureEach {
preserveFileTimestamps = false
reproducibleFileOrder = true
Expand Down
1 change: 1 addition & 0 deletions gradle/java-setup.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ tasks.withType(com.github.spotbugs.snom.SpotBugsTask).configureEach {
reports {
html.enabled = true
}
notCompatibleWithConfigurationCache("https://github.com/spotbugs/spotbugs-gradle-plugin/issues/670")
}

tasks.named('spotbugsMain') {
Expand Down
2 changes: 2 additions & 0 deletions lib/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ tasks.named("check").configure {

dependencies {
compileOnly 'org.slf4j:slf4j-api:2.0.0'
testCommonImplementation 'org.slf4j:slf4j-api:2.0.0'

// zero runtime reqs is a hard requirements for spotless-lib
// if you need a dep, put it in lib-extra
testCommonImplementation "org.junit.jupiter:junit-jupiter:$VER_JUNIT"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
*/
package com.diffplug.spotless.npm;

import static com.diffplug.spotless.LazyArgLogger.lazy;
import static java.util.Objects.requireNonNull;

import java.io.File;
Expand Down Expand Up @@ -71,13 +70,13 @@ public static Map<String, String> defaultDevDependenciesWithEslint(String versio
return Collections.singletonMap("eslint", version);
}

public static FormatterStep create(Map<String, String> devDependencies, Provisioner provisioner, File projectDir, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) {
public static FormatterStep create(Map<String, String> devDependencies, Provisioner provisioner, File projectDir, File buildDir, File cacheDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) {
requireNonNull(devDependencies);
requireNonNull(provisioner);
requireNonNull(projectDir);
requireNonNull(buildDir);
return FormatterStep.createLazy(NAME,
() -> new State(NAME, devDependencies, projectDir, buildDir, npmPathResolver, eslintConfig),
() -> new State(NAME, devDependencies, projectDir, buildDir, cacheDir, npmPathResolver, eslintConfig),
State::createFormatterFunc);
}

Expand All @@ -89,20 +88,20 @@ private static class State extends NpmFormatterStepStateBase implements Serializ
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
private transient EslintConfig eslintConfigInUse;

State(String stepName, Map<String, String> devDependencies, File projectDir, File buildDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) throws IOException {
State(String stepName, Map<String, String> devDependencies, File projectDir, File buildDir, File cacheDir, NpmPathResolver npmPathResolver, EslintConfig eslintConfig) throws IOException {
super(stepName,
new NpmConfig(
replaceDevDependencies(
NpmResourceHelper.readUtf8StringFromClasspath(EslintFormatterStep.class, "/com/diffplug/spotless/npm/eslint-package.json"),
new TreeMap<>(devDependencies)),
"eslint",
NpmResourceHelper.readUtf8StringFromClasspath(EslintFormatterStep.class,
"/com/diffplug/spotless/npm/common-serve.js",
"/com/diffplug/spotless/npm/eslint-serve.js"),
npmPathResolver.resolveNpmrcContent()),
new NpmFormatterStepLocations(
projectDir,
buildDir,
cacheDir,
npmPathResolver::resolveNpmExecutable,
npmPathResolver::resolveNodeExecutable));
this.origEslintConfig = requireNonNull(eslintConfig.verify());
Expand All @@ -116,7 +115,7 @@ protected void prepareNodeServerLayout() throws IOException {
// If any config files are provided, we need to make sure they are at the same location as the node modules
// as eslint will try to resolve plugin/config names relatively to the config file location and some
// eslint configs contain relative paths to additional config files (such as tsconfig.json e.g.)
logger.info("Copying config file <{}> to <{}> and using the copy", origEslintConfig.getEslintConfigPath(), nodeServerLayout.nodeModulesDir());
logger.debug("Copying config file <{}> to <{}> and using the copy", origEslintConfig.getEslintConfigPath(), nodeServerLayout.nodeModulesDir());
File configFileCopy = NpmResourceHelper.copyFileToDir(origEslintConfig.getEslintConfigPath(), nodeServerLayout.nodeModulesDir());
this.eslintConfigInUse = this.origEslintConfig.withEslintConfigPath(configFileCopy).verify();
}
Expand Down Expand Up @@ -162,8 +161,6 @@ public EslintFilePathPassingFormatterFunc(File projectDir, File nodeModulesDir,

@Override
public String applyWithFile(String unix, File file) throws Exception {
logger.info("formatting String '{}[...]' in file '{}'", lazy(() -> unix.substring(0, Math.min(50, unix.length()))), file);

Map<FormatOption, Object> eslintCallOptions = new HashMap<>();
setConfigToCallOptions(eslintCallOptions);
setFilePathToCallOptions(eslintCallOptions, file);
Expand Down
88 changes: 88 additions & 0 deletions lib/src/main/java/com/diffplug/spotless/npm/NodeApp.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed 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 com.diffplug.spotless.npm;

import java.util.Objects;

import javax.annotation.Nonnull;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class NodeApp {

private static final Logger logger = LoggerFactory.getLogger(NodeApp.class);

private static final TimedLogger timedLogger = TimedLogger.forLogger(logger);

@Nonnull
protected final NodeServerLayout nodeServerLayout;

@Nonnull
protected final NpmConfig npmConfig;

@Nonnull
protected final NpmProcessFactory npmProcessFactory;

@Nonnull
protected final NpmFormatterStepLocations formatterStepLocations;

public NodeApp(@Nonnull NodeServerLayout nodeServerLayout, @Nonnull NpmConfig npmConfig, @Nonnull NpmFormatterStepLocations formatterStepLocations) {
this.nodeServerLayout = Objects.requireNonNull(nodeServerLayout);
this.npmConfig = Objects.requireNonNull(npmConfig);
this.npmProcessFactory = processFactory(formatterStepLocations);
this.formatterStepLocations = Objects.requireNonNull(formatterStepLocations);
}

private static NpmProcessFactory processFactory(NpmFormatterStepLocations formatterStepLocations) {
if (formatterStepLocations.cacheDir() != null) {
logger.info("Caching npm install results in {}.", formatterStepLocations.cacheDir());
return NodeModulesCachingNpmProcessFactory.create(formatterStepLocations.cacheDir());
}
logger.debug("Not caching npm install results.");
return StandardNpmProcessFactory.INSTANCE;
}

boolean needsNpmInstall() {
return !this.nodeServerLayout.isNodeModulesPrepared();
}

boolean needsPrepareNodeAppLayout() {
return !this.nodeServerLayout.isLayoutPrepared();
}

void prepareNodeAppLayout() {
timedLogger.withInfo("Preparing {} for npm step {}.", this.nodeServerLayout, getClass().getName()).run(() -> {
NpmResourceHelper.assertDirectoryExists(nodeServerLayout.nodeModulesDir());
NpmResourceHelper.writeUtf8StringToFile(nodeServerLayout.packageJsonFile(), this.npmConfig.getPackageJsonContent());
if (this.npmConfig.getServeScriptContent() != null) {
NpmResourceHelper.writeUtf8StringToFile(nodeServerLayout.serveJsFile(), this.npmConfig.getServeScriptContent());
} else {
NpmResourceHelper.deleteFileIfExists(nodeServerLayout.serveJsFile());
}
if (this.npmConfig.getNpmrcContent() != null) {
NpmResourceHelper.writeUtf8StringToFile(nodeServerLayout.npmrcFile(), this.npmConfig.getNpmrcContent());
} else {
NpmResourceHelper.deleteFileIfExists(nodeServerLayout.npmrcFile());
}
});
}

void npmInstall() {
timedLogger.withInfo("Installing npm dependencies for {} with {}.", this.nodeServerLayout, this.npmProcessFactory.describe())
.run(() -> npmProcessFactory.createNpmInstallProcess(nodeServerLayout, formatterStepLocations).waitFor());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright 2023 DiffPlug
*
* Licensed 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 com.diffplug.spotless.npm;

import java.io.File;
import java.util.List;
import java.util.Objects;

import javax.annotation.Nonnull;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.diffplug.spotless.ProcessRunner.Result;

public class NodeModulesCachingNpmProcessFactory implements NpmProcessFactory {

private static final Logger logger = LoggerFactory.getLogger(NodeModulesCachingNpmProcessFactory.class);

private static final TimedLogger timedLogger = TimedLogger.forLogger(logger);

private final File cacheDir;

private final ShadowCopy shadowCopy;

private NodeModulesCachingNpmProcessFactory(@Nonnull File cacheDir) {
this.cacheDir = Objects.requireNonNull(cacheDir);
assertDir(cacheDir);
this.shadowCopy = new ShadowCopy(cacheDir);
}

private void assertDir(File cacheDir) {
if (cacheDir.exists() && !cacheDir.isDirectory()) {
throw new IllegalArgumentException("Cache dir must be a directory");
}
if (!cacheDir.exists()) {
if (!cacheDir.mkdirs()) {
throw new IllegalArgumentException("Cache dir could not be created.");
}
}
}

public static NodeModulesCachingNpmProcessFactory create(@Nonnull File cacheDir) {
return new NodeModulesCachingNpmProcessFactory(cacheDir);
}

@Override
public NpmProcess createNpmInstallProcess(NodeServerLayout nodeServerLayout, NpmFormatterStepLocations formatterStepLocations) {
NpmProcess actualNpmInstallProcess = StandardNpmProcessFactory.INSTANCE.createNpmInstallProcess(nodeServerLayout, formatterStepLocations);
return new CachingNmpInstall(actualNpmInstallProcess, nodeServerLayout);
}

@Override
public NpmLongRunningProcess createNpmServeProcess(NodeServerLayout nodeServerLayout, NpmFormatterStepLocations formatterStepLocations) {
return StandardNpmProcessFactory.INSTANCE.createNpmServeProcess(nodeServerLayout, formatterStepLocations);
}

private class CachingNmpInstall implements NpmProcess {

private final NpmProcess actualNpmInstallProcess;
private final NodeServerLayout nodeServerLayout;

public CachingNmpInstall(NpmProcess actualNpmInstallProcess, NodeServerLayout nodeServerLayout) {
this.actualNpmInstallProcess = actualNpmInstallProcess;
this.nodeServerLayout = nodeServerLayout;
}

@Override
public Result waitFor() {
String entryName = entryName();
if (shadowCopy.entryExists(entryName, NodeServerLayout.NODE_MODULES)) {
timedLogger.withInfo("Using cached node_modules for {} from {}", entryName, cacheDir)
.run(() -> shadowCopy.copyEntryInto(entryName(), NodeServerLayout.NODE_MODULES, nodeServerLayout.nodeModulesDir()));
return new CachedResult();
} else {
Result result = timedLogger.withInfo("calling actual npm install {}", actualNpmInstallProcess.describe())
.call(actualNpmInstallProcess::waitFor);
assert result.exitCode() == 0;
storeShadowCopy(entryName);
return result;
}
}

private void storeShadowCopy(String entryName) {
timedLogger.withInfo("Caching node_modules for {} in {}", entryName, cacheDir)
.run(() -> shadowCopy.addEntry(entryName(), new File(nodeServerLayout.nodeModulesDir(), NodeServerLayout.NODE_MODULES)));
}

private String entryName() {
return nodeServerLayout.nodeModulesDir().getName();
}

@Override
public String describe() {
return String.format("Wrapper around [%s] to cache node_modules in [%s]", actualNpmInstallProcess.describe(), cacheDir.getAbsolutePath());
}
}

private class CachedResult extends Result {

public CachedResult() {
super(List.of("(from cache dir " + cacheDir + ")"), 0, new byte[0], new byte[0]);
}
}
}
Loading

0 comments on commit ab288a8

Please sign in to comment.