SonarQube Cloud (SonarCloud) is a Software-as-a-Service (SaaS) code analysis tool that helps maintain code quality by identifying issues related to maintainability, reliability, and security.
The amazon.aws collection uses SonarQube Cloud to analyze the main branch and pull requests, and to track unit test coverage and quality gate compliance.
-
Clean as You Code: Development practice where new code must meet quality standards.
-
Clean Code Attributes: Consistency, Intentionality, Adaptability, and Responsibility. See Code analysis metrics.
-
Software Quality: SonarQube Cloud evaluates quality by flagging issues that violate clean code principles.
-
Quality Standards: Defined by a quality profile (rules) and a quality gate (conditions that must pass). The gate shows pass or fail based on whether all conditions are met.
The collection uses CI-based analysis with GitHub Actions:
- The SonarScanner runs inside the CI build.
- Configuration is controlled in the repository (properties file and workflow).
- Code coverage is produced by a dedicated coverage job (via
tox) before the SonarCloud workflow runs; coverage reports are downloaded and passed to the scanner.
Automatic analysis is not used because it does not support code coverage and has limited branch support.
References:
Project-level analysis settings live in sonar-project.properties at the repository root:
| Parameter | Value | Purpose |
|---|---|---|
sonar.projectKey |
ansible-collections_amazon.aws |
SonarCloud project identifier |
sonar.organization |
ansible-collections |
SonarCloud organization |
sonar.projectName |
amazon.aws |
Display name |
sonar.sources |
. |
Root of analyzed source tree |
sonar.tests |
tests/unit,tests/integration |
Test directories (for test-aware analysis) |
sonar.exclusions |
tests/**,.tox/** |
Paths excluded from analysis |
sonar.python.coverage.reportPaths |
coverage.xml |
Default coverage report path (overridden by CI when present) |
sonar.python.version |
3.13 |
Python version for analysis |
sonar.newCode.referenceBranch |
main |
Branch used as baseline for "new code" |
Full reference: Analysis parameters.
Unit test coverage is produced by tox. The default test environment runs pytest with pytest-cov:
- Coverage scope:
plugins/callback,plugins/inventory,plugins/lookup,plugins/module_utils,plugins/modules,plugins/plugin_utils, andplugins. - Reports: HTML (
--cov-report html) and Cobertura XML (--cov-report xml:coverage.xml). - Output:
coverage.xmlis written under thetoxenvironment directory (e.g..tox/ansible2.20-py314-with_constraints/...).
The CI coverage job uses the environment ansible2.20-py314-with_constraints so that a single, consistent coverage report is produced for SonarCloud.
pyproject.toml does not define SonarQube-specific settings. It configures tools used by tests and coverage:
[tool.pytest]: Pytest options (e.g.xfail_strict = true).[tool.coverage.report]: Coverage report options, includingalso_excludefor lines that are not required to be covered (e.g.raise NotImplementedError).
These affect how coverage is generated (and thus what appears in coverage.xml), but SonarCloud is configured only via sonar-project.properties and the workflow arguments.
Two workflows implement the integration:
-
all_green(.github/workflows/all_green_check.yml) runs on pull requests and pushes tomainandstable-*. It runs linters, sanity, units, and a coverage job. When all required jobs succeed, it triggers the SonarCloud workflow viaworkflow_run. -
SonarCloud (
.github/workflows/sonarcloud.yml) runs after all_green completes successfully. It checks out the repo, downloads the coverage artifact from that run, locates the coverage XML, gathers PR metadata when applicable, and runs the SonarScanner with the appropriate arguments (including coverage paths).
- Secrets: Workflows triggered by
pull_requestfrom forks run in the fork context and do not have access to repository secrets (e.g.SONAR_TOKEN). By triggering SonarCloud from all_green viaworkflow_run, the SonarCloud job runs in the upstream repository context and can use the token. - Ordering: SonarCloud runs only after
all_green(including the coverage job) has finished, so coverage data is available when the scanner runs.
Flow:
- PR or
pushtriggersall_green(linters, sanity, units, coverage). - The coverage job runs
tox -e ansible2.20-py314-with_constraints, findscoverage.xmlunder.tox, rewrites paths in the XML to be repo-relative, and uploads it as thecoverageartifact. - When
all_greencompletes successfully, the SonarCloud workflow is triggered. - The SonarCloud job checks out the repo at the run's commit, downloads the
coverageartifact, setsCOVERAGE_PATHS, and runs the scanner withsonar.python.coverage.reportPathsset so SonarCloud receives the coverage report.
- Trigger: Same as
all_green(pull_requestand push tomainandstable-*). The coverage job runs on every such run (linters/sanity only run onpull_request; units and coverage always run). - Steps:
- Checkout repository.
- Set up Python 3.14 and install
tox. - Run
tox -e ansible2.20-py314-with_constraintsto execute unit tests and generatecoverage.xmlunder.tox. - Rewrite coverage paths: Locate
coverage.xmlunder.tox, then replace the absolute path prefix (thetoxenv collection path) with an empty string so paths in the XML are repo-relative. This allows SonarCloud to match coverage to the repository layout when it checks out the same commit. - Upload the rewritten
coverage.xmlas the artifact namedcoverage.
- Gate: The
all_greenjob only succeeds when linters (on PRs), sanity (on PRs), units, and coverage all succeed (or are skipped where allowed).
- Trigger:
workflow_runwhen theall_greenworkflow completes (success only). - Permissions:
contents: read,pull-requests: read,actions: read(needed to download artifacts from the triggering run). - Steps:
- Checkout the repository at
workflow_run.head_sha. - Download coverage artifacts from the triggering all_green run using
dawidd6/action-download-artifact(pinned by full commit SHA, v16 equivalent). Pattern:coverage*. - Set coverage report paths: Run
find . -name "coverage*.xml", comma-separate the paths, and setCOVERAGE_PATHSin the environment. - Get PR number and info (when the triggering event was a pull_request): Use
gh pr listandgh apito setPR_NUMBER,PR_BASE, andPR_HEAD. - Prepare SonarCloud args: Set
sonar.scm.revisionto the commit SHA. For pull requests, addsonar.pullrequest.key,sonar.pullrequest.branch, andsonar.pullrequest.base. WhenCOVERAGE_PATHSis set, addsonar.python.coverage.reportPaths. - SonarCloud Scan: Run
SonarSource/sonarqube-scan-action@v7with the prepared args andSONAR_TOKENfrom repository secrets.
- Checkout the repository at
Coverage is only passed to the scanner when the download step finds at least one coverage*.xml file; otherwise the scan runs without coverage (and may rely on the default path in sonar-project.properties if no args override it).
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
all_green |
.github/workflows/all_green_check.yml |
pull_request, push to main and stable-* |
Run linters, sanity, units, and coverage; gate on success |
| SonarCloud | .github/workflows/sonarcloud.yml |
After all_green completes successfully |
Download coverage from all_green run and run SonarScanner with PR or branch analysis |
| Artifact / config | Source | Use |
|---|---|---|
| Coverage XML | tox (coverage job) → path rewrite → artifact coverage |
Downloaded by SonarCloud job and passed as sonar.python.coverage.reportPaths |
sonar-project.properties |
Repository root | Project key, organization, sources, tests, exclusions, default coverage path, Python version, reference branch |
tox.ini |
Repository root | Defines testenv that runs pytest with pytest-cov and writes coverage.xml |
pyproject.toml |
Repository root | Pytest and coverage report options (exclude_lines, etc.); no SonarCloud-specific config |
If analysis fails or coverage is missing:
- Check
all_green: Ensure theall_greenworkflow (including the coverage job) succeeded for the same commit. SonarCloud downloads artifacts from that run. - Check artifact: In the SonarCloud run, confirm the "Download coverage artifacts" and "Set coverage report paths steps" found at least one
coverage*.xmland setCOVERAGE_PATHS. - Run SonarScanner locally: Install SonarScanner CLI, set
SONAR_TOKENfrom SonarCloud Security, and run from the repo root:Fix any errors reported at the end of the output.sonar-scanner -Dsonar.projectBaseDir=. -Dsonar.host.url=https://sonarcloud.io
Note: Changes that break sonar-project.properties can still leave the PR status green because the SonarCloud workflow may not block the check. Use the SonarCloud project page and local sonar-scanner runs to verify configuration.