Integração e inspeção contínua com Jenkins e SonarQube

A integração contínua é uma prática de desenvolvimento em que os desenvolvedores integram frequentemente suas alterações de código em um repositório de controle de fontes, a partir do qual o software será compilado e testado periodicamente. Com um processo de build e teste automatizados, os defeitos no software podem ser identificados e tratados mais rapidamente pela equipe de desenvolvimento, trazendo mais segurança ao processo. Uma das ferramentas open source que podem ser usadas para integração contínua é o Jenkins.

A inspeção contínua é uma extensão da prática anterior, adicionando uma etapa de análise automatizada de qualidade a cada integração. Enquanto a integração contínua se preocupa mais com a estabilidade da construção do projeto, a inspeção contínua avalia várias métricas com a intenção de controlar a qualidade final do produto.

Para a inspeção contínua, uma opção é o SonarQube. O SonarQube também é um projeto open source e possui plugins para análise estática uma grande variedade de linguages de programação, com plugins desenvolvidos pela própria SonarSource (nota: alguns plugins exigem aquisição de uma licença) e plugins desenvolvidos pela comunidade.

Configurando um ambiente

Nesse exemplo, será utilizado o Docker para criar um ambiente de exemplo e então demonstrar um pouco essas duas ferramentas. Para facilitar a inicialização do ambiente, o docker-compose será usado usado para executar três containers: Jenkins, SonarQube e PostgreSQL (que será usado pelo SonarQube).

Começamos com a criação de um arquivo docker-compose.yml:

version: "2"

services:
  jenkins:
    image: jenkins/jenkins:alpine
    networks:
      - sonarnet
    ports:
      - "8080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
  
  sonarqube:
    image: sonarqube
    ports:
      - "9000:9000"
    networks:
      - sonarnet
    environment:
      - sonar.jdbc.url=jdbc:postgresql://db:5432/sonar
    volumes:
      - sonarqube_conf:/opt/sonarqube/conf
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions

  db:
    image: postgres:alpine
    networks:
      - sonarnet
    environment:
      - POSTGRES_USER=sonar
      - POSTGRES_PASSWORD=sonar
    volumes:
      - postgresql:/var/lib/postgresql
      - postgresql_data:/var/lib/postgresql/data

networks:
  sonarnet:
    driver: bridge

volumes:
  jenkins_home:
  sonarqube_conf:
  sonarqube_data:
  sonarqube_extensions:
  postgresql:
  postgresql_data: 

E então, na pasta desse arquivo, executar docker-compose up para criar e iniciar todo o ambiente declarado.

Configurando o Jenkins

Como é a primeira vez que esse ambiente é iniciado, será necessário concluir a configuração do Jenkins acessando http://localhost:8080.

Screenshot mostrando a página de configuração inicial do Jenkins que solicita uma senha para desbloqueio.
Screenshot mostrando a página de configuração inicial do Jenkins que solicita uma senha para desbloqueio.

Essa senha pode ser obtida olhando os logs gerados anteriormente pelo docker-compose up, ou seguindo os seguintes passos:

  • Executar docker-compose ps para verificar o nome do container. Exemplo:
       Name                     Command               State                 Ports              
-----------------------------------------------------------------------------------------------
docker_db_1          docker-entrypoint.sh postgres    Up      5432/tcp                         
docker_jenkins_1     /bin/tini -- /usr/local/bi ...   Up      50000/tcp, 0.0.0.0:8080->8080/tcpdocker_sonarqube_1   ./bin/run.sh                     Up      0.0.0.0:9000->9000/tcp           
  • Com o nome do container do Jenkins, executar docker exec <container do jenkins> cat /var/jenkins_home/secrets/initialAdminPassword. Exemplo:
$ docker exec docker_jenkins_1 cat /var/jenkins_home/secrets/initialAdminPassword
9c986ab357fc4782a1cb1c99bd32fa9c

Esse código é a senha que o Jenkins solicitada. As demais etapas de configuração do Jenkins se resumem na criação do usuário admin e instalação dos plugins.

