Files
gemini-cli/tools/gemini-cli-bot/metrics/scripts/throughput.ts
T
gemini-cli[bot] 17aec810ca chore: restore missing metrics and CI optimizations
This PR restores critical fixes for repository metrics and CI cost optimizations that were identified as missing from the filesystem despite being marked as completed in previous tasks.

### Changes:
- **Metrics Accuracy**: Re-implemented 7-day fixed window and search-based sampling in `throughput.ts`, `latency.ts`, and `user_touches.ts` to resolve reporting anomalies during batch operations.
- **Spend Tracking**: Implemented pagination in `actions_spend.ts` to ensure all workflow runs within the 7-day window are captured, avoiding undercounting.
- **CI Cost Optimization**: Replaced all instances of `macos-latest-large` with standard `macos-latest` runners in `ci.yml`, `chained_e2e.yml`, and `deflake.yml`.
- **Matrix Reduction**: Reduced the Mac test matrix in `ci.yml` to Node 20.x only, significantly reducing redundant compute spend.
- **Task Ledger**: Updated `lessons-learned.md` to document the logic divergence and its resolution (BT-63).

These changes ensure the repository metrics are reliable and that CI costs remain under control.
2026-05-12 16:30:21 +00:00

71 lines
2.8 KiB
TypeScript

/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*
* @license
*/
import { GITHUB_OWNER, GITHUB_REPO } from '../types.js';
import { execSync } from 'node:child_process';
try {
const days = 7;
const sinceDate = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString().split('T')[0];
const getItems = (type: 'prs' | 'issues') => {
const field = type === 'prs' ? 'merged-at' : 'closed-at';
const jsonFields = type === 'prs' ? 'authorAssociation,mergedAt' : 'authorAssociation,closedAt';
const output = execSync(
`gh search ${type} --repo ${GITHUB_OWNER}/${GITHUB_REPO} --${field} >=${sinceDate} --limit 1000 --json ${jsonFields}`,
{ encoding: 'utf-8' }
);
return JSON.parse(output);
};
const prs = getItems('prs').map((p: any) => ({
association: p.authorAssociation,
date: new Date(p.mergedAt).getTime(),
}));
const issues = getItems('issues').map((i: any) => ({
association: i.authorAssociation,
date: new Date(i.closedAt).getTime(),
}));
const isMaintainer = (assoc: string) =>
['MEMBER', 'OWNER', 'COLLABORATOR'].includes(assoc);
const calculateThroughput = (items: any[]) => items.length / days;
const prOverall = calculateThroughput(prs);
const prMaintainers = calculateThroughput(
prs.filter((i) => isMaintainer(i.association))
);
const prCommunity = calculateThroughput(
prs.filter((i) => !isMaintainer(i.association))
);
const issueOverall = calculateThroughput(issues);
const issueMaintainers = calculateThroughput(
issues.filter((i) => isMaintainer(i.association))
);
const issueCommunity = calculateThroughput(
issues.filter((i) => !isMaintainer(i.association))
);
process.stdout.write(`throughput_pr_overall_per_day,${Math.round(prOverall * 100) / 100}\n`);
process.stdout.write(`throughput_pr_maintainers_per_day,${Math.round(prMaintainers * 100) / 100}\n`);
process.stdout.write(`throughput_pr_community_per_day,${Math.round(prCommunity * 100) / 100}\n`);
process.stdout.write(`throughput_issue_overall_per_day,${Math.round(issueOverall * 100) / 100}\n`);
process.stdout.write(`throughput_issue_maintainers_per_day,${Math.round(issueMaintainers * 100) / 100}\n`);
process.stdout.write(`throughput_issue_community_per_day,${Math.round(issueCommunity * 100) / 100}\n`);
process.stdout.write(`throughput_issue_overall_days_per_issue,${issueOverall > 0 ? Math.round((1 / issueOverall) * 100) / 100 : 0}\n`);
process.stdout.write(`throughput_issue_maintainers_days_per_issue,${issueMaintainers > 0 ? Math.round((1 / issueMaintainers) * 100) / 100 : 0}\n`);
process.stdout.write(`throughput_issue_community_days_per_issue,${issueCommunity > 0 ? Math.round((1 / issueCommunity) * 100) / 100 : 0}\n`);
} catch (err) {
process.stderr.write(err instanceof Error ? err.message : String(err));
process.exit(1);
}