diff --git a/scripts/README.md b/scripts/README.md index 7d16e41..24e7950 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -31,7 +31,8 @@ Unavailable properties on `workflowRuns` records, including component AIC values omitted rather than serialized as `null`. Pass `--repo REPO` to collect one non-archived, non-fork repository within `ORG` instead -of the full visible organization. The resulting snapshot records the repository filter. +of the full visible organization. The resulting snapshot records the repository filter as +`repositoryFilter`; like `bot`, it is omitted when the corresponding option is not given. It requires authenticated `gh` and `unzip`. The snapshot is intentionally written to `/tmp` by default, not committed, and reports partial repository/artifact coverage. diff --git a/scripts/org-history.js b/scripts/org-history.js index a3d4298..b0bfb13 100644 --- a/scripts/org-history.js +++ b/scripts/org-history.js @@ -558,13 +558,17 @@ function selectedRepositories(org, repositoryFilter) { return [repository]; } +function snapshotHeader(org, interval, bot, repositoryFilter) { + return sparseProperties({ schemaVersion: SCHEMA_VERSION, org, period: interval.period, bot, repositoryFilter, interval: { start: interval.start, end: interval.end } }); +} + function collect(org, interval, bot, repositoryFilter) { // These are global prerequisites: do not turn a missing CLI/authentication // problem into misleading per-repository coverage gaps. exec('gh', ['auth', 'status']); exec('unzip', ['-v']); const repositories = selectedRepositories(org, repositoryFilter); - const snapshot = { schemaVersion: SCHEMA_VERSION, org, period: interval.period, bot: bot || null, repositoryFilter: repositoryFilter || null, interval: { start: interval.start, end: interval.end }, repositories: [], items: [], workflowRuns: [], commentAic: [], coverage: { repositories: { included: repositories.length, failed: [] }, aic: { relevantRuns: 0, eligibleRuns: 0, runsWithValues: 0, artifactBackedRuns: 0, commentBackedRuns: 0, ineligibleRuns: 0, missingOrExpired: 0, total: null } }, errors: [] }; + const snapshot = { ...snapshotHeader(org, interval, bot, repositoryFilter), repositories: [], items: [], workflowRuns: [], commentAic: [], coverage: { repositories: { included: repositories.length, failed: [] }, aic: { relevantRuns: 0, eligibleRuns: 0, runsWithValues: 0, artifactBackedRuns: 0, commentBackedRuns: 0, ineligibleRuns: 0, missingOrExpired: 0, total: null } }, errors: [] }; for (const repo of repositories) { let repositoryAic; try { @@ -650,4 +654,4 @@ function main(argv) { } if (require.main === module) { try { main(process.argv.slice(2)); } catch (error) { console.error(`org-history: ${error.message}`); process.exitCode = 1; } } -module.exports = { aicLinkedItems, applyCommentAicFallback, artifactAicProperties, deduplicateCommentAic, historyFilename, normalizedPullRequests, parseCommentAic, parsePeriod, previousCompleteIsoWeek, summarizeAicCoverage, validateDetailedItems, workflowRunRecord }; +module.exports = { aicLinkedItems, applyCommentAicFallback, artifactAicProperties, deduplicateCommentAic, historyFilename, normalizedPullRequests, parseCommentAic, parsePeriod, previousCompleteIsoWeek, snapshotHeader, summarizeAicCoverage, validateDetailedItems, workflowRunRecord }; diff --git a/tests/org-history.test.js b/tests/org-history.test.js index c1efded..14a9a24 100644 --- a/tests/org-history.test.js +++ b/tests/org-history.test.js @@ -4,7 +4,7 @@ const assert = require('node:assert/strict'); const childProcess = require('node:child_process'); const path = require('node:path'); const test = require('node:test'); -const { aicLinkedItems, applyCommentAicFallback, artifactAicProperties, deduplicateCommentAic, historyFilename, normalizedPullRequests, parseCommentAic, parsePeriod, previousCompleteIsoWeek, summarizeAicCoverage, validateDetailedItems, workflowRunRecord } = require('../scripts/org-history.js'); +const { aicLinkedItems, applyCommentAicFallback, artifactAicProperties, deduplicateCommentAic, historyFilename, normalizedPullRequests, parseCommentAic, parsePeriod, previousCompleteIsoWeek, snapshotHeader, summarizeAicCoverage, validateDetailedItems, workflowRunRecord } = require('../scripts/org-history.js'); const script = path.join(__dirname, '..', 'scripts', 'org-history.js'); @@ -94,6 +94,14 @@ test('normalizes only same-repository workflow PR references', () => { assert.deepEqual(normalizedPullRequests({ pull_requests: [{ url: 'https://api.github.com/repos/org/repo/pulls/2' }, { url: 'https://api.github.com/repos/org/repo/pulls/1' }, { url: 'https://api.github.com/repos/org/repo/pulls/2' }, { url: 'https://api.github.com/repos/org/other/pulls/3' }] }, 'org', 'repo'), [{ number: 1, url: 'https://github.com/org/repo/pull/1' }, { number: 2, url: 'https://github.com/org/repo/pull/2' }]); }); +test('omits unset optional snapshot header properties', () => { + const interval = parsePeriod('2026-W38'); + const expected = { schemaVersion: 5, org: 'org', period: '2026-W38', interval: { start: interval.start, end: interval.end } }; + assert.deepEqual(snapshotHeader('org', interval, undefined, undefined), expected); + assert.deepEqual(Object.keys(snapshotHeader('org', interval, 'bot[bot]', 'repo')), ['schemaVersion', 'org', 'period', 'bot', 'repositoryFilter', 'interval']); + assert.deepEqual(snapshotHeader('org', interval, 'bot[bot]', 'repo'), { ...expected, bot: 'bot[bot]', repositoryFilter: 'repo' }); +}); + test('serializes sparse workflow-run AIC properties without losing zero values', () => { const record = workflowRunRecord({ id: 1, path: 'drafter.lock.yml', aic: 0, aicRecords: 0, aicAgent: 0,