Setting up SonarCloud for one repository is straightforward. Setting it up for 50 repositories is painful. Here's how to automate it.
The Problem
You want code quality scanning on all repositories, but:
- Each repo needs its own SonarCloud project
- Each repo needs pipeline changes
- New repos get forgotten
- Configuration drifts between repos
The Solution: Dynamic Pipeline
Create a single pipeline that discovers all repos and scans them:
trigger: none
schedules:
- cron: "0 2 * * *" # 2am daily
displayName: Nightly scan
branches:
include:
- main
pool:
vmImage: 'ubuntu-latest'
steps:
- task: PowerShell@2
displayName: 'Discover repositories'
inputs:
targetType: 'inline'
script: |
$org = "$(System.CollectionUri)".TrimEnd('/')
$project = "$(System.TeamProject)"
$pat = "$(System.AccessToken)"
$base64Auth = [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes(":$pat"))
$headers = @{ Authorization = "Basic $base64Auth" }
$reposUrl = "$org/$project/_apis/git/repositories?api-version=7.0"
$repos = (Invoke-RestMethod -Uri $reposUrl -Headers $headers).value
$repoList = $repos | ForEach-Object { $_.name } | ConvertTo-Json -Compress
Write-Host "##vso[task.setvariable variable=RepoList]$repoList"
Write-Host "Found $($repos.Count) repositories"
- ${{ each repo in split(variables.RepoList, ',') }}:
- template: templates/sonar-scan.yml
parameters:
repoName: ${{ repo }}
The Scan Template
Create templates/sonar-scan.yml:
parameters:
- name: repoName
type: string
steps:
- checkout: git://$(System.TeamProject)/${{ parameters.repoName }}
displayName: 'Checkout ${{ parameters.repoName }}'
persistCredentials: true
- task: SonarCloudPrepare@1
displayName: 'Prepare SonarCloud - ${{ parameters.repoName }}'
inputs:
SonarCloud: 'SonarCloud-Connection'
organization: 'your-org'
scannerMode: 'CLI'
configMode: 'manual'
cliProjectKey: 'your-org_${{ parameters.repoName }}'
cliProjectName: '${{ parameters.repoName }}'
cliSources: '.'
- task: SonarCloudAnalyze@1
displayName: 'Run SonarCloud Analysis'
continueOnError: true
- task: SonarCloudPublish@1
displayName: 'Publish Results'
continueOnError: true
Handling Different Languages
Detect project type and adjust scan configuration:
- task: PowerShell@2
displayName: 'Detect project type'
inputs:
targetType: 'inline'
script: |
$projectType = "generic"
if (Test-Path "*.csproj") { $projectType = "dotnet" }
elseif (Test-Path "package.json") { $projectType = "node" }
elseif (Test-Path "pom.xml") { $projectType = "maven" }
elseif (Test-Path "requirements.txt") { $projectType = "python" }
Write-Host "##vso[task.setvariable variable=ProjectType]$projectType"
Then use conditional tasks based on ProjectType.
Auto-Creating SonarCloud Projects
SonarCloud can auto-create projects on first scan, but for better control use the API:
$sonarToken = "your-sonar-token"
$headers = @{
Authorization = "Basic $([Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("${sonarToken}:")))"
}
$body = @{
name = $repoName
project = "your-org_$repoName"
organization = "your-org"
}
Invoke-RestMethod -Uri "https://sonarcloud.io/api/projects/create" `
-Method Post -Headers $headers -Body $body
Filtering Repos
Not every repo needs scanning. Add filtering:
$excludePatterns = @("archived-*", "test-*", "docs")
$repos = $repos | Where-Object {
$name = $_.name
$exclude = $false
foreach ($pattern in $excludePatterns) {
if ($name -like $pattern) { $exclude = $true }
}
-not $exclude
}
Or use repo topics/tags to include/exclude.
Reporting
Aggregate results across all repos:
$projects = Invoke-RestMethod -Uri "https://sonarcloud.io/api/projects/search?organization=your-org" -Headers $headers
$summary = $projects.components | ForEach-Object {
$measures = Invoke-RestMethod -Uri "https://sonarcloud.io/api/measures/component?component=$($_.key)&metricKeys=bugs,vulnerabilities,code_smells" -Headers $headers
[PSCustomObject]@{
Project = $_.name
Bugs = ($measures.component.measures | Where-Object { $_.metric -eq "bugs" }).value
Vulnerabilities = ($measures.component.measures | Where-Object { $_.metric -eq "vulnerabilities" }).value
CodeSmells = ($measures.component.measures | Where-Object { $_.metric -eq "code_smells" }).value
}
}
$summary | Export-Csv "sonar-summary.csv" -NoTypeInformation
Need help implementing code quality scanning across your organisation? Get in touch - we help teams build secure development pipelines.