Validando código com GitHub Actions e SonarCloud

Freneticamente os versionadores de código estão juntando o fruto do trabalho das várias pessoas envolvidas em um projeto. Contudo, ainda que você tenha testes unitários, se a execução deles não estiver atrelada e condicionando o PR, nada impede que um código que quebre padrões e comportamentos seja adicionado a sua base de códigos. Para situações como essas existem as GitHub Actions.

Neste breve manual, vamos aprender como configurar uma GitHub Action que rode os testes de unidade e rode a análise do Sonar, enviando para o SonaCloud.io. Chamo apenas atenção que, para utilizar o SonarCloud gratuitamente, você terá que tornar público o seu repositório. Caso opte por pagar a assinatura do SonarCloud, os passos são os mesmos. Apenas o repositório que poderá ser privado. Caso você também possua uma opção on-premisse do SonarQube, poderá utilizá-la normalmente. Vamos ao conteúdo.

Configurando o SonarCloud.io

Integração com GitHub

Acesse sonarcloud.io. A tela inicial irá pedir que você se cadastre ou faça o login. Aconselho que você faça o cadastro e integre a sua conta do GitHub com o SonarCloud. Vai facilitar a sua vida. Infelizmente não tenho os prints para te mostrar aqui como fazer. Mas o processo é realmente bastante simples.

Após cadastrado você terá acesso a tela de projetos. Se não me engano, já até poderá selecionar projetos durante o processo de cadastro. Mas não faça isso por agora. Vamos seguir aqui os passos juntos.

Agora é o momento de você configurar um projeto. Diferente da versão on-premisse, onde os projetos são adicionados ao seu sonar por padrão, no SonarCloud você precisa configurá-los de antemão. É o que vamos fazer. Você pode clicar no botão “Analyze new project”, que está no centro da tela, ou no botão com o símbolo “+” que está no canto superior direito da página, ao lado da sua foto de perfil e selecionar a opção “Analyze new project”.

Na janela que se abre, você verá todos os projetos do seu GitHub. Os privados aparecem acinzentados e com um cadeado, impedindo sua seleção. Marque o projeto que deseja analisar e clique no botão “Set Up”.

Neste momento, o SonarCloud fará uma análise do conteúdo do seu projeto, tentando descobrir se ele oferece suporte. Para alguns tipos de projeto o sonar provê integração automática. Com projetos C#, contudo, é preciso que você faça uso de algum pipeline de CI. Que no nosso caso é o GitHub. Clique no botão “With GitHub Actions”.

A próxima etapa é muito importante. Para que você não exponha suas credenciais de acesso, as GitHub Actions e o SonarCloud fazem uso de um token de autenticação. O token é mostrada logo após clicar no botão. COPIE IMEDIATAMENTE o conteúdo do token. Se você o perder, terá de criar outro. Já te explico o que você vai fazer com esse token. Apenas clique no botão “Continue”.

Ao selecionar a opção “.Net”, o sonar te mostra um arquivo de exemplo de workflow. Em situações mais simples, você poderia utilizar esse arquivo. Mas como temos outros planos, apenas ignore.

E assim está completa a configuração do nosso projeto. Pelo menos o que diz respeito a integração. Você pode clicar no ícone “SonarCloud” ou ainda em “My Projects” para visualizar os projetos configurados.

Configurações de análise

Mas ainda é necessário configurar a análise. Esta etapa serve para você dizer ao sonar onde estão os arquivos com informações do code-coverage, quais projetos devem ser ignorados, quais arquivos devem ser ignorados (os gerados automaticamente, por exemplo) e afins.

Se você não acertar todas a configurações de primeira, não se preocupe. Você pode alterar e disparar manualmente novas execuções. Clique no projeto ou em “Configure Analysis”. O SonarCloud irá te redirecionar para uma página que já vimos antes. Mas diferente do que fizemos, clique no menu Administration -> General Settings.

Nesta tela, todas as configurações de análise de código estão acessíveis. Apenas vá alternando entre as abas para configurar o que for pertinente.

