DevOps e CI/CD não são mais diferenciais, são requisitos básicos em projetos profissionais. Para projetos Kotlin, montar pipelines de integração e entrega continua garante qualidade, velocidade e confianca em cada deploy. Neste artigo, vamos explorar como configurar CI/CD eficaz para projetos Kotlin.
O Que e CI/CD e Por Que Importa
CI (Continuous Integration) é a prática de integrar código frequentemente, com verificacoes automaticas a cada push. CD (Continuous Delivery/Deployment) automatiza o processo de levar o código para producao.
Para projetos Kotlin, isso significa:
- Compilar e testar automaticamente a cada commit
- Verificar qualidade de código com análise estática
- Gerar artefatos (JARs, APKs, imagens Docker) automaticamente
- Fazer deploy em ambientes de staging e producao
Estrutura de Pipeline para Kotlin
Um pipeline tipico para um projeto Kotlin backend tem as seguintes etapas:
1. Build e Compilação
// build.gradle.kts - Configuração otimizada para CI
plugins {
kotlin("jvm") version "2.1.0"
kotlin("plugin.spring") version "2.1.0"
id("org.springframework.boot") version "3.3.0"
id("io.spring.dependency-management") version "1.1.4"
id("org.jetbrains.kotlinx.kover") version "0.7.5" // Cobertura de codigo
id("io.gitlab.arturbosch.detekt") version "1.23.4" // Análise estática
}
detekt {
config.setFrom(files("config/detekt/detekt.yml"))
buildUponDefaultConfig = true
allRules = false
}
kover {
reports {
filters {
excludes {
classes("*.config.*", "*.Application*")
}
}
verify {
rule {
minBound(80) // Minimo 80% de cobertura
}
}
}
}
2. Testes Automatizados
// Testes bem estruturados são a base do CI
@SpringBootTest
class PedidoServiceIntegrationTest {
@Autowired
private lateinit var pedidoService: PedidoService
@Autowired
private lateinit var pedidoRepository: PedidoRepository
@Test
fun `deve criar pedido e persistir no banco`() = runTest {
// Given
val request = CriarPedidoRequest(
clienteId = "cliente-1",
itens = listOf(
ItemRequest(produtoId = "prod-1", quantidade = 2),
ItemRequest(produtoId = "prod-2", quantidade = 1)
)
)
// When
val pedido = pedidoService.criarPedido(request)
// Then
assertThat(pedido.id).isNotNull()
assertThat(pedido.status).isEqualTo(PedidoStatus.CRIADO)
val persistido = pedidoRepository.findById(pedido.id)
assertThat(persistido).isNotNull()
assertThat(persistido!!.itens).hasSize(2)
}
@AfterEach
fun cleanup() = runBlocking {
pedidoRepository.deleteAll()
}
}
3. Análise Estática com Detekt
Detekt e a ferramenta padrão para análise estática de código Kotlin:
// config/detekt/detekt.yml (em formato YAML, referenciado pelo Gradle)
// Configurações tipicas incluem:
// - Complexidade ciclomatica maxima
// - Tamanho maximo de funcoes
// - Deteccao de code smells
// - Regras de formatação
// Exemplo de codigo que detekt flagraria:
// RUIM - Função muito complexa
fun processarDados(dados: List<Any>): String {
// detekt avisaria sobre complexidade excessiva
var resultado = ""
for (dado in dados) {
when (dado) {
is String -> resultado += dado.uppercase()
is Int -> resultado += dado.toString()
is List<*> -> resultado += dado.size.toString()
else -> resultado += "unknown"
}
}
return resultado
}
// BOM - Função simples e clara
fun processarDados(dados: List<Any>): String {
return dados.joinToString("") { dado ->
formatarDado(dado)
}
}
private fun formatarDado(dado: Any): String = when (dado) {
is String -> dado.uppercase()
is Int -> dado.toString()
is List<*> -> dado.size.toString()
else -> "unknown"
}
4. Build de Imagem Docker
// Dockerfile para aplicacao Kotlin/Spring Boot
// Multi-stage build para imagem otimizada
// Usando Gradle para build
// Stage 1: Build
// FROM gradle:8.5-jdk21 AS build
// COPY . /app
// WORKDIR /app
// RUN gradle bootJar --no-daemon
// Stage 2: Runtime
// FROM eclipse-temurin:21-jre-alpine
// COPY --from=build /app/build/libs/*.jar app.jar
// EXPOSE 8080
// ENTRYPOINT ["java", "-jar", "app.jar"]
Para otimizar o build Docker em CI, use cache de dependências Gradle:
// build.gradle.kts - Task para copiar dependências (util para cache Docker)
tasks.register<Copy>("copyDependencies") {
from(configurations.runtimeClasspath)
into("$buildDir/dependencies")
}
Ferramentas de CI/CD
GitHub Actions
A ferramenta mais popular para projetos open source e muitas empresas:
O workflow tipico inclui:
- Checkout do código
- Setup do JDK 21
- Cache de dependências Gradle
- Execução de testes
- Análise estática com detekt
- verificação de cobertura com kover
- Build da imagem Docker
- Deploy para staging/producao
GitLab CI
Popular em empresas que usam GitLab para hospedagem de código. Oferece pipelines visuais e integração nativa com Kubernetes.
Jenkins
Ainda muito usado em empresas enterprise, especialmente bancos e fintechs. Oferece maxima flexibilidade mas exige mais manutenção.
Pipeline para Projeto Android
Para projetos Android com Kotlin, o pipeline tem particularidades:
// Pipeline Android tipico:
// 1. Build do APK/AAB
// 2. Testes unitarios
// 3. Testes instrumentados (opcional, com emulador)
// 4. Lint e detekt
// 5. Publicacao na Play Store (via Fastlane ou Gradle Play Publisher)
// build.gradle.kts (app module)
android {
lint {
abortOnError = true
warningsAsErrors = true
xmlReport = true
}
testOptions {
unitTests.all {
it.useJUnitPlatform()
}
}
}
// Plugin para publicacao automatica
plugins {
id("com.github.triplet.play") version "3.9.0"
}
play {
serviceAccountCredentials.set(file("play-service-account.json"))
track.set("internal") // internal -> alpha -> beta -> production
defaultToAppBundles.set(true)
}
Melhores Práticas de CI/CD para Kotlin
Cache de Dependências
Gradle downloads podem levar minutos. Configurar cache e essencial:
// Configuração de cache no build.gradle.kts
buildCache {
local {
isEnabled = true
directory = File(rootDir, ".gradle/build-cache")
}
}
Testes Paralelos
// Executar testes em paralelo para reduzir tempo de CI
tasks.withType<Test> {
maxParallelForks = Runtime.getRuntime().availableProcessors() / 2
forkEvery = 100 // Reinicia JVM a cada 100 testes para evitar memory leaks
}
Build Incremental
// Habilitar compilacao incremental
kotlin {
compilerOptions {
freeCompilerArgs.add("-Xuse-k2") // Compilador K2 mais rapido
}
}
Segredos e Configuração
Nunca coloque segredos no código. Use variaveis de ambiente ou vault:
// Acessar configurações de forma segura
@Configuration
class DatabaseConfig(
@Value("\${DATABASE_URL}") private val databaseUrl: String,
@Value("\${DATABASE_USERNAME}") private val username: String,
@Value("\${DATABASE_PASSWORD}") private val password: String
) {
@Bean
fun dataSource(): HikariDataSource {
return HikariDataSource().apply {
jdbcUrl = databaseUrl
this.username = this@DatabaseConfig.username
this.password = this@DatabaseConfig.password
maximumPoolSize = 10
}
}
}
Monitoramento do Pipeline
Acompanhe métricas do seu pipeline:
- Tempo médio de build: Deve ficar abaixo de 10 minutos para feedback rápido
- Taxa de falha: Acima de 10% indica problemas no processo
- Cobertura de testes: Mantenha acima de 80% para o core business
- Tempo de deploy: Do merge ao producao, idealmente menos de 30 minutos
Estrategias de Deploy
Blue-Green Deployment
Mantem duas versões do ambiente (blue e green). O deploy vai para o ambiente inativo, e apos válidação, o trafego e direcionado para ele.
Canary Release
Direciona uma pequena porcentagem do trafego (1-5%) para a nova versão. Se tudo estiver bem, aumenta gradualmente até 100%.
Rolling Update
Atualiza instancias gradualmente, substituindo uma por vez. E a abordagem padrão no Kubernetes.
Conclusão
CI/CD bem implementado transforma a forma como você desenvolve e entrega software Kotlin. Automação de testes, análise estática, builds reprodutiveis e deploys automatizados criam um ciclo de feedback rápido que aumenta a qualidade e a velocidade de entrega.
Comece com o básico (build e testes automaticos) e evolua gradualmente para um pipeline completo com análise de código, cobertura de testes e deploy automatizado. Vale saber que muitas ferramentas de CI/CD como Docker, Kubernetes e Terraform são escritas em Go, e scripts de automação frequentemente usam Python. O investimento em CI/CD se paga rapidamente em menos bugs, menos trabalho manual e mais confianca em cada release.