Skip to content

Commit 7f21945

Browse files
Refactor formatting tasks into separate plugin and address maintainer feedback
1 parent e886bca commit 7f21945

4 files changed

Lines changed: 106 additions & 119 deletions

File tree

build-logic/plugins/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ gradlePlugin {
6262
id = 'org.apache.grails.gradle.grails-code-style'
6363
implementationClass = 'org.apache.grails.buildsrc.GrailsCodeStylePlugin'
6464
}
65+
register('grailsFormat') {
66+
id = 'org.apache.grails.gradle.grails-format'
67+
implementationClass = 'org.apache.grails.buildsrc.GrailsFormatPlugin'
68+
}
6569
register('groovydocEnhancer') {
6670
id = 'org.apache.grails.buildsrc.groovydoc-enhancer'
6771
implementationClass = 'org.apache.grails.buildsrc.GroovydocEnhancerPlugin'

build-logic/plugins/src/main/groovy/org/apache/grails/buildsrc/GrailsCodeStylePlugin.groovy

Lines changed: 0 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,12 @@ import groovy.transform.CompileStatic
2525

2626
import org.gradle.api.Plugin
2727
import org.gradle.api.Project
28-
import org.gradle.api.file.Directory
2928
import org.gradle.api.plugins.quality.Checkstyle
3029
import org.gradle.api.plugins.quality.CheckstyleExtension
3130
import org.gradle.api.plugins.quality.CheckstylePlugin
3231
import org.gradle.api.plugins.quality.CodeNarc
3332
import org.gradle.api.plugins.quality.CodeNarcExtension
3433
import org.gradle.api.plugins.quality.CodeNarcPlugin
35-
import org.gradle.api.tasks.Copy
36-
import org.gradle.process.ExecSpec
37-
import org.apache.tools.ant.taskdefs.condition.Os
3834

3935
@CompileStatic
4036
class GrailsCodeStylePlugin implements Plugin<Project> {
@@ -52,18 +48,11 @@ class GrailsCodeStylePlugin implements Plugin<Project> {
5248
void apply(Project project) {
5349
initExtension(project)
5450
configureCodeStyle(project)
55-
registerFormattingTasks(project)
56-
doNotApplyStylingToTests(project)
5751
}
5852

5953
private static void initExtension(Project project) {
6054
def gce = project.extensions.create('grailsCodeStyle', GrailsCodeStyleExtension)
6155

62-
// Unfortunately, the codenarc plugin is still using a non-lazy property.
63-
// Rather than rewrite the plugin to use afterEvaluate,
64-
// this plugin uses properties to override the configuration location by default
65-
66-
6756
gce.checkstyleDirectory.set(project.provider {
6857
def directory = project.hasProperty(CHECKSTYLE_DIR_PROPERTY) ?
6958
project.rootProject.layout.projectDirectory.dir(project.property(CHECKSTYLE_DIR_PROPERTY) as String) :
@@ -111,25 +100,6 @@ class GrailsCodeStylePlugin implements Plugin<Project> {
111100
}
112101
}
113102

114-
private static void doNotApplyStylingToTests(Project project) {
115-
if (project.tasks.names.contains('checkstyleTest')) {
116-
project.tasks.named('checkstyleTest') {
117-
it.enabled = false // Do not check test sources at this time
118-
}
119-
}
120-
121-
project.afterEvaluate {
122-
// Do not check test sources at this time
123-
['codenarcIntegrationTest', 'codenarcTest'].each { testTaskName ->
124-
if (project.tasks.names.contains(testTaskName)) {
125-
project.tasks.named(testTaskName) {
126-
it.enabled = false
127-
}
128-
}
129-
}
130-
}
131-
}
132-
133103
private static void configureCodeStyle(Project project) {
134104
configureCheckstyle(project)
135105
configureCodenarc(project)
@@ -197,88 +167,4 @@ class GrailsCodeStylePlugin implements Plugin<Project> {
197167
)
198168
}
199169
}
200-
201-
private static void registerFormattingTasks(Project project) {
202-
if (project == project.rootProject) {
203-
project.tasks.register('installGitHooks', Copy) {
204-
it.group = 'verification'
205-
it.description = 'Installs the git pre-commit hook for automatic code formatting'
206-
it.from(project.rootProject.layout.projectDirectory.file('etc/hooks/pre-commit'))
207-
it.into(project.rootProject.layout.projectDirectory.dir('.git/hooks'))
208-
it.fileMode = 0755
209-
}
210-
}
211-
212-
project.tasks.register('formatCode') {
213-
it.group = 'verification'
214-
it.description = 'Formats Java and Groovy source files using the IntelliJ command line formatter'
215-
216-
it.doLast {
217-
String ideaHome = (project.findProperty('idea.home') ?: System.getenv('IDEA_HOME')) as String
218-
String executable = Os.isFamily(Os.FAMILY_WINDOWS) ? 'format.bat' : 'format.sh'
219-
File formatExec = null
220-
221-
if (ideaHome) {
222-
formatExec = new File(ideaHome, "bin/$executable")
223-
} else {
224-
// Try common paths on macOS
225-
if (Os.isFamily(Os.FAMILY_MAC)) {
226-
def commonPaths = [
227-
"/Applications/IntelliJ IDEA.app/Contents/bin/$executable",
228-
"/Applications/IntelliJ IDEA CE.app/Contents/bin/$executable"
229-
]
230-
for (path in commonPaths) {
231-
File f = new File(path)
232-
if (f.exists()) {
233-
formatExec = f
234-
break
235-
}
236-
}
237-
}
238-
239-
if (formatExec == null && !Os.isFamily(Os.FAMILY_WINDOWS)) {
240-
// On Linux/Mac, try to find 'idea' in PATH
241-
try {
242-
def out = new ByteArrayOutputStream()
243-
project.exec { ExecSpec exec ->
244-
exec.commandLine 'which', 'idea'
245-
exec.standardOutput = out
246-
exec.ignoreExitValue = true
247-
}
248-
def path = out.toString().trim()
249-
if (path) {
250-
formatExec = new File(new File(path).parentFile, executable)
251-
}
252-
} catch (Exception ignored) { }
253-
}
254-
}
255-
256-
if (formatExec == null || !formatExec.exists()) {
257-
project.logger.error("IntelliJ formatter executable not found.")
258-
project.logger.error("Please set 'idea.home' property or IDEA_HOME environment variable to your IntelliJ installation directory.")
259-
project.logger.error("Example: ./gradlew formatCode -Pidea.home=/Applications/IntelliJ\\ IDEA.app/Contents")
260-
throw new RuntimeException("IntelliJ formatter executable not found.")
261-
}
262-
263-
def filesToFormat = project.findProperty('formatFiles')
264-
def settingsFile = project.rootProject.file('.idea/codeStyles/Project.xml')
265-
266-
if (!settingsFile.exists()) {
267-
throw new RuntimeException("IntelliJ code style settings not found at ${settingsFile.absolutePath}")
268-
}
269-
270-
project.exec { ExecSpec exec ->
271-
exec.commandLine formatExec.absolutePath
272-
exec.args '-s', settingsFile.absolutePath
273-
exec.args '-mask', '*.java,*.groovy'
274-
exec.args '-r'
275-
if (filesToFormat) {
276-
exec.args((filesToFormat.toString()).split(','))
277-
} else {
278-
exec.args project.projectDir.absolutePath
279-
}
280-
}
281-
}
282-
}
283-
}
284170
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* https://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.grails.buildsrc
20+
21+
import groovy.transform.CompileStatic
22+
import org.gradle.api.Plugin
23+
import org.gradle.api.Project
24+
import org.gradle.api.tasks.Copy
25+
import org.gradle.process.ExecSpec
26+
import org.gradle.process.ExecOperations
27+
import javax.inject.Inject
28+
import org.apache.tools.ant.taskdefs.condition.Os
29+
30+
@CompileStatic
31+
class GrailsFormatPlugin implements Plugin<Project> {
32+
33+
@Override
34+
void apply(Project project) {
35+
registerFormattingTasks(project)
36+
}
37+
38+
private static void registerFormattingTasks(Project project) {
39+
if (project == project.rootProject) {
40+
project.tasks.register('installGitHooks', Copy) {
41+
it.group = 'verification'
42+
it.description = 'Installs the git pre-commit hook for automatic code formatting'
43+
it.from(project.rootProject.layout.projectDirectory.file('etc/hooks/pre-commit'))
44+
it.into(project.rootProject.layout.projectDirectory.dir('.git/hooks'))
45+
it.fileMode = 0755
46+
}
47+
}
48+
49+
ExecOperationsSupport execSupport = project.objects.newInstance(ExecOperationsSupport)
50+
def ideaExecProvider = project.providers.gradleProperty('idea.exec')
51+
.orElse(Os.isFamily(Os.FAMILY_WINDOWS) ? 'format.bat' : 'idea')
52+
def formatFilesProvider = project.providers.gradleProperty('formatFiles')
53+
def rootProjectDir = project.rootProject.projectDir
54+
def projectDir = project.projectDir
55+
56+
project.tasks.register('formatCode') { task ->
57+
task.group = 'verification'
58+
task.description = 'Formats Java and Groovy source files using the IntelliJ command line formatter'
59+
60+
task.doLast {
61+
String ideaExec = ideaExecProvider.get()
62+
def filesToFormat = formatFilesProvider.getOrNull()
63+
def settingsFile = new File(rootProjectDir, '.idea/codeStyles/Project.xml')
64+
65+
if (!settingsFile.exists()) {
66+
throw new RuntimeException("IntelliJ code style settings not found at ${settingsFile.absolutePath}")
67+
}
68+
69+
try {
70+
execSupport.execOperations.exec { ExecSpec exec ->
71+
exec.commandLine ideaExec
72+
if (ideaExec == 'idea') {
73+
exec.args 'format'
74+
}
75+
exec.args '-s', settingsFile.absolutePath
76+
exec.args '-mask', '*.java,*.groovy'
77+
exec.args '-r'
78+
if (filesToFormat) {
79+
exec.args((filesToFormat.toString()).split(','))
80+
} else {
81+
exec.args projectDir.absolutePath
82+
}
83+
}
84+
} catch (Exception e) {
85+
task.logger.error("IntelliJ formatter failed to execute.")
86+
task.logger.error("Please ensure IntelliJ command line tools are installed and available on your PATH.")
87+
task.logger.error("See: https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html")
88+
throw new RuntimeException("IntelliJ formatter failed. See logs for details.", e)
89+
}
90+
}
91+
}
92+
}
93+
}
94+
95+
interface ExecOperationsSupport {
96+
@Inject
97+
ExecOperations getExecOperations()
98+
}

build.gradle

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
plugins {
19-
id 'org.apache.grails.gradle.grails-code-style'
19+
id 'org.apache.grails.gradle.grails-format'
2020
}
2121

2222
import java.time.Instant
@@ -31,10 +31,9 @@ import org.apache.tools.ant.taskdefs.condition.Os
3131
ext {
3232
isReproducibleBuild = System.getenv("SOURCE_DATE_EPOCH") != null
3333
buildInstant = java.util.Optional.ofNullable(System.getenv("SOURCE_DATE_EPOCH"))
34-
.filter { s -> !s.isEmpty() }
35-
.map { s -> Long.parseLong(s as String) }
36-
.map { l -> Instant.ofEpochSecond(l as Long) }
37-
.orElseGet { Instant.now() }
34+
.map(Long::parseLong)
35+
.map(Instant::ofEpochSecond)
36+
.orElseGet(Instant::now)
3837
formattedBuildDate = System.getenv("SOURCE_DATE_EPOCH") ?
3938
DateTimeFormatter.ISO_INSTANT.format(buildInstant) :
4039
DateTimeFormatter.ISO_DATE.format(LocalDate.ofInstant(buildInstant as Instant, ZoneOffset.UTC))

0 commit comments

Comments
 (0)