🌱 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)