# Pipeline com SonarQubeCloud + Slack

**Laboratório 3 – SonarQubeCloud + ESLint + Prettier e Slack**

* Análise estática com SonarQube/SonarCloud
    
* Integração de ESLint + Prettier na pipeline
    
* Bloquear PRs com cobertura &lt; 50%
    
* Notificações no Teams/Slack
    
* **Tópicos cobertos:** `SAST`, `Code Smells`, `Cobertura de testes`, `Notificações`
    

## **Pré requisitos**

* Ter feito o laboratório **Integração Contínua + ACR** **(**[https://joaochiroli.com.br/integracao-continua-acr](https://joaochiroli.com.br/integracao-continua-acr)) iremos usar a mesma estrutura
    
* Criar uma conta no Slack ([https://slack.com/](https://slack.com/))
    
* Criar uma conta SonarQubeCloud integrada com sua pipeline ([https://sonarcloud.io/login](https://sonarcloud.io/login))
    
    ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748310435658/c3727e84-e3fe-4c1f-9e15-e9ca0289b926.png align="center")
    
    * Basta ir dando next até aparecer a sessão com o **token**
        
    * Depois disso instale a extensão do SonarqubeCloud ([https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarcloud](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarcloud))
        
    * Depois copie o token que foi criado anteriormente → vá até o service connection no AzureDevops e efetue a configuração do SonarqubeCloud
        
        ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748310900240/7b038ba4-39c0-4084-be27-b9b6a14daf30.png align="center")
        

## **1️⃣ Step 1: Ponto de partida** `azure-pipelines.yml`

* Iremos criar uma variável para `workingDirectory: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'` deixando assim a pipeline mais clean
    
* E vamos comentar todo o stage 2 porque não iremos utiliza-lo agora
    

```yaml
trigger:
  branches:
    include:
    -  main

pr:
  branches:
    include:
    -  main

variables:
  workingDirectory: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'


stages:

# 1️⃣ Instala, Valida e Publica o Código

  - stage: InstallAndValidate
    displayName: Install And Validate
    pool: 
      vmImage: 'ubuntu-latest'
    jobs:
    - job: InstallAndValidate
      displayName: 'InstallAndValidate'
      steps:
      - checkout: self
        displayName: 'Checkout code'

      - task: UseNode@1
        inputs:
          version: '15.x'
        displayName: 'Install Node.js'

      - script: npm ci
        workingDirectory: '$(workingDirectory)'
        displayName: 'Install dependencies with ci'
        
      - script: npm run lint
        workingDirectory: '$(workingDirectory)'
        displayName: 'Linter (ESLint)'      

      - script: |
          npm install --save-dev prettier
          npm run prettier
        workingDirectory: '$(workingDirectory)'
        displayName: 'Formatter (Prettier)'
        continueOnError: 'true'

      - script: npm test 
        workingDirectory: '$(workingDirectory)'
        displayName: 'Run Tests with Coverage'
         
      - task: PublishPipelineArtifact@1
        inputs:
          targetPath: '$(workingDirectory)'
          artifact: 'validated-source'
          publishLocation: 'pipeline'
        displayName: 'Validated Source'

      
# # 2️⃣ Builda e publica a imagem no Acr

#   - stage: BuildAndPackage
#     displayName: Build And Package
#     dependsOn: InstallAndValidate
#     condition: succeeded()
#     pool: 
#       vmImage: 'ubuntu-latest'
#     jobs:
#     - job: BuildAndPackage
#       displayName: 'BuildAndPackage'
#       steps:
#       - task: DownloadPipelineArtifact@2
#         inputs:
#           artifactName: 'validated-source'
#           targetPath: '$(Pipeline.Workspace)/validated-source'

#       - task: AzureCLI@2
#         inputs:
#           azureSubscription: 'projeto-user-arm'
#           scriptType: 'bash'
#           scriptLocation: 'inlineScript'
#           inlineScript: | 
#             az acr login --name acrplaygroundproject         
#             docker build -t acrplaygroundproject.azurecr.io/playgroundgapp:$(Build.BuildId) $(Pipeline.Workspace)/validated-source
#             docker push acrplaygroundproject.azurecr.io/playgroundgapp:$(Build.BuildId)
#         displayName: 'Build and Push Docker Image'
```

## 2️⃣**Step 2: Adicionando o SonarQubeCloud na pipeline e alterando Dockerfile**

* Nessa step além de colocar o sonarqube na nossa pipeline iremos:
    
    * Comentar o ruin build
        
    * O Sonar pra funcionar corretamente é preciso acrescentar 3 tasks novas:
        
        * `task:SonarCloudPrepare@3` precisa estar no começo da pipeline
            
        * `task:SonarCloudAnalyze@3` e `task:SonarCloudPublish@3` devem estar na fase final da pipeline
            
    * Vamos acresentar o parâmetro fetchDepth: 0 logo após do parâmetro steps:
        
        * ```yaml
            jobs:
                - job: InstallAndValidate
                  displayName: 'InstallAndValidate'
                  steps:
                  - checkout: self
                    fetchDepth: 0 
                    displayName: 'Checkout code'
            ```
            
    * Também vamos ajustar a task de testes para que, ao executá-lo, seja gerado um arquivo de cobertura (coverage). Esse arquivo será enviado ao SonarQube para análise dos dados. Além disso, definiremos um limiar mínimo (threshold) de cobertura para garantir a qualidade do código.
        
* Nessa etapa também iremos alterar o Dockerfile que está na raiz do projeto, iremos fazer o build usando o Dockerfile e não na pipeline como foi feito nos últimos laboratórios, agora ele terá a seguinte estrutura:
    
    * ```yaml
        FROM node:15 AS builder
        WORKDIR /app
        COPY . .
        RUN npm ci --ignore-scripts && npm run build
        
        FROM node:15
        WORKDIR /app
        COPY --from=builder /app .
        RUN npm ci --ignore-scripts --omit=dev 
        EXPOSE 3000
        CMD ["npm", "start"]
        ```
        
* Como ficou o `azure-pipelines.yml`:
    
    ```yaml
    trigger:
      branches:
        include:
        -  main
    
    pr:
      branches:
        include:
        -  main
    
    variables:
      workingDirectory: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
    
    
    stages:
    
    # 1️⃣ Instala, Valida e Publica o Código
    
      - stage: InstallAndValidate
        displayName: Install And Validate
        pool: 
          vmImage: 'ubuntu-latest'
        jobs:
        - job: InstallAndValidate
          displayName: 'InstallAndValidate'
          steps:
          - checkout: self
            fetchDepth: 0 
            displayName: 'Checkout code'
    
          - task: UseNode@1
            inputs:
              version: '15.x'
            displayName: 'Install Node.js'
    
          - task: SonarCloudPrepare@3
            inputs:
              SonarQube: 'projeto-user-sonar'
              organization: 'joaochiroli123'
              scannerMode: 'cli'
              configMode: 'manual'
              cliProjectKey: 'joaochiroli123_Projeto-Pessoal'
              cliProjectName: 'Projeto Pessoal'
              cliSources: '.'
              extraProperties: |
                extraProperties: |
                  sonar.qualitygate.wait=true            
                  sonar.javascript.lcov.reportPaths=$(workingDirectory)/coverage/lcov.info
    
          - script: npm ci
            workingDirectory: '$(workingDirectory)'
            displayName: 'Install dependencies with ci'
            
          - script: npm run lint
            workingDirectory: '$(workingDirectory)'
            displayName: 'Linter (ESLint)'      
    
          - script: |
              npm install --save-dev prettier
              npm run prettier
            workingDirectory: '$(workingDirectory)'
            displayName: 'Formatter (Prettier)'
            continueOnError: 'true'
    
    
          - script: npm test -- --coverage --watchAll=false  --coverageThreshold='{"global":{"lines":50}}'
            workingDirectory: '$(workingDirectory)'
            displayName: 'Run Tests with Coverage'
            env:
              CI: true
    
          
          - task: PublishCodeCoverageResults@2
            inputs:
              codeCoverageTool: 'Clover'
              summaryFileLocation: '$(workingDirectory)/coverage/clover.xml'
              reportDirectory: '$(workingDirectory)/coverage'
              
    
          - task: SonarCloudAnalyze@3
            inputs:
              jdkversion: 'JAVA_HOME_17_X64'
            
    
          - task: SonarCloudPublish@3
            inputs:
              pollingTimeoutSec: '300'
    
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(workingDirectory)'
              artifact: 'validated-source'
              publishLocation: 'pipeline'
            displayName: 'Validated Source'
    ```
    
    * Após a execução da pipeline podemos verficar no SonarQube que algumas métricas já começam a aparecer
        
        ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748795450333/b613fc3a-2703-4b8a-ab82-9f55e0f5cb2e.png align="center")
        
    
    ## 3️⃣**Step 3: Integração com Slack**
    
    * Após criação da sua conta → Vá em canais e crie um projeto
        
    * No canto superior direito vá até os 3 pontos → clique em editar configurações
        
    * ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748796021333/68ac0a27-4f77-47d2-9f23-9aab3a5da8f5.png align="center")
        
        Ao abrir Editar configurações → Clique em integrações → Adicionar Apps e procure por Azure Devops Pipelines
        
        ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748796067994/20850716-12f5-48de-9b91-ebfdfe792786.png align="center")
        
        * Agora você precisa interagir com o bot usando `/azrepos signin` para fazer a conexão entre sua pipeline e o slack
            
        * ![Screenshot of Sign in prompt for Slack.](https://learn.microsoft.com/en-us/azure/devops/repos/integrations/media/integrations-slack/sign-in.png?view=azure-devops align="left")
            
        * Selecione `sign in` → copie o código que vai aparecer pra você → use este código em `enter code`
            
        
    * Agora é preciso fazer um ajuste nas politicas da organização do Azure Devops para que essa integração realmente funcione então siga os passos abaixo:
        
        * Entre na sua organização ([`https://dev.azure.com/{yourorganization}`](https://dev.azure.com/{yourorganization})).
            
        * Selecione **Organization settings**.
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748796878586/8f18b005-bdf7-4a60-9a42-5dde2e3b54f9.png align="center")
            
            * Deixe as configurações seguindo esta estrutura:
                
            * ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748796915102/d0535ac3-363b-4683-ae0b-919fbf4d3ab3.png align="center")
                
        * Agora basta executar sua pipeline que seu slack vai começar a receber as notificações:
            
            ![](https://cdn.hashnode.com/res/hashnode/image/upload/v1748797024700/9d0dab5a-53dc-4be6-bec0-645fb0429f9c.png align="center")
            
    
    ## 4️⃣**Step 4: Pipeline final** `azure-pipelines.yml`
    
    ```yaml
    trigger:
      branches:
        include:
        -  main
    
    pr:
      branches:
        include:
        -  main
    
    variables:
      workingDirectory: '$(Build.SourcesDirectory)/assessment-cc-sre-kubernetes-sr-01/codebase/rdicidr-0.1.0'
    
    
    stages:
    
    # 1️⃣ Instala, Valida e Publica o Código
    
      - stage: InstallAndValidate
        displayName: Install And Validate
        pool: 
          vmImage: 'ubuntu-latest'
        jobs:
        - job: InstallAndValidate
          displayName: 'InstallAndValidate'
          steps:
          - checkout: self
            fetchDepth: 0 
            displayName: 'Checkout code'
    
          - task: UseNode@1
            inputs:
              version: '15.x'
            displayName: 'Install Node.js'
    
          - task: SonarCloudPrepare@3
            inputs:
              SonarQube: 'projeto-user-sonar'
              organization: 'joaochiroli123'
              scannerMode: 'cli'
              configMode: 'manual'
              cliProjectKey: 'joaochiroli123_Projeto-Pessoal'
              cliProjectName: 'Projeto Pessoal'
              cliSources: '.'
              extraProperties: |
                extraProperties: |
                  sonar.qualitygate.wait=true            
                  sonar.javascript.lcov.reportPaths=$(workingDirectory)/coverage/lcov.info
    
          - script: npm ci
            workingDirectory: '$(workingDirectory)'
            displayName: 'Install dependencies with ci'
            
          - script: npm run lint
            workingDirectory: '$(workingDirectory)'
            displayName: 'Linter (ESLint)'      
    
          - script: |
              npm install --save-dev prettier
              npm run prettier
            workingDirectory: '$(workingDirectory)'
            displayName: 'Formatter (Prettier)'
            continueOnError: 'true'
    
    
          - script: npm test -- --coverage --watchAll=false  --coverageThreshold='{"global":{"lines":50}}'
            workingDirectory: '$(workingDirectory)'
            displayName: 'Run Tests with Coverage'
            env:
              CI: true
    
          
          - task: PublishCodeCoverageResults@2
            inputs:
              codeCoverageTool: 'Clover'
              summaryFileLocation: '$(workingDirectory)/coverage/clover.xml'
              reportDirectory: '$(workingDirectory)/coverage'
              
    
          - task: SonarCloudAnalyze@3
            inputs:
              jdkversion: 'JAVA_HOME_17_X64'
            
    
          - task: SonarCloudPublish@3
            inputs:
              pollingTimeoutSec: '300'
    
          - task: PublishPipelineArtifact@1
            inputs:
              targetPath: '$(workingDirectory)'
              artifact: 'validated-source'
              publishLocation: 'pipeline'
            displayName: 'Validated Source'
    
          
    # 2️⃣ Builda e publica a imagem no Acr
    
      - stage: BuildAndPackage
        displayName: Build And Package
        dependsOn: InstallAndValidate
        condition: succeeded()
        pool: 
          vmImage: 'ubuntu-latest'
        jobs:
        - job: BuildAndPackage
          displayName: 'BuildAndPackage'
          steps:
          - task: DownloadPipelineArtifact@2
            inputs:
              artifactName: 'validated-source'
              targetPath: '$(Pipeline.Workspace)/validated-source'
    
          - task: AzureCLI@2
            inputs:
              azureSubscription: 'projeto-user-arm'
              scriptType: 'bash'
              scriptLocation: 'inlineScript'
              inlineScript: | 
                az acr login --name acrplaygroundproject         
                docker build -t acrplaygroundproject.azurecr.io/playgroundgapp:$(Build.BuildId) $(Pipeline.Workspace)/validated-source
                docker push acrplaygroundproject.azurecr.io/playgroundgapp:$(Build.BuildId)
            displayName: 'Build and Push Docker Image'
    ```
