diff --git a/tools/gemini-cli-bot/metrics/scripts/latency.ts b/tools/gemini-cli-bot/metrics/scripts/latency.ts index 7dd5dcd1f6..849fb19823 100644 --- a/tools/gemini-cli-bot/metrics/scripts/latency.ts +++ b/tools/gemini-cli-bot/metrics/scripts/latency.ts @@ -10,18 +10,24 @@ import { GITHUB_OWNER, GITHUB_REPO } from '../types.js'; import { execSync } from 'node:child_process'; try { + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const dateStr = sevenDaysAgo.toISOString().split('T')[0]; + const query = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - pullRequests(last: 100, states: MERGED) { - nodes { + query($prQuery: String!, $issueQuery: String!) { + prSearch: search(query: $prQuery, type: ISSUE, first: 1000) { + nodes { + ... on PullRequest { authorAssociation createdAt mergedAt } } - issues(last: 100, states: CLOSED) { - nodes { + } + issueSearch: search(query: $issueQuery, type: ISSUE, first: 1000) { + nodes { + ... on Issue { authorAssociation createdAt closedAt @@ -30,30 +36,29 @@ try { } } `; + + const prQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:pr is:merged merged:>=${dateStr}`; + const issueQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:issue is:closed closed:>=${dateStr}`; + const output = execSync( - `gh api graphql -F owner=${GITHUB_OWNER} -F repo=${GITHUB_REPO} -f query='${query}'`, + `gh api graphql -F prQuery='${prQuery}' -F issueQuery='${issueQuery}' -f query='${query}'`, { encoding: 'utf-8' }, ); - const data = JSON.parse(output).data.repository; + const data = JSON.parse(output).data; + if (!data) { + throw new Error('No data returned from GraphQL API'); + } - const prs = data.pullRequests.nodes.map( - (p: { - authorAssociation: string; - mergedAt: string; - createdAt: string; - }) => ({ + const prs = (data.prSearch?.nodes || []).map( + (p: any) => ({ association: p.authorAssociation, latencyHours: (new Date(p.mergedAt).getTime() - new Date(p.createdAt).getTime()) / (1000 * 60 * 60), }), ); - const issues = data.issues.nodes.map( - (i: { - authorAssociation: string; - closedAt: string; - createdAt: string; - }) => ({ + const issues = (data.issueSearch?.nodes || []).map( + (i: any) => ({ association: i.authorAssociation, latencyHours: (new Date(i.closedAt).getTime() - new Date(i.createdAt).getTime()) / diff --git a/tools/gemini-cli-bot/metrics/scripts/review_distribution.ts b/tools/gemini-cli-bot/metrics/scripts/review_distribution.ts index eda4044916..1534d94253 100644 --- a/tools/gemini-cli-bot/metrics/scripts/review_distribution.ts +++ b/tools/gemini-cli-bot/metrics/scripts/review_distribution.ts @@ -10,11 +10,15 @@ import { GITHUB_OWNER, GITHUB_REPO } from '../types.js'; import { execSync } from 'node:child_process'; try { + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const dateStr = sevenDaysAgo.toISOString().split('T')[0]; + const query = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - pullRequests(last: 100) { - nodes { + query($prQuery: String!) { + prSearch: search(query: $prQuery, type: ISSUE, first: 1000) { + nodes { + ... on PullRequest { reviews(first: 50) { nodes { author { login } @@ -26,16 +30,22 @@ try { } } `; + const prQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:pr updated:>=${dateStr}`; + const output = execSync( - `gh api graphql -F owner=${GITHUB_OWNER} -F repo=${GITHUB_REPO} -f query='${query}'`, + `gh api graphql -F prQuery='${prQuery}' -f query='${query}'`, { encoding: 'utf-8' }, ); - const data = JSON.parse(output).data.repository; + const data = JSON.parse(output).data; + if (!data) { + throw new Error('No data returned from GraphQL API'); + } const reviewCounts: Record = {}; - for (const pr of data.pullRequests.nodes) { - if (!pr.reviews?.nodes) continue; + const nodes = data.prSearch?.nodes || []; + for (const pr of nodes) { + if (!pr?.reviews?.nodes) continue; // We only count one review per author per PR to avoid counting multiple review comments as multiple reviews const reviewersOnPR = new Set(); diff --git a/tools/gemini-cli-bot/metrics/scripts/throughput.ts b/tools/gemini-cli-bot/metrics/scripts/throughput.ts index 3806dd407a..dc24fc42b2 100644 --- a/tools/gemini-cli-bot/metrics/scripts/throughput.ts +++ b/tools/gemini-cli-bot/metrics/scripts/throughput.ts @@ -10,17 +10,23 @@ import { GITHUB_OWNER, GITHUB_REPO } from '../types.js'; import { execSync } from 'node:child_process'; try { + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const dateStr = sevenDaysAgo.toISOString().split('T')[0]; + const query = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - pullRequests(last: 100, states: MERGED) { - nodes { + query($prQuery: String!, $issueQuery: String!) { + prSearch: search(query: $prQuery, type: ISSUE, first: 1000) { + nodes { + ... on PullRequest { authorAssociation mergedAt } } - issues(last: 100, states: CLOSED) { - nodes { + } + issueSearch: search(query: $issueQuery, type: ISSUE, first: 1000) { + nodes { + ... on Issue { authorAssociation closedAt } @@ -28,25 +34,28 @@ try { } } `; + + const prQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:pr is:merged merged:>=${dateStr}`; + const issueQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:issue is:closed closed:>=${dateStr}`; + const output = execSync( - `gh api graphql -F owner=${GITHUB_OWNER} -F repo=${GITHUB_REPO} -f query='${query}'`, + `gh api graphql -F prQuery='${prQuery}' -F issueQuery='${issueQuery}' -f query='${query}'`, { encoding: 'utf-8' }, ); - const data = JSON.parse(output).data.repository; + const data = JSON.parse(output).data; + if (!data) { + throw new Error('No data returned from GraphQL API'); + } - const prs = data.pullRequests.nodes - .map((p: { authorAssociation: string; mergedAt: string }) => ({ - association: p.authorAssociation, - date: new Date(p.mergedAt).getTime(), - })) - .sort((a: { date: number }, b: { date: number }) => a.date - b.date); + const prs = (data.prSearch?.nodes || []).map((p: any) => ({ + association: p.authorAssociation, + date: new Date(p.mergedAt).getTime(), + })); - const issues = data.issues.nodes - .map((i: { authorAssociation: string; closedAt: string }) => ({ - association: i.authorAssociation, - date: new Date(i.closedAt).getTime(), - })) - .sort((a: { date: number }, b: { date: number }) => a.date - b.date); + const issues = (data.issueSearch?.nodes || []).map((i: any) => ({ + association: i.authorAssociation, + date: new Date(i.closedAt).getTime(), + })); const isMaintainer = (assoc: string) => ['MEMBER', 'OWNER', 'COLLABORATOR'].includes(assoc); @@ -54,11 +63,7 @@ try { const calculateThroughput = ( items: { association: string; date: number }[], ) => { - if (items.length < 2) return 0; - const first = items[0].date; - const last = items[items.length - 1].date; - const days = (last - first) / (1000 * 60 * 60 * 24); - return days > 0 ? items.length / days : items.length; // items per day + return items.length / 7; // items per day over 7-day window }; const prOverall = calculateThroughput(prs); diff --git a/tools/gemini-cli-bot/metrics/scripts/time_to_first_response.ts b/tools/gemini-cli-bot/metrics/scripts/time_to_first_response.ts index 3585d6443b..8784524e7d 100644 --- a/tools/gemini-cli-bot/metrics/scripts/time_to_first_response.ts +++ b/tools/gemini-cli-bot/metrics/scripts/time_to_first_response.ts @@ -10,11 +10,15 @@ import { GITHUB_OWNER, GITHUB_REPO } from '../types.js'; import { execSync } from 'node:child_process'; try { + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const dateStr = sevenDaysAgo.toISOString().split('T')[0]; + const query = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - pullRequests(last: 100) { - nodes { + query($prQuery: String!, $issueQuery: String!) { + prSearch: search(query: $prQuery, type: ISSUE, first: 1000) { + nodes { + ... on PullRequest { authorAssociation author { login } createdAt @@ -32,15 +36,17 @@ try { } } } - issues(last: 100) { - nodes { - authorAssociation - author { login } - createdAt - comments(first: 20) { - nodes { - author { login } - createdAt + } + issueSearch: search(query: $issueQuery, type: ISSUE, first: 1000) { + ... on Issue { + authorAssociation + author { login } + createdAt + comments(first: 20) { + nodes { + author { login } + createdAt + } } } } @@ -48,11 +54,18 @@ try { } } `; + + const prQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:pr created:>=${dateStr}`; + const issueQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:issue created:>=${dateStr}`; + const output = execSync( - `gh api graphql -F owner=${GITHUB_OWNER} -F repo=${GITHUB_REPO} -f query='${query}'`, + `gh api graphql -F prQuery='${prQuery}' -F issueQuery='${issueQuery}' -f query='${query}'`, { encoding: 'utf-8' }, ); - const data = JSON.parse(output).data.repository; + const data = JSON.parse(output).data; + if (!data) { + throw new Error('No data returned from GraphQL API'); + } const getFirstResponseTime = (item: { createdAt: string; @@ -91,31 +104,23 @@ try { } return null; // No response yet }; + const processItems = ( - items: { - authorAssociation: string; - createdAt: string; - author: { login: string }; - comments: { - nodes: { createdAt: string; author?: { login: string } }[]; - }; - reviews?: { - nodes: { createdAt: string; author?: { login: string } }[]; - }; - }[], + items: any[], ) => { - return items + return (items || []) .map((item) => ({ - association: item.authorAssociation, - ttfr: getFirstResponseTime(item), + association: item?.authorAssociation, + ttfr: item ? getFirstResponseTime(item) : null, })) .filter((i) => i.ttfr !== null) as { association: string; ttfr: number; }[]; }; - const prs = processItems(data.pullRequests.nodes); - const issues = processItems(data.issues.nodes); + + const prs = processItems(data.prSearch?.nodes); + const issues = processItems(data.issueSearch?.nodes); const allItems = [...prs, ...issues]; const isMaintainer = (assoc: string) => diff --git a/tools/gemini-cli-bot/metrics/scripts/user_touches.ts b/tools/gemini-cli-bot/metrics/scripts/user_touches.ts index 5ccffa94fc..7895e63f65 100644 --- a/tools/gemini-cli-bot/metrics/scripts/user_touches.ts +++ b/tools/gemini-cli-bot/metrics/scripts/user_touches.ts @@ -10,18 +10,24 @@ import { GITHUB_OWNER, GITHUB_REPO } from '../types.js'; import { execSync } from 'node:child_process'; try { + const sevenDaysAgo = new Date(); + sevenDaysAgo.setDate(sevenDaysAgo.getDate() - 7); + const dateStr = sevenDaysAgo.toISOString().split('T')[0]; + const query = ` - query($owner: String!, $repo: String!) { - repository(owner: $owner, name: $repo) { - pullRequests(last: 100, states: MERGED) { - nodes { + query($prQuery: String!, $issueQuery: String!) { + prSearch: search(query: $prQuery, type: ISSUE, first: 1000) { + nodes { + ... on PullRequest { authorAssociation comments { totalCount } reviews { totalCount } } } - issues(last: 100, states: CLOSED) { - nodes { + } + issueSearch: search(query: $issueQuery, type: ISSUE, first: 1000) { + nodes { + ... on Issue { authorAssociation comments { totalCount } } @@ -29,14 +35,21 @@ try { } } `; + + const prQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:pr updated:>=${dateStr}`; + const issueQuery = `repo:${GITHUB_OWNER}/${GITHUB_REPO} is:issue updated:>=${dateStr}`; + const output = execSync( - `gh api graphql -F owner=${GITHUB_OWNER} -F repo=${GITHUB_REPO} -f query='${query}'`, + `gh api graphql -F prQuery='${prQuery}' -F issueQuery='${issueQuery}' -f query='${query}'`, { encoding: 'utf-8' }, ); - const data = JSON.parse(output).data.repository; + const data = JSON.parse(output).data; + if (!data) { + throw new Error('No data returned from GraphQL API'); + } - const prs = data.pullRequests.nodes; - const issues = data.issues.nodes; + const prs = data.prSearch?.nodes || []; + const issues = data.issueSearch?.nodes || []; const allItems = [ ...prs.map(