Skip to content

Commit 7da4090

Browse files
committed
implement buf format gradle step
1 parent 21b089f commit 7da4090

File tree

12 files changed

+331
-0
lines changed

12 files changed

+331
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ extra('wtp.EclipseWtpFormatterStep') +'{{yes}} | {{yes}}
114114
| [`npm.PrettierFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/PrettierFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
115115
| [`npm.TsFmtFormatterStep`](lib/src/main/java/com/diffplug/spotless/npm/TsFmtFormatterStep.java) | :+1: | :+1: | :white_large_square: | :white_large_square: |
116116
| [`pom.SortPomStepStep`](lib/src/main/java/com/diffplug/spotless/pom/SortPomStepStep.java) | :white_large_square: | :+1: | :white_large_square: | :white_large_square: |
117+
| [`protobuf.BufStep`](lib/src/main/java/com/diffplug/spotless/protobuf/BufStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
117118
| [`python.BlackStep`](lib/src/main/java/com/diffplug/spotless/python/BlackStep.java) | :+1: | :white_large_square: | :white_large_square: | :white_large_square: |
118119
| [`scala.ScalaFmtStep`](lib/src/main/java/com/diffplug/spotless/scala/ScalaFmtStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
119120
| [`sql.DBeaverSQLFormatterStep`](lib/src/main/java/com/diffplug/spotless/sql/DBeaverSQLFormatterStep.java) | :+1: | :+1: | :+1: | :white_large_square: |
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.protobuf;
17+
18+
import java.io.File;
19+
import java.io.IOException;
20+
import java.io.Serializable;
21+
import java.nio.charset.StandardCharsets;
22+
import java.util.Arrays;
23+
import java.util.List;
24+
import java.util.regex.Pattern;
25+
26+
import javax.annotation.Nullable;
27+
28+
import com.diffplug.spotless.ForeignExe;
29+
import com.diffplug.spotless.FormatterFunc;
30+
import com.diffplug.spotless.FormatterStep;
31+
import com.diffplug.spotless.ProcessRunner;
32+
33+
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
34+
35+
public class BufStep {
36+
public static String name() {
37+
return "buf";
38+
}
39+
40+
public static String defaultVersion() {
41+
return "1.4.0";
42+
}
43+
44+
private final String version;
45+
private final @Nullable String pathToExe;
46+
47+
private BufStep(String version, @Nullable String pathToExe) {
48+
this.version = version;
49+
this.pathToExe = pathToExe;
50+
}
51+
52+
public static BufStep withVersion(String version) {
53+
return new BufStep(version, null);
54+
}
55+
56+
public BufStep withPathToExe(String pathToExe) {
57+
return new BufStep(version, pathToExe);
58+
}
59+
60+
public FormatterStep create() {
61+
return FormatterStep.createLazy(name(), this::createState, State::toFunc);
62+
}
63+
64+
private State createState() throws IOException, InterruptedException {
65+
String instructions = "https://docs.buf.build/installation";
66+
String exeAbsPath = ForeignExe.nameAndVersion("buf", version)
67+
.pathToExe(pathToExe)
68+
.versionRegex(Pattern.compile("(\\S*)"))
69+
.fixCantFind("Try following the instructions at " + instructions + ", or else tell Spotless where it is with {@code buf().pathToExe('path/to/executable')}")
70+
.confirmVersionAndGetAbsolutePath();
71+
return new State(this, exeAbsPath);
72+
}
73+
74+
@SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED")
75+
static class State implements Serializable {
76+
private static final long serialVersionUID = -1825662356883926318L;
77+
// used for up-to-date checks and caching
78+
final String version;
79+
// used for executing
80+
final transient List<String> args;
81+
82+
State(BufStep step, String exeAbsPath) {
83+
this.version = step.version;
84+
this.args = Arrays.asList(exeAbsPath, "format");
85+
}
86+
87+
String format(ProcessRunner runner, String input, File file) throws IOException, InterruptedException {
88+
String[] processArgs = args.toArray(new String[args.size() + 1]);
89+
// add an argument to the end
90+
processArgs[args.size()] = file.getAbsolutePath();
91+
return runner.exec(input.getBytes(StandardCharsets.UTF_8), processArgs).assertExitZero(StandardCharsets.UTF_8);
92+
}
93+
94+
FormatterFunc.Closeable toFunc() {
95+
ProcessRunner runner = new ProcessRunner();
96+
return FormatterFunc.Closeable.of(runner, this::format);
97+
}
98+
}
99+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.spotless.protobuf;
17+
18+
public class ProtobufConstants {
19+
public static final String LICENSE_HEADER_DELIMITER = "syntax";
20+
}

plugin-gradle/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Spotless supports all of Gradle's built-in performance features (incremental bui
6565
- [Kotlin](#kotlin) ([ktfmt](#ktfmt), [ktlint](#ktlint), [diktat](#diktat), [prettier](#prettier))
6666
- [Scala](#scala) ([scalafmt](#scalafmt))
6767
- [C/C++](#cc) ([clang-format](#clang-format), [eclipse cdt](#eclipse-cdt))
68+
- [Protobuf](#protobuf) ([buf](#buf), [clang-format](#clang-format))
6869
- [Python](#python) ([black](#black))
6970
- [FreshMark](#freshmark) aka markdown
7071
- [Antlr4](#antlr4) ([antlr4formatter](#antlr4formatter))
@@ -457,6 +458,23 @@ black().pathToExe('C:/myuser/.pyenv/versions/3.8.0/scripts/black.exe')
457458

458459
<a name="applying-freshmark-to-markdown-files"></a>
459460

461+
## Protobuf
462+
463+
### buf
464+
465+
`com.diffplug.gradle.spotless.ProtobufExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.6.0/com/diffplug/gradle/spotless/ProtobufExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/ProtobufExtension.java)
466+
467+
```gradle
468+
spotless {
469+
protobuf {
470+
// by default the target is every '.proto' file in the project
471+
buf()
472+
473+
licenseHeader '/* (C) $YEAR */' // or licenseHeaderFile
474+
}
475+
}
476+
```
477+
460478
## FreshMark
461479