Ao clicar em “Language” e selecionar C#, por exemplo, você pode informar a localização dos arquivos de cobertura em vários formatos. Na aplicação de exemplo, estamos utilizando o opencover, que é gerado dentro dos projetos de testes, que estão em __tests__. Como o nome dos arquivos tem suporte a Glob, posso digitar __tests__/**/coverage.opencover.xml para encontrar a cobertura de código.

Você pode fazer mais configurações no SonarCloud, definindo arquivos que deve ignorar na análise, por exemplo (veja a aba Analysis Scope), mas não vou me aprofundar nesse ponto agora. Este é o momento em que mudamos do SonarCloud para o GitHub.

Configurando o repositório no GitHub

Depois de acessar o seu repositório no GitHub, clique na aba “Actions”. O GitHub já é esperto o bastante para saber qual é a linguagem do seu projeto e já sugere opções de actions para utilizar. Clique na opção “.Net Core”.

Gentilmente o GitHub irá criar um template de action que, basicamente, já compila e roda os testes do seu projeto. E do lado direito também é apresentada uma lista de snippets que podem auxiliar na criação da sua action.

Customizando a Action padrão de .Net Core

Vamos aproveitar esse arquivo para que eu te explique algumas coisas básicas:

Nesta parte do código você está definindo quando a sua action deve ser executada. Para este caso, nossa action será executado sempre que um push for feito na branch develop ou um PR for aberto para a branch develop. Caso você queira que a execução seja feita para todas as branches, substitua develop por ‘**’.

on:
  push:
    branches: [ develop ]
  pull_request:
    branches: [ develop ]

Os demais pontos são autoexplicativos, mas ainda quero te chamar atenção para:

    - name: Setup .NET Core
      uses: actions/setup-dotnet@v1
      with:
        dotnet-version: 3.1.301

Esse conjunto de código instalada a versão especificada em dotnet-version no ambiente em que estamos rodando – ubuntu-latest. Será possível utilizar a versão dotnet 5, que está em pré-release? Claro que sim! Basta que você altere a versão do SDK para:

      with:
        dotnet-version: 5.0.100-preview.2.20176.6

