Skip to content

Commit 5251fb7

Browse files
feat: add IntelliJ-based code formatting and optional git hook setup (#15535)
Introduce a dedicated Grails format plugin and pre-commit hook support while keeping test style checks disabled per project policy and preserving existing CodeNarc configuration behavior. Made-with: Cursor
1 parent 3cf7d70 commit 5251fb7

5 files changed

Lines changed: 150 additions & 4 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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ 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
@@ -58,8 +57,6 @@ class GrailsCodeStylePlugin implements Plugin<Project> {
5857
// Unfortunately, the codenarc plugin is still using a non-lazy property.
5958
// Rather than rewrite the plugin to use afterEvaluate,
6059
// this plugin uses properties to override the configuration location by default
61-
62-
6360
gce.checkstyleDirectory.set(project.provider {
6461
def directory = project.hasProperty(CHECKSTYLE_DIR_PROPERTY) ?
6562
project.rootProject.layout.projectDirectory.dir(project.property(CHECKSTYLE_DIR_PROPERTY) as String) :
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
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+
registerGitHooks(project)
36+
registerFormattingTasks(project)
37+
}
38+
39+
private static void registerGitHooks(Project project) {
40+
if (project == project.rootProject) {
41+
project.tasks.register('installGitHooks', Copy) {
42+
it.group = 'verification'
43+
it.description = 'Installs the git pre-commit hook for automatic code formatting'
44+
it.from(project.rootProject.layout.projectDirectory.file('etc/hooks/pre-commit'))
45+
it.into(project.rootProject.layout.projectDirectory.dir('.git/hooks'))
46+
it.fileMode = 0755
47+
}
48+
}
49+
}
50+
51+
private static void registerFormattingTasks(Project project) {
52+
ExecOperationsSupport execSupport = project.objects.newInstance(ExecOperationsSupport)
53+
def ideaExecProvider = project.providers.gradleProperty('idea.exec')
54+
.orElse(Os.isFamily(Os.FAMILY_WINDOWS) ? 'format.bat' : 'idea')
55+
def formatFilesProvider = project.providers.gradleProperty('formatFiles')
56+
def rootProjectDir = project.rootProject.projectDir
57+
def projectDir = project.projectDir
58+
59+
project.tasks.register('formatCode') { task ->
60+
task.group = 'verification'
61+
task.description = 'Formats Java and Groovy source files using the IntelliJ command line formatter'
62+
63+
task.doLast {
64+
String ideaExec = ideaExecProvider.get()
65+
def filesToFormat = formatFilesProvider.getOrNull()
66+
def settingsFile = new File(rootProjectDir, '.idea/codeStyles/Project.xml')
67+
68+
if (!settingsFile.exists()) {
69+
throw new RuntimeException("IntelliJ code style settings not found at ${settingsFile.absolutePath}")
70+
}
71+
72+
try {
73+
execSupport.execOperations.exec { ExecSpec exec ->
74+
exec.commandLine ideaExec
75+
if (ideaExec == 'idea') {
76+
exec.args 'format'
77+
}
78+
exec.args '-s', settingsFile.absolutePath
79+
exec.args '-mask', '*.java,*.groovy'
80+
exec.args '-r'
81+
if (filesToFormat) {
82+
exec.args((filesToFormat.toString()).split(','))
83+
} else {
84+
exec.args projectDir.absolutePath
85+
}
86+
}
87+
} catch (Exception e) {
88+
task.logger.error("IntelliJ formatter failed to execute.")
89+
task.logger.error("Please ensure IntelliJ command line tools are installed and available on your PATH.")
90+
task.logger.error("See: https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html")
91+
throw new RuntimeException("IntelliJ formatter failed. See logs for details.", e)
92+
}
93+
}
94+
}
95+
}
96+
}
97+
98+
interface ExecOperationsSupport {
99+
@Inject
100+
ExecOperations getExecOperations()
101+
}

build.gradle

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
* limitations under the License.
1616
*/
1717

18+
plugins {
19+
id 'org.apache.grails.gradle.grails-format'
20+
}
21+
1822
import java.time.Instant
1923
import java.time.LocalDate
2024
import java.time.ZoneOffset
@@ -27,7 +31,6 @@ import org.apache.tools.ant.taskdefs.condition.Os
2731
ext {
2832
isReproducibleBuild = System.getenv("SOURCE_DATE_EPOCH") != null
2933
buildInstant = java.util.Optional.ofNullable(System.getenv("SOURCE_DATE_EPOCH"))
30-
.filter(s -> !s.isEmpty())
3134
.map(Long::parseLong)
3235
.map(Instant::ofEpochSecond)
3336
.orElseGet(Instant::now)

etc/hooks/pre-commit

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/bin/bash
2+
#
3+
# Licensed to the Apache Software Foundation (ASF) under one
4+
# or more contributor license agreements. See the NOTICE file
5+
# distributed with this work for additional information
6+
# regarding copyright ownership. The ASF licenses this file
7+
# to you under the Apache License, Version 2.0 (the
8+
# "License"); you may not use this file except in compliance
9+
# with the License. You may obtain a copy of the License at
10+
#
11+
# https://www.apache.org/licenses/LICENSE-2.0
12+
#
13+
# Unless required by applicable law or agreed to in writing,
14+
# software distributed under the License is distributed on an
15+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16+
# KIND, either express or implied. See the License for the
17+
# specific language governing permissions and limitations
18+
# under the License.
19+
#
20+
21+
set -e
22+
23+
# Get staged files that are Groovy or Java
24+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep -E '\.(groovy|java)$' || true)
25+
26+
if [ -n "$STAGED_FILES" ]; then
27+
echo "Formatting staged Groovy/Java files using IntelliJ formatter..."
28+
29+
# Convert newline-separated list to comma-separated for Gradle property
30+
FILES_COMMAS=$(echo "$STAGED_FILES" | tr '\n' ',' | sed 's/,$//')
31+
32+
# Run the Gradle formatting task
33+
./gradlew :formatCode -PformatFiles="$FILES_COMMAS"
34+
35+
# Re-stage the files in case they were modified by the formatter
36+
for FILE in $STAGED_FILES; do
37+
if [ -f "$FILE" ]; then
38+
git add "$FILE"
39+
fi
40+
done
41+
fi

0 commit comments

Comments
 (0)