diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 91d9faa..f5212cd 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,28 +4,36 @@ All notable changes to CloudVoyager are documented in this file. Entries are ord --- -## Bug Fix: Issue Migration Data Loss for Large Projects (2026-04-23) - +## Bug Fix: Issue Migration Data Loss + SCM Date-Bucket Distribution (2026-04-23) + -Fixed two bugs causing significant data loss during migration of projects with >5K issues. +Fixed issue migration data loss and implemented SCM-based date-bucket distribution to work around SonarCloud's 10K Elasticsearch visualization cap. -**Bug 1 — Batch distributor silently drops issues (critical):** The batch distributor split large issue sets into 5K-batch analyses with backdated dates. However, SonarCloud's issue tracker treats each analysis as a complete snapshot — when batch N+1 is processed, all issues from batch N that don't appear in batch N+1 are closed. Result: only the last batch's issues survive. For Angular Framework (31K issues split into 7 batches), only ~2K issues remained on SonarCloud. **Fix:** Disabled batch distribution entirely. All issues are now uploaded in a single analysis. The 10K Elasticsearch visualization cap in the SonarCloud UI is a display limitation only — measures and underlying data are accurate regardless. +**Bug 1 — Batch distributor silently drops issues (critical):** The old batch distributor split large issue sets into separate 5K-batch analyses. SonarCloud's issue tracker treats each analysis as a complete snapshot — issues from prior analyses not in the current one are closed. Only the last batch's issues survived. **Fix:** Disabled multi-analysis batching. All issues now upload in a single analysis. -**Bug 2 — Missing IN_SANDBOX status in SQ 2025 pipeline:** The `issueStatuses` parameter in the SQ 2025 client's `getIssues` and `getIssuesWithComments` methods was missing the `IN_SANDBOX` status (new in SQ 2025) and incorrectly included `CLOSED` (not a valid `issueStatuses` value in SQ 2025). Updated both `issue-methods.js` and `issues-hotspots.js` in the sq-2025 pipeline. +**Bug 2 — Missing IN_SANDBOX status in SQ 2025 pipeline:** The `issueStatuses` parameter was missing `IN_SANDBOX` (new in SQ 2025) and incorrectly included `CLOSED`. Fixed in both `issue-methods.js` and `issues-hotspots.js`. -**Verification results (3 test projects):** -| Project | SQ violations | SC violations | Match | -|---------|-------------|-------------|-------| -| Sonar Solutions Easy Nodejs | 15 | 15 | exact | -| Angular Framework | 31,642 | 31,641 | 99.997% | -| My MuleSoft Project | 1,278 | 6,716* | pending metadata sync | +**Feature — SCM date-bucket distribution:** SonarCloud's UI caps the issues list at 10K per creation-date bucket. To work around this, issues are sorted by file and grouped into ≤5K-issue batches. Each batch's files have their SCM changeset blame dates set to a different date (30 days apart). The CE uses SCM blame dates to set creation dates for new issues, spreading them across multiple date buckets within a single analysis. -\* MuleSoft shows higher violations on SC because all 6,732 migrated issues are initially OPEN; the metadata sync transitions them to their correct statuses (FALSE_POSITIVE, ACCEPTED, FIXED, etc.), after which the count matches. +**Verification results (Angular Framework — 31,642 issues):** +| Date Bucket | Issues | +|-------------|--------| +| Oct 2025 | 4,854 | +| Nov 2025 | 4,816 | +| Dec 2025 | 4,959 | +| Jan 2026 | 4,767 | +| Feb 2026 | 4,921 | +| Mar 2026 | 4,958 | +| Apr 2026 | 2,366 | +| **Total** | **31,641** (SQ: 31,642) | **Files changed:** -- `src/shared/utils/batch-distributor/helpers/should-batch.js` — disabled batching +- `src/shared/utils/batch-distributor/helpers/should-batch.js` — disabled multi-analysis batching +- `src/shared/utils/batch-distributor/helpers/backdate-changesets.js` — new: SCM date-bucket distribution +- `src/shared/utils/batch-distributor/helpers/compute-batch-date.js` — 30-day spacing between batches - `src/pipelines/sq-2025/sonarqube/api-client/helpers/issue-methods.js` — fixed status list - `src/pipelines/sq-2025/sonarqube/api/issues-hotspots.js` — fixed status constant +- All 6 pipeline `transfer-branch` entry points — call `backdateChangesets` before protobuf build --- diff --git a/src/pipelines/sq-10.0/transfer-branch/helpers/transfer-branch-batched.js b/src/pipelines/sq-10.0/transfer-branch/helpers/transfer-branch-batched.js index 9bd4bb8..730c442 100644 --- a/src/pipelines/sq-10.0/transfer-branch/helpers/transfer-branch-batched.js +++ b/src/pipelines/sq-10.0/transfer-branch/helpers/transfer-branch-batched.js @@ -12,6 +12,8 @@ export async function transferBranchBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap, wait = true } = opts; + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-10.0/transfer-branch/index.js b/src/pipelines/sq-10.0/transfer-branch/index.js index 4f85a7f..67ef55c 100644 --- a/src/pipelines/sq-10.0/transfer-branch/index.js +++ b/src/pipelines/sq-10.0/transfer-branch/index.js @@ -5,7 +5,7 @@ import { buildAndEncodeReport } from './helpers/build-and-encode-report.js'; import { uploadReport } from './helpers/upload-report.js'; import { computeBranchStats } from './helpers/compute-branch-stats.js'; import { transferBranchBatched } from './helpers/transfer-branch-batched.js'; -import { shouldBatch } from '../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../shared/utils/batch-distributor.js'; export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { if (shouldBatch(extractedData)) { @@ -17,6 +17,8 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo return { stats: computeBranchStats(extractedData), ceTask }; } + backdateChangesets(extractedData); + const encodedReport = buildAndEncodeReport({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch-batched.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch-batched.js index fabe0fe..37c0097 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch-batched.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch-batched.js @@ -13,6 +13,8 @@ export async function transferBranchBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap } = opts; + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch.js index a73aeaa..5adc9a7 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-branch.js @@ -4,7 +4,7 @@ import { ProtobufEncoder } from '../../protobuf/encoder.js'; import { ReportUploader } from '../../sonarcloud/uploader.js'; import { computeBranchStats } from './compute-branch-stats.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; -import { shouldBatch } from '../../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; // -------- Transfer Single Branch -------- @@ -21,6 +21,8 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo return { stats: computeBranchStats(extractedData), ceTask }; } + backdateChangesets(extractedData); + logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(extractedData, sonarcloudConfig, sonarCloudProfiles, { sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, diff --git a/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch-batched.js b/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch-batched.js index 6dc5149..ffbb94b 100644 --- a/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch-batched.js +++ b/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch-batched.js @@ -12,6 +12,8 @@ export async function transferBranchBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap, wait = true } = opts; + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch.js b/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch.js index 3b715dd..f69d047 100644 --- a/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch.js +++ b/src/pipelines/sq-10.4/transfer-branch/helpers/transfer-branch.js @@ -2,7 +2,7 @@ import { buildAndEncodeReport } from './build-and-encode-report.js'; import { uploadReport } from './upload-report.js'; import { computeBranchStats } from './compute-branch-stats.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; -import { shouldBatch } from '../../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; // -------- Main Logic -------- @@ -17,6 +17,8 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo return { stats: computeBranchStats(extractedData), ceTask }; } + backdateChangesets(extractedData); + const encodedReport = await buildAndEncodeReport({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, label, sonarCloudRepos, ruleEnrichmentMap diff --git a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch-batched.js b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch-batched.js index fabe0fe..37c0097 100644 --- a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch-batched.js +++ b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch-batched.js @@ -13,6 +13,8 @@ export async function transferBranchBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap } = opts; + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch.js b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch.js index e9f9b7a..7211860 100644 --- a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch.js +++ b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-branch.js @@ -3,7 +3,7 @@ import { ProtobufEncoder } from '../../protobuf/encoder.js'; import { ReportUploader } from '../../sonarcloud/uploader.js'; import { computeBranchStats } from './compute-branch-stats.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; -import { shouldBatch } from '../../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; import logger from '../../../../shared/utils/logger.js'; // -------- Main Logic -------- @@ -21,6 +21,8 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo return { stats: computeBranchStats(extractedData), ceTask }; } + backdateChangesets(extractedData); + logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(extractedData, sonarcloudConfig, sonarCloudProfiles, { sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, diff --git a/src/pipelines/sq-2025/transfer-branch/helpers/build-and-upload-batched.js b/src/pipelines/sq-2025/transfer-branch/helpers/build-and-upload-batched.js index e6bc0bd..1e24e96 100644 --- a/src/pipelines/sq-2025/transfer-branch/helpers/build-and-upload-batched.js +++ b/src/pipelines/sq-2025/transfer-branch/helpers/build-and-upload-batched.js @@ -8,6 +8,9 @@ import logger from '../../../../shared/utils/logger.js'; /** Split issues into batches and upload each as a separate scanner report. */ export async function buildAndUploadBatched(opts) { const { extractedData, label } = opts; + + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/transfer-batched.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/transfer-batched.js index 449c068..8ec7dc9 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/transfer-batched.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/transfer-batched.js @@ -12,6 +12,11 @@ export async function transferBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap } = opts; + // Sort issues by component key so cumulative batches add issues from NEW + // files each time. This prevents the CE's fuzzy issue tracker from matching + // new issues with existing ones on the same files. + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/index.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/index.js index 534fc25..210f189 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/index.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/index.js @@ -1,7 +1,7 @@ import { buildProtobufMessages, encodeMessages } from './helpers/build-and-encode.js'; import { uploadReport } from './helpers/upload-report.js'; import { transferBatched } from './helpers/transfer-batched.js'; -import { shouldBatch } from '../../../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../../../shared/utils/batch-distributor.js'; // -------- Branch Transfer -------- @@ -23,6 +23,8 @@ export async function transferBranch(options) { return { stats: computeBranchStats(extractedData), ceTask }; } + backdateChangesets(extractedData); + const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label); const encodedReport = await encodeMessages(messages, label); const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label); diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch-batched.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch-batched.js index dde8a10..a90d9ea 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch-batched.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch-batched.js @@ -13,6 +13,8 @@ export async function transferBranchBatched(opts) { referenceBranchName, sonarCloudClient, label, isMainBranch, sonarCloudRepos, ruleEnrichmentMap } = opts; + extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + const plan = computeBatchPlan(extractedData.issues.length); const baseDate = extractedData.metadata.extractedAt; diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch.js index f92b3a0..dce9aee 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-branch.js @@ -3,7 +3,7 @@ import { encodeReport } from './encode-report.js'; import { uploadReport } from './upload-report.js'; import { buildBranchResult } from './build-branch-result.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; -import { shouldBatch } from '../../../../shared/utils/batch-distributor.js'; +import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; // -------- Single Branch Transfer (build, encode, upload) -------- @@ -17,6 +17,8 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo return buildBranchResult(extractedData, ceTask); } + backdateChangesets(extractedData); + const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label); const encodedReport = await encodeReport(messages, label); const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label); diff --git a/src/shared/utils/batch-distributor.js b/src/shared/utils/batch-distributor.js index d2212f8..951408b 100644 --- a/src/shared/utils/batch-distributor.js +++ b/src/shared/utils/batch-distributor.js @@ -1,3 +1,3 @@ // -------- Batch Distributor Re-export -------- -export { shouldBatch, computeBatchPlan, computeBatchDate, createBatchExtractedData } from './batch-distributor/index.js'; +export { shouldBatch, computeBatchPlan, computeBatchDate, createBatchExtractedData, backdateChangesets } from './batch-distributor/index.js'; diff --git a/src/shared/utils/batch-distributor/helpers/backdate-changesets.js b/src/shared/utils/batch-distributor/helpers/backdate-changesets.js new file mode 100644 index 0000000..bb3bd16 --- /dev/null +++ b/src/shared/utils/batch-distributor/helpers/backdate-changesets.js @@ -0,0 +1,117 @@ +import logger from '../../logger.js'; +import { ISSUE_BATCH_SIZE } from './should-batch.js'; +import { computeBatchDate } from './compute-batch-date.js'; + +// -------- Backdate Changesets -------- + +const STUB_AUTHOR = 'cloudvoyager-migration@sonarcloud.io'; + +/** + * Modify SCM changeset blame dates so the CE assigns different creation + * dates to different groups of issues. This spreads issues across + * multiple date buckets within a **single** analysis. + * + * Strategy: sort issues by file, group files into ≤5K-issue batches, + * then set ALL lines of each file to that batch's date. Setting every + * line ensures the CE has no "newer" line to fall back on. + * + * Mutates extractedData.issues (sort order) and + * extractedData.changesets (dates) in place. + */ +export function backdateChangesets(extractedData) { + const issues = extractedData.issues || []; + if (issues.length <= ISSUE_BATCH_SIZE) return; + + const baseDateISO = extractedData.metadata?.extractedAt || new Date().toISOString(); + + // Sort issues by component so files are contiguous + issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); + + // Group files into batches of ≤ISSUE_BATCH_SIZE issues. + // Each file goes entirely into one batch (no splitting a file across batches). + const fileBatches = buildFileBatches(issues); + const batchCount = fileBatches.length; + + if (batchCount <= 1) return; + + logger.info( + `Backdating SCM data: ${issues.length} issues → ${batchCount} date buckets of ≤${ISSUE_BATCH_SIZE}` + ); + + let modifiedFiles = 0; + + for (let batchIdx = 0; batchIdx < batchCount; batchIdx++) { + if (batchIdx === batchCount - 1) break; // last batch keeps original date + + const batchDateMs = new Date( + computeBatchDate(baseDateISO, batchIdx, batchCount) + ).getTime(); + + const batchRevision = `cloudvoyager-batch-${String(batchIdx + 1).padStart(4, '0')}`; + + for (const compKey of fileBatches[batchIdx].files) { + const cs = extractedData.changesets.get(compKey); + if (!cs) continue; + + // Replace the single stub changeset with the backdated one + cs.changesets = [{ + revision: batchRevision, + author: STUB_AUTHOR, + date: batchDateMs, + }]; + // Point every line to changeset index 0 (the only entry) + cs.changesetIndexByLine.fill(0); + + modifiedFiles++; + } + } + + logger.info( + `Modified SCM data for ${modifiedFiles} files across ${batchCount} date buckets` + ); +} + +/** + * Walk the sorted issues array and collect files into batches. + * Each batch accumulates files until the issue count exceeds + * ISSUE_BATCH_SIZE, then a new batch starts. + */ +function buildFileBatches(issues) { + const batches = [{ files: new Set(), issueCount: 0 }]; + let currentFile = null; + let currentFileIssueCount = 0; + + for (const issue of issues) { + const compKey = issue.component; + if (!compKey) continue; + + if (compKey !== currentFile) { + // New file — flush previous file's count and decide batch placement + if (currentFile && currentFileIssueCount > 0) { + let batch = batches[batches.length - 1]; + if (batch.issueCount + currentFileIssueCount > ISSUE_BATCH_SIZE && batch.issueCount > 0) { + batches.push({ files: new Set(), issueCount: 0 }); + batch = batches[batches.length - 1]; + } + batch.files.add(currentFile); + batch.issueCount += currentFileIssueCount; + } + currentFile = compKey; + currentFileIssueCount = 0; + } + currentFileIssueCount++; + } + + // Flush last file + if (currentFile && currentFileIssueCount > 0) { + let batch = batches[batches.length - 1]; + if (batch.issueCount + currentFileIssueCount > ISSUE_BATCH_SIZE && batch.issueCount > 0) { + batches.push({ files: new Set(), issueCount: 0 }); + batch = batches[batches.length - 1]; + } + batch.files.add(currentFile); + batch.issueCount += currentFileIssueCount; + } + + return batches; +} diff --git a/src/shared/utils/batch-distributor/helpers/compute-batch-date.js b/src/shared/utils/batch-distributor/helpers/compute-batch-date.js index 7eaafac..7bcb5db 100644 --- a/src/shared/utils/batch-distributor/helpers/compute-batch-date.js +++ b/src/shared/utils/batch-distributor/helpers/compute-batch-date.js @@ -1,12 +1,14 @@ // -------- Compute Batch Date -------- +const DAYS_BETWEEN_BATCHES = 30; + /** Compute an ISO date string for a batch, going backwards from the base date. */ export function computeBatchDate(baseDateISO, batchIndex, totalBatches) { if (totalBatches <= 1) return baseDateISO; const baseDate = new Date(baseDateISO); if (isNaN(baseDate.getTime())) throw new Error(`Invalid base date: ${baseDateISO}`); - const daysBack = totalBatches - 1 - batchIndex; + const daysBack = (totalBatches - 1 - batchIndex) * DAYS_BETWEEN_BATCHES; const batchDate = new Date(baseDate); batchDate.setDate(baseDate.getDate() - daysBack); diff --git a/src/shared/utils/batch-distributor/helpers/should-batch.js b/src/shared/utils/batch-distributor/helpers/should-batch.js index 8e092c7..203c651 100644 --- a/src/shared/utils/batch-distributor/helpers/should-batch.js +++ b/src/shared/utils/batch-distributor/helpers/should-batch.js @@ -2,12 +2,9 @@ export const ISSUE_BATCH_SIZE = 5000; -// Batch distribution is disabled: each subsequent batch's analysis closes the -// previous batch's issues via SonarCloud's issue tracker (which treats each -// analysis as a complete snapshot). This means only the LAST batch's issues -// survive, silently dropping all earlier batches. Uploading all issues in a -// single analysis preserves the correct total even though the SonarCloud UI -// caps the issues list at 10K — the measures and underlying data are accurate. +// Multi-analysis batching is disabled — the CE's issue tracker closes +// issues from prior analyses. Instead, backdateChangesets() spreads +// creation dates within a single analysis via SCM blame data. /** Returns true if the extracted data has more issues than the batch threshold. */ export function shouldBatch(_extractedData) { return false; diff --git a/src/shared/utils/batch-distributor/index.js b/src/shared/utils/batch-distributor/index.js index 16dfebf..f387b74 100644 --- a/src/shared/utils/batch-distributor/index.js +++ b/src/shared/utils/batch-distributor/index.js @@ -4,3 +4,4 @@ export { shouldBatch } from './helpers/should-batch.js'; export { computeBatchPlan } from './helpers/compute-batch-plan.js'; export { computeBatchDate } from './helpers/compute-batch-date.js'; export { createBatchExtractedData } from './helpers/create-batch-extracted-data.js'; +export { backdateChangesets } from './helpers/backdate-changesets.js';