@@ -27862,7 +27862,79 @@ function makeSummaryTable(resources) {
2786227862 return summaryTable;
2786327863}
2786427864
27865- module.exports = { formatMs, makeSummaryTable };
27865+ /**
27866+ * Computes usage stats for a single bucket using pre/post snapshots.
27867+ *
27868+ * @param {object} startingBucket - bucket from the pre snapshot.
27869+ * @param {object} endingBucket - bucket from the post snapshot.
27870+ * @param {number} endTimeSeconds - post snapshot time in seconds.
27871+ * @returns {object} usage details and validation status.
27872+ */
27873+ function computeBucketUsage(startingBucket, endingBucket, endTimeSeconds) {
27874+ const result = {
27875+ valid: false,
27876+ used: 0,
27877+ remaining: undefined,
27878+ crossed_reset: false,
27879+ warnings: []
27880+ };
27881+
27882+ if (!startingBucket || !endingBucket) {
27883+ result.reason = 'missing_bucket';
27884+ return result;
27885+ }
27886+
27887+ const startingRemaining = Number(startingBucket.remaining);
27888+ const endingRemaining = Number(endingBucket.remaining);
27889+ if (!Number.isFinite(startingRemaining) || !Number.isFinite(endingRemaining)) {
27890+ result.reason = 'invalid_remaining';
27891+ return result;
27892+ }
27893+
27894+ const startingLimit = Number(startingBucket.limit);
27895+ const endingLimit = Number(endingBucket.limit);
27896+ const resetPre = Number(startingBucket.reset);
27897+ const crossedReset = Number.isFinite(resetPre) && endTimeSeconds >= resetPre;
27898+ result.crossed_reset = crossedReset;
27899+
27900+ let used;
27901+ if (crossedReset) {
27902+ if (!Number.isFinite(startingLimit) || !Number.isFinite(endingLimit)) {
27903+ result.reason = 'invalid_limit';
27904+ return result;
27905+ }
27906+ if (startingLimit !== endingLimit) {
27907+ result.warnings.push('limit_changed_across_reset');
27908+ }
27909+ used = startingLimit - startingRemaining + (endingLimit - endingRemaining);
27910+ } else {
27911+ if (
27912+ Number.isFinite(startingLimit) &&
27913+ Number.isFinite(endingLimit) &&
27914+ startingLimit !== endingLimit
27915+ ) {
27916+ result.reason = 'limit_changed_without_reset';
27917+ return result;
27918+ }
27919+ used = startingRemaining - endingRemaining;
27920+ if (used < 0) {
27921+ result.reason = 'remaining_increased_without_reset';
27922+ return result;
27923+ }
27924+ }
27925+
27926+ if (used < 0) {
27927+ result.reason = 'negative_usage';
27928+ return result;
27929+ }
27930+
27931+ result.valid = true;
27932+ result.used = used;
27933+ result.remaining = endingRemaining;
27934+ return result;
27935+ }
27936+
27937+ module.exports = { formatMs, makeSummaryTable, computeBucketUsage };
2786627938
2786727939
2786827940/***/ }),
@@ -27980,7 +28052,7 @@ const fs = __nccwpck_require__(9896);
2798028052const path = __nccwpck_require__(6928);
2798128053const { fetchRateLimit } = __nccwpck_require__(5042);
2798228054const { log, parseBuckets } = __nccwpck_require__(9630);
27983- const { formatMs, makeSummaryTable } = __nccwpck_require__(5828);
28055+ const { formatMs, makeSummaryTable, computeBucketUsage } = __nccwpck_require__(5828);
2798428056
2798528057/**
2798628058 * Writes JSON-stringified data to a file if a valid pathname is provided.
@@ -28032,6 +28104,7 @@ async function run() {
2803228104 );
2803328105 }
2803428106 const endTime = Date.now();
28107+ const endTimeSeconds = Math.floor(endTime / 1000);
2803528108 const duration = hasStartTime ? endTime - startTime : null;
2803628109
2803728110 log('[github-api-usage-tracker] Fetching final rate limits...');
@@ -28044,33 +28117,77 @@ async function run() {
2804428117 log(`[github-api-usage-tracker] ${JSON.stringify(endingResources, null, 2)}`);
2804528118
2804628119 const data = {};
28120+ const crossedBuckets = [];
2804728121 let totalUsed = 0;
2804828122
2804928123 for (const bucket of buckets) {
28050- const startingUsed = startingResources[bucket]?.used ;
28051- const endingUsed = endingResources[bucket]?.used ;
28052- if (startingUsed === undefined ) {
28124+ const startingBucket = startingResources[bucket];
28125+ const endingBucket = endingResources[bucket];
28126+ if (!startingBucket ) {
2805328127 core.warning(
2805428128 `[github-api-usage-tracker] Starting rate limit bucket "${bucket}" not found; skipping`
2805528129 );
2805628130 continue;
2805728131 }
28058- if (endingUsed === undefined ) {
28132+ if (!endingBucket ) {
2805928133 core.warning(
2806028134 `[github-api-usage-tracker] Ending rate limit bucket "${bucket}" not found; skipping`
2806128135 );
2806228136 continue;
2806328137 }
28064- let used = endingUsed - startingUsed;
28065- if (used < 0) {
28138+
28139+ const usage = computeBucketUsage(startingBucket, endingBucket, endTimeSeconds);
28140+ if (!usage.valid) {
28141+ switch (usage.reason) {
28142+ case 'invalid_remaining':
28143+ core.warning(
28144+ `[github-api-usage-tracker] Invalid remaining count for bucket "${bucket}"; skipping`
28145+ );
28146+ break;
28147+ case 'invalid_limit':
28148+ core.warning(
28149+ `[github-api-usage-tracker] Invalid limit for bucket "${bucket}" during reset crossing; skipping`
28150+ );
28151+ break;
28152+ case 'limit_changed_without_reset':
28153+ core.warning(
28154+ `[github-api-usage-tracker] Limit changed without reset for bucket "${bucket}"; skipping`
28155+ );
28156+ break;
28157+ case 'remaining_increased_without_reset':
28158+ core.warning(
28159+ `[github-api-usage-tracker] Remaining increased without reset for bucket "${bucket}"; skipping`
28160+ );
28161+ break;
28162+ case 'negative_usage':
28163+ core.warning(
28164+ `[github-api-usage-tracker] Negative usage for bucket "${bucket}" detected; skipping`
28165+ );
28166+ break;
28167+ default:
28168+ core.warning(
28169+ `[github-api-usage-tracker] Invalid usage data for bucket "${bucket}"; skipping`
28170+ );
28171+ break;
28172+ }
28173+ continue;
28174+ }
28175+
28176+ if (usage.warnings.includes('limit_changed_across_reset')) {
2806628177 core.warning(
28067- `[github-api-usage-tracker] Negative usage for bucket "${bucket}" detected; clamping to 0 `
28178+ `[github-api-usage-tracker] Limit changed across reset for bucket "${bucket}"; results may reflect a token change `
2806828179 );
28069- used = 0;
2807028180 }
28071- const remaining = endingResources[bucket].remaining;
28072- data[bucket] = { used, remaining };
28073- totalUsed += used;
28181+
28182+ data[bucket] = {
28183+ used: usage.used,
28184+ remaining: usage.remaining,
28185+ crossed_reset: usage.crossed_reset
28186+ };
28187+ if (usage.crossed_reset) {
28188+ crossedBuckets.push(bucket);
28189+ }
28190+ totalUsed += usage.used;
2807428191 }
2807528192
2807628193 // Set output
@@ -28088,9 +28205,16 @@ async function run() {
2808828205 log(
2808928206 `[github-api-usage-tracker] Preparing summary table for ${Object.keys(data).length} bucket(s)`
2809028207 );
28091- core.summary
28208+ const summary = core.summary
2809228209 .addHeading('GitHub API Usage Tracker Summary')
28093- .addTable(makeSummaryTable(data))
28210+ .addTable(makeSummaryTable(data));
28211+ if (crossedBuckets.length > 0) {
28212+ summary.addRaw(
28213+ `<p><strong>Reset Window Crossed:</strong> Yes (${crossedBuckets.join(', ')})</p>`,
28214+ true
28215+ );
28216+ }
28217+ summary
2809428218 .addRaw(
2809528219 `<p><strong>Action Duration:</strong> ${
2809628220 hasStartTime ? formatMs(duration) : 'Unknown (data missing)'
0 commit comments