Skip to content

Commit 8557a52

Browse files
committed
Merge remote-tracking branch 'origin/upstream'
# Conflicts: # testlib/src/main/java/com/diffplug/spotless/StepHarnessBase.java
2 parents a5b7bec + 2e33d3b commit 8557a52

File tree

27 files changed

+351
-46
lines changed

27 files changed

+351
-46
lines changed

.github/workflows/ci.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ jobs:
5757
- kind: npm
5858
jre: 11
5959
os: ubuntu-latest
60+
- kind: shfmt
61+
jre: 11
62+
os: ubuntu-latest
63+
shfmt-version: 3.7.0
6064
runs-on: ${{ matrix.os }}
6165
steps:
6266
- name: Checkout
@@ -79,6 +83,14 @@ jobs:
7983
- name: test npm
8084
if: matrix.kind == 'npm'
8185
run: ./gradlew testNpm
86+
- name: Install shfmt
87+
if: matrix.kind == 'shfmt'
88+
run: |
89+
curl -sSfL "https://github.com/mvdan/sh/releases/download/v${{ matrix.shfmt-version }}/shfmt_v${{ matrix.shfmt-version }}_linux_amd64" -o /usr/local/bin/shfmt
90+
chmod +x /usr/local/bin/shfmt
91+
- name: Test shfmt
92+
if: matrix.kind == 'shfmt'
93+
run: ./gradlew testShfmt
8294
- name: junit result
8395
uses: mikepenz/action-junit-report@v4
8496
if: always() # always run even if the previous step fails

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (
1212
## [Unreleased]
1313
### Added
1414
* `FileSignature.Promised` and `JarState.Promised` to facilitate round-trip serialization for the Gradle configuration cache. ([#1945](https://github.com/diffplug/spotless/pull/1945))
15+
* Respect `.editorconfig` settings for formatting shell via `shfmt` ([#2031](https://github.com/diffplug/spotless/pull/2031))
16+
1517
### Removed
1618
* **BREAKING** Remove `JarState.getMavenCoordinate(String prefix)`. ([#1945](https://github.com/diffplug/spotless/pull/1945))
1719
* **BREAKING** Replace `PipeStepPair` with `FenceStep`. ([#1954](https://github.com/diffplug/spotless/pull/1954))

lib/src/main/java/com/diffplug/spotless/ForeignExe.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2023 DiffPlug
2+
* Copyright 2020-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.
@@ -16,6 +16,7 @@
1616
package com.diffplug.spotless;
1717

1818
import java.io.IOException;
19+
import java.io.Serializable;
1920
import java.nio.charset.Charset;
2021
import java.util.Objects;
2122
import java.util.regex.Matcher;
@@ -31,7 +32,8 @@
3132
* Usage: {@code ForeignExe.nameAndVersion("grep", "2.5.7").confirmVersionAndGetAbsolutePath()}
3233
* will find grep, confirm that it is version 2.5.7, and then return.
3334
*/
34-
public class ForeignExe {
35+
public class ForeignExe implements Serializable {
36+
private static final long serialVersionUID = 1L;
3537
private @Nullable String pathToExe;
3638
private String versionFlag = "--version";
3739
private Pattern versionRegex = Pattern.compile("version (\\S*)");

lib/src/main/java/com/diffplug/spotless/cpp/ClangFormatStep.java

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 DiffPlug
2+
* Copyright 2020-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.
@@ -64,10 +64,10 @@ public ClangFormatStep withPathToExe(String pathToExe) {
6464
}
6565

6666
public FormatterStep create() {
67-
return FormatterStep.createLazy(name(), this::createState, State::toFunc);
67+
return FormatterStep.createLazy(name(), this::createState, RoundtripState::state, State::toFunc);
6868
}
6969

70-
private State createState() throws IOException, InterruptedException {
70+
private RoundtripState createState() throws IOException, InterruptedException {
7171
String howToInstall = "" +
7272
"You can download clang-format from https://releases.llvm.org and " +
7373
"then point Spotless to it with {@code pathToExe('/path/to/clang-format')} " +
@@ -82,7 +82,25 @@ private State createState() throws IOException, InterruptedException {
8282
.fixWrongVersion(
8383
"You can tell Spotless to use the version you already have with {@code clangFormat('{versionFound}')}" +
8484
"or you can download the currently specified version, {version}.\n" + howToInstall);
85-
return new State(this, exe);
85+
return new RoundtripState(this, exe);
86+
}
87+
88+
static class RoundtripState implements Serializable {
89+
private static final long serialVersionUID = 1L;
90+
91+
final String version;
92+
final @Nullable String style;
93+
final ForeignExe exe;
94+
95+
RoundtripState(ClangFormatStep step, ForeignExe exe) {
96+
this.version = step.version;
97+
this.style = step.style;
98+
this.exe = exe;
99+
}
100+
101+
private State state() {
102+
return new State(version, style, exe);
103+
}
86104
}
87105

88106
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
@@ -95,9 +113,9 @@ static class State implements Serializable {
95113
// used for executing
96114
private transient @Nullable List<String> args;
97115

98-
State(ClangFormatStep step, ForeignExe pathToExe) {
99-
this.version = step.version;
100-
this.style = step.style;
116+
State(String version, @Nullable String style, ForeignExe pathToExe) {
117+
this.version = version;
118+
this.style = style;
101119
this.exe = Objects.requireNonNull(pathToExe);
102120
}
103121

lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,16 +58,32 @@ public BufStep withPathToExe(String pathToExe) {
5858
}
5959

6060
public FormatterStep create() {
61-
return FormatterStep.createLazy(name(), this::createState, State::toFunc);
61+
return FormatterStep.createLazy(name(), this::createRoundtrip, RoundtripState::state, State::toFunc);
6262
}
6363

64-
private State createState() {
64+
private RoundtripState createRoundtrip() {
6565
String instructions = "https://docs.buf.build/installation";
6666
ForeignExe exe = ForeignExe.nameAndVersion("buf", version)
6767
.pathToExe(pathToExe)
6868
.versionRegex(Pattern.compile("(\\S*)"))
6969
.fixCantFind("Try following the instructions at " + instructions + ", or else tell Spotless where it is with {@code buf().pathToExe('path/to/executable')}");
70-
return new State(this, exe);
70+
return new RoundtripState(version, exe);
71+
}
72+
73+
private static class RoundtripState implements Serializable {
74+
private static final long serialVersionUID = 1L;
75+
76+
final String version;
77+
final ForeignExe exe;
78+
79+
RoundtripState(String version, ForeignExe exe) {
80+
this.version = version;
81+
this.exe = exe;
82+
}
83+
84+
private State state() {
85+
return new State(version, exe);
86+
}
7187
}
7288

7389
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
@@ -80,9 +96,9 @@ static class State implements Serializable {
8096
private final transient ForeignExe exe;
8197
private transient String exeAbsPath;
8298

83-
State(BufStep step, ForeignExe exe) {
84-
this.version = step.version;
85-
this.exe = Objects.requireNonNull(exe);
99+
State(String version, ForeignExe exeAbsPath) {
100+
this.version = version;
101+
this.exe = Objects.requireNonNull(exeAbsPath);
86102
}
87103

88104
String format(ProcessRunner runner, String input, File file) throws IOException, InterruptedException {

lib/src/main/java/com/diffplug/spotless/python/BlackStep.java

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2020-2022 DiffPlug
2+
* Copyright 2020-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.
@@ -56,17 +56,33 @@ public BlackStep withPathToExe(String pathToExe) {
5656
}
5757

5858
public FormatterStep create() {
59-
return FormatterStep.createLazy(name(), this::createState, State::toFunc);
59+
return FormatterStep.createLazy(name(), this::createState, RoundtripState::state, State::toFunc);
6060
}
6161

62-
private State createState() throws IOException, InterruptedException {
62+
private RoundtripState createState() {
6363
String trackingIssue = "\n github issue to handle this better: https://github.com/diffplug/spotless/issues/674";
6464
ForeignExe exeAbsPath = ForeignExe.nameAndVersion("black", version)
6565
.pathToExe(pathToExe)
6666
.versionRegex(Pattern.compile("(?:black, version|black,|version) (\\S*)"))
6767
.fixCantFind("Try running {@code pip install black=={version}}, or else tell Spotless where it is with {@code black().pathToExe('path/to/executable')}" + trackingIssue)
6868
.fixWrongVersion("Try running {@code pip install --force-reinstall black=={version}}, or else specify {@code black('{versionFound}')} to Spotless" + trackingIssue);
69-
return new State(this, exeAbsPath);
69+
return new RoundtripState(version, exeAbsPath);
70+
}
71+
72+
static class RoundtripState implements Serializable {
73+
private static final long serialVersionUID = 1L;
74+
75+
final String version;
76+
final ForeignExe exe;
77+
78+
RoundtripState(String version, ForeignExe exe) {
79+
this.version = version;
80+
this.exe = exe;
81+
}
82+
83+
private State state() {
84+
return new State(version, exe);
85+
}
7086
}
7187

7288
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
@@ -78,8 +94,8 @@ static class State implements Serializable {
7894
// used for executing
7995
private transient @Nullable String[] args;
8096

81-
State(BlackStep step, ForeignExe exeAbsPath) {
82-
this.version = step.version;
97+
State(String version, ForeignExe exeAbsPath) {
98+
this.version = version;
8399
this.exe = Objects.requireNonNull(exeAbsPath);
84100
}
85101

lib/src/main/java/com/diffplug/spotless/shell/ShfmtStep.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import java.util.List;
2323
import java.util.Objects;
2424
import java.util.regex.Pattern;
25+
import java.util.stream.Collectors;
26+
import java.util.stream.Stream;
2527

2628
import javax.annotation.Nullable;
2729

@@ -72,7 +74,7 @@ private State createState() throws IOException, InterruptedException {
7274
"\n github issue to handle this better: https://github.com/diffplug/spotless/issues/673";
7375
final ForeignExe exe = ForeignExe.nameAndVersion("shfmt", version)
7476
.pathToExe(pathToExe)
75-
.versionRegex(Pattern.compile("(\\S*)"))
77+
.versionRegex(Pattern.compile("([\\d.]+)"))
7678
.fixCantFind(howToInstall)
7779
.fixWrongVersion(
7880
"You can tell Spotless to use the version you already have with {@code shfmt('{versionFound}')}" +
@@ -83,9 +85,11 @@ private State createState() throws IOException, InterruptedException {
8385
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
8486
static class State implements Serializable {
8587
private static final long serialVersionUID = -1825662356883926318L;
88+
8689
// used for up-to-date checks and caching
8790
final String version;
8891
final transient ForeignExe exe;
92+
8993
// used for executing
9094
private transient @Nullable List<String> args;
9195

@@ -96,10 +100,16 @@ static class State implements Serializable {
96100

97101
String format(ProcessRunner runner, String input, File file) throws IOException, InterruptedException {
98102
if (args == null) {
99-
args = List.of(exe.confirmVersionAndGetAbsolutePath(), "-i", "2", "-ci");
103+
// args will be reused during a single Spotless task execution,
104+
// so this "prefix" is being "cached" for each Spotless format with shfmt.
105+
args = List.of(exe.confirmVersionAndGetAbsolutePath(), "--filename");
100106
}
101107

102-
return runner.exec(input.getBytes(StandardCharsets.UTF_8), args).assertExitZero(StandardCharsets.UTF_8);
108+
// This will ensure that the next file name is retrieved on every format
109+
final List<String> finalArgs = Stream.concat(args.stream(), Stream.of(file.getAbsolutePath()))
110+
.collect(Collectors.toList());
111+
112+
return runner.exec(input.getBytes(StandardCharsets.UTF_8), finalArgs).assertExitZero(StandardCharsets.UTF_8);
103113
}
104114

105115
FormatterFunc.Closeable toFunc() {

plugin-gradle/CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ 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

9+
### Added
10+
* Respect `.editorconfig` settings for formatting shell via `shfmt` ([#2031](https://github.com/diffplug/spotless/pull/2031))
11+
912
## [6.25.0] - 2024-01-23
1013
### Added
1114
* Maven / Gradle - Support for formatting Java Docs for the Palantir formatter ([#2009](https://github.com/diffplug/spotless/pull/2009))

plugin-gradle/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@ spotless {
992992
```gradle
993993
spotless {
994994
shell {
995-
target 'scripts/**/*.sh' // default: '*.sh'
995+
target 'scripts/**/*.sh' // default: '**/*.sh'
996996
997997
shfmt() // has its own section below
998998
}
@@ -1003,11 +1003,15 @@ spotless {
10031003
10041004
[homepage](https://github.com/mvdan/sh). [changelog](https://github.com/mvdan/sh/blob/master/CHANGELOG.md).
10051005
1006+
When formatting shell scripts via `shfmt`, configure `shfmt` settings via `.editorconfig`.
1007+
Refer to the `shfmt` [man page](https://github.com/mvdan/sh/blob/master/cmd/shfmt/shfmt.1.scd) for `.editorconfig` settings.
1008+
10061009
```gradle
10071010
shfmt('3.7.0') // version is optional
10081011
10091012
// if shfmt is not on your path, you must specify its location manually
10101013
shfmt().pathToExe('/opt/homebrew/bin/shfmt')
1014+
10111015
// Spotless always checks the version of the shfmt it is using
10121016
// and will fail with an error if it does not match the expected version
10131017
// (whether manually specified or default). If there is a problem, Spotless

plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ShellExtension.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import com.diffplug.spotless.shell.ShfmtStep;
2424

2525
public class ShellExtension extends FormatExtension {
26-
private static final String SHELL_FILE_EXTENSION = "*.sh";
26+
private static final String SHELL_FILE_EXTENSION = "**/*.sh";
2727

2828
static final String NAME = "shell";
2929

0 commit comments

Comments
 (0)