462480
`com.diffplug.gradle.spotless.FreshMarkExtension` [javadoc](https://javadoc.io/doc/com.diffplug.spotless/spotless-plugin-gradle/6.6.0/com/diffplug/gradle/spotless/FreshMarkExtension.html), [code](https://github.com/diffplug/spotless/blob/main/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FreshMarkExtension.java)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.gradle.spotless;
17+
18+
import static com.diffplug.spotless.protobuf.ProtobufConstants.LICENSE_HEADER_DELIMITER;
19+
20+
import java.util.Objects;
21+
22+
import javax.inject.Inject;
23+
24+
import com.diffplug.spotless.FormatterStep;
25+
import com.diffplug.spotless.protobuf.BufStep;
26+
27+
public class ProtobufExtension extends FormatExtension implements HasBuiltinDelimiterForLicense {
28+
static final String NAME = "protobuf";
29+
30+
@Inject
31+
public ProtobufExtension(SpotlessExtension spotless) {
32+
super(spotless);
33+
}
34+
35+
@Override
36+
public LicenseHeaderConfig licenseHeader(String licenseHeader) {
37+
return licenseHeader(licenseHeader, LICENSE_HEADER_DELIMITER);
38+
}
39+
40+
@Override
41+
public LicenseHeaderConfig licenseHeaderFile(Object licenseHeaderFile) {
42+
return licenseHeaderFile(licenseHeaderFile, LICENSE_HEADER_DELIMITER);
43+
}
44+
45+
/** Adds the specified version of <a href="https://github.com/pinterest/ktlint">ktlint</a>. */
46+
public BufFormatExtension buf(String version) {
47+
Objects.requireNonNull(version);
48+
return new BufFormatExtension(version);
49+
}
50+
51+
public BufFormatExtension buf() {
52+
return buf(BufStep.defaultVersion());
53+
}
54+
55+
public class BufFormatExtension {
56+
private final String version;
57+
58+
BufFormatExtension(String version) {
59+
this.version = version;
60+
addStep(createStep());
61+
}
62+
63+
private FormatterStep createStep() {
64+
return BufStep.withVersion(version).create();
65+
}
66+
}
67+
68+
/** If the user hasn't specified files, assume all protobuf files should be checked. */
69+
@Override
70+
protected void setupTask(SpotlessTask task) {
71+
if (target == null) {
72+
target = parseTarget("**/*.proto");
73+
}
74+
super.setupTask(task);
75+
}
76+
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,12 @@ public void json(Action<JsonExtension> closure) {
188188
format(JsonExtension.NAME, JsonExtension.class, closure);
189189
}
190190

191+
/** Configures the special protobuf-specific extension. */
192+
public void protobuf(Action<ProtobufExtension> closure) {
193+
requireNonNull(closure);
194+
format(ProtobufExtension.NAME, ProtobufExtension.class, closure);
195+
}
196+
191197
/** Configures a custom extension. */
192198
public void format(String name, Action<FormatExtension> closure) {
193199
requireNonNull(name, "name");
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/*
2+
* Copyright 2022 DiffPlug
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.diffplug.gradle.spotless;
17+
18+
import java.io.IOException;
19+
20+
import org.junit.jupiter.api.Test;
21+
22+
class BufIntegrationTest extends GradleIntegrationHarness {
23+
@Test
24+
void buf() throws IOException {
25+
setFile("build.gradle").toLines(
26+
"plugins {",
27+
" id 'com.diffplug.spotless'",
28+
"}",
29+
"spotless {",
30+
" protobuf {",
31+
" buf()",
32+
" }",
33+
"}");
34+
setFile("buf.proto").toResource("protobuf/buf/buf.proto");
35+
gradleRunner().withArguments("spotlessApply").build();
36+
assertFile("buf.proto").sameAsResource("protobuf/buf/buf.proto.clean");
37+
}
38+
39+
@Test
40+
void bufWithLicense() throws IOException {
41+
setFile("build.gradle").toLines(
42+
"plugins {",
43+
" id 'com.diffplug.spotless'",
44+
"}",
45+
"spotless {",
46+
" protobuf {",
47+
" buf()",
48+
" licenseHeader '/* (C) 2022 */'",
49+
" }",
50+
"}");
51+
setFile("license.proto").toResource("protobuf/buf/license.proto");
52+
gradleRunner().withArguments("spotlessApply").build();
53+
assertFile("license.proto").sameAsResource("protobuf/buf/license.proto.clean");
54+
}
55+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
message Testing {
2+
required string field1 = 1;
3+
required int32 field2 = 2;
4+
optional string field3 = 3;
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
message Testing {
2+
required string field1 = 1;
3+
required int32 field2 = 2;
4+
optional string field3 = 3;
5+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
syntax = "proto3";
2+
3+
message Testing {
4+
required string field1 = 1;
5+
required int32 field2 = 2;
6+
optional string field3 = 3;
7+
}

0 commit comments

Comments
 (0)