Speeding up Sonarcloud Scanning with NX
Our monorepo contains many files, and whilst integrating Sonarcloud it was taking in excess of 15 minutes to scan the whole repo. I needed to speed this up as it would be an unacceptable time to wait for developers using our platform.
As we were running this on PR checks, the first thought is to tell Sonarcloud to only scan files that have been changed in that PR. However, there are several explanations as to why that won't work. It boils down to this: if you change a file, you might introduce quality issues in the files that depend on the one you changed.
Luckily, with NX managing our repo, we can use the --affected
tooling to work out what apps and libs were affected by the changes in the PR, cutting down the amount of files that need to be scanned by Sonarcloud.
First in the Github action, we need to find the affected apps and libs:
- name: Get projects
id: get-projects
shell: bash
run: |
COMMAND_APP="pnpm nx show projects --affected --type="app" --json"
COMMAND_LIB="pnpm nx show projects --affected --type="app" --json"
FOUND_APPS=$( ${COMMAND_APP} | tr -d "\n" )
FOUND_LIBS=$( ${COMMAND_LIB} | tr -d "\n" )
echo "affected-apps=${FOUND_APPS}" >> "$GITHUB_OUTPUT"
echo "affected-libs=${FOUND_LIBS}" >> "$GITHUB_OUTPUT"
Then, we need to do a bit of manipulation of the strings to get folder paths. We also need to filter to make sure there are no folder paths that contain other folders within the output, otherwise Sonarcloud will error:
- uses: actions/github-script@v7.0.1
name: Create sources
id: create-sources
env:
AFFECTED_APPS: $NaN
AFFECTED_LIBS: $NaN
with:
script: |
const affectedApps = JSON.parse(process.env.AFFECTED_APPS);
const affectedLibs = JSON.parse(process.env.AFFECTED_LIBS);
const allAffected = [...affectedApps.map(a => `apps/${a}`), ...affectedLibs.map(l => `libs/${l}`)];
allAffected.sort();
let currentTopLevel = null;
const allAffectedFiltered = allAffected.reduce((acc, path) => {
if (!currentTopLevel || !path.startsWith(currentTopLevel + '/')) {
acc.push(path);
currentTopLevel = path;
}
return acc;
}, []);
core.setOutput('sources', allAffectedFiltered.join(','));
Finally we use the combined folder path string as an input override for the Sonarcloud scan action:
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@v3.1.0
env:
GITHUB_TOKEN: $
SONAR_TOKEN: $
with:
args: >
-Dsonar.sources=$NaN
-Dsonar.tests=$NaN
By doing this, we can make the scan time a lot shorter as only the files that could possibly have been affected are scanned. The scan time now depends on the amount of affected apps, but for most cases this has come down from over 15 minutes to just 5.