Gandalf: Bad code shall not
pass
Ilya Ghirici
Agenda
● Code formatting
● Coding Conventions
● Writing Gradle build scripts
● Gandalf Gradle Plugin
Who deals with your code?
● Computer
● Your colleagues
What is “bad” code?
fun Fibonacci( n:Int){
var a=0; var b = 1;
(1.. n ) .forEach { val temp =b
b += a; a= temp; }
}
Code Conventions
Do.
val v = ""
println(v)
Don't.
val v="";
println(v);
Tools
● Java:
○ Checkstyle
● Kotlin:
○ Ktlint
○ Detekt
Tools integration
● Java:
○ Checkstyle - Gradle Plugin
● Kotlin:
○ Ktlint - Java application
○ Detekt - Java application
Ktlint integration
configurations {
ktlint
}
dependencies {
ktlint "com.github.shyiko:ktlint:0.31.0"
}
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
main = "com.github.shyiko.ktlint.Main"
classpath = configurations.ktlint
args "src/**/*.kt"
}
check.dependsOn ktlint
Ktlint task
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
main = "com.github.shyiko.ktlint.Main"
classpath = configurations.ktlint
args = "src/**/*.kt"
}
check.dependsOn ktlint
Evolution of code verification tools at Ellation
● Our team joining Ellation - no style on projects
● Defining code convention
● Project Clean up
● Checkstyle integration
● Kotlin integration
● Ktlint & Detekt integration
Cons of using build script integration
● Code duplication
● Not scalable
● Hard to maintain
Arrival of Gandalf Plugin
● Code style tools integration
● Single source of configs and custom rules
● Report generation
Gandalf’s heart
class GandalfPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
...
def configuration = project.configurations.create("ktlint")
project.dependencies.add(configuration, com.github.shyiko:ktlint:0.30.0")
project.tasks.create(name, JavaExec) {
main "com.github.shyiko.ktlint.Main"
group "verification"
description "Runs Ktlint"
classpath configuration
args "src/**/*.kt"
}
}
}
Incremental tasks
./gradlew ktlint
BUILD SUCCESSFUL in 36s
1 actionable task: 1 executed
./gradlew ktlint
BUILD SUCCESSFUL in 1s
1 actionable task: 1 up-to-date
Incremental tasks
class KtlintPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
def configuration = project.configurations.create("ktlint")
project.dependencies.add(configuration, "com.github.shyiko:ktlint:0.30.0")
project.tasks.create(name, JavaExec) {
main "com.github.shyiko.ktlint.Main"
group "verification"
description "Runs Ktlint"
classpath configuration
args "src/**/*.kt"
outputs.file reportPath
inputs.files files
}
}
}
Gradle remote build cache
class KtlintPlugin implements Plugin<Project> {
@Override
void apply(Project project) {
def configuration = project.configurations.create("ktlint")
project.dependencies.add(configuration,
"com.github.shyiko:ktlint:0.30.0")
project.tasks.create(name, JavaExec) {
main "com.github.shyiko.ktlint.Main"
group "verification"
description "Runs Ktlint"
classpath configuration
args "src/**/*.kt"
outputs.file reportPath
inputs.files files
outputs.cacheIf { true }
}
}
}
Gradle plugin = Java application
def copyConfigFileToProject(Project project) {
def inputStream = getClass()
.getClassLoader()
.getResourceAsStream("detekt-config.yml")
FileUtil.copyFileToRootProject(project, inputStream, "detekt-config.yml")
}
Custom rules
● NoKtFilesInJavaDirectoryRule
● TodoFormatRule
class NoKtFilesInJavaDirectoryRule : Rule(Config.empty) {
override fun visitKtFile(file: KtFile) {
super.visitKtFile(file)
if (file.virtualFile.path.contains("java/")) {
report(
CodeSmell(
issue,
Entity.from(file),
message = "The $file should be in the kotlin dir."
)
)
}
}
}
Gandalf integration tests
@Test
void 'runs with failure gandalf task on Android Kotlin Project'() {
androidTestBuildFile += """
apply plugin: 'com.ellation.gandalf'
"""
runCommand(command: "gandalf", fixtureName: "failing_project", shouldSucceed: false)
}
Challenges we faced
● Groovy
● Learning curve
● Debugging
● Reduced the overhead of maintaining similar logic
● Increased degree of modularization
● Made developers a bit happier
What we achieved by using Gandalf
About me
● https://github.com/Ilya-Gh
● ilya.ghiric@gmail.com

CodeWay 2019 - Gandalf: Bad code shall not pass

  • 1.
    Gandalf: Bad codeshall not pass Ilya Ghirici
  • 2.
    Agenda ● Code formatting ●Coding Conventions ● Writing Gradle build scripts ● Gandalf Gradle Plugin
  • 3.
    Who deals withyour code? ● Computer ● Your colleagues
  • 4.
    What is “bad”code? fun Fibonacci( n:Int){ var a=0; var b = 1; (1.. n ) .forEach { val temp =b b += a; a= temp; } }
  • 5.
    Code Conventions Do. val v= "" println(v) Don't. val v=""; println(v);
  • 6.
    Tools ● Java: ○ Checkstyle ●Kotlin: ○ Ktlint ○ Detekt
  • 7.
    Tools integration ● Java: ○Checkstyle - Gradle Plugin ● Kotlin: ○ Ktlint - Java application ○ Detekt - Java application
  • 8.
    Ktlint integration configurations { ktlint } dependencies{ ktlint "com.github.shyiko:ktlint:0.31.0" } task ktlint(type: JavaExec, group: "verification") { description = "Check Kotlin code style." main = "com.github.shyiko.ktlint.Main" classpath = configurations.ktlint args "src/**/*.kt" } check.dependsOn ktlint
  • 9.
    Ktlint task task ktlint(type:JavaExec, group: "verification") { description = "Check Kotlin code style." main = "com.github.shyiko.ktlint.Main" classpath = configurations.ktlint args = "src/**/*.kt" } check.dependsOn ktlint
  • 10.
    Evolution of codeverification tools at Ellation ● Our team joining Ellation - no style on projects ● Defining code convention ● Project Clean up ● Checkstyle integration ● Kotlin integration ● Ktlint & Detekt integration
  • 11.
    Cons of usingbuild script integration ● Code duplication ● Not scalable ● Hard to maintain
  • 12.
    Arrival of GandalfPlugin ● Code style tools integration ● Single source of configs and custom rules ● Report generation
  • 13.
    Gandalf’s heart class GandalfPluginimplements Plugin<Project> { @Override void apply(Project project) { ... def configuration = project.configurations.create("ktlint") project.dependencies.add(configuration, com.github.shyiko:ktlint:0.30.0") project.tasks.create(name, JavaExec) { main "com.github.shyiko.ktlint.Main" group "verification" description "Runs Ktlint" classpath configuration args "src/**/*.kt" } } }
  • 14.
    Incremental tasks ./gradlew ktlint BUILDSUCCESSFUL in 36s 1 actionable task: 1 executed ./gradlew ktlint BUILD SUCCESSFUL in 1s 1 actionable task: 1 up-to-date
  • 15.
    Incremental tasks class KtlintPluginimplements Plugin<Project> { @Override void apply(Project project) { def configuration = project.configurations.create("ktlint") project.dependencies.add(configuration, "com.github.shyiko:ktlint:0.30.0") project.tasks.create(name, JavaExec) { main "com.github.shyiko.ktlint.Main" group "verification" description "Runs Ktlint" classpath configuration args "src/**/*.kt" outputs.file reportPath inputs.files files } } }
  • 16.
    Gradle remote buildcache class KtlintPlugin implements Plugin<Project> { @Override void apply(Project project) { def configuration = project.configurations.create("ktlint") project.dependencies.add(configuration, "com.github.shyiko:ktlint:0.30.0") project.tasks.create(name, JavaExec) { main "com.github.shyiko.ktlint.Main" group "verification" description "Runs Ktlint" classpath configuration args "src/**/*.kt" outputs.file reportPath inputs.files files outputs.cacheIf { true } } } }
  • 17.
    Gradle plugin =Java application def copyConfigFileToProject(Project project) { def inputStream = getClass() .getClassLoader() .getResourceAsStream("detekt-config.yml") FileUtil.copyFileToRootProject(project, inputStream, "detekt-config.yml") }
  • 18.
  • 19.
    class NoKtFilesInJavaDirectoryRule :Rule(Config.empty) { override fun visitKtFile(file: KtFile) { super.visitKtFile(file) if (file.virtualFile.path.contains("java/")) { report( CodeSmell( issue, Entity.from(file), message = "The $file should be in the kotlin dir." ) ) } } }
  • 21.
    Gandalf integration tests @Test void'runs with failure gandalf task on Android Kotlin Project'() { androidTestBuildFile += """ apply plugin: 'com.ellation.gandalf' """ runCommand(command: "gandalf", fixtureName: "failing_project", shouldSucceed: false) }
  • 22.
    Challenges we faced ●Groovy ● Learning curve ● Debugging
  • 23.
    ● Reduced theoverhead of maintaining similar logic ● Increased degree of modularization ● Made developers a bit happier What we achieved by using Gandalf
  • 24.