fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253) - #4528
fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253)#4528gaspergrom wants to merge 4 commits into
Conversation
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
…N-1253) Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
PR SummaryMedium Risk Overview
Both use the insights-app-token and are intended to ship before the paired insights app routes that call them by name. Reviewed by Cursor Bugbot for commit c1828c9. Bugbot is set up for automated code reviews on this repo. Configure here. |
There was a problem hiding this comment.
Pull request overview
Adds project-scoped Tinybird endpoints for live repository-filtered lifecycle and Health Score breakdown calculations.
Changes:
- Adds repository-filtered lifecycle aggregation.
- Adds repository-filtered Health Score signal aggregation.
- Restricts results using the calling project slug.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
repo_lifecycle_v2.pipe |
Adds scoped lifecycle rollup. |
repo_health_score_v2_breakdown.pipe |
Adds scoped health breakdown rollup. |
Suppressed comments (1)
services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:48
- This does not actually mirror the project lifecycle population:
project_insights_copy_health_v2_projectfiltersrep.enabled = true AND rep.excluded = false, whilehealth_score_v2_repo_copy_dsstill contains disabled repositories. A caller-supplied disabled URL can therefore contribute (and potentially win) the lifecycle rollup. Filtering these flags still preserves archived labels becausearchivedis a separate field.
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) }}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| AND rep.enabled = true | ||
| AND rep.excluded = false | ||
| AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }} |
| - 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. |
…(IN-1253) Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (3)
services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:47
- The PR contract says an all-archived/excluded selection must collapse to
NULL, and the project-level counterpart restricts its population. This query only scopes by URL/project, so an archived repository remains ingroupArrayand returnsarchived. Filter to enabled, non-archived, non-excluded repositories so the empty aggregate produces the documentedNULL.
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) }}
services/libs/tinybird/pipes/repo_health_score_v2_breakdown.pipe:120
enabledandarchivedare independent flags (repository upserts can setarchivedwhile forcingenabled = true), so this does not actually drop every archived repo.health_score_v2_signal_detail_dsretains a base row for archived repos with NULL category signals; thereforecount() > 0passes and returns a NULL-filled row instead of the promised zero rows. Explicitly exclude archived repositories.
AND rep.enabled = true
AND rep.excluded = false
AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
HAVING count() > 0
services/libs/tinybird/pipes/repo_lifecycle_v2.pipe:10
- This description contradicts both the PR contract and the SQL: it says archived/excluded selections produce a real label and that there is no
repositoriesjoin, while the stated endpoint behavior requiresNULLfor that population and lines 43–44 perform both joins. Update the metadata together with the population-filter fix so operators are not given the opposite contract.
- 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.
- Reads `health_score_v2_repo_copy_ds` directly, no join with `repositories`.
Summary
Adds two new Tinybird pipes,
repo_lifecycle_v2andrepo_health_score_v2_breakdown, that recompute Lifecycle and the Health Score breakdown live for a caller-supplied set of repo URLs — powering the Overview page's per-repo filter (IN-1253). Both pipes joininsightsProjectsand require the caller'sslugto match, so areposvalue from a different project can never leak that project's data.repo_health_score_v2_breakdown.pipe: mirrorsproject_insights_health_breakdown_copy.pipe's per-column aggregation, scoped torepos;HAVING count() > 0returns zero rows when every selected repo is archived/excluded.repo_lifecycle_v2.pipe: mirrors the project-level lifecycle logic, scoped torepos; deliberately has noHAVINGguard, so an all-archived/excluded selection collapses to one row withlifecycleLabel: null.Already deployed and validated in staging + production (cross-project leakage test against a foreign repo URL confirmed zero data returned; happy-path validated against OpenStack and project-jupyter).
Deploy order: this PR must be live in production before the paired insights PR (linuxfoundation/insights#TBD) is merged, since the insights server routes call these pipes by name.
Test plan
tb checkpasses on both pipes