---
title: "CI/CD para Kotlin em 2026: GitHub Actions, GitLab CI, Gradle e Deploy"
url: "https://kotlin.dev.br/guias/guia-kotlin-ci-cd/"
markdown_url: "https://kotlin.dev.br/guias/guia-kotlin-ci-cd.MD"
description: "Guia prático de CI/CD para projetos Kotlin: GitHub Actions, GitLab CI, Gradle, testes, detekt, ktlint, Docker, deploy e escolhas entre pipelines."
date: "2025-07-23"
author: "Karina Melo"
---

# CI/CD para Kotlin em 2026: GitHub Actions, GitLab CI, Gradle e Deploy

Guia prático de CI/CD para projetos Kotlin: GitHub Actions, GitLab CI, Gradle, testes, detekt, ktlint, Docker, deploy e escolhas entre pipelines.


CI/CD é o conjunto de práticas que transforma um projeto Kotlin em software entregue com previsibilidade: cada mudança compila, roda testes, passa por análise de qualidade, gera artefatos e chega ao ambiente certo sem depender de uma sequência manual frágil. Em Kotlin, isso vale para Android, backend com Ktor ou Spring Boot, bibliotecas multiplataforma, CLIs e serviços que rodam em Docker ou Kubernetes.

Este guia foi atualizado para 2026 com um foco prático: montar pipelines úteis em GitHub Actions e GitLab CI, configurar Gradle para ambientes de integração contínua, encaixar testes, detekt, ktlint, cache, Docker e deploy sem transformar o pipeline em um labirinto. Se você ainda está escolhendo a stack do projeto, leia também o guia de [Kotlin para backend](/guias/kotlin-para-backend/), o artigo de [Kotlin com Spring Boot](/blog/kotlin-spring-boot/) e o material de [detekt e ktlint em Kotlin](/blog/detekt-ktlint-kotlin-qualidade-2026/).

## O que um pipeline Kotlin precisa fazer

Um pipeline Kotlin saudável costuma ter sete etapas:

1. **Preparar o ambiente** com JDK, Gradle Wrapper e cache.
2. **Compilar** o projeto para detectar erros rápidos.
3. **Executar testes unitários** com JUnit, Kotest ou outra stack.
4. **Executar testes de integração** quando houver banco, fila, API externa ou container.
5. **Verificar qualidade** com detekt, ktlint e, quando fizer sentido, análise de cobertura.
6. **Gerar artefatos**: JAR, imagem Docker, APK, AAB ou pacote multiplataforma.
7. **Publicar ou fazer deploy** apenas quando as etapas anteriores passarem.

O princípio é simples: falhar cedo, explicar bem a falha e evitar que código ruim chegue ao branch principal. O pipeline não precisa ser perfeito no primeiro dia. Ele precisa ser confiável o bastante para o time confiar nele.

## GitHub Actions para projetos Kotlin

O GitHub Actions é uma boa escolha para projetos hospedados no GitHub e funciona muito bem com Gradle. Um pipeline inicial pode compilar, testar, rodar detekt e ktlint em pull requests e pushes para `main`.

```yaml
# .github/workflows/ci.yml
name: Kotlin CI

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]

concurrency:
  group: kotlin-ci-${{ github.ref }}
  cancel-in-progress: true

env:
  GRADLE_OPTS: -Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=-Xmx2g

jobs:
  build-test-quality:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:16
        env:
          POSTGRES_DB: app_test
          POSTGRES_USER: app
          POSTGRES_PASSWORD: app
        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
        uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - name: Configurar Gradle
        uses: gradle/actions/setup-gradle@v4

      - name: Permitir Gradle Wrapper
        run: chmod +x ./gradlew

      - name: Compilar
        run: ./gradlew assemble

      - name: Testes unitários
        run: ./gradlew test

      - name: Testes de integração
        run: ./gradlew integrationTest
        env:
          DATABASE_URL: jdbc:postgresql://localhost:5432/app_test
          DATABASE_USER: app
          DATABASE_PASSWORD: app

      - name: Qualidade de código
        run: ./gradlew ktlintCheck detekt

      - name: Publicar relatórios de teste
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: test-reports
          path: |
            **/build/reports/tests/
            **/build/test-results/
            **/build/reports/detekt/
```

Para Android, a estrutura é parecida, mas você normalmente usa JDK 17 ou 21, configura o Android Gradle Plugin e roda tarefas como `./gradlew testDebugUnitTest`, `./gradlew lintDebug` e, em pipelines mais completos, testes instrumentados em emulador. Para bibliotecas Kotlin Multiplatform, separe jobs por alvo: JVM em Linux, iOS em macOS e JS/Wasm quando aplicável.

## Deploy com GitHub Actions

O deploy não deve acontecer em qualquer push. Uma regra comum é publicar apenas quando uma tag `v*` é criada ou quando o branch `main` passa por revisão.

```yaml
# .github/workflows/deploy.yml
name: Deploy

on:
  push:
    tags:
      - 'v*'

jobs:
  docker-deploy:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-java@v4
        with:
          distribution: temurin
          java-version: '21'

      - uses: gradle/actions/setup-gradle@v4

      - name: Build do JAR
        run: ./gradlew clean build

      - name: Login no registry
        uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Build e push da imagem
        uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ghcr.io/sua-org/seu-app:${{ github.ref_name }}
```

Se o destino for Kubernetes, a etapa seguinte pode atualizar um manifesto, acionar Argo CD, chamar um webhook interno ou aplicar um chart Helm. Para entender melhor esse caminho, veja também [Kotlin com Kubernetes](/blog/kotlin-kubernetes/).

## GitLab CI para Kotlin

GitLab CI é forte quando o repositório, o registry, os ambientes e as regras de deploy vivem dentro do GitLab. Para Kotlin, a diferença principal é a sintaxe do `.gitlab-ci.yml`, mas a lógica do pipeline continua a mesma.

```yaml
# .gitlab-ci.yml
stages:
  - build
  - test
  - quality
  - package
  - deploy

variables:
  GRADLE_OPTS: "-Dorg.gradle.daemon=false -Dorg.gradle.jvmargs=-Xmx2g"
  GRADLE_USER_HOME: "$CI_PROJECT_DIR/.gradle"

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .gradle/wrapper
    - .gradle/caches

build:
  stage: build
  image: eclipse-temurin:21-jdk
  script:
    - chmod +x ./gradlew
    - ./gradlew assemble
  artifacts:
    paths:
      - build/libs/*.jar
    expire_in: 1 week

unit-tests:
  stage: test
  image: eclipse-temurin:21-jdk
  script:
    - ./gradlew test
  artifacts:
    when: always
    reports:
      junit: build/test-results/test/*.xml
    paths:
      - build/reports/tests/

integration-tests:
  stage: test
  image: eclipse-temurin:21-jdk
  services:
    - name: postgres:16
      alias: postgres
  variables:
    POSTGRES_DB: app_test
    POSTGRES_USER: app
    POSTGRES_PASSWORD: app
    DATABASE_URL: jdbc:postgresql://postgres:5432/app_test
  script:
    - ./gradlew integrationTest

quality:
  stage: quality
  image: eclipse-temurin:21-jdk
  script:
    - ./gradlew ktlintCheck detekt
  artifacts:
    when: always
    paths:
      - build/reports/detekt/
      - build/reports/ktlint/

docker-build:
  stage: package
  image: docker:27
  services:
    - docker:27-dind
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  rules:
    - if: '$CI_COMMIT_BRANCH == "main"'

deploy-production:
  stage: deploy
  image: alpine:3.20
  script:
    - echo "Acione aqui Helm, Argo CD, SSH ou API interna de deploy"
  rules:
    - if: '$CI_COMMIT_TAG'
      when: manual
```

## GitHub Actions ou GitLab CI: qual escolher para Kotlin?

Para a consulta "GitLab CI vs Kotlin", a resposta correta é que não existe uma disputa entre a ferramenta de CI e a linguagem. GitLab CI e GitHub Actions são orquestradores de pipeline; Kotlin é o projeto que será compilado, testado e entregue por eles. A decisão real é entre **GitLab CI para um repositório Kotlin** e **GitHub Actions para um repositório Kotlin**.

Escolha GitHub Actions quando:

- o código já está no GitHub;
- você quer marketplace grande de actions prontas;
- o time usa pull requests e branch protection do GitHub;
- o deploy é feito para GitHub Packages, GHCR, cloud providers ou serviços externos.

Escolha GitLab CI quando:

- o código está no GitLab;
- você quer registry, environments, approvals e runners no mesmo produto;
- o time precisa de pipelines complexos com regras internas;
- a empresa já opera GitLab Runner próprio.

Para Kotlin em si, ambos resolvem. O ponto crítico é manter o Gradle Wrapper versionado, cachear dependências, separar testes rápidos de testes lentos e rodar qualidade de código antes do deploy. Se você quer exemplos focados no GitHub, veja [Kotlin com GitHub Actions](/blog/kotlin-github-actions/).

## Configuração do Gradle para CI

O `build.gradle.kts` deve facilitar o trabalho do pipeline. Não dependa de tarefas locais misteriosas ou scripts que só funcionam na máquina de uma pessoa.

```kotlin
tasks.test {
    useJUnitPlatform()

    reports {
        junitXml.required.set(true)
        html.required.set(true)
    }

    maxParallelForks = (Runtime.getRuntime().availableProcessors() / 2).coerceAtLeast(1)
}

val integrationTest by tasks.registering(Test::class) {
    description = "Executa testes de integração"
    group = "verification"

    testClassesDirs = sourceSets["integrationTest"].output.classesDirs
    classpath = sourceSets["integrationTest"].runtimeClasspath

    useJUnitPlatform()
    shouldRunAfter(tasks.test)
}
```

Também vale padronizar versões com version catalog, travar a versão do JDK e evitar `latest` em imagens Docker. Builds reproduzíveis são mais fáceis de depurar e mais seguros para deploy.

## detekt e ktlint no pipeline

Qualidade de código não deve depender de revisão manual. detekt encontra complexidade, problemas de estilo e padrões perigosos; ktlint padroniza formatação. Uma configuração inicial pode ser simples:

```kotlin
plugins {
    id("io.gitlab.arturbosch.detekt") version "1.23.8"
    id("org.jlleitschuh.gradle.ktlint") version "12.1.1"
}

detekt {
    buildUponDefaultConfig = true
    allRules = false
    config.setFrom("$rootDir/config/detekt/detekt.yml")
}

ktlint {
    android.set(false)
    outputToConsole.set(true)
}
```

Em times grandes, evite bloquear tudo no primeiro dia. Crie um baseline, rode as ferramentas no pipeline e vá reduzindo violações por módulo. O artigo [Detekt e ktlint em Kotlin: qualidade de código em 2026](/blog/detekt-ktlint-kotlin-qualidade-2026/) mostra esse rollout em mais detalhes.

## Secrets, variáveis e segurança

Nunca coloque credenciais no repositório. Use secrets do GitHub, variáveis protegidas do GitLab, vault interno ou secret manager da sua nuvem. No código Kotlin, leia valores do ambiente e mantenha defaults apenas para desenvolvimento local.

```kotlin
val databaseUrl = System.getenv("DATABASE_URL")
    ?: "jdbc:postgresql://localhost:5432/devdb"

val databaseUser = System.getenv("DATABASE_USER") ?: "devuser"
val databasePassword = System.getenv("DATABASE_PASSWORD") ?: "devpass"
```

Também cuide dos logs. Um pipeline pode vazar tokens quando imprime comandos com variáveis, executa scripts com `set -x` ou mostra payloads de deploy. Secrets devem ser mascarados e acessíveis apenas nos jobs que realmente precisam deles.

## Boas práticas de CI/CD para Kotlin

- **Fail fast:** rode compilação e testes unitários antes de etapas caras.
- **Cache de Gradle:** reduza tempo de build sem esconder problemas de dependência.
- **Gradle Wrapper versionado:** não dependa do Gradle instalado no runner.
- **JDK fixo:** use uma versão explícita, como 17 ou 21, alinhada ao projeto.
- **Relatórios sempre publicados:** quando falhar, o time precisa ver o motivo.
- **Separação por ambiente:** staging, homologação e produção têm regras diferentes.
- **Deploy manual onde existe risco:** produção pode exigir aprovação, tag ou janela.
- **Observabilidade:** publique versão, commit e ambiente para facilitar rollback.
- **Qualidade incremental:** detekt e ktlint funcionam melhor quando viram hábito, não punição.

## Erros comuns

O erro mais frequente é começar pelo deploy e esquecer a base: testes instáveis, cache mal configurado, secrets expostos e tarefas lentas demais. Outro problema é tratar CI/CD como arquivo esquecido: o pipeline precisa evoluir junto com o projeto. Se o projeto ganhou banco, fila ou cache, o pipeline precisa testar esse caminho. Se o projeto virou monólito modular, talvez seja hora de paralelizar módulos.

Também evite transformar o pipeline em uma cerimônia impossível de manter. Um bom pipeline Kotlin é explícito, rápido o suficiente para rodar em pull requests e rígido o suficiente para proteger `main`.

## FAQ rápido

### Preciso de GitHub Actions para usar Kotlin?

Não. Você pode usar GitHub Actions, GitLab CI, Jenkins, Buildkite, CircleCI ou qualquer runner que tenha JDK e consiga executar o Gradle Wrapper.

### GitLab CI é melhor que GitHub Actions para Kotlin?

Não por causa da linguagem. GitLab CI costuma ser melhor quando a empresa já usa GitLab como plataforma completa. GitHub Actions costuma ser mais natural quando o repositório vive no GitHub e o time quer um ecossistema grande de actions prontas.

### Devo rodar detekt e ktlint antes ou depois dos testes?

Em projetos pequenos, pode rodar tudo junto. Em projetos maiores, rode compilação e testes unitários primeiro para feedback rápido, depois qualidade e integração.

### CI/CD para Android Kotlin é diferente?

A ideia é a mesma, mas as tarefas mudam: `testDebugUnitTest`, `lintDebug`, build de APK/AAB e, quando necessário, testes instrumentados com emulador ou device farm.

## Conclusão

CI/CD para Kotlin em 2026 não é apenas um YAML bonito no repositório. É uma rede de segurança para entregar software com menos medo: Gradle previsível, testes confiáveis, qualidade automatizada, secrets protegidos e deploy controlado. Comece pequeno com build, teste e qualidade. Depois adicione Docker, ambientes, approvals e observabilidade. O importante é que cada mudança no projeto Kotlin passe por um caminho repetível antes de virar produção.

Pipelines semelhantes podem ser configurados para projetos <a href="https://golang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'golang.com.br' })">Go</a>, <a href="https://python.dev.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'python.dev.br' })">Python</a> e <a href="https://rustlang.com.br/" target="_blank" rel="noopener" onclick="umami.track('portfolio-site-click', { destination: 'rustlang.com.br' })">Rust</a>, mas a disciplina é a mesma: automatizar o caminho crítico e deixar o time livre para escrever código melhor.
