Skip to content

Commit de8ce16

Browse files
authored
Bugfix: Count WP cycle contributor breakdowns by unique contributor
Count WP cycle contributor breakdowns by unique contributor
2 parents f8168b3 + bb51454 commit de8ce16

9 files changed

Lines changed: 3340 additions & 3178 deletions

File tree

public/data/gb-releases.json

Lines changed: 2205 additions & 2451 deletions
Large diffs are not rendered by default.

public/data/wp-cycles.json

Lines changed: 656 additions & 702 deletions
Large diffs are not rendered by default.

scripts/types.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* These are internal to scripts - UI uses types from src/data/normalized.ts
44
*/
55

6+
import type { AIBreakdown } from '../src/data/normalized.js';
7+
68
/** Contributor aggregate statistics */
79
export interface ReleaseContributorAggregates {
810
stats: {
@@ -30,6 +32,8 @@ export interface Release {
3032
changelogUrl: string;
3133
parsedAt: string;
3234
parserVersion: string;
35+
aiPRs?: number;
36+
aiBreakdown?: AIBreakdown;
3337
}
3438

3539
/** WordPress release schedule entry */
@@ -40,4 +44,3 @@ export interface WPRelease {
4044
lastGBVersion: string;
4145
gbVersionRange: string;
4246
}
43-

scripts/utils/contributor-data.ts

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,26 @@ export interface ContributorData {
3131
location: string | null;
3232
}
3333

34+
let didWarnGitHubRateLimit = false;
35+
36+
/**
37+
* Profile values that should not count as an employer.
38+
*/
39+
export function isUnavailableSponsorValue( value: string | null ): boolean {
40+
if ( ! value ) {
41+
return true;
42+
}
43+
44+
const normalized = value.toLowerCase().trim();
45+
return [
46+
'n/a',
47+
'na',
48+
'none',
49+
'-',
50+
'not applicable',
51+
].includes( normalized );
52+
}
53+
3454
/**
3555
* Delay helper for rate limiting.
3656
*/
@@ -64,12 +84,12 @@ export function resolveWporgUsername(
6484
): string {
6585
const githubLower = githubUsername.toLowerCase();
6686

67-
// Check if we have a mapping for this GitHub username
87+
// Use the mapping when GitHub and WP.org usernames differ.
6888
if ( mapping && mapping.githubToWporg[ githubLower ] ) {
6989
return mapping.githubToWporg[ githubLower ];
7090
}
7191

72-
// No mapping found - assume GitHub username = WP.org username
92+
// Most contributors use the same username on both sites.
7393
return githubUsername;
7494
}
7595

@@ -89,22 +109,33 @@ export async function fetchContributorData(
89109
// Fetch WP.org profile
90110
const wpProfile = await fetchWPOrgProfile( wporgUsername );
91111

92-
let sponsor = wpProfile.employer || null;
112+
let sponsor = isUnavailableSponsorValue( wpProfile.employer )
113+
? null
114+
: wpProfile.employer || null;
93115
let location = wpProfile.location || null;
94116

95-
// Check GitHub for missing sponsor OR missing location
117+
// Use GitHub to fill whichever profile field WP.org did not provide.
96118
if ( ! sponsor || ! location ) {
97119
const ghUsername = wpProfile.wporgLinkedGitHubUsername || githubUsername;
98120
try {
99121
const ghProfile = await fetchGitHubUserProfile( ghUsername );
100122
if ( ! sponsor && ghProfile?.company ) {
101-
sponsor = ghProfile.company.replace( /^@/, '' ).trim() || null;
123+
const company = ghProfile.company.replace( /^@/, '' ).trim();
124+
sponsor = isUnavailableSponsorValue( company ) ? null : company;
102125
}
103126
if ( ! location && ghProfile?.location ) {
104127
location = ghProfile.location;
105128
}
106-
} catch {
107-
if ( verbose ) {
129+
} catch ( error ) {
130+
const message = error instanceof Error ? error.message : String( error );
131+
if ( message.includes( 'rate limit exceeded' ) ) {
132+
if ( ! didWarnGitHubRateLimit ) {
133+
console.warn(
134+
`\n ⚠️ GitHub profile fallback hit the rate limit. Set GITHUB_TOKEN and rerun if you want fewer Unknown contributors. ${ message }`
135+
);
136+
didWarnGitHubRateLimit = true;
137+
}
138+
} else if ( verbose ) {
108139
console.error( `\n Warning: GitHub fetch failed for ${ ghUsername }` );
109140
}
110141
}
@@ -133,7 +164,7 @@ export async function fetchContributorProfiles(
133164
existingData || []
134165
);
135166

136-
// Filter to usernames we don't already have
167+
// Skip profiles already fetched in this run.
137168
const toFetch = usernames.filter(
138169
( u ) => ! contributorDataMap.has( u.toLowerCase() )
139170
);

0 commit comments

Comments
 (0)