Configurando uma análise no Jenkins

Para essa demonstração, utilizarei o seguinte exemplo do repositório do Spring Boot: https://github.com/spring-projects/spring-boot/tree/master/spring-boot-samples/spring-boot-sample-test. Esse é um projeto Java, que utiliza o Maven como ferramenta de build.

Antes de tudo, precisamos configurar uma instalação do Maven no Jenkins. Para isso, iremos em "Manage Jenkins" > "Global Tool Configuration". Na seção "Maven", clicar em "Maven Installations..." e adicionar uma nova configuração. Nesse exemplo, chamei a instalação de "Maven" também.

Screenshot mostrando que o campo
Screenshot mostrando que o campo "Name" foi preenchido com o texto "Maven" e a checkbox "Install automatically" está marcada.

Voltando então à página inicial do Jenkins, no menu lateral vamos em "New Item". Para esse exemplo, escolhi o nome "spring-boot-test-sample" e tipo "Pipeline".

Screenshot confirmando o que o texto diz anteriormente.
Screenshot confirmando o que o texto diz anteriormente.

Após confirmar, a página de configuração do novo projeto será aberta.

Na seção "Pipeline", no campo "Script" informar o seguinte código:

node {
   def mvnHome
   stage('Preparation') {
      git 'https://github.com/spring-projects/spring-boot.git'
      mvnHome = tool 'Maven'
   }
   stage('Build') {
      dir('spring-boot-samples/spring-boot-sample-test') {
        sh "'${mvnHome}/bin/mvn' clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=http://sonarqube:9000"
      }
   }
}

Esse script é um Jenkins Pipeline que fará o clone do repositório Git correspondente e executará o Maven, que também executará a análise de qualidade do fonte no SonarQube. Aqui foram incluídos os seguintes goals na chamada do maven:

  • org.jacoco:jacoco-maven-plugin:prepare-agent: instrumenta o código para coletar dados sobre a cobertura dos testes unitários.
  • sonar:sonar -Dsonar.host.url=http://sonarqube:9000: executa a análise do SonarQube, definindo a URL do SonarQube.

Após confirmar e voltar para a página do projeto, podemos clicar em "Build Now" e aguardar o resultado da pipeline.

Screenshot mostrando que a pipeline executou corretamente.
Screenshot mostrando que a pipeline executou corretamente.

Esse projeto pode posteriormente ser configurado para fazer pooling no repositório Git em busca de novos commits (ou até usar um hook do Git/GitHub) para que o build seja executado automaticamente a cada alteração do fonte.

Verificando o resultado no SonarQube

O SonarQube pode ser acessado pelo endereço http://localhost:9000. Ao acessá-lo, é possível ver que o projeto configurado anteriormente no Jenkins já é listado.

Listagem de projetos no SonarQube. Aparece um projeto chamado
Listagem de projetos no SonarQube. Aparece um projeto chamado "Spring Boot Test Sample".

É possível clicar no projeto e visualizar todos os detalhes.

Screenshot mostrando as métricas calculadas pelo SonarQube: 9 code smells, 31 testes e 86,2% de cobertura.
Screenshot mostrando as métricas calculadas pelo SonarQube: 9 code smells, 31 testes e 86,2% de cobertura.

Veja que o SonarQube identificou 9 code smells nesse projeto. Clicando no indicador de code smells ou abrindo a aba "Issues" podemos obter mais detalhes sobre os problemas identificamos pelo analisador de código Java, exemplo:

Listagem de problemas no SonarQube.
Listagem de problemas no SonarQube.

O SonarQube oferece vários recursos para medir e gerenciar a qualidade do código, como quality profiles e quality gates, mas não entrarei em detalhes aqui.

Pronto!

Um processo de integração de inspeção contínua bem estruturado com certeza ajudarão você e a sua empresa a melhorarem a qualidade do software e a reduzir os custos relacionados aos defeitos que passariam despercebidos.

Espero que esse post tenha sido útil. ;-)