Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
140 changes: 140 additions & 0 deletions services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
DESCRIPTION >
- `repo_health_score_v2_breakdown.pipe` is the live, request-time counterpart to
`project_insights_health_breakdown_copy.pipe`'s project-level Health Score v2 signal breakdown
rollup, scoped to a caller-supplied set of repo URLs instead of a project's full repo set. Powers
the Overview page's repo selector (IN-1253): when a subset of a project's repos is selected, the
Health breakdown box is recomputed live for just that subset's active repos.
- Per-column aggregation is copied 1:1 from `project_insights_health_breakdown_copy.pipe` — see
that pipe's DESCRIPTION for the full per-column methodology and rationale (coverage-filtered
`sumIf(score, available) / nullIf(countIf(available), 0)` for `*Available`-backed scores, `max()`
for counts/flags, `min()` for `daysSinceLatest`/`daysBetweenRecent`/`isGerrit`/`isExcluded`,
`avg()` for median-seconds columns and `commitActivityScore`).
- Join path: `repositories` (filtered by the `repos` param, `enabled = true AND excluded = false`)
-> `health_score_v2_signal_detail_ds` (matched on `repoUrl` = `rep.url`) — same population filter
as the project-level rollup, so archived/excluded repos in a mixed selection are dropped from the
aggregation. `HAVING count() > 0` ensures that when every repo in `repos` is archived/excluded,
this query returns zero rows rather than one row of NULL/NaN aggregates over an empty input.
- No `slug`/project grouping — this collapses to a single row for the given repo set, unlike the
project-level copy pipe which groups by project.
- Additionally joins `insightsProjects` (matched on `repositories.insightsProjectId` = `id`) and
requires `insightsProjects.slug` = the caller's `slug` param, so a `repos` value from a different
project can't leak that project's repo-level data into this response.
- `maintainerHealthScoreV2` / `securitySupplyChainScoreV2` / `developmentActivityScoreV2` (IN-1253):
same exact-median (`quantileExact(0.5)`) rollup of `health_score_v2_repo_copy_ds`'s per-repo category
scores that `project_insights_copy.pipe` uses at the project level, scoped here to the caller-supplied
`repos` set instead of a project's full repo set — kept consistent so a single-repo selection reduces
to that repo's own per-repo score. Joined via the same `repositories` (enabled/non-excluded, project-
scope-checked) population as the rest of this pipe, so a repo with no `health_score_v2_repo_copy_ds`
row (LEFT JOIN) contributes NULL rather than being silently dropped from the median.
- `healthScoreV2` / `healthLabel` (IN-1253): same exact-median rollup of `health_score_v2_repo_copy_ds`'s
per-repo total, with the label bands copied 1:1 from `project_insights_copy.pipe`
(>=85 excellent, >=70 healthy, >=50 fair, >=30 concerning, else critical). This pipe only computes the
value — whether the caller's UI actually displays a repo-scoped total (e.g. a dedicated single-repo
page) versus suppressing it (a multi-repo filter selection, where the total is intentionally hidden per
IN-1253 State 2) is a display decision made by the frontend, not this pipe.
- Parameters: `slug` (required, the calling project's slug), `repos` (required array of repo URLs).

TOKEN "insights-app-token" READ

TAGS "Insights, Widget", "Project", "Health"

NODE repo_health_score_v2_breakdown_category_totals
SQL >
%
SELECT
sumIf(sd.busFactorScore, sd.busFactorAvailable)
/ nullIf(countIf(sd.busFactorAvailable), 0) AS busFactorScore,
max(sd.busFactorAvailable) AS busFactorAvailable,
max(sd.busFactorCount) AS busFactorCount,
sumIf(sd.orgDiversityScore, sd.orgDiversityAvailable)
/ nullIf(countIf(sd.orgDiversityAvailable), 0) AS orgDiversityScore,
max(sd.orgDiversityAvailable) AS orgDiversityAvailable,
max(sd.orgCount) AS orgCount,
sumIf(sd.responsivenessScore, sd.responsivenessAvailable)
/ nullIf(countIf(sd.responsivenessAvailable), 0) AS responsivenessScore,
max(sd.responsivenessAvailable) AS responsivenessAvailable,
avg(sd.medianPrResponseS) AS medianPrResponseS,
avg(sd.medianIssueResponseS) AS medianIssueResponseS,
min(sd.isGerrit) AS isGerrit,
min(sd.isExcluded) AS isExcluded,
sumIf(sd.openVulnScore, sd.openVulnAvailable)
/ nullIf(countIf(sd.openVulnAvailable), 0) AS openVulnScore,
max(sd.openVulnAvailable) AS openVulnAvailable,
max(sd.openCriticals) AS openCriticals,
max(sd.openHighs) AS openHighs,
max(sd.openModerates) AS openModerates,
sumIf(sd.scorecardScorePts, sd.scorecardAvailable)
/ nullIf(countIf(sd.scorecardAvailable), 0) AS scorecardScorePts,
max(sd.scorecardAvailable) AS scorecardAvailable,
sumIf(toFloat64OrNull(sd.scorecardScore), sd.scorecardAvailable)
/ nullIf(countIf(sd.scorecardAvailable), 0) AS scorecardScore,
sumIf(sd.securityPracticesScore, sd.securityPracticesAvailable)
/ nullIf(countIf(sd.securityPracticesAvailable), 0) AS securityPracticesScore,
max(sd.securityPracticesAvailable) AS securityPracticesAvailable,
max(sd.securityPolicyEnabled) AS securityPolicyEnabled,
max(sd.branchProtectionEnabled) AS branchProtectionEnabled,
max(sd.branchProtectionRequiredReviews) AS branchProtectionRequiredReviews,
max(sd.branchProtectionRequiresStatusChecks) AS branchProtectionRequiresStatusChecks,
max(sd.branchProtectionAllowsForcePush) AS branchProtectionAllowsForcePush,
sumIf(sd.dependencyHealthScore, sd.dependencyHealthAvailable)
/ nullIf(countIf(sd.dependencyHealthAvailable), 0) AS dependencyHealthScore,
max(sd.dependencyHealthAvailable) AS dependencyHealthAvailable,
max(sd.vulnerableDeps) AS vulnerableDeps,
sumIf(sd.releaseCadenceScore, sd.releaseCadenceAvailable)
/ nullIf(countIf(sd.releaseCadenceAvailable), 0) AS releaseCadenceScore,
max(sd.releaseCadenceAvailable) AS releaseCadenceAvailable,
min(sd.daysSinceLatest) AS daysSinceLatest,
min(sd.daysBetweenRecent) AS daysBetweenRecent,
avg(sd.commitActivityScore) AS commitActivityScore,
max(sd.commitsLast6m) AS commitsLast6m,
max(sd.lastCommitAt) AS lastCommitAt,
sumIf(sd.issueResolutionScore, sd.issueResolutionAvailable)
/ nullIf(countIf(sd.issueResolutionAvailable), 0) AS issueResolutionScore,
max(sd.issueResolutionAvailable) AS issueResolutionAvailable,
max(sd.closed12m) AS closed12m,
max(sd.opened12m) AS opened12m,
avg(sd.medianCloseS) AS medianCloseS,
sumIf(sd.prMergeScore, sd.prMergeAvailable)
/ nullIf(countIf(sd.prMergeAvailable), 0) AS prMergeScore,
max(sd.prMergeAvailable) AS prMergeAvailable,
max(sd.merged12m) AS merged12m,
max(sd.closedUnmerged12m) AS closedUnmerged12m,
avg(sd.medianMergeS) AS medianMergeS,
quantileExact(0.5)
(hv2.maintainerHealthScoreV2) AS maintainerHealthScoreV2,
Comment on lines +103 to +104
quantileExact(0.5)
(hv2.securitySupplyChainScoreV2) AS securitySupplyChainScoreV2,
quantileExact(0.5)
(hv2.developmentActivityScoreV2) AS developmentActivityScoreV2,
quantileExact(0.5)
(hv2.healthScoreV2) AS healthScoreV2Raw

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Null health totals become critical

Medium Severity

quantileExact on healthScoreV2 returns 0 when every selected repo is NULL (IN-1248: fewer than two categories), so the later IS NULL check never runs and the endpoint emits healthScoreV2: 0 with healthLabel: critical. A single-repo selection that should reduce to that repo's own NULL score is shown as a failing zero instead of unavailable.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 58fcdcc. Configure here.

FROM repositories rep FINAL
INNER JOIN health_score_v2_signal_detail_ds sd ON sd.repoUrl = rep.url
INNER JOIN insightsProjects ip FINAL ON ip.id = rep.insightsProjectId
LEFT JOIN health_score_v2_repo_copy_ds hv2 ON hv2.repoUrl = rep.url
WHERE
rep.url IN {{ Array(repos, 'String', description="Selected repo URLs", required=True) }}
AND rep.enabled = true
AND rep.excluded = false
AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
Comment on lines +117 to +119
HAVING count() > 0

NODE repo_health_score_v2_breakdown_endpoint
SQL >
SELECT
* EXCEPT (healthScoreV2Raw),
toUInt8(round(healthScoreV2Raw)) AS healthScoreV2,
multiIf(
healthScoreV2Raw IS NULL,
NULL,
healthScoreV2Raw >= 85,
'excellent',
healthScoreV2Raw >= 70,
'healthy',
healthScoreV2Raw >= 50,
'fair',
healthScoreV2Raw >= 30,
'concerning',
'critical'
) AS healthLabel
FROM repo_health_score_v2_breakdown_category_totals
47 changes: 47 additions & 0 deletions services/libs/tinybird/pipes/repo_lifecycle_v2.pipe
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
DESCRIPTION >
- `repo_lifecycle_v2.pipe` is the live, request-time counterpart to `project_insights_copy.pipe`'s
`project_insights_copy_health_v2_project` lifecycle rollup, scoped to a caller-supplied set of repo
URLs instead of a project's full repo set. Powers the Overview page's repo selector (IN-1253):
when a subset of a project's repos is selected, Lifecycle is recomputed live for just that subset.
- Deliberately does NOT filter on `enabled`/`excluded` — `archived` is itself a valid
`lifecycleLabelV2` value (per the spec's decision tree: archived flag > abandoned > inert >
declining > stable > active, first match wins), so an all-archived/excluded selection must still
produce a real lifecycle label rather than being dropped from the rollup.
Comment on lines +6 to +9
- Reads `health_score_v2_repo_copy_ds` directly, no join with `repositories`.
- Same best-state-wins precedence and `if(empty(groupArray(...)), NULL, ...)` NULL-guard as
`project_insights_copy_health_v2_project` (see that node's DESCRIPTION for the `arrayElement` on
an empty array returning `''` instead of NULL rationale, IN-1196).
- Joins `repositories` (matched on `url` = `repoUrl`) to `insightsProjects` (matched on
`insightsProjectId` = `id`) and requires `insightsProjects.slug` = the caller's `slug` param, so a
`repos` value from a different project can't leak that project's repo-level data into this response.
- Parameters: `slug` (required, the calling project's slug), `repos` (required array of repo URLs).

TOKEN "insights-app-token" READ

TAGS "Insights, Widget", "Project", "Health"

NODE repo_lifecycle_v2_endpoint
SQL >
%
SELECT
if(
empty(groupArray(lifecycleLabelV2)),
NULL,
toNullable(
arrayElement(
arraySort(
x -> indexOf(
['active', 'stable', 'declining', 'inert', 'abandoned', 'archived'], x
),
groupArray(lifecycleLabelV2)
),
1
)
)
) AS lifecycleLabel
FROM health_score_v2_repo_copy_ds hs
INNER JOIN repositories rep FINAL ON rep.url = hs.repoUrl
INNER JOIN insightsProjects ip FINAL ON ip.id = rep.insightsProjectId
WHERE
hs.repoUrl IN {{ Array(repos, 'String', description="Selected repo URLs", required=True) }}
AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
Loading