Come integrare Sonarqube in Gitlab

Bentornati in questo nuovo articolo dedicato al mondo del DevOps!

Recentemente mi sono ritrovato a dover configurare un ambiente DevOps da zero e tra le varie attività c’è stata quella di integrare Sonarqube con Gitlab.

Sonarqube è uno strumento molto utile e popolare, in grado di effettuare delle analisi del codice sorgente e di trovare bug, problemi di sicurezza (OWASP), ecc.

Nel disegno delle mie pipeline l’analisi con questo strumento è un passaggio obbligatorio, sia per cercare di mantenere “alta” la qualità del codice, sia per rilevare eventuali problemi introdotti con gli ultimi commit. Vediamo ora come configurare il tutto.

Requisiti

  • Istanze già installate – Do per scontato che abbiate un’istanza di Gitlab e una di sonarqube già installata. Gli screen che vedrete in questo articolo si riferiscono ad un Gitlab self-hosted ed un Sonarqube community edition (avviato con Docker).
  • Runner Gitlab già attivi – Ne ho parlato in questo tutorial, i runner sono i processi che permettono di eseguire le pipeline. Se hai un’istanza self hosted dovrai configurarli, se hai Gitlab Cloud li avrai già configurati

Obiettivo

Eseguire l’analisi Sonarqube durante l’esecuzione delle pipeline CI/CD di Gitlab per rilevare eventuali problemi

Step di configurazione

1. Gitlab – Creazione utenza tecnica

Consiglio sempre di creare un’utenza tecnica, principalmente per il fatto che non leghiamo nessun utente dell’azienda ad una funzione aziendale (e se poi se ne va e le vostre pipeline smettono di funzionare?)

Il primo passo è quindi di creare una nuova utenza, con un’email condivisa tra più membri del team. Colleghiamoci a Gitlab con un account amministratore e premiamo su Admin–> Users –> New users. Chiamiamolo “sonarqube_user” e come access_level lasciamo “Regular

Ora che l’utente è creato, eseguiamo l’accesso con le sue credenziali e clicchiamo su “Access tokens” nel menù di sinistra. Dovremo creare un token di accesso da usare in Sonarqube. Premiamo su Add new token

2. Sonarqube – Integrazione con Gitlab

Spostiamoci su Sonarqube per integrare i due strumenti mediante il token utente creato prima. Clicchiamo su Administration presente sul menù in alto.

Subito dopo clicchiamo su DevOps Platform Integrations –> Gitlab.

Premiamo su Create Configuration, inserendo un nome, un URL del vostro Gitlab (es. https://gitlab.yourdomain.com/api/v4) e il token dell’utente. Salviamo e premiamo il pulsante “Check Configuration” per testare che l’integrazione sia corretta.

3. Sonarqube – importazione progetto

A questo punto i due ambienti sono collegati ma manca ancora qualche passaggio per concludere il setup. In Sonarqube ogni progetto deve avere una propria configurazione, dal menù in alto clicchiamo su “Projects” per tornare alla home e subito dopo Create project –> From Gitlab.

Nella pagina che apparirà vedremo la lista di tutti i progetti presenti su Gitlab, selezionandoli sarà possibile importarli

Nello step successivo verrà chiesto cosa considerare “nuovo codice”: quello prodotto ad ogni commit, la sommatoria del codice dopo N giorni, in funzione delle versioni, ecc. La scelta è vostra, nel caso foste in dubbio la cosa migliore è scegliere “use global settings”, che considera nuovo il codice modificato ad ogni commit.

Nella pagina successiva ci verrà chiesto con quale strumento eseguiamo la nostra pipeline e Gitlab ci aiuterà nella configurazione dell’ambiente. Ci verranno forniti un token e la project key: il primo verrà usato per autenticarsi su Sonarqube, il secondo servirà per legare l’analisi fatta al progetto corretto.

ATTENZIONE

Ci sono alcuni casi particolari dove è bene selezionare l’opzione “Locally” anziché With Gitlab CI. In questo step verrà semplicemente generato il token del progetto senza nessun dettaglio rispetto all’ambiente. Io ho dovuto seguire questo step perché la mia macchina di Devops ha un’architettura di tipo ARM e l’immagine Docker di analisi non la supporta (al momento). Per eseguire l’analisi ho semplicemente eseguito il comando tramite node nella pipeline.

4. Gitlab – Creazione variabili d’ambiente

A prescindere che il vostro repository abbia un solo progetto o molti, è buona cosa creare delle variabili da utilizzare nelle vostre pipeline, in modo tale da separare i valori di configurazione dalle pipeline.

La prima variabile di ambiente che andremo configurare sarà quella che conterrà l’indirizzo del nostro Sonarqube e la creeremo come variabile di istanza: qualsiasi progetto la vedrà e potrà utilizzarla nelle sue pipeline. Tutte le pipeline si riferiranno a questo valore e se un domani l’indirizzo dovesse cambiare voi dovrete aggiornare solamente la variabile anziché tutte le pipeline.

Andiamo su Admin –> Settings –> CI/CD ed espandiamo la sezione “Variables“. Premiamo sul pulsante “Add variable

Rimuoviamo il flag “Protected value” (in modo tale da rendere visibile il valore a qualsiasi pipeline/branch) e lasciamo la visibilità “Visible“. Inseriamo SONAR_HOST_URL come chiave e l’indirizzo di Sonarqube  come valore, senza / finale.

Ora dobbiamo creare un’altra variable con il token del progetto ma lo creeremo a livello di progetto anziché a livello di istanza. In Gitlab andiamo sul progetto che abbiamo appena importato in Sonarqube e dal menù laterale selezioniamo Settings –> CI/CD

La schermata è simile a quella di prima (con qualche voce in più), espandiamo “variables” e aggiungiamo una nuova variabile, avendo cura di selezionare “Masked” nella sezione di Visibility.

Aggiungiamo anche la terza ed ultima variabile (a livello di progetto), quella con il project-key di sonarqube (chiamiamola SONAR_PROJECT_KEY).

La configurazione dell’ambiente è completata, possiamo ora aggiungere uno step di analisi alla nostra pipeline!

5. Configurazione pipeline

Di seguito vi riporto delle pipeline di esempio che eseguono l’analisi di Sonarqube. Se non avete una pipeline potrete utilizzarla come base di partenza, se invece già ne avete una potrete prendere spunto per migliorarla.

.NET Core

gitlab-ci.yml
#Docker image
image: mcr.microsoft.com/dotnet/sdk:9.0

#Stage of pipeline
stages:
  - sonar-analysis
  - build

#Global variables
variables:
  DOTNET_SKIP_FIRST_TIME_EXPERIENCE: "true"
  DOTNET_CLI_TELEMETRY_OPTOUT: "true"
  PROJECT_PATH: "<yout-project-name>"  # --> Name of your project

  #Defines the location of the analysis task cache
  SONAR_USER_HOME: "${CI_PROJECT_DIR}/.sonar" 

  #Shallow clone
  GIT_DEPTH: "0" 

# SonarQube
sonar-analysis:
  stage: sonar-analysis
  tags:
    - build_dotnet
  cache:
    policy: pull-push
    key: "sonar-cache-$CI_COMMIT_REF_SLUG"
    paths:
      - "${SONAR_USER_HOME}/cache"
      - sonar-scanner/
  script:
    - "dotnet tool install --global dotnet-sonarscanner"
    - "export PATH=\"$PATH:$HOME/.dotnet/tools\""
    - "cd $PROJECT_PATH"
    - "dotnet sonarscanner begin /k:\"${SONAR_PROJECT_KEY}\" /d:sonar.token=\"$SONAR_TOKEN\" /d:\"sonar.host.url=$SONAR_HOST_URL\" "
    - "dotnet build --configuration Release"
    - "dotnet sonarscanner end /d:sonar.token=\"$SONAR_TOKEN\""
  allow_failure: false

build:
  stage: build
  tags:
    - build_dotnet
  script:
    - echo "Start building.."
    - dotnet --version
    - cd $PROJECT_PATH
    - dotnet restore
    - dotnet build --configuration Release --no-restore
    - echo ".. building completed!"
  artifacts:
    paths:
      - $PROJECT_PATH/bin/Release/
    expire_in: 1 hour

Angular

Sui progetti Angular sarà necessario aggiungere un file chiamato “sonar-project.properties” con questo contenuto:

sonar-project.properties
sonar.projectName=Your project name

sonar.sources.inclusions=**/*.ts,**/*.js,**/*.html,**/*.css,**/*.scss

sonar.test.inclusions=**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts

sonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**,**/*.spec.ts,**/*.test.ts,**/*.e2e-spec.ts

Ogni volta che Sonarqube eseguirà l’analisi cercherà un file con questo nome e leggerà questi parametri come configurazione.

ATTENZIONE In questo file le variabili Gitlab non vengono sostituite

La pipeline sarà invece leggermente più complessa rispetto a quella di .NET, in una prima fase scaricheremo i pacchetti e le dipendenze, nella seconda eseguiremo l’analisi e ella terza compileremo il progetto.

gitlab-ci.yml
stages:
  - setup
  - analysis
  - build

##############################################################
# Cahce to improve performance 
##############################################################
.node_cache:
  cache:
    - key:
        files:
          - package-lock.json
        prefix: "node-modules-${CI_COMMIT_REF_SLUG}"
      paths:
        - node_modules/
        - .npm/
      policy: pull-push
    - key:
        files:
          - package-lock.json
        prefix: "node-modules-main"
      paths:
        - node_modules/
        - .npm/
      policy: pull
    - key: "${CACHE_FALLBACK_KEY}"
      paths:
        - node_modules/
        - .npm/
      policy: pull

##############################################################
# Init repo dependencies
##############################################################
install-dependencies:
  stage: setup
  tags:
    - build_angular
  extends: .node_cache
  script:
    - echo "Setup dipendencies completed"
    - npm list --depth=0 2>/dev/null || true
  cache:
    - key:
        files:
          - package-lock.json
        prefix: "node-modules-${CI_COMMIT_REF_SLUG}"
      paths:
        - node_modules/
        - .npm/
      policy: push
  artifacts:
    paths:
      - node_modules/
    expire_in: 1 hour
    reports:
      dotenv: build.env
  rules:
    - allow_failure: false

##############################################################
# Sonar scan - this work here/on your machine/ARM server
##############################################################
sonarqube-analysis:
  stage: analysis
  tags:
    - build_angular
  extends: .setup_node
  script:   
    # Verifica configurazione SonarQube
    - |
      if [ ! -f "sonar-project.properties" ]; then
        echo "sonar-project.properties missing"
        exit 1
      fi
      
      if [ -z "$SONAR_HOST_URL" ] || [ -z "$SONAR_TOKEN" ] || [ -z "$SONAR_PROJECT_KEY" ]; then
        echo "$SONAR_HOST_URL or $SONAR_TOKEN or $SONAR_PROJECT_KEY missing - skip analysis"
        exit 0
      fi
    
    # Install and execute SonarQube scanner
    - npm install -g sonarqube-scanner@latest --no-audit --no-fund
    - |
      sonar-scanner \
        -Dsonar.host.url="${SONAR_HOST_URL}" \
        -Dsonar.token="${SONAR_TOKEN}" \
        -Dsonar.projectKey="${SONAR_PROJECT_KEY}"
    - echo "Sonar scan completed"

  dependencies:
    - install-dependencies
  allow_failure: true

##############################################################
# Build project
##############################################################
build-project:
  stage: build
  tags:
    - build_angular
  extends: .setup_node
  variables:
    BUILD_CONFIGURATION: "production"
  script:
    - echo "Building Angular project..."
    - |
      npm run build -- \
        --configuration=${BUILD_CONFIGURATION} \
        --optimization=true \
        --aot=true \
        --stats-json=true

      echo "Build completed!"
  dependencies:
    - install-dependencies
  needs:
    - job: install-dependencies
      optional: false

Conclusioni

In questo articolo abbiamo visto come integrare Sonarqube in una pipeline di Gitlab. Automatizzare il monitoraggio dei nostri progetti è sia fondamentale quanto strategico. Non solo questi strumenti ci forniscono alert su eventuali bug o problemi di sicurezza (sarebbe peggio accorgersi quando il software è in produzione), ma ci forniscono anche dei consigli su come migliorare ciò che abbiamo scritto.

Che ci piaccia o no l’informatica è in continua evoluzione e in qualità di sviluppatori è necessario stare al passo per poter essere sempre preparati ed evitare di accumulare “debiti tecnici” quando scriviamo le nostre applicazioni. Non mi aspetto che tutti condividano questo mio punto di vista, nella mia esperienza la maggior parte delle persone non vuole – per comodità – rimanere aggiornato, però a volte basta davvero poco: se ogni giorno ognuno di noi imparasse anche solo un piccolo miglioramento diventeremmo ogni giorno sviluppatori sempre migliori.

Spero che questo articolo possa averti lasciato un qualcosa nel tuo bagaglio tecnico, alla prossima!

Condividi questo articolo
Shareable URL
Post precedente

Architettura e organizzazione di un’applicazione enterprise dinamica

Prosimo post

Aggiungere un server MCP ad un progetto esistente

Leggi il prossimo articolo