Precisa ser esse nome grande, porque é o disponível no metadata da versão (disponível em https://dotnetcli.blob.core.windows.net/dotnet/release-metadata/5.0/releases.json). Um ponto triste é que AINDA não há versão do dotnet-sonarscanner rodando na versão net5. Contudo, já existe PR aberto para essa atualização. Aguarde um pouco antes de atualizar os projetos 😉

Adicionar análise do SonarCloud na Action de .Net Core

Eu não utilizei o arquivo que o SonarCloud sugeriu por um motivo simples: Ele usa Windows por padrão. E eu quero que os meus artefatos sejam produzidos e validados no Linux. Por isso grande parte do que vamos utilizar aqui é o mesmo que já foi fornecido pela SonarCloud, mas com as devida adaptações. Vamos lá.

O script que o SonarCloud fornece possui algumas opções de caching que são interessantes. Elas podem tornar o processo mais rápido, já que reaproveitam etapas já executadas. Por isso, vamos introduzir esse conteúdo no nosso arquivo:

      - name: Cache SonarCloud packages
        uses: actions/cache@v1
        with:
          path: ~\sonar\cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar
      
      - name: Cache SonarCloud scanner
        id: cache-sonar-scanner
        uses: actions/cache@v1
        with:
          path: .\.sonar\scanner
          key: ${{ runner.os }}-sonar-scanner
          restore-keys: ${{ runner.os }}-sonar-scanner
      
      - name: Install SonarCloud scanner
        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
        run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner

Aqui entra a parte de teste e análise, que de fato tanto importa para nós.

      - name: Test and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: |
          ./.sonar/scanner/dotnet-sonarscanner begin /k:"Project-key" /o:"user" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
          dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover -l trx
          ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"

A parte importante nesse trecho de código é o ${{ secretes.SONAR_TOKEN }}. Lembra que no começo nós guardamos essa informação que o SonarCloud gerou para nós? É hora de utilizá-la! NÃO! Você não vai colar essa chave no seu arquivo. Para funcionar o CI corretamente, o GitHub implementou uma área no repositório chamada “Secrets”, onde todos os dados sensíveis podem ser armazenados. Entre eles, o nosso Token! Para acessar, você precisa ir até o seu repositório, clicar na aba Settings, e depois acessar o menu Secrets. Clique em New Secret para criarmos uma nova Secret com o nome SONAR_TOKEN (ou qualquer outro que você preferir).

Nem você e nem ninguém jamais terá acesso ao conteúdo dessa secret novamente. E agora voltando ao nosso arquivo, ele deverá ficar assim:

name: Validate code

on:
  push:
    branches: ["**"]
  pull_request:
    branches: [develop]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2

      - name: Setup .NET Core
        uses: actions/setup-dotnet@v1
        with:
          dotnet-version: 5.0.100-preview.2.20176.6

      - name: Cache SonarCloud packages
        uses: actions/cache@v1
        with:
          path: ~\sonar\cache
          key: ${{ runner.os }}-sonar
          restore-keys: ${{ runner.os }}-sonar

      - name: Cache SonarCloud scanner
        id: cache-sonar-scanner
        uses: actions/cache@v1
        with:
          path: .\.sonar\scanner
          key: ${{ runner.os }}-sonar-scanner
          restore-keys: ${{ runner.os }}-sonar-scanner

      - name: Install SonarCloud scanner
        if: steps.cache-sonar-scanner.outputs.cache-hit != 'true'
        run: dotnet tool update dotnet-sonarscanner --tool-path ./.sonar/scanner

      - name: Install project dependencies
        run: dotnet restore

      - name: Test and analyze
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        run: |
          ./.sonar/scanner/dotnet-sonarscanner begin /k:"ftathiago_esbc" /o:"ftathiago" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"
          dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover -l trx
          ./.sonar/scanner/dotnet-sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"

      - name: Build
        run: dotnet build --configuration Release --no-restore

Particularmente eu prefiro criar dois Jobs: Um para análise e outro para o build (e quem sabe um terceiro para deploy?). Porém isso ampliaria demais o contexto e a complexidade desse artigo.

Agora, quando você abrir um pull request para a develop ou fazer qualquer push, o seu PR ficará decorado da seguinte maneira:

Mas claro, só se você tiver passado pelo code review 😉

E voltando para o SonarCloud, o que nós temos todos os detalhes da análise do nosso código:

Nada mal. Mas ainda tem algumas coisinhas para melhorar.

Espero que essas dicas sirvam pra te ajudar a manter a qualidade do código, além de tirar o medo de brincar com as Actions do GitHub!

8 thoughts on “Validando código com GitHub Actions e SonarCloud

        1. Cara, o que pode estar acontecendo é você não ter instalado o `coverlet.msbuild` ao seu projeto de teste. Esse pacote que é o responsável por gerar os arquivos de análise de cobertura. Dê uma olhada nesse outro artigo, onde eu falo do coverlet e até como gerar esse repo localmente (https://www.blogdoft.com.br/2020/06/11/como-gerar-relatorios-de-code-coverage-para-codigo-c/)

          Outro provável ponto de erro pode estar na configuração do Sonar. Tente entrar nas configurações do sonar e confirmar se o path que vc informou está correto. Um teste válido é: se vc rodar os mesmos comandos na sua máquina… deve funcionar (desde que esteja com as secrets iguais e tal). Depois volta aí e diz se funcionou 🙂

          1. CONSEGUIIIIIIIIII KKKKKK
            Muito obrigado!!!! peguei o caminho “chumbado” do log do GitHub e coloquei na config do Sonar e foi

          2. Show!!! Sobre ter de baixar o SDK, eu acho estranho isso. Você utilizou os templates de teste do .net ou criou um projeto na mão?

            Sobre o path chumbado… funciona, mas eu aconselho utilizar os path relativos.
            Agora que vc viu que funciona, continue testando! É bem da hora, né? Ver o trem rodando é sensacional!

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Esse site utiliza o Akismet para reduzir spam. Aprenda como seus dados de comentários são processados.