From 3d65a4ab6d4e2fe97c4ac8372a1d5d2869ac7db1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Grom?= Date: Tue, 13 Oct 2026 12:07:16 +0100 Subject: [PATCH 1/2] fix: count the last 365 days in the active contributors badge IN-1222 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gašper Grom --- .../api/badge/active-contributors.test.ts | 71 +++++++++++++++++++ .../server/api/badge/active-contributors.ts | 16 +++-- 2 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 frontend/server/api/badge/active-contributors.test.ts diff --git a/frontend/server/api/badge/active-contributors.test.ts b/frontend/server/api/badge/active-contributors.test.ts new file mode 100644 index 000000000..7664a01e2 --- /dev/null +++ b/frontend/server/api/badge/active-contributors.test.ts @@ -0,0 +1,71 @@ +// Copyright (c) 2025 The Linux Foundation and each contributor. +// SPDX-License-Identifier: MIT + +import type { H3Event } from 'h3'; +import type { DateTime } from 'luxon'; +import { describe, it, expect, vi, beforeAll, beforeEach, afterEach } from 'vitest'; + +const mockSendRedirect = vi.fn(); +const mockFetchFromTinybird = vi.fn(); + +vi.mock('~~/server/data/tinybird/tinybird', () => ({ + fetchFromTinybird: (...args: unknown[]) => mockFetchFromTinybird(...args), +})); + +vi.mock('~~/server/utils/log', () => ({ logError: vi.fn() })); + +global.defineEventHandler = vi.fn((handler) => handler); +global.createError = vi.fn((error) => error); +global.getQuery = vi.fn(() => ({ project: 'envoy' })); +global.sendRedirect = mockSendRedirect; + +let handler: (event: H3Event) => Promise; + +beforeAll(async () => { + handler = (await import('./active-contributors')).default as unknown as typeof handler; +}); + +beforeEach(() => { + vi.clearAllMocks(); + vi.useFakeTimers(); + vi.setSystemTime(new Date('2026-10-13T12:00:00Z')); +}); + +afterEach(() => { + vi.useRealTimers(); +}); + +const redirectParam = (name: string) => + new URL(mockSendRedirect.mock.calls[0]?.[1] as string).searchParams.get(name); + +describe('GET /api/badge/active-contributors', () => { + it('counts active contributors over the last 365 days, like the share preview', async () => { + mockFetchFromTinybird.mockResolvedValue({ + data: [{ contributorCount: 1580, maintainerCount: 10, reviewerCount: 20 }], + }); + + await handler({} as H3Event); + + const [path, params] = mockFetchFromTinybird.mock.calls[0] as [ + string, + { startDate: DateTime; endDate: DateTime } & Record, + ]; + expect(path).toBe('/v0/pipes/active_contributors.json'); + expect(params).toMatchObject({ + project: 'envoy', + includeCodeContributions: true, + includeCollaborations: false, + }); + expect(params.startDate.toFormat('yyyy-MM-dd')).toBe('2025-10-13'); + expect(params.endDate.toFormat('yyyy-MM-dd')).toBe('2026-10-13'); + expect(redirectParam('label')).toBe('Active contributors (1Y)'); + expect(redirectParam('message')).toBe('1.6K'); + }); + + it('returns 404 when the pipe has no row for the project', async () => { + mockFetchFromTinybird.mockResolvedValue({ data: [] }); + + await expect(handler({} as H3Event)).rejects.toMatchObject({ statusCode: 404 }); + expect(mockSendRedirect).not.toHaveBeenCalled(); + }); +}); diff --git a/frontend/server/api/badge/active-contributors.ts b/frontend/server/api/badge/active-contributors.ts index d8d33597b..2ec88f201 100644 --- a/frontend/server/api/badge/active-contributors.ts +++ b/frontend/server/api/badge/active-contributors.ts @@ -1,9 +1,11 @@ // Copyright (c) 2025 The Linux Foundation and each contributor. // SPDX-License-Identifier: MIT +import { DateTime } from 'luxon'; + import { formatNumberShort } from '~/components/shared/utils/formatter'; +import type { TinybirdActiveContributorsSummary } from '~~/server/data/tinybird/responses.types'; import { fetchFromTinybird } from '~~/server/data/tinybird/tinybird'; import { logError } from '~~/server/utils/log'; -import type { HealthScoreTinybird } from '~~/types/overview/responses.types'; export default defineEventHandler(async (event): Promise => { const query = getQuery(event); @@ -11,17 +13,23 @@ export default defineEventHandler(async (event): Promise => { const repos: string[] = (query?.repos as string)?.split(',') || undefined; try { - const res = await fetchFromTinybird( - '/v0/pipes/health_score_active_contributors.json', + // Same pipe, filters and 365-day window as the share preview of this badge (active-contributor-badge.vue). + const now = DateTime.utc(); + const res = await fetchFromTinybird( + '/v0/pipes/active_contributors.json', { project, repos, + includeCodeContributions: true, + includeCollaborations: false, + startDate: now.minus({ days: 365 }), + endDate: now, }, ); if (!res.data || res.data.length === 0) { throw createError({ statusCode: 404, statusMessage: 'Project not found' }); } - const activeContributors = res.data[0].activeContributors; + const activeContributors = res.data[0].contributorCount; const label = encodeURIComponent('Active contributors (1Y)'); const message = encodeURIComponent(formatNumberShort(activeContributors || 0)); const url = `https://img.shields.io/static/v1?label=${label}&message=${message}&color=0094FF&logo=linuxfoundation&logoColor=white&style=flat`; From aed9f9c52d7c7679b72b2f6ab1815029244c0bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C5=A1per=20Grom?= Date: Tue, 13 Oct 2026 12:07:23 +0100 Subject: [PATCH 2/2] fix: remove the v1 health score comparison copy and methodology banner IN-1222 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Gašper Grom --- .../overview/health-score-banner.vue | 36 ------------------- .../modules/project/views/overview.vue | 2 -- frontend/docs/metrics/health-score/index.md | 32 ++--------------- 3 files changed, 2 insertions(+), 68 deletions(-) delete mode 100644 frontend/app/components/modules/project/components/overview/health-score-banner.vue diff --git a/frontend/app/components/modules/project/components/overview/health-score-banner.vue b/frontend/app/components/modules/project/components/overview/health-score-banner.vue deleted file mode 100644 index 7caecaefa..000000000 --- a/frontend/app/components/modules/project/components/overview/health-score-banner.vue +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - diff --git a/frontend/app/components/modules/project/views/overview.vue b/frontend/app/components/modules/project/views/overview.vue index f37856ae2..83a677336 100644 --- a/frontend/app/components/modules/project/views/overview.vue +++ b/frontend/app/components/modules/project/views/overview.vue @@ -6,7 +6,6 @@ SPDX-License-Identifier: MIT
-