Skip to content

fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253) - #4528

Open
gaspergrom wants to merge 4 commits into
mainfrom
feat/IN-1253-repo-filtered-health-score
Open

fix: scope repo-filtered health/lifecycle pipes to calling project (IN-1253)#4528
gaspergrom wants to merge 4 commits into
mainfrom
feat/IN-1253-repo-filtered-health-score

Conversation

@gaspergrom

Copy link
Copy Markdown
Contributor

Summary

Adds two new Tinybird pipes, repo_lifecycle_v2 and repo_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 join insightsProjects and require the caller's slug to match, so a repos value from a different project can never leak that project's data.

  • repo_health_score_v2_breakdown.pipe: mirrors project_insights_health_breakdown_copy.pipe's per-column aggregation, scoped to repos; HAVING count() > 0 returns zero rows when every selected repo is archived/excluded.
  • repo_lifecycle_v2.pipe: mirrors the project-level lifecycle logic, scoped to repos; deliberately has no HAVING guard, so an all-archived/excluded selection collapses to one row with lifecycleLabel: 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 check passes on both pipes
  • Deployed to prod via crowd-tinybird-manager; cross-project leakage test (foreign repo URL) returns empty/null, not leaked data
  • Happy-path validated against real projects (OpenStack, project-jupyter)

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>
Copilot AI balanced review requested due to automatic review settings August 28, 2026 12:47
@cursor

cursor Bot commented Aug 28, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
New read APIs touch cross-project authorization via slug scoping and health aggregations; incorrect joins or filters could leak or miscompute project health data.

Overview
Adds two Tinybird pipes so the Insights Overview repo filter can recompute Health v2 breakdown and lifecycle for a selected URL set instead of the whole project.

repo_health_score_v2_breakdown mirrors project_insights_health_breakdown_copy aggregation (signal detail + median category/total scores from health_score_v2_repo_copy_ds), but takes required slug and repos and returns one row. It only includes enabled, non-excluded repos; HAVING count() > 0 avoids a bogus aggregate row when every selected repo is filtered out. insightsProjects.slug must match slug so foreign-project repo URLs cannot leak data.

repo_lifecycle_v2 mirrors the project lifecycle “best state wins” rollup over lifecycleLabelV2, scoped the same way via slug + repos, and does not apply enabled/excluded filters so archived lifecycle still surfaces. Empty selections yield lifecycleLabel: null via the existing empty-groupArray guard.

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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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_project filters rep.enabled = true AND rep.excluded = false, while health_score_v2_repo_copy_ds still contains disabled repositories. A caller-supplied disabled URL can therefore contribute (and potentially win) the lifecycle rollup. Filtering these flags still preserves archived labels because archived is 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.

Comment on lines +96 to +98
AND rep.enabled = true
AND rep.excluded = false
AND ip.slug = {{ String(slug, description="Calling project's slug", required=True) }}
Comment on lines +6 to +9
- 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.
@gaspergrom
gaspergrom requested a review from joanagmaia August 28, 2026 13:19
…(IN-1253)

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
Copilot AI review requested due to automatic review settings August 28, 2026 17:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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 in groupArray and returns archived. Filter to enabled, non-archived, non-excluded repositories so the empty aggregate produces the documented NULL.
    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

  • enabled and archived are independent flags (repository upserts can set archived while forcing enabled = true), so this does not actually drop every archived repo. health_score_v2_signal_detail_ds retains a base row for archived repos with NULL category signals; therefore count() > 0 passes 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 repositories join, while the stated endpoint behavior requires NULL for 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`.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants