feat: add SonarQube integration to entity pages - #662
Conversation
|
Warning Review limit reached
Next review available in: 18 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (4)
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
3878d19 to
7d232a3
Compare
Thanks! I've run Prettier locally to fix the formatting issues, amended the commit with the required DCO sign-off, and updated the PR. Everything should be passing now! |
|
@kavix Please attach a screenshot of Portal UI how it looks like with the SonarQube plugin Also take a pull from the main branch, there are conflicts in the yarn.lock file |
7d232a3 to
4d5c528
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/backend/src/index.ts`:
- Around line 31-32: Update the backend initialization around the SonarQube
plugin registration to avoid calling its setup when the `sonarqube`
configuration is absent; conditionally register `plugin-sonarqube-backend` only
when the configuration exists, or otherwise handle the missing configuration
before initialization. Add a startup test covering configuration without a
`sonarqube` section and verify the backend starts successfully.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: fffb90ab-4c44-4b55-8f5a-4041a9bb4ae5
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (9)
.changeset/sonarqube-entity-integration.mdapp-config.yamlpackages/backend/package.jsonpackages/backend/src/index.tspackages/portal-app/package.jsonpackages/portal-app/src/apis.tspackages/portal-app/src/components/catalog/EntityPage.tsxplugins/openchoreo-observability/src/components/RuntimeLogs/LogsTable.test.tsxplugins/openchoreo/src/components/Environments/components/ReleaseBrowserDialog.tsx
| // SonarQube: Proxies metrics API. Self-disables when sonarqube config is absent. | ||
| backend.add(import('@backstage-community/plugin-sonarqube-backend')); |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage-community%2Fplugin-sonarqube-backend/1.1.1')"
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/plugin.tgz"
tar -xzf "$tmp_dir/plugin.tgz" -C "$tmp_dir"
rg -n "getConfig\\(['\"]sonarqube['\"]\\)|getOptionalConfig\\(['\"]sonarqube['\"]\\)|self.?disab" "$tmp_dir/package" || true
rg -n "^[[:space:]]*sonarqube:" --glob 'app-config*.yaml' . || trueRepository: openchoreo/backstage-plugins
Length of output: 18509
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- backend registration ---'
cat -n packages/backend/src/index.ts | sed -n '20,40p'
printf '%s\n' '--- SonarQube configuration references ---'
rg -n -C 2 'sonarqube' --glob 'app-config*.yaml' --glob '!node_modules' . || true
printf '%s\n' '--- package metadata and runtime entrypoints ---'
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage-community%2Fplugin-sonarqube-backend/1.1.1')"
printf '%s\n' "$metadata" | jq -r '.version, .main, .module, .types, .dist.tarball'
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/plugin.tgz"
tar -xzf "$tmp_dir/plugin.tgz" -C "$tmp_dir"
printf '%s\n' '--- configuration access in distributed JavaScript ---'
rg -n -C 8 'getConfig\("sonarqube"\)|getConfig\('\''sonarqube'\' \)' "$tmp_dir/package/dist" || true
printf '%s\n' '--- plugin startup exports ---'
rg -n -C 5 'createRouter|createBackendPlugin|DefaultSonarqubeInfoProvider|fromConfig' "$tmp_dir/package/dist" | head -120 || trueRepository: openchoreo/backstage-plugins
Length of output: 2331
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage-community%2Fplugin-sonarqube-backend/1.1.1')"
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/plugin.tgz"
tar -xzf "$tmp_dir/plugin.tgz" -C "$tmp_dir"
printf '%s\n' '--- package metadata ---'
cat "$tmp_dir/package/package.json" | jq '{version,main,dependencies,peerDependencies}'
printf '%s\n' '--- configuration access ---'
rg -n -C 10 'getConfig\("sonarqube"\)|getOptionalConfig\("sonarqube"\)' "$tmp_dir/package/dist" || true
printf '%s\n' '--- provider construction and backend registration ---'
rg -n -C 8 'DefaultSonarqubeInfoProvider|fromConfig|createBackendPlugin|createRouter' "$tmp_dir/package/dist/index.cjs.js" "$tmp_dir/package/dist/service" | head -160 || trueRepository: openchoreo/backstage-plugins
Length of output: 38844
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage-community%2Fplugin-sonarqube-backend/1.1.1')"
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/plugin.tgz"
tar -xzf "$tmp_dir/plugin.tgz" -C "$tmp_dir"
printf '%s\n' '--- distributed files ---'
find "$tmp_dir/package/dist" -maxdepth 2 -type f -printf '%P\n' | sort
printf '%s\n' '--- package entrypoint ---'
wc -l "$tmp_dir/package/dist/index.cjs.js"
cat -n "$tmp_dir/package/dist/index.cjs.js" | sed -n '1,240p'
printf '%s\n' '--- source-map source names ---'
if [ -f "$tmp_dir/package/dist/index.cjs.js.map" ]; then
jq -r '.sources[]' "$tmp_dir/package/dist/index.cjs.js.map"
jq -r '.sourcesContent[]' "$tmp_dir/package/dist/index.cjs.js.map' 2>/dev/null || true
fiRepository: openchoreo/backstage-plugins
Length of output: 833
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage-community%2Fplugin-sonarqube-backend/1.1.1')"
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/plugin.tgz"
tar -xzf "$tmp_dir/plugin.tgz" -C "$tmp_dir"
printf '%s\n' '--- plugin bundle ---'
wc -l "$tmp_dir/package/dist/plugin.cjs.js"
cat -n "$tmp_dir/package/dist/plugin.cjs.js" | sed -n '1,220p'
printf '%s\n' '--- plugin source map source content ---'
python3 - "$tmp_dir/package/dist/plugin.cjs.js.map" <<'PY'
import json
import sys
with open(sys.argv[1], encoding="utf-8") as stream:
data = json.load(stream)
for name, content in zip(data.get("sources", []), data.get("sourcesContent", [])):
print(f"--- {name} ---")
print(content)
PYRepository: openchoreo/backstage-plugins
Length of output: 4118
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
printf '%s\n' '--- resolved Backstage config references ---'
rg -n '`@backstage/config`' --glob 'package.json' --glob '*lock*' . | head -80 || true
tmp_dir="$(mktemp -d)"
trap 'rm -rf "$tmp_dir"' EXIT
metadata="$(curl -fsSL 'https://registry.npmjs.org/@backstage%2Fconfig/1.3.6')"
tarball="$(printf '%s' "$metadata" | jq -r '.dist.tarball')"
curl -fsSL "$tarball" -o "$tmp_dir/config.tgz"
tar -xzf "$tmp_dir/config.tgz" -C "$tmp_dir"
printf '%s\n' '--- ConfigReader.getConfig implementation ---'
rg -n -C 12 'getConfig\(' "$tmp_dir/package/dist" | head -120Repository: openchoreo/backstage-plugins
Length of output: 50385
Prevent backend startup failure when SonarQube is disabled.
When sonarqube is absent, plugin initialization calls config.getConfig('sonarqube'), which throws before the router is mounted. Handle the absent configuration or register the plugin only when the configuration exists. Add a startup test without a sonarqube section.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@packages/backend/src/index.ts` around lines 31 - 32, Update the backend
initialization around the SonarQube plugin registration to avoid calling its
setup when the `sonarqube` configuration is absent; conditionally register
`plugin-sonarqube-backend` only when the configuration exists, or otherwise
handle the missing configuration before initialization. Add a startup test
covering configuration without a `sonarqube` section and verify the backend
starts successfully.
Signed-off-by: Kavindu Sachinthe <kavix@yahoo.com>
4d5c528 to
b7e3431
Compare
attached the screenshot showing how the Portal UI looks with the SonarQube plugin. |
Purpose
Resolves openchoreo/openchoreo#3935
Currently, developers have to leave the developer portal to check their code quality metrics on SonarQube. This PR integrates the
@backstage-community/plugin-sonarqubeecosystem to bring those metrics directly into the entity pages.Goals
Add a dedicated Code Quality tab to the Backstage entity pages that conditionally renders SonarQube metrics when the
sonarqube.org/project-keyannotation is present.Approach
@backstage-community/plugin-sonarqube-backendplugin inpackages/backend/src/index.tsto proxy API requests safely to SonarQube.@backstage-community/plugin-sonarqubeand@backstage-community/plugin-sonarqube-reactinpackages/app.<EntitySonarQubeContentPage />toServiceEntityPageandGenericComponentEntityPageinsidepackages/app/src/components/catalog/EntityPage.tsx.isSonarQubeAvailablepredicate.sonarqubeconfig block (expectingbaseUrlandapiKey) inapp-config.yaml.User stories
As a developer, I want to see my component's SonarQube code quality metrics directly on its Backstage entity page, so that I can easily track technical debt, bugs, and coverage without context-switching.
Release note
Add SonarQube integration to display code quality metrics on Backstage entity pages.
Documentation
N/A - Standard Backstage community plugin integration. The required
sonarqubeconfiguration block has been documented inapp-config.yamlas part of this PR.Training
N/A
Certification
N/A - No impact on certification exams.
Marketing
N/A
Automation tests
Security checks
Samples
N/A
Related PRs
N/A
Migrations (if applicable)
N/A
Test environment
Learning
Leveraged the Backstage community patterns for conditional entity page rendering using the plugin's built-in
isSonarQubeAvailablehook to ensure the Code Quality tab only appears for annotated entities (matching the pattern used by the Jenkins and GitLab integrations).Summary by CodeRabbit
New Features
Bug Fixes