🌱 Branch: 15/enhancing-code-quality
🔗 Repositório: github.com/rsicarelli/kotlin-gradle-android-platform
⬅️ Artigo Anterior: Parte 14: Aderindo a funcionalidades experimentais do compilador do Kotlin
➡️ Próximo Artigo: Parte 16: Considerações finais
No último artigo, abordamos a capacidade de nossa plataforma aderir a funcionalidades experimentais em diferentes módulos.
Agora, vamos explorar a garantia da qualidade do código através da integração de plugins.
Por que focar em automatizar verificações do código?
Quando se trabalha em equipe, é vital ter padrões de estilo e nomenclatura para manter a consistência. Estabelecer um padrão sólido ajuda a reduzir a sobrecarga de decisões e facilita a colaboração.
Pense assim: ao se juntar a uma orquestra, seguimos a pessoa contudora que dita ritmo da música. É o mesmo com nosso módulos; seguimos padrões preestabelecidos e acordados pelo time de forma automatizada.
Essa prática é especialmente útil para quando uma pessoa nova entra no time, além de que os acordos fiquem documentados e codificados, abertos para colaboração.
Incluíndo análise de código estático com Detekt
O Detekt
é talvez a ferramenta mais famosa em Kotlin para analisar o código e garantir que algumas práticas são aplicadas.
Não iremos focar muito nas suas funcionalidades, vamos direto pra implementação
Passo a passo
1 - Vamos começar declarando o detekt
no nossos libs.versions.toml
:
[versions]
detekt = "1.23.1"
detektCompose = "0.2.3"
[libraries]
gradlePlugin-detekt = { module = "io.gitlab.arturbosch.detekt:detekt-gradle-plugin", version.ref = "detekt" }
detektRules-compose = { module = "io.nlopez.compose.rules:detekt", version.ref = "detektCompose" }
detektRules-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
detektRules-libraries = { module = "io.gitlab.arturbosch.detekt:detekt-rules-libraries", version.ref = "detekt" }
[plugins]
arturbosch-detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
2 - Sincronize o projeto. Navegue até build-logic/build.gradle.kts
e vamos compilar a dependencia do detekt
na nossa plataforma:
dependencies {
compileOnly(libs.gradlePlugin.android)
compileOnly(libs.gradlePlugin.kotlin)
compileOnly(libs.gradlePlugin.detekt)
}
3 - Sincronize o projeto. Agora, vamos declarar nossa DSL do DetektOptions
.
Crie um arquivo DetektOptions
em build-logic/src/../options
data class DetektOptions(
val parallel: Boolean,
val buildUponDefaultConfig: Boolean,
val configFileNames: List<String>,
val includes: List<String>,
val excludes: List<String>
)
class DetektOptionsBuilder {
var parallel: Boolean = true
var configFiles: List<String> = listOf(".detekt.yml, .detekt-compose.yml")
var buildUponDefaultConfig: Boolean = true
var includes: List<String> = listOf("**/*.kt", "**/*.kts")
var excludes: List<String> = listOf(".*/resources/.*", ".*/build/.*")
internal fun build(): DetektOptions = DetektOptions(
parallel = parallel,
configFileNames = configFiles,
includes = includes,
excludes = excludes,
buildUponDefaultConfig = buildUponDefaultConfig
)
}
4 - Em seguida, crie um novo arquivo detekt.kt
em build-logic/src/.../decorations
e declare uma função applyDetekt()
Essa configurações impoe que:
- Esse plugin só poderá ser chamado no
build.gradle.kts
da raíz - Exista um arquivo
.detekt.yml
na raíz do projeto - Exista um arquivo
.detekt-compose.yml
na raíz do projeto
import com.rsicarelli.kplatform.options.DetektOptions
import io.gitlab.arturbosch.detekt.Detekt
import io.gitlab.arturbosch.detekt.extensions.DetektExtension
import org.gradle.api.Project
import org.gradle.api.artifacts.MinimalExternalModuleDependency
import org.gradle.kotlin.dsl.DependencyHandlerScope
import org.gradle.kotlin.dsl.configure
import org.gradle.kotlin.dsl.dependencies
import org.gradle.kotlin.dsl.withType
internal fun Project.applyDetekt(
detektOptions: DetektOptions
) {
check(rootProject == this) { "Must be called on a root project" }
pluginManager.apply("io.gitlab.arturbosch.detekt")
extensions.configure<DetektExtension> {
parallel = detektOptions.parallel
toolVersion = libs.version("detekt")
buildUponDefaultConfig = detektOptions.buildUponDefaultConfig
config.setFrom(detektOptions.configFileNames.map { "$rootDir/$it" })
}
tasks.withType<Detekt> {
setSource(files(projectDir))
include(detektOptions.includes)
exclude(detektOptions.excludes)
}
addDetektPlugins(listOf("compose", "formatting", "libraries"))
}
fun Project.addDetektPlugins(detektPlugins: List<String>) {
fun DependencyHandlerScope.detektPlugin(dependency: MinimalExternalModuleDependency) {
add("detektPlugins", dependency)
}
dependencies {
detektPlugins.forEach { plugin ->
detektPlugin(libs.findLibrary("detektRules-$plugin").get().get())
}
}
}
5 - Em seguida. vamos expor essa decoração no KPlatformPlugin.kt
:
fun Project.detekt(builderAction: DetektBuilder = {}) =
applyDetekt(DetektOptionsBuilder().apply(builderAction).build())
6 - Sincronize o projeto. Em seguida, va até o build.gradle.kts
da raiz do projeto, e inclua o plugin do detekt
:
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.arturbosch.detekt) apply false
id(libs.plugins.rsicarelli.kplatform.get().pluginId)
}
6 - Sincronize o projeto. Em seguida, aplique a decoração detekt()
no mesmo arquivo:
import com.rsicarelli.kplatform.detekt
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.arturbosch.detekt) apply false
id(libs.plugins.rsicarelli.kplatform.get().pluginId)
}
detekt()
7 - Vamos criar 2 arquivos na raíz do projeto: .detekt.yml
e .detekt-compose.yml
8 - Sincronize o projeto. Perceba que uma série de tasks detektX
foram adicionadas no projeto:
8 - Verifique que está funcionando rodando o seguinte comando.
Alternativamente, pode simplesmente dar um clique duplo na task detekt
na lista de tasks do Gradle:
./gradlew detekt
Você vai perceber que vamos ter várias penalidades.
A seguir, vamos usar o Spotless para nos ajudar a reduzir a lista de faltas.
Adicionando o Spotless
O spotless é outra ferramenta indispensável nos projetos Kotlin.
Seu objetivo é formatar seu código magicamente de acordo com um estilo de código/configurações pre estabelecidas.
Novamente, não vamos focar muito nos detalhes da biblioteca, vamos direto ao uso
Passo a passo
1 - Declare as coordenadas do spotless
no libs.versions.toml
[versions]
spotless = "6.21.0"
[libraries]
gradlePlugin-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version.ref = "spotless" }
[plugins]
diffplug-spotless = { id = "com.diffplug.spotless", version.ref = "spotless" }
2 - Sincronize o projeto. Em seguida, vamos criar os arquivos SpotlessOptions
na pasta build-logic/src/.../options
:
Aqui, nossa plataforma será capaz de:
- Fornecer 2 configurações padrões para o projeto:
SpotlessKtsRule
eSpotlessXmlRule
. Isso configura o spotless para nossos arquivos Gradle com extensão.kts
, além de.xml
do Android. - Possibilta outras configurações de arquivos, de acordo com a necessidade de cada projeto.
data class SpotlessOptions(
val fileRules: List<SpotlessFileRule> = listOf(SpotlessKtRule, SpotlessXmlRule),
)
interface SpotlessFileRule {
val fileExtension: String
val targets: List<String>
val excludes: List<String>
}
object SpotlessKtsRule : SpotlessFileRule {
override val fileExtension: String = "kts"
override val targets: List<String> = listOf("**/*.kts")
override val excludes: List<String> = listOf("**/build/**/*.kts")
}
object SpotlessXmlRule : SpotlessFileRule {
override val fileExtension: String = "xml"
override val targets: List<String> = listOf("**/*.xml")
override val excludes: List<String> = listOf("**/build/**/*.xml")
}
class SpotlessOptionsBuilder {
var fileRules: List<SpotlessFileRule> = listOf(SpotlessKtRule, SpotlessXmlRule)
internal fun build(): SpotlessOptions = SpotlessOptions(
fileRules = fileRules
)
}
3 - Vamos criar um arquivo spotless.kt
dentro de build-logic/src/.../decorations
e declarar a função applySpotless()
Note que:
- Estamos aplicando o Spotless no projeto raíz. Isso faz com que as formatações também aconteçam nos scripts da raíz, assim como na plataforma
build-logic
- Estamos aplicando o Spotless também para todos os sub projetos.
- Estamos utilizando o
klint
como regras doSpotless
- O plugin assume que existe um arquivo
.editorconfig
na raíz do projeto
import com.diffplug.gradle.spotless.SpotlessExtension
import com.diffplug.gradle.spotless.SpotlessPlugin
import com.rsicarelli.kplatform.options.SpotlessOptions
import org.gradle.api.Project
import org.gradle.kotlin.dsl.apply
import org.gradle.kotlin.dsl.configure
internal fun Project.applySpotless(spotlessConfig: SpotlessOptions) {
val project = this
configureSpotlessPlugin(spotlessConfig, project)
rootProject.subprojects {
configureSpotlessPlugin(spotlessConfig, project)
}
}
private fun Project.configureSpotlessPlugin(
spotlessConfig: SpotlessOptions,
project: Project
) {
apply<SpotlessPlugin>()
extensions.configure<SpotlessExtension> {
kotlin {
target("src/**/*.kt")
ktlint().setEditorConfigPath("${project.rootDir}/.editorconfig")
}
spotlessConfig.fileRules.forEach { spotlessFileRule ->
with(spotlessFileRule) {
format(fileExtension) {
target(targets)
targetExclude(excludes)
}
}
}
}
}
4 - Crie um arquivo .editorconfig
na raiz do projeto:
5 - Vamos expor essa decoração no KPlatformPlugin.kt
:
fun Project.spotless(builderAction: SpotlessBuilder = { }) =
applySpotless(SpotlessOptionsBuilder().apply(builderAction).build())
6 - Sincronize o projeto. Em seguida, navegue até build.gradle.kts
da raiz do projeto e declare o plugin do spotless:
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.arturbosch.detekt) apply false
alias(libs.plugins.diffplug.spotless) apply false
id(libs.plugins.rsicarelli.kplatform.get().pluginId)
}
7 - Sincronize o projeto. Em seguida. altere o mesmo build.gradle.kts
e aplique a decoração spotless()
:
import com.rsicarelli.kplatform.detekt
import com.rsicarelli.kplatform.spotless
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.arturbosch.detekt) apply false
alias(libs.plugins.diffplug.spotless) apply false
id(libs.plugins.rsicarelli.kplatform.get().pluginId)
}
detekt()
spotless()
8 - Sincronize o projeto. Perceba que agora várias tasks spotless
estarão disponíveis na lista de tarefas do Gradle:
9 - Verifque o funcionamento rodando o comando, ou clique duplo na tarefa spotlessApply
na lista de tarefas do Gradle:
./gradlew spotlessApply
Sucesso!
O Spotless vai conseguir solucionar várias faltas automaticamente pra gente. Porém, tem algumas, como por exemplo nomeação dos arquivos, que não é suportado pelo Spotless.
Aproveitando, nessa branch, adicionei várias documentações para todas nossas API's da plataforma!
No próximo artigo, iremos fechar o essa série de posts, e contar um pouquinho sobre os próximos passos!
Top comments (0)