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
#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.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.tsOgni 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.
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: falseConclusioni
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!