How to integrate SonarQube (SonarCloud) for free without loosing Control over my Code
Intro
There are already some posts that explain how to use SonarQube and SonarCloud, along with their related scanners, in general. However, these posts either don’t cover the integration aspects, are quite old, or don’t address my specific questions. So, I’ve decided to embark on my own journey and share my insights here.
Goal
I’m privately developing some applications and, at some point, I wanted to use Static Source Code Analysis to check my code. Since I work in this field, I was already familiar with SonarQube and was quite satisfied with its results. However, I don’t currently have a proper license, and I’m not comfortable sharing too much control over my data or code. Therefore, my goals and restrictions are as follows:
- Scan Go, JS, CSS, HTML
- Integrate in Visual Studio Code
- Keep control as much as possible
- Provide nice comprehensive reports and statistics
- Stay independent on a concrete public Git registry
TL;DR
For those who lack the time to read the entire content, here’s a concise summary:
The "Safe" SonarCloud Account Setup
- Registration: You cannot sign up with just an email. You must use another account like GitHub.
- Privacy Trick: To keep control over your code, authorize the SonarCloud GitHub App only for a single "Dummy/Empty" repository.
- Project Creation: Inside SonarCloud, do not "Import" from GitHub. Instead, choose "Create a project manually." This creates an "Unbound" project that waits for you to push data to it, rather than SonarCloud pulling data from a repo.
Scanning Strategies
There are two distinct tools because they serve different purposes. Neither one can replace the other.
| Tool | SonarQube vscode plugin | sonar-scanner CLI |
|---|---|---|
| Purpose | Instant feedback ("Spell checker") while typing | Full project health report & Dashboard update |
| Scope | Only open files | The entire repository |
| Data Flow | Downloads rules from Cloud when Connect mode configured No code upload. |
Scans locally → Uploads report to SonarCloud. |
| Trigger | Automatic (Real-time). | Manual (Command Line). |
The final Workflow
- Code: You write code in vscode. The plugin (in Connected Mode) underlines issues using SonarCloud rules.
- Commit: You commit your changes to Git.
- Repo scan: You run the scanner CLI command in your terminal or in a CI/CD pipeline to scan the whole repo and upload the results to SonarCloud.
- Reports: You check the results in SonarCloud.
The long Story
Start
Apparently, I was thinking too simply. I thought, I only would have to register for a free developer account at SonarQube Cloud (SonarCloud), create a project with a token there, install the SonarQube vscode plugin, create the connection to the SonarCloud via the token, and that's it. Hopefully, the code stays locally, only scan results are transferred to the cloud and stored there for nice reports and statistics. Far from it...
Register at SonarCloud / Grant access to GitHub
SonarCloud doesn't provide the usual email address-based register option. They force us to use one of the public or private Git repo providers. If it were only about authentication, then it would be okay. But SonarCloud wants to get read/write access to the repos in my GitHub account. At least it can be limited to a single repo.
The intention behind is clear. SonarCloud strongly integrates into the GitHub repo and executes all scans directly on the code there. But this requires granting write permissions in this repo. So, you have to trust Sonar. Currently, I wouldn’t do this. Even if SonarQube is still partly open source and there is a kind of community edition, I would not bet that it’s forever. And we have seen several times what happens with our code when we are too lazy. Anyway, I don’t want to grant this kind of trust and permissions on my code repo.
So, what else can I do? To at least finish the registration at SonarCloud, I created a dummy repo in GitHub and granted access only to this repository. As a result, SonarQube Cloud is now listed in GitHub Applications under both “Authorized GitHub Apps” and “Installed GitHub Apps”.
I don’t really understand why 2 entries are needed. Even the dedicated GitHub documentation is not that clear and a bit misleading. As a result of my studies, I assume the former is for using GitHub as an OAuth provider to log in to SonarCloud. The latter defines how SonarCloud is entitled to access my GitHub repo(s). When you click on the Configure button, you can configure the repos to which SonarCloud shall get access. Finally, I found out that the entry at "Installed GitHub Apps" and the dummy repo can be deleted. So, (hopefully) only the OAuth authorization remains. If your are still unsure, create a separate fully isolated GitHub account to handle SonarCloud registration and authentication. Maybe, you need the account for other cases like this.
But it’s clear: If you don’t grant access to the actual code repo but only to a dummy repo to get the registration done, then your actual code won’t be scanned by SonarCloud directly on the GitHub repo. This is intended in my personal case.
This means that we must manage to interconnect SonarCloud with local software by any way. Here, the actual journey starts. We must understand the different elements at the local side, how they interact with each other, and what added value each of them provides. Then we can decide, what we really want to install and use.
The Components
I read many pages, tried some things out, and asked AI. As a result, I got an overarching picture of how the whole stuff can be put together. To explain any further, I will describe the involved components, their purpose, and added value.
The local code repository and shell
This is easy. This is the local code on your machine which shall be scanned and evaluated. And there is a shell terminal, where commands can be executed.
The remote repo
If you work on a serious project, you will push your code to a kind of remote Git repository. This can be a privately hosted Git server or most usually a public repo like GitHub or Codeberg (here, either a public or private repo). Because I stated that I don't want to tightly integrate my remote repo with SonarCloud, the remote repo is not involved in the further concept. (Even though if I push my code to one of these repos too.)
The SonarCloud
This provides the features of SonarQube as a cloud service. The features are pretty much the same compared with the self-hosted SonarQube, but of course, pricing, licenses, and subscription plans differ. You can set up different organizations and projects. Each project can cover different Git repos.
To create a new project in SonarCloud, click on the plus sign in the top right corner, select "Analyze new project", and then resist to select a GitHub repo when you want to keep control (like me). Instead, use the small link on the right side "create a project manually". This is the crucial step to setup a SonarCloud project which is not bound to a repo on GitHub or similar.
It's possible to centrally configure sonar scan properties, quality profiles and gates and much more. In the Free plan, built-in rules and gates must be used. These configuration settings will apply when you either:
- bind a project to a GitHub repo (what I don't want)
- install the SonarQube plugin into vscode and setup the Connected Mode (see below)
- run the sonar-scanner locally and push the results to SonarCloud (see below)
SonarCloud provides nice reports about different aspects of software quality. The main areas are general code quality, security, and test coverage. Reports are shown for new and overall code, branches are distinguished, and even a trend can be visualized.
But presenting these nice reports require that any scan results are delivered to SonarCloud. This can be done by:
- direct repo binding (again, not preferred)
- local execution of the sonar-scanner including push of the results to the cloud
It's essential to understand that the SonarQube plugin for vscode doesn't deliver scan results to SonarCloud even not in the Connected mode. This plugin has a different purpose, see below. Executing the sonar-scanner CLI is required for this. This was my main misunderstanding when I started the journey.
In general, SonarCloud is really useful when you want to work in a team, consolidate quality properties, have a common view on the quality status, and see the trend over the software lifecycle.
But even if you are "only" a single developer, it is really useful to have this kind of insights into your software quality.
And when you don't want to use the cloud service, then deploy your self-hosted instance of SonarQube. But you have to pay some money to get the full palette of features.
The SonarQube plugin for vscode
This plugin checks your code while you type. Issues are visualized in your code editor and in the Problems window. When you click in a marked code, you get explained what's wrong and how it can be fixed.
When you configure the connected mode (see https://docs.sonarsource.com/sonarqube-for-vs-code/connect-your-ide/connected-mode), then only any centrally managed scan configuration is pulled from SonarCloud to your local machine. This allows you to ensure that any team member uses the same configuration even when locally applied.
This is what the plugin does not:
- It doesn't scan the whole code in your local repo. It only works on the open file. The "Problems" window only contains issues for open files.
- It doesn't deliver scan results to SonarCloud even not in the Connected mode. This plugin has a different purpose to support you while working on your code.
The sonar-scanner
This is the most useful tool when you want to check your software completely and locally. Best, you download the binary from the SonarCloud website. Go to a Project → Administration → Analysis Method → Manually. Then you can select your OS, etc., and get a download link and some additional information on how to run the sonar-scanner. Especially, you get config information on how to push sonar-scanner results to SonarCloud and how to deal with the auth token.
If this has been set up, simply execute the sonar-scanner in your local terminal. It scans the whole repo, which takes about 30 seconds for 4.5k lines of code. As part of the execution, the results are delivered to SonarCloud where the results can be seen and assessed. In contrast to the vscode plugin, this scan also contains some more complicated security-relevant checks.
If sonar-scanner is configured to use SonarCloud, it pulls quality profiles, quality gates, and general settings. But any local configuration sonar-project.properties file (see the code example below) would have precedence over configuration in SonarCloud. This applies especially for code-relevant settings like file exclusions, inclusions, unit test settings, etc. It's recommended to keep these config items in the local file under Git control rather than managing it in the SonarCloud UI.
To automate the execution of sonar-scanner along with e.g. test and build steps, it's good practice to create a tasks.json in vscode (see code example below).
When a CI/CD pipeline like Gitlab, Jenkins, Woodpecker, or GitHub Actions is used, then the execution of the sonar-scanner should be an integrated part of the build and test process.
You could ask if we still need SonarCloud at all if we can execute the local sonar-scanner. Unfortunately, yes! If you run sonar-scanner without a configured and reachable Sonar server (either SonarCloud or a local SonarQube instance), the scan will fail. It does not produce a standalone HTML or PDF report on your hard drive. Sonar works on a client-server architecture:
- The Scanner (Client): Its only job is to read local files, extract some data, and ship data files to the server. It does not calculate the final results or aggregate the issues itself.
- The SonarCloud (Server): This is where the heavy work happens. The server processes the raw data, applies the rules, calculates "New Code" deltas, and generates the visual report.
Overarching Diagram
This self-made diagram tries to depict the overall relations, without GitHub or similar.
Decision Options
Based on all these facts, what options do we have now:
- The "magic" option: Bind your project/repo in SonarCloud directly to a Git repo at e.g. GitHub. Some magic happens now: Sonar gets notified when there is a commit to GitHub, clones the repo, and executes the scan. Additionally, use the vscode plugin, and for special cases, execute sonar-scanner as part of your CI/CD pipeline.
- The "fully on my own" option: Don't use SonarCloud at all. Only install the SonarQube plugin for vscode without connected mode to scan your code while you are typing. You get instant feedback on your code. Optionally, you can deploy your self-hosted instance of SonarQube and execute sonar-scanner connected to this instance.
- The "pragmatic" option: Use SonarCloud, but don't bind to a GitHub repo. Instead, set up a "manual" project in SonarCloud. Install the vscode plugin using connected mode for instant feedback in your IDE. Execute sonar-scanner locally and/or via CI/CD pipeline against your manually set-up SonarCloud project.
→ For the time being, I decided for option 3. It's a good balance between code control, convenience and results.
Code Examples
.vscode/settings.json → local config file for vscode plugin
{
"sonarlint.connectedMode.project": {
"connectionId": "connection_name",
"projectKey": "sonarcloud_project_key"
}
}
sonar-project.properties → local sonar-scanner configuration
# This tells the scanner: "Send the results to the Cloud, not Localhost"
sonar.host.url=https://sonarcloud.io
# Uniquely identifies your project on SonarCloud
# You get these from the project Information page on SonarCloud
sonar.organization=sonar_org
sonar.projectKey=sonarcloud_project_key
# Source code location.
# "." means "everything in this directory"
# we exclude some files and folders later
sonar.sources=.
# Exclusions (Optional)
# Don't scan build results and database files
sonar.exclusions=bin/**,*.db*
# Specify test files
sonar.tests=.
sonar.test.inclusions=**/*_test.go
# Test coverage report
sonar.go.coverage.reportPaths=coverage.out
sonar.coverage.inclusions=backend/**/*.go
# the inclusion is not sufficient to exclude static files, so we need to exclude them explicitly
sonar.coverage.exclusions=static/**/*,main.go
.vscode/tasks.json → automation file for test, build and scan
{
"version": "2.0.0",
"tasks": [
{
"label": "1. Go Test",
"type": "shell",
"command": "go test -v -coverprofile=coverage.out ./backend/...",
"group": "test",
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": [
"$go"
]
},
{
"label": "2. Go Build",
"type": "shell",
"command": "go build -v main.go",
"group": "build",
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": [
"$go"
]
},
{
"label": "3. Sonar Scanner",
"type": "shell",
"command": "sonar-scanner",
// Use a login interactive shell to ensure the user's PATH and SONAR_TOKEN are loaded correctly (finding sonar-scanner binary)
"options": {
"shell": {
"args": [
"-l",
"-i",
"-c"
]
}
},
// Add your specific arguments here if not using a sonar-project.properties file
"args": [
"-Dsonar.token=${env:SONAR_TOKEN}" // we keep it here to document that the envvar is used
],
"presentation": {
"reveal": "always",
"panel": "shared"
},
"problemMatcher": []
},
{
"label": "Run Full Pipeline",
"dependsOn": [
"1. Go Test",
"2. Go Build",
"3. Sonar Scanner"
],
"dependsOrder": "sequence",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}



Top comments (0)