CI/CD (Continuous Integration e Continuous Delivery) é a prática de automatizar a integração, teste e entrega de software. Para projetos Kotlin, um pipeline de CI/CD bem configurado garante que cada mudanca de código seja compilada, testada e, quando aprovada, entregue automaticamente ao ambiente de producao. Neste guia, vamos configurar pipelines completos usando GitHub Actions, GitLab CI e Jenkins, cobrindo desde testes unitarios até deploy em ambientes de producao com Docker e Kubernetes.
Fundamentos de CI/CD
O ciclo básico de CI/CD para um projeto Kotlin envolve: compilar o código, executar testes unitarios e de integração, analisar qualidade de código, gerar artefatos (JAR, Docker image) e fazer deploy. Cada etapa e automatizada e executada em sequência, com falhas bloqueando etapas subsequentes.
Um bom pipeline segue o princípio de fail fast: testes rápidos rodam primeiro para dar feedback imediato, enquanto testes mais demorados e deploy ficam em etapas posteriores.
GitHub Actions para Projetos Kotlin
O GitHub Actions é a solução de CI/CD integrada ao GitHub. Crie o arquivo .github/workflows/ci.yml:
// .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
env:
GRADLE_OPTS: -Dorg.gradle.daemon=false
jobs:
build-and-test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
ports:
- 5432:5432
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v4
- name: Configurar JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Cache Gradle
uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: gradle-${{ hashFiles('**/*.gradle.kts') }}
restore-keys: gradle-
- name: Dar permissao ao Gradle Wrapper
run: chmod +x gradlew
- name: Compilar
run: ./gradlew build -x test
- name: Executar testes unitarios
run: ./gradlew test
- name: Executar testes de integracao
run: ./gradlew integrationTest
env:
DATABASE_URL: jdbc:postgresql://localhost:5432/testdb
DATABASE_USER: testuser
DATABASE_PASSWORD: testpass
- name: Publicar relatorio de testes
uses: dorny/test-reporter@v1
if: always()
with:
name: Relatorio de Testes
path: '**/build/test-results/test/*.xml'
reporter: java-junit
- name: Upload artefatos
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: build/libs/*.jar
Pipeline de Deploy
// .github/workflows/deploy.yml
name: Deploy Pipeline
on:
push:
tags:
- 'v*'
jobs:
deploy:
runs-on: ubuntu-latest
needs: build-and-test
steps:
- uses: actions/checkout@v4
- name: Configurar JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'temurin'
- name: Build do projeto
run: ./gradlew build
- name: Login no Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build e push da imagem Docker
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: |
meuusuario/meu-app:${{ github.ref_name }}
meuusuario/meu-app:latest
- name: Deploy para producao
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.SERVER_HOST }}
username: ${{ secrets.SERVER_USER }}
key: ${{ secrets.SERVER_SSH_KEY }}
script: |
docker pull meuusuario/meu-app:latest
docker-compose up -d --force-recreate app
GitLab CI para Projetos Kotlin
O GitLab CI usa o arquivo .gitlab-ci.yml na raiz do projeto:
// .gitlab-ci.yml
stages:
- build
- test
- quality
- package
- deploy
variables:
GRADLE_OPTS: "-Dorg.gradle.daemon=false"
GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- .gradle/wrapper
- .gradle/caches
build:
stage: build
image: eclipse-temurin:17-jdk
script:
- chmod +x gradlew
- ./gradlew assemble
artifacts:
paths:
- build/libs/*.jar
unit-tests:
stage: test
image: eclipse-temurin:17-jdk
script:
- ./gradlew test
artifacts:
when: always
reports:
junit: build/test-results/test/*.xml
integration-tests:
stage: test
image: eclipse-temurin:17-jdk
services:
- postgres:16
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
DATABASE_URL: "jdbc:postgresql://postgres:5432/testdb"
script:
- ./gradlew integrationTest
code-quality:
stage: quality
image: eclipse-temurin:17-jdk
script:
- ./gradlew detekt
artifacts:
paths:
- build/reports/detekt/
docker-build:
stage: package
image: docker:24
services:
- docker:24-dind
script:
- docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
- docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
only:
- tags
deploy-production:
stage: deploy
image: alpine
script:
- apk add --no-cache openssh-client
- ssh $SERVER_USER@$SERVER_HOST "docker pull $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG && docker-compose up -d"
only:
- tags
when: manual
Configuração do Gradle para CI
Otimize o build do Gradle para ambientes de CI:
// build.gradle.kts
tasks.test {
useJUnitPlatform()
// Relatorios para CI
reports {
junitXml.required.set(true)
html.required.set(true)
}
// Testes em paralelo
maxParallelForks = Runtime.getRuntime().availableProcessors() / 2
}
// Task separada para testes de integracao
val integrationTest by tasks.registering(Test::class) {
description = "Executa testes de integracao"
group = "verificacao"
testClassesDirs = sourceSets["integrationTest"].output.classesDirs
classpath = sourceSets["integrationTest"].runtimeClasspath
useJUnitPlatform()
shouldRunAfter(tasks.test)
}
// Análise de codigo com Detekt
plugins {
id("io.gitlab.arturbosch.detekt") version "1.23.4"
}
detekt {
config.setFrom("$rootDir/config/detekt/detekt.yml")
buildUponDefaultConfig = true
allRules = false
}
Análise de Qualidade de Código
Integre ferramentas de análise estática no pipeline:
// Detekt - Análise estática para Kotlin
// config/detekt/detekt.yml
complexity:
LongMethod:
threshold: 30
ComplexCondition:
threshold: 4
TooManyFunctions:
thresholdInFiles: 15
style:
MagicNumber:
ignoreNumbers:
- '-1'
- '0'
- '1'
- '2'
MaxLineLength:
maxLineLength: 120
// Ktlint - Formatação de codigo
plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.0.3"
}
ktlint {
android.set(true)
outputToConsole.set(true)
reporters {
reporter(ReporterType.CHECKSTYLE)
}
}
Segurança no Pipeline
Proteja secrets e credenciais no pipeline:
// Nunca hardcode credenciais. Use variaveis de ambiente:
// GitHub: Settings > Secrets and variables > Actions
// GitLab: Settings > CI/CD > Variables
// No codigo Kotlin, leia do ambiente:
val databaseUrl = System.getenv("DATABASE_URL")
?: "jdbc:postgresql://localhost:5432/devdb"
val databaseUser = System.getenv("DATABASE_USER")
?: "devuser"
Boas Práticas de CI/CD para Kotlin
- Fail fast: coloque testes unitarios antes de testes de integração no pipeline. Feedback rápido e essencial.
- Cache de dependências: configure cache do Gradle para evitar download de dependências a cada build.
- Builds reproduziveis: use versões fixas de JDK, Gradle e dependências. Evite
latestem imagens Docker. - Testes em paralelo: configure
maxParallelForksno Gradle para aproveitar múltiplos cores. - Branch protection: exija que o pipeline passe antes de permitir merge em branches protegidas.
- Deploy gradual: use estrategias como blue-green ou canary para deploys seguros.
- Monitore o pipeline: acompanhe métricas como tempo de build, taxa de falha e cobertura de testes.
Erros Comuns e Armadilhas
- Pipeline lento sem cache: sem cache de Gradle, cada build baixa todas as dependências novamente, adicionando minutos ao pipeline.
- Testes flakey: testes que falham intermitentemente corroem a confianca no pipeline. Investigue e corrija testes instáveis imediatamente.
- Secrets expostos em logs: cuidado com comandos que imprimem variaveis de ambiente. Use mascaramento de secrets.
- Falta de testes de integração: confiar apenas em testes unitarios não garante que componentes funcionem juntos.
- Deploy manual como única opção: se o deploy depende de um processo manual, não e CD verdadeiro. Automatize o maximo possível.
- Ignorar falhas do pipeline: nunca faça merge com pipeline falhando. Corrija antes de prosseguir.
Conclusão e Próximos Passos
Um pipeline de CI/CD robusto e o alicerce de entregas confiaveis e frequentes. Com GitHub Actions ou GitLab CI, você pode automatizar todo o ciclo de vida do seu projeto Kotlin, desde a compilação até o deploy em producao. Para aprofundar, explore nossos guias sobre Docker para containerizacao, microsserviços para arquitetura distribuida e testes para maximizar a cobertura do seu pipeline. A automação bem feita libera tempo para o que realmente importa: escrever código de qualidade.