Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - Gerenciador de versões de bibliotecas com Versions

arthurfnsc

Arthur Fonseca

Posted on December 14, 2022

Cursos que formaram meu caráter: Desenvolvimento web com Quarkus - Gerenciador de versões de bibliotecas com Versions

"Você torna o mundo um lugar melhor fazendo melhorias diárias para se tornar a melhor versão de si mesmo". Roy T. Bennett, The Light in the Heart

Nesse terceiro posts falaremos sobre o gerenciamento de versões utilizando o Gradle Versions Plugin do Ben Manes. Isso nos ajudará a manter nosso projeto com as dependências atualizadas.

Discutiremos também os problemas que isso pode causar, e o porque não termos um fluxo de Continuous Integration pode nos dar grandes dores de cabeça.

Por fim, falarei do Dependabot. Não apliquei nesse projeto, mas vou colocar no backlog, e quem sabe escrever em um outro post.

Esse artigo faz parte de uma série, abaixo é possível encontrar a lista completa de artigos.

Estamos nos baseando no curso Desenvolvimento web com Quarkus do @viniciusfcf.

O repositório que estamos utilizando é:


O tempo passa, o tempo voa

O tempo é implacável! Mudanças acontecem constantemente, e isso impacta decisões passadas que tomamos.

No mundo do desenvolvimento de software essa máxima permanece! Bibliotecas e frameworks são atualizados ou descontinuados; outras vezes podemos descobrir uma vulnerabilidade nas nossas dependências e sermos forçados a realizar mudanças (falaremos um pouco disso em um post futuro chamado Plugins do Gradle: Validação de vulnerabilidades de dependências com OWASP Dependency Check).

Uma das primeiras ideias que temos é: Vamos deixar o nosso projeto sempre com a última versão das bibliotecas disponíveis!

"Para todo problema complexo existe sempre uma solução simples, elegante e completamente errada". H L Mencken

Qual é o problema podemos ter nessa primeira abordagem?

Para atualizar uma dependência em um projeto é importante termos algumas coisas em mente, como:

  • Existe uma equipe de sustentação que precisa garantir que as bibliotecas que estamos usando estão em conformidade com uma arquitetura corporativa?
  • As bibliotecas que estamos usando funcionam corretamente se todas forem atualizadas para sua última versão?
  • O servidor em que a solução será implantada tem suporte a tais bibliotecas?
  • dentre outros.

Nesse post falarei de uma estratégia para essa atualização, porém, é importante ressaltar que isso não é a bala de prata para todos os projetos, e que sempre é bom acordar as decisões com o time, equipe de arquitetura e/ou liderança técnica responsável.


Utilizando um plugin

Um plugin bem interessante do Gradle é o Gradle Versions Plugin do Ben Manes. Sua aplicação é bem simples.

Utilizando o nosso projeto de referência, no nosso arquivo build.gradle na raiz do projeto podemos ver algo como o seguinte:

plugins {
    ...
    id "com.github.ben-manes.versions" version "$versionsVersion" apply false
    ...
}

...

subprojects {
    if (it.name != 'applications') {
        ...
        apply from: "$rootDir/plugins/docs.gradle"
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode

As configurações do arquivo plugins/docs.gradle serão melhor explicadas adiante.

Caso você esteja com dúvida sobre esses $ no código, dá uma olhada no post anterior em que falamos sobre Variáveis de projeto.

Essa primeira parte da configuração nos ajuda a buscar as atualizações de todas as nossas bibliotecas através da seguinte task do Gradle:

foo@bar:~$ ./gradlew dependencyUpdates
Enter fullscreen mode Exit fullscreen mode

Executando a task vamos ter um resultado semelhante a:

./gradlew dependencyUpdates

------------------------------------------------------------
:applications:cadastro Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
 - com.approvaltests:approvaltests:18.5.0
 - com.github.database-rider:rider-cdi:1.35.0
 - com.github.database-rider:rider-core:1.35.0
 - com.google.code.gson:gson:2.10
 - org.mapstruct:mapstruct:1.5.3.Final
 - org.mapstruct:mapstruct-processor:1.5.3.Final
 - stax:stax:1.2.0

The following dependencies have later milestone versions:
 - io.opentracing.contrib:opentracing-jdbc [0.2.4 -> 0.2.15]
     https://github.com/opentracing-contrib/java-jdbc
 - io.quarkus:io.quarkus.gradle.plugin [2.14.3.Final -> 3.0.0.Alpha2]
 - io.quarkus:quarkus-arc [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-arc-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-config-yaml [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-config-yaml-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-core-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-flyway [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-flyway-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-orm-panache [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-orm-panache-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-validator [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-validator-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-jdbc-postgresql [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-jdbc-postgresql-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-junit5 [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-resteasy-jsonb [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-resteasy-jsonb-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-jwt [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-jwt-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-metrics [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-metrics-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-openapi [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-openapi-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-opentracing [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-opentracing-deployment [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus
 - io.quarkus.platform:quarkus-bom [2.14.3.Final -> 3.0.0.Alpha2]
     https://github.com/quarkusio/quarkus-platform
 - io.rest-assured:rest-assured [4.5.1 -> 5.3.0]
     http://code.google.com/p/rest-assured
 - org.testcontainers:postgresql [1.17.5 -> 1.17.6]
     https://testcontainers.org

Gradle release-candidate updates:
 - Gradle: [7.6: UP-TO-DATE] 
------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Nesse caso, removi os logs referentes aos demais subprojetos só para termos um resultado menor.

Podemos ver por exemplo que as seguintes dependências estão atualizadas no nosso projeto:

The following dependencies are using the latest milestone version:
  - com.approvaltests:approvaltests:18.5.0
  - com.github.database-rider:rider-cdi:1.35.0
  - com.github.database-rider:rider-core:1.35.0
  - com.google.code.gson:gson:2.10
  - org.mapstruct:mapstruct:1.5.3.Final
  - org.mapstruct:mapstruct-processor:1.5.3.Final
  - stax:stax:1.2.0
Enter fullscreen mode Exit fullscreen mode

E que a dependência do opentracing-jdbc está desatualizada:

The following dependencies have later milestone versions:
  - io.opentracing.contrib:opentracing-jdbc [0.2.4 -> 0.2.15]
Enter fullscreen mode Exit fullscreen mode

No entanto, temos um efeito colateral da forma como o plugin está funcionando. E isso pode ser visto em todas as dependências do Quarkus que estamos usando, a sugestão de atualização para versão 3.0.0.Alpha2:

  - io.quarkus:io.quarkus.gradle.plugin [2.14.3.Final -> 3.0.0.Alpha2]
Enter fullscreen mode Exit fullscreen mode

Na maioria das vezes não queremos bibliotecas de Release, Alpha ou Beta no nosso projeto por não serem versões finais, e poderem conter alterações futuras. Para resolver esse problema, o próprio plugin nos oferece uma solução em suas configurações. No nosso caso, podemos encontrar elas em plugins/docs.gradle; eu havia comentado elas para evidenciar o problema anterior:

apply plugin: "com.github.ben-manes.versions"
...

dependencyUpdates {
    checkForGradleUpdate = true
    outputFormatter = "json"
    outputDir = "build/reports/docs/dependencyUpdates"
    reportfileName = "report"

    resolutionStrategy = {
        componentSelection { rules ->
            rules.all { ComponentSelection selection ->
                boolean rejected = ["alpha", "beta", "rc", "cr", "m"].any { qualifier ->
                    selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
                }
                if (rejected) {
                    selection.reject("Release candidate")
                }
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Executando novamente a task, nosso resultado para :applications:cadastro é o seguinte:

./gradlew dependencyUpdates

------------------------------------------------------------
:applications:cadastro Project Dependency Updates (report to plain text file)
------------------------------------------------------------

The following dependencies are using the latest milestone version:
 - com.approvaltests:approvaltests:18.5.0
 - com.github.database-rider:rider-cdi:1.35.0
 - com.github.database-rider:rider-core:1.35.0
 - com.google.code.gson:gson:2.10
 - io.quarkus.platform:quarkus-bom:2.14.3.Final
 - org.mapstruct:mapstruct:1.5.3.Final
 - org.mapstruct:mapstruct-processor:1.5.3.Final
 - stax:stax:1.2.0

The following dependencies have later milestone versions:
 - io.opentracing.contrib:opentracing-jdbc [0.2.4 -> 0.2.15]
     https://github.com/opentracing-contrib/java-jdbc
 - io.quarkus:io.quarkus.gradle.plugin [2.14.3.Final -> 2.15.0.Final]
 - io.quarkus:quarkus-arc [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-arc-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-config-yaml [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-config-yaml-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-core-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-flyway [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-flyway-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-orm-panache [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-orm-panache-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-validator [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-hibernate-validator-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-jdbc-postgresql [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-jdbc-postgresql-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-junit5 [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-resteasy-jsonb [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-resteasy-jsonb-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-jwt [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-jwt-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-metrics [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-metrics-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-openapi [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-openapi-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-opentracing [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.quarkus:quarkus-smallrye-opentracing-deployment [2.14.3.Final -> 2.15.0.Final]
     https://github.com/quarkusio/quarkus
 - io.rest-assured:rest-assured [4.5.1 -> 5.3.0]
     http://code.google.com/p/rest-assured
 - org.testcontainers:postgresql [1.17.5 -> 1.17.6]
     https://testcontainers.org

Gradle release-candidate updates:
 - Gradle: [7.6: UP-TO-DATE]

------------------------------------------------------------
Enter fullscreen mode Exit fullscreen mode

Problema de atualização de bibliotecas resolvido? É só ir no nosso gradle.properties e pronto.

Americas Got Talent What GIF by iOne Digital - Find & Share on GIPHY

Discover & share this iOne Digital GIF with everyone you know. GIPHY is how you search, share, discover, and create GIFs.

favicon giphy.com

O que fizemos foi apenas uma forma de evidenciar quais as bibliotecas que estão desatualizadas.

Antes de mudar as versões de todas as que estão aparecendo como desatualizadas é importante validarmos se isso não impacta as outras bibliotecas do nosso projeto. Em muitos casos, uma biblioteca utiliza outras bibliotecas, o que chamamos de dependências transitivas. Com isso, para que uma biblioteca funcione corretamente é importante que suas dependências transitivas também estejam.

Pegue por exemplo a Spring Boot Starter Data JPA. Não vou me lembrar da versão exata, mas o Java teve uma grande mudança na versão 8, dentre elas, a adição de Optional.

Me lembro quando utilizei o plugin de versões para mudar a versão do Spring Boot em um outro projeto que eu estava trabalhando há alguns anos atrás, e de repente minhas classes de service começaram a dar problema. Ao investigar, percebi que a assinatura de alguns métodos mudaram, e ao invés de retornar uma entidade, estavam retornando Optional.

Nesse caso em específico, eu consegui mudar o meu código para que a atualização fizesse sentido, mas imagina se o problema fosse em uma classe de dependência transitiva que temos no projeto.

Como então resolver o problema de saber se a atualização de uma dependência impacta ou não outras dependências, e consequentemente o projeto? Podemos ir na tentativa e erro, subindo nossa aplicação e vendo se todos os fluxos dela continuam funcionando. Mas será que não tem uma forma melhor?

Continuous Integration

Em posts mais adiante falaremos sobre GitLab e pipelines. Quando falarmos sobre pipelines discorreremos sobre a importância do processo de automação dado o número de alterações de um projeto por várias pessoas.

Uma forma que poderíamos fazer seria então termos a nossa fase de testes executada em nossa pipeline sempre que um merge request for realizado, por exemplo.

Podemos agora fazer mudanças em versões de uma biblioteca e ver se nossos testes não quebram. Para essa abordagem é importante que confiemos nos nossos testes e na quantidade de camadas cobertas por eles.

Quando digo em confiar em nossos testes, me refiro aos seguintes testes que muitas vezes encontramos em nossos projetos:

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

@QuarkusTest
public class Teste {

    private int adicao(int a, int b) {
        return a + b;
    }

    @Test
    void deveAdicionar() {
        Assertions.assertNotNull(adicao(1, 2));
    }
}
Enter fullscreen mode Exit fullscreen mode

O teste com assertNotNull pode nos dar um falso positivo sobre o funcionamento do nosso método. Imagine por exemplo que um desenvolvedor subiu uma versão mudando o método para algo como:

private int adicao(int a, int b) {
    return a - b;
}
Enter fullscreen mode Exit fullscreen mode

Nosso teste não iria quebrar! Bem importante usar testes com assertNotNull com parcimônia.

Esse foi apenas um exemplo de teste que poderia ser melhor testado, mas imagina se fizermos testes que não quebrem, porém, quando fizermos o deploy em produção aparecerem incompatibilidades.

Uma abordagem que podemos seguir para saber se temos bons testes foi escrita por @alexandreaquiles:

Testes de mutantes: quem vigia os vigilantes? | Alura

Quis custodiet ipsos custodes? - Decimus Junius Juvelanis A frase acima, do poeta romano Juvelanis, é geralmente traduzida como: "Quem vigia os vigilant

favicon alura.com.br

Mas voltando à atualização das nossas bibliotecas, não seria maravilhoso se tivesse uma forma melhor do que nós mesmos abrirmos merges request para cada mudança?

Não seria maravilho se...?

Dependabot

O Dependabot é uma solução que funciona com Ruby, JavaScript, Python, PHP, Elixir, Elm, Go, Rust, Java e .NET.

E seu funcionamento se dá em uma constante validação de novas versões e abertura de merge requests para um repositório configurado. Não configurei nesse projeto, mas vocês podem ver a ideia dele nesse outro repositório que criei.

Com isso, é possível aprovar ou não os merge requests que o Dependabot fará para o nosso projeto.


Conclusão

A atualização de dependências de um projeto é um processo que poderá ser necessário em projetos, porém é importante avaliarmos os impactos das mesmas e consequências em outras bibliotecas do projeto.

Soluções como Gradle Versions Plugin ou Dependabot podem fazer parte do nosso processo para uma maior assertividade na alterações de tais versões, atrelados a processos como Continuous Integration.


Esse post faz parte de uma série sobre Cursos que formaram meu caráter: Desenvolvimento web com Quarkus.

A série completa é:

💖 💪 🙅 🚩
arthurfnsc
Arthur Fonseca

Posted on December 14, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related