diff --git a/src/pipelines/sq-10.0/protobuf/builder/helpers/build-metadata.js b/src/pipelines/sq-10.0/protobuf/builder/helpers/build-metadata.js index a708232..4100eb1 100644 --- a/src/pipelines/sq-10.0/protobuf/builder/helpers/build-metadata.js +++ b/src/pipelines/sq-10.0/protobuf/builder/helpers/build-metadata.js @@ -18,7 +18,7 @@ export function buildMetadata(ctx) { branchName, branchType: 1, referenceBranchName: referenceBranch, scmRevisionId: ctx.data.metadata.scmRevisionId || generateFakeCommitHash(), - projectVersion: '1.0.0', + projectVersion: ctx.sourceProjectVersion || '1.0.0', analyzedIndexedFileCountPerType: ctx.buildFileCountsByType(), }; logger.debug(`Metadata built: projectKey=${metadata.projectKey}, branch=${metadata.branchName}, scmRevisionId=${metadata.scmRevisionId}`); diff --git a/src/pipelines/sq-10.0/protobuf/builder/index.js b/src/pipelines/sq-10.0/protobuf/builder/index.js index 5d2587f..f60027f 100644 --- a/src/pipelines/sq-10.0/protobuf/builder/index.js +++ b/src/pipelines/sq-10.0/protobuf/builder/index.js @@ -10,6 +10,7 @@ export function createProtobufBuilder(extractedData, scConfig = {}, scProfiles = referenceBranchName: options.referenceBranchName || null, sonarCloudRepos: options.sonarCloudRepos || new Set(), ruleEnrichmentMap: options.ruleEnrichmentMap || new Map(), + sourceProjectVersion: options.sourceProjectVersion || null, }; bindBuilderMethods(ctx); return ctx; diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/finalize-transfer.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/finalize-transfer.js index 17cb9e0..e2067a1 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/finalize-transfer.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/finalize-transfer.js @@ -23,7 +23,7 @@ export async function finalizeTransfer({ mainResult, sonarCloudMainBranch, syncA if (nonMainBranches.length > 0) { await waitForMainAnalysis(sonarCloudClient, mainResult.ceTask?.id, wait); logger.info(`Syncing ${nonMainBranches.length} additional branch(es): ${nonMainBranches.map(b => b.name).join(', ')}`); - await transferNonMainBranches({ nonMainBranches, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, journal, cache, stateTracker, isIncremental, shutdownCheck, performanceConfig, sonarCloudRepos, ruleEnrichmentMap, aggregatedStats }); + await transferNonMainBranches({ nonMainBranches, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, cache, stateTracker, isIncremental, shutdownCheck, performanceConfig, sonarCloudRepos, ruleEnrichmentMap, aggregatedStats }); } else { logger.info('No additional branches to sync (only the main branch exists)'); } 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 37c0097..415b66b 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 @@ -11,7 +11,7 @@ import logger from '../../../../shared/utils/logger.js'; export async function transferBranchBatched(opts) { const { extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap } = opts; + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion } = opts; extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); @@ -29,7 +29,7 @@ export async function transferBranchBatched(opts) { logger.info(`[${batchLabel}] Issues ${batch.startIndex + 1}-${batch.endIndex} | date=${batchDate}`); const builder = new ProtobufBuilder(batchData, sonarcloudConfig, sonarCloudProfiles, { - sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); const messages = builder.buildAll(); const encoder = new ProtobufEncoder(); @@ -38,7 +38,7 @@ export async function transferBranchBatched(opts) { const uploader = new ReportUploader(sonarCloudClient); const metadata = { projectKey: sonarcloudConfig.projectKey, organization: sonarcloudConfig.organization, - version: '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; lastCeTask = await uploader.uploadAndWait(encodedReport, metadata); 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 5adc9a7..06615f2 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 @@ -5,18 +5,21 @@ import { ReportUploader } from '../../sonarcloud/uploader.js'; import { computeBranchStats } from './compute-branch-stats.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; +import { resolveSourceProjectVersion } from '../../../../shared/utils/source-version/resolve-source-project-version.js'; // -------- Transfer Single Branch -------- /** * Build, encode, and upload a single branch report to SonarCloud. */ -export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { +export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, sonarQubeClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { + const sourceProjectVersion = await resolveSourceProjectVersion(sonarQubeClient, sonarQubeClient?.projectKey, isMainBranch ? null : branchName); + if (shouldBatch(extractedData)) { const ceTask = await transferBranchBatched({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap, + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); return { stats: computeBranchStats(extractedData), ceTask }; } @@ -25,7 +28,7 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(extractedData, sonarcloudConfig, sonarCloudProfiles, { - sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); const messages = builder.buildAll(); @@ -39,7 +42,7 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo const metadata = { projectKey: sonarcloudConfig.projectKey, organization: sonarcloudConfig.organization, - version: '1.0.0', + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-main-branch.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-main-branch.js index 76198bd..76175d2 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-main-branch.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-main-branch.js @@ -6,7 +6,7 @@ import { transferBranch } from './transfer-branch.js'; const ZERO_STATS = { issuesTransferred: 0, hotspotsTransferred: 0, componentsTransferred: 0, sourcesTransferred: 0, linesOfCode: 0 }; -export async function transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, journal, sonarCloudRepos, ruleEnrichmentMap }) { +export async function transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, sonarCloudRepos, ruleEnrichmentMap }) { const alreadyCompleted = journal?.getBranchStatus(sonarCloudMainBranch) === 'completed'; if (alreadyCompleted) { @@ -26,7 +26,7 @@ export async function transferMainBranch({ extractedData, sonarcloudConfig, sona const result = await transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName: sonarCloudMainBranch, referenceBranchName: sonarCloudMainBranch, - wait, sonarCloudClient, label: 'main', isMainBranch: true, + wait, sonarCloudClient, sonarQubeClient, label: 'main', isMainBranch: true, sonarCloudRepos, ruleEnrichmentMap, }); diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-non-main-branches.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-non-main-branches.js index a20efac..d40c6c4 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-non-main-branches.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-non-main-branches.js @@ -4,10 +4,10 @@ import { aggregateBranchStats } from './aggregate-branch-stats.js'; // -------- Transfer Non-Main Branches -------- -export async function transferNonMainBranches({ nonMainBranches, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, journal, cache, stateTracker, isIncremental, shutdownCheck, performanceConfig, sonarCloudRepos, ruleEnrichmentMap, aggregatedStats }) { +export async function transferNonMainBranches({ nonMainBranches, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, cache, stateTracker, isIncremental, shutdownCheck, performanceConfig, sonarCloudRepos, ruleEnrichmentMap, aggregatedStats }) { const branchResults = await mapConcurrent( nonMainBranches, - async (branch) => transferSingleNonMainBranch({ branch, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap }), + async (branch) => transferSingleNonMainBranch({ branch, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap }), { concurrency: performanceConfig?.maxConcurrency || 4, settled: true } ); aggregateBranchStats(aggregatedStats, branchResults); diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-project.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-project.js index 37c3765..eeb5f34 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-project.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-project.js @@ -38,7 +38,7 @@ export async function transferProject(opts) { const sonarCloudRepos = await sonarCloudClient.getRuleRepositories(); const ruleEnrichmentMap = p.prebuiltEnrichmentMap || new Map(); checkShutdown(p.shutdownCheck); - const mainResult = await transferMainBranch({ extractedData, sonarcloudConfig: p.sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait: p.wait, sonarCloudClient, journal, sonarCloudRepos, ruleEnrichmentMap }); + const mainResult = await transferMainBranch({ extractedData, sonarcloudConfig: p.sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait: p.wait, sonarCloudClient, sonarQubeClient, journal, sonarCloudRepos, ruleEnrichmentMap }); return await finalizeTransfer({ mainResult, sonarCloudMainBranch, ...p, extractedData, extractor, sonarCloudProfiles, sonarCloudClient, sonarQubeClient, journal, cache, stateTracker, sonarCloudRepos, ruleEnrichmentMap, lockFile }); } catch (error) { if (!(error instanceof GracefulShutdownError) && journal) { await journal.markInterrupted().catch(() => {}); } diff --git a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-single-non-main-branch.js b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-single-non-main-branch.js index ffa20d3..5be43b7 100644 --- a/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-single-non-main-branch.js +++ b/src/pipelines/sq-10.0/transfer-pipeline/helpers/transfer-single-non-main-branch.js @@ -4,7 +4,7 @@ import { transferBranch } from './transfer-branch.js'; // -------- Transfer Single Non-Main Branch -------- -export async function transferSingleNonMainBranch({ branch, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap }) { +export async function transferSingleNonMainBranch({ branch, extractedData, extractor, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap }) { const branchName = branch.name; if (shutdownCheck()) return { skipped: true, branchName, reason: 'shutdown' }; if (isIncremental && stateTracker.isBranchCompleted(branchName)) { @@ -24,7 +24,7 @@ export async function transferSingleNonMainBranch({ branch, extractedData, extra const branchResult = await transferBranch({ extractedData: branchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName: sonarCloudMainBranch, wait, - sonarCloudClient, label: branchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudClient, sonarQubeClient, label: branchName, sonarCloudRepos, ruleEnrichmentMap, }); if (journal) { await journal.recordUpload(branchName, branchResult.ceTask?.id); diff --git a/src/pipelines/sq-10.4/protobuf/builder/helpers/build-metadata.js b/src/pipelines/sq-10.4/protobuf/builder/helpers/build-metadata.js index 580e4e2..f8b0070 100644 --- a/src/pipelines/sq-10.4/protobuf/builder/helpers/build-metadata.js +++ b/src/pipelines/sq-10.4/protobuf/builder/helpers/build-metadata.js @@ -17,7 +17,7 @@ export function buildMetadata(instance) { qprofilesPerLanguage: instance.buildQProfiles(), branchName, branchType: 1, referenceBranchName: referenceBranch, scmRevisionId: instance.data.metadata.scmRevisionId || instance.generateFakeCommitHash(), - projectVersion: '1.0.0', + projectVersion: instance.sourceProjectVersion || '1.0.0', analyzedIndexedFileCountPerType: instance.buildFileCountsByType(), }; logger.debug(`Metadata built: projectKey=${metadata.projectKey}, branch=${metadata.branchName}, scmRevisionId=${metadata.scmRevisionId}`); diff --git a/src/pipelines/sq-10.4/protobuf/builder/helpers/create-protobuf-builder.js b/src/pipelines/sq-10.4/protobuf/builder/helpers/create-protobuf-builder.js index 449465d..c27ab0f 100644 --- a/src/pipelines/sq-10.4/protobuf/builder/helpers/create-protobuf-builder.js +++ b/src/pipelines/sq-10.4/protobuf/builder/helpers/create-protobuf-builder.js @@ -23,6 +23,7 @@ export function createProtobufBuilder(extractedData, sonarCloudConfig = {}, sona referenceBranchName: options.referenceBranchName || null, sonarCloudRepos: options.sonarCloudRepos || new Set(), ruleEnrichmentMap: options.ruleEnrichmentMap || new Map(), + sourceProjectVersion: options.sourceProjectVersion || null, }; attachUtilityMethods(instance); diff --git a/src/pipelines/sq-10.4/transfer-pipeline/helpers/execute-transfer.js b/src/pipelines/sq-10.4/transfer-pipeline/helpers/execute-transfer.js index 12eaf28..729e3ae 100644 --- a/src/pipelines/sq-10.4/transfer-pipeline/helpers/execute-transfer.js +++ b/src/pipelines/sq-10.4/transfer-pipeline/helpers/execute-transfer.js @@ -30,14 +30,14 @@ export async function executeTransfer(opts) { checkShutdown(shutdownCheck); - const mainBranchResult = await transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName: sonarCloudMainBranch, wait, sonarCloudClient, journal, sonarCloudRepos, ruleEnrichmentMap }); + const mainBranchResult = await transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName: sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, journal, sonarCloudRepos, ruleEnrichmentMap }); const aggregatedStats = { issuesTransferred: mainBranchResult.stats.issuesTransferred || 0, hotspotsTransferred: mainBranchResult.stats.hotspotsTransferred || 0, componentsTransferred: mainBranchResult.stats.componentsTransferred || 0, sourcesTransferred: mainBranchResult.stats.sourcesTransferred || 0, linesOfCode: mainBranchResult.stats.linesOfCode || 0, branchesTransferred: [sonarCloudMainBranch] }; if (isIncremental) { stateTracker.markBranchCompleted(sonarCloudMainBranch); await stateTracker.save(); } checkShutdown(shutdownCheck); if (syncAllBranches) { - await transferNonMainBranches({ extractedData, sonarcloudConfig, sonarCloudProfiles, mainBranchResult, sonarCloudMainBranch, wait, sonarCloudClient, extractor, journal, cache, stateTracker, isIncremental, shutdownCheck, excludeBranches, includeBranches, performanceConfig, aggregatedStats, sonarCloudRepos, ruleEnrichmentMap }); + await transferNonMainBranches({ extractedData, sonarcloudConfig, sonarCloudProfiles, mainBranchResult, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, extractor, journal, cache, stateTracker, isIncremental, shutdownCheck, excludeBranches, includeBranches, performanceConfig, aggregatedStats, sonarCloudRepos, ruleEnrichmentMap }); } // -------- Phase 2: Metadata Sync -------- 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 37c0097..415b66b 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 @@ -11,7 +11,7 @@ import logger from '../../../../shared/utils/logger.js'; export async function transferBranchBatched(opts) { const { extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap } = opts; + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion } = opts; extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); @@ -29,7 +29,7 @@ export async function transferBranchBatched(opts) { logger.info(`[${batchLabel}] Issues ${batch.startIndex + 1}-${batch.endIndex} | date=${batchDate}`); const builder = new ProtobufBuilder(batchData, sonarcloudConfig, sonarCloudProfiles, { - sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); const messages = builder.buildAll(); const encoder = new ProtobufEncoder(); @@ -38,7 +38,7 @@ export async function transferBranchBatched(opts) { const uploader = new ReportUploader(sonarCloudClient); const metadata = { projectKey: sonarcloudConfig.projectKey, organization: sonarcloudConfig.organization, - version: '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; lastCeTask = await uploader.uploadAndWait(encodedReport, metadata); 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 7211860..7672fe4 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 @@ -4,6 +4,7 @@ import { ReportUploader } from '../../sonarcloud/uploader.js'; import { computeBranchStats } from './compute-branch-stats.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; +import { resolveSourceProjectVersion } from '../../../../shared/utils/source-version/resolve-source-project-version.js'; import logger from '../../../../shared/utils/logger.js'; // -------- Main Logic -------- @@ -11,12 +12,14 @@ import logger from '../../../../shared/utils/logger.js'; /** * Build, encode, and upload a single branch report to SonarCloud. */ -export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { +export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, sonarQubeClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { + const sourceProjectVersion = await resolveSourceProjectVersion(sonarQubeClient, sonarQubeClient?.projectKey, isMainBranch ? null : branchName); + if (shouldBatch(extractedData)) { const ceTask = await transferBranchBatched({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap, + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); return { stats: computeBranchStats(extractedData), ceTask }; } @@ -25,7 +28,7 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(extractedData, sonarcloudConfig, sonarCloudProfiles, { - sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); const messages = builder.buildAll(); @@ -38,7 +41,7 @@ export async function transferBranch({ extractedData, sonarcloudConfig, sonarClo const uploader = new ReportUploader(sonarCloudClient); const metadata = { projectKey: sonarcloudConfig.projectKey, organization: sonarcloudConfig.organization, - version: '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; let ceTask; diff --git a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-main-branch.js b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-main-branch.js index 5dffc85..b919eef 100644 --- a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-main-branch.js +++ b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-main-branch.js @@ -11,7 +11,7 @@ import logger from '../../../../shared/utils/logger.js'; * @param {object} opts - Transfer options * @returns {Promise} { stats, ceTask } */ -export async function transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, wait, sonarCloudClient, journal, sonarCloudRepos, ruleEnrichmentMap }) { +export async function transferMainBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, wait, sonarCloudClient, sonarQubeClient, journal, sonarCloudRepos, ruleEnrichmentMap }) { const mainBranchCompleted = journal?.getBranchStatus(branchName) === 'completed'; const emptyStats = { issuesTransferred: 0, hotspotsTransferred: 0, componentsTransferred: 0, sourcesTransferred: 0, linesOfCode: 0 }; @@ -33,7 +33,7 @@ export async function transferMainBranch({ extractedData, sonarcloudConfig, sona const result = await transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, - referenceBranchName: branchName, wait, sonarCloudClient, label: 'main', + referenceBranchName: branchName, wait, sonarCloudClient, sonarQubeClient, label: 'main', isMainBranch: true, sonarCloudRepos, ruleEnrichmentMap, }); diff --git a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-single-branch.js b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-single-branch.js index 9db8b5d..5afcf4b 100644 --- a/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-single-branch.js +++ b/src/pipelines/sq-10.4/transfer-pipeline/helpers/transfer-single-branch.js @@ -12,7 +12,7 @@ import logger from '../../../../shared/utils/logger.js'; * @returns {Promise} { branchName, branchResult } or { skipped, branchName } */ export async function transferSingleBranch(opts) { - const { branch, extractedData, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, extractor, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap } = opts; + const { branch, extractedData, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, extractor, journal, cache, stateTracker, isIncremental, shutdownCheck, sonarCloudRepos, ruleEnrichmentMap } = opts; const branchName = branch.name; const skipResult = checkBranchSkip(branchName, { shutdownCheck, isIncremental, stateTracker, journal }); @@ -28,7 +28,7 @@ export async function transferSingleBranch(opts) { const branchResult = await transferBranch({ extractedData: branchData, sonarcloudConfig, sonarCloudProfiles, branchName, - referenceBranchName: sonarCloudMainBranch, wait, sonarCloudClient, label: branchName, + referenceBranchName: sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, label: branchName, sonarCloudRepos, ruleEnrichmentMap, }); diff --git a/src/pipelines/sq-2025/protobuf/builder/helpers/build-metadata.js b/src/pipelines/sq-2025/protobuf/builder/helpers/build-metadata.js index fc15f3e..c9fb1f1 100644 --- a/src/pipelines/sq-2025/protobuf/builder/helpers/build-metadata.js +++ b/src/pipelines/sq-2025/protobuf/builder/helpers/build-metadata.js @@ -19,7 +19,7 @@ export function buildMetadata(inst) { qprofilesPerLanguage: inst.buildQProfiles(), branchName, branchType: 1, referenceBranchName: referenceBranch, scmRevisionId: inst.data.metadata.scmRevisionId || randomBytes(20).toString('hex'), - projectVersion: '1.0.0', + projectVersion: inst.sourceProjectVersion || '1.0.0', analyzedIndexedFileCountPerType: inst.buildFileCountsByType(), }; diff --git a/src/pipelines/sq-2025/protobuf/builder/helpers/create-protobuf-builder.js b/src/pipelines/sq-2025/protobuf/builder/helpers/create-protobuf-builder.js index 8bd1f8d..38fe11a 100644 --- a/src/pipelines/sq-2025/protobuf/builder/helpers/create-protobuf-builder.js +++ b/src/pipelines/sq-2025/protobuf/builder/helpers/create-protobuf-builder.js @@ -14,6 +14,7 @@ export function createProtobufBuilder(extractedData, sonarCloudConfig = {}, sona referenceBranchName: options.referenceBranchName || null, sonarCloudRepos: options.sonarCloudRepos || new Set(), ruleEnrichmentMap: options.ruleEnrichmentMap || new Map(), + sourceProjectVersion: options.sourceProjectVersion || null, }; inst.getComponentRef = (key) => { diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/execute-transfer/helpers/run-transfer-phases.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/execute-transfer/helpers/run-transfer-phases.js index 065852e..ce756e8 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/execute-transfer/helpers/run-transfer-phases.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/execute-transfer/helpers/run-transfer-phases.js @@ -15,13 +15,13 @@ export async function runTransferPhases(opts) { extractedData, extractor, projectKey, performanceConfig } = opts; const { mainBranchResult, aggregatedStats } = await transferMainBranch({ - journal, sonarCloudMainBranch, sonarCloudClient, extractedData, + journal, sonarCloudMainBranch, sonarCloudClient, sonarQubeClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap, stateTracker, isIncremental, }); checkShutdown(shutdownCheck); if (syncAllBranches) { - await transferNonMainBranches({ extractedData, excludeBranches, includeBranches, mainBranchResult, sonarCloudClient, sonarCloudMainBranch, wait, aggregatedStats, extractor, journal, cache, shutdownCheck, stateTracker, isIncremental, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, performanceConfig }); + await transferNonMainBranches({ extractedData, excludeBranches, includeBranches, mainBranchResult, sonarCloudClient, sonarQubeClient, sonarCloudMainBranch, wait, aggregatedStats, extractor, journal, cache, shutdownCheck, stateTracker, isIncremental, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, performanceConfig }); } // -------- Phase 2: Metadata Sync -------- diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/build-and-encode.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/build-and-encode.js index c7876f2..dbee837 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/build-and-encode.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/build-and-encode.js @@ -5,11 +5,11 @@ import logger from '../../../../../../shared/utils/logger.js'; // -------- Build and Encode -------- /** Build protobuf messages and encode them. */ -export function buildProtobufMessages(data, scConfig, profiles, branch, refBranch, repos, enrichMap, label) { +export function buildProtobufMessages(data, scConfig, profiles, branch, refBranch, repos, enrichMap, label, sourceProjectVersion) { logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(data, scConfig, profiles, { sonarCloudBranchName: branch, referenceBranchName: refBranch, - sonarCloudRepos: repos, ruleEnrichmentMap: enrichMap, + sonarCloudRepos: repos, ruleEnrichmentMap: enrichMap, sourceProjectVersion, }); return builder.buildAll(); } 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 8ec7dc9..07ad5d2 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 @@ -10,7 +10,7 @@ import logger from '../../../../../../shared/utils/logger.js'; export async function transferBatched(opts) { const { extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap } = opts; + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion } = 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 @@ -30,9 +30,9 @@ export async function transferBatched(opts) { logger.info(`[${batchLabel}] Issues ${batch.startIndex + 1}-${batch.endIndex} | date=${batchDate}`); - const messages = buildProtobufMessages(batchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, batchLabel); + const messages = buildProtobufMessages(batchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, batchLabel, sourceProjectVersion); const encodedReport = await encodeMessages(messages, batchLabel); - lastCeTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, true, batchLabel); + lastCeTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, true, batchLabel, sourceProjectVersion); logger.info(`[${batchLabel}] CE task completed: ${lastCeTask.id}`); } diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/upload-report.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/upload-report.js index dbbd301..e432890 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/upload-report.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-branch/helpers/upload-report.js @@ -4,13 +4,13 @@ import logger from '../../../../../../shared/utils/logger.js'; // -------- Upload Report -------- /** Upload an encoded report to SonarCloud. */ -export async function uploadReport(encodedReport, scConfig, scClient, branchName, isMainBranch, wait, label) { +export async function uploadReport(encodedReport, scConfig, scClient, branchName, isMainBranch, wait, label, sourceProjectVersion) { logger.info(`[${label}] Uploading to SonarCloud...`); const uploader = new ReportUploader(scClient); const metadata = { projectKey: scConfig.projectKey, organization: scConfig.organization, - version: '1.0.0', + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; 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 210f189..1854c6f 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 @@ -2,6 +2,7 @@ import { buildProtobufMessages, encodeMessages } from './helpers/build-and-encod import { uploadReport } from './helpers/upload-report.js'; import { transferBatched } from './helpers/transfer-batched.js'; import { shouldBatch, backdateChangesets } from '../../../../../shared/utils/batch-distributor.js'; +import { resolveSourceProjectVersion } from '../../../../../shared/utils/source-version/resolve-source-project-version.js'; // -------- Branch Transfer -------- @@ -9,25 +10,27 @@ import { shouldBatch, backdateChangesets } from '../../../../../shared/utils/bat export async function transferBranch(options) { const { extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, - referenceBranchName, wait, sonarCloudClient, label, + referenceBranchName, wait, sonarCloudClient, sonarQubeClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map(), } = options; + const sourceProjectVersion = await resolveSourceProjectVersion(sonarQubeClient, sonarQubeClient?.projectKey, isMainBranch ? null : branchName); + if (shouldBatch(extractedData)) { const ceTask = await transferBatched({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap, + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); return { stats: computeBranchStats(extractedData), ceTask }; } backdateChangesets(extractedData); - const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label); + const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label, sourceProjectVersion); const encodedReport = await encodeMessages(messages, label); - const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label); + const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label, sourceProjectVersion); return { stats: computeBranchStats(extractedData), ceTask }; } diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-main-branch/helpers/execute-main-branch-transfer.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-main-branch/helpers/execute-main-branch-transfer.js index 70259b5..789bc50 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-main-branch/helpers/execute-main-branch-transfer.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-main-branch/helpers/execute-main-branch-transfer.js @@ -6,7 +6,7 @@ import logger from '../../../../../../shared/utils/logger.js'; /** Execute the actual main branch transfer (build, encode, upload). */ export async function executeMainBranchTransfer(opts) { - const { journal, sonarCloudMainBranch, sonarCloudClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap } = opts; + const { journal, sonarCloudMainBranch, sonarCloudClient, sonarQubeClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap } = opts; if (journal) await journal.startBranch(sonarCloudMainBranch); @@ -23,7 +23,7 @@ export async function executeMainBranchTransfer(opts) { result = await transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName: sonarCloudMainBranch, referenceBranchName: sonarCloudMainBranch, - wait, sonarCloudClient, label: 'main', isMainBranch: true, + wait, sonarCloudClient, sonarQubeClient, label: 'main', isMainBranch: true, sonarCloudRepos, ruleEnrichmentMap, }); } diff --git a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-non-main-branches/helpers/transfer-one-branch/index.js b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-non-main-branches/helpers/transfer-one-branch/index.js index eeb6135..faef4b9 100644 --- a/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-non-main-branches/helpers/transfer-one-branch/index.js +++ b/src/pipelines/sq-2025/transfer-pipeline/helpers/transfer-non-main-branches/helpers/transfer-one-branch/index.js @@ -9,7 +9,7 @@ import logger from '../../../../../../../shared/utils/logger.js'; export async function transferOneBranch(branch, opts) { const { shutdownCheck, isIncremental, stateTracker, journal, cache, extractor, extractedData, sonarcloudConfig, sonarCloudProfiles, - sonarCloudMainBranch, wait, sonarCloudClient, + sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, sonarCloudRepos, ruleEnrichmentMap } = opts; const branchName = branch.name; @@ -26,7 +26,7 @@ export async function transferOneBranch(branch, opts) { const branchResult = await transferBranch({ extractedData: branchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName: sonarCloudMainBranch, - wait, sonarCloudClient, label: branchName, + wait, sonarCloudClient, sonarQubeClient, label: branchName, sonarCloudRepos, ruleEnrichmentMap, }); diff --git a/src/pipelines/sq-9.9/protobuf/builder/helpers/build-metadata.js b/src/pipelines/sq-9.9/protobuf/builder/helpers/build-metadata.js index aad9792..f967b62 100644 --- a/src/pipelines/sq-9.9/protobuf/builder/helpers/build-metadata.js +++ b/src/pipelines/sq-9.9/protobuf/builder/helpers/build-metadata.js @@ -18,7 +18,7 @@ export function buildMetadata(ctx) { qprofilesPerLanguage: ctx.buildQProfiles(), branchName, branchType: 1, referenceBranchName: referenceBranch, scmRevisionId: ctx.data.metadata.scmRevisionId || randomBytes(20).toString('hex'), - projectVersion: '1.0.0', + projectVersion: ctx.sourceProjectVersion || '1.0.0', analyzedIndexedFileCountPerType: ctx.buildFileCountsByType(), }; diff --git a/src/pipelines/sq-9.9/protobuf/builder/index.js b/src/pipelines/sq-9.9/protobuf/builder/index.js index 0a2a952..5a1e80c 100644 --- a/src/pipelines/sq-9.9/protobuf/builder/index.js +++ b/src/pipelines/sq-9.9/protobuf/builder/index.js @@ -22,6 +22,7 @@ export function createProtobufBuilder(extractedData, scConfig = {}, scProfiles = referenceBranchName: options.referenceBranchName || null, sonarCloudRepos: options.sonarCloudRepos || new Set(), ruleEnrichmentMap: options.ruleEnrichmentMap || new Map(), + sourceProjectVersion: options.sourceProjectVersion || null, getComponentRef(key) { if (!this.componentRefMap.has(key)) this.componentRefMap.set(key, this.nextRef++); return this.componentRefMap.get(key); }, mapSeverity, buildQProfiles() { return buildQProfiles(this); }, buildFileCountsByType() { return buildFileCountsByType(this); }, buildMetadata() { return buildMetadata(this); }, buildComponents() { return buildComponents(this); }, diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/build-protobuf-messages.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/build-protobuf-messages.js index 403f114..7d0ff50 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/build-protobuf-messages.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/build-protobuf-messages.js @@ -3,10 +3,10 @@ import logger from '../../../../shared/utils/logger.js'; // -------- Build Protobuf Messages -------- -export function buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label) { +export function buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label, sourceProjectVersion) { logger.info(`[${label}] Building protobuf messages...`); const builder = new ProtobufBuilder(extractedData, sonarcloudConfig, sonarCloudProfiles, { - sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, + sonarCloudBranchName: branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); return builder.buildAll(); } diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/run-transfer-phases.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/run-transfer-phases.js index 8b5b1df..5492baf 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/run-transfer-phases.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/run-transfer-phases.js @@ -22,7 +22,7 @@ export async function runTransferPhases({ sonarQubeClient, sonarCloudClient, son const { sonarCloudProfiles, sonarCloudMainBranch, sonarCloudRepos, ruleEnrichmentMap } = await fetchCloudContext(sonarCloudClient, prebuiltEnrichmentMap); checkShutdown(shutdownCheck); - const mainBranchResult = await transferMainBranch({ journal, sonarCloudMainBranch, sonarCloudClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap }); + const mainBranchResult = await transferMainBranch({ journal, sonarCloudMainBranch, sonarCloudClient, sonarQubeClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap }); const aggregatedStats = { issuesTransferred: mainBranchResult.stats.issuesTransferred || 0, @@ -37,7 +37,7 @@ export async function runTransferPhases({ sonarQubeClient, sonarCloudClient, son checkShutdown(shutdownCheck); if (syncAllBranches) { - const branchResults = await transferNonMainBranches({ extractedData, excludeBranches, includeBranches, sonarCloudMainBranch, mainBranchCeTaskId: mainBranchResult.ceTask?.id, wait, sonarCloudClient, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker, performanceConfig }); + const branchResults = await transferNonMainBranches({ extractedData, excludeBranches, includeBranches, sonarCloudMainBranch, mainBranchCeTaskId: mainBranchResult.ceTask?.id, wait, sonarCloudClient, sonarQubeClient, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker, performanceConfig }); aggregateBranchResults(branchResults, aggregatedStats); } 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 a90d9ea..63ae624 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 @@ -11,7 +11,7 @@ import logger from '../../../../shared/utils/logger.js'; export async function transferBranchBatched(opts) { const { extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap } = opts; + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion } = opts; extractedData.issues.sort((a, b) => (a.component || '').localeCompare(b.component || '')); @@ -28,9 +28,9 @@ export async function transferBranchBatched(opts) { logger.info(`[${batchLabel}] Issues ${batch.startIndex + 1}-${batch.endIndex} | date=${batchDate}`); - const messages = buildProtobufMessages(batchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, batchLabel); + const messages = buildProtobufMessages(batchData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, batchLabel, sourceProjectVersion); const encodedReport = await encodeReport(messages, batchLabel); - lastCeTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, true, batchLabel); + lastCeTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, true, batchLabel, sourceProjectVersion); logger.info(`[${batchLabel}] CE task completed: ${lastCeTask.id}`); } 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 dce9aee..83d9903 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 @@ -4,23 +4,26 @@ import { uploadReport } from './upload-report.js'; import { buildBranchResult } from './build-branch-result.js'; import { transferBranchBatched } from './transfer-branch-batched.js'; import { shouldBatch, backdateChangesets } from '../../../../shared/utils/batch-distributor.js'; +import { resolveSourceProjectVersion } from '../../../../shared/utils/source-version/resolve-source-project-version.js'; // -------- Single Branch Transfer (build, encode, upload) -------- -export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { +export async function transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, wait, sonarCloudClient, sonarQubeClient, label, isMainBranch = false, sonarCloudRepos = new Set(), ruleEnrichmentMap = new Map() }) { + const sourceProjectVersion = await resolveSourceProjectVersion(sonarQubeClient, sonarQubeClient?.projectKey, isMainBranch ? null : branchName); + if (shouldBatch(extractedData)) { const ceTask = await transferBranchBatched({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudClient, label, isMainBranch, - sonarCloudRepos, ruleEnrichmentMap, + sonarCloudRepos, ruleEnrichmentMap, sourceProjectVersion, }); return buildBranchResult(extractedData, ceTask); } backdateChangesets(extractedData); - const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label); + const messages = buildProtobufMessages(extractedData, sonarcloudConfig, sonarCloudProfiles, branchName, referenceBranchName, sonarCloudRepos, ruleEnrichmentMap, label, sourceProjectVersion); const encodedReport = await encodeReport(messages, label); - const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label); + const ceTask = await uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label, sourceProjectVersion); return buildBranchResult(extractedData, ceTask); } diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-main-branch.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-main-branch.js index cdb32b0..eead7af 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-main-branch.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-main-branch.js @@ -4,7 +4,7 @@ import logger from '../../../../shared/utils/logger.js'; // -------- Transfer Main Branch -------- -export async function transferMainBranch({ journal, sonarCloudMainBranch, sonarCloudClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap }) { +export async function transferMainBranch({ journal, sonarCloudMainBranch, sonarCloudClient, sonarQubeClient, extractedData, sonarcloudConfig, sonarCloudProfiles, wait, sonarCloudRepos, ruleEnrichmentMap }) { const mainBranchCompleted = journal?.getBranchStatus(sonarCloudMainBranch) === 'completed'; if (mainBranchCompleted) { @@ -36,7 +36,7 @@ export async function transferMainBranch({ journal, sonarCloudMainBranch, sonarC const result = await transferBranch({ extractedData, sonarcloudConfig, sonarCloudProfiles, branchName: sonarCloudMainBranch, referenceBranchName: sonarCloudMainBranch, - wait, sonarCloudClient, label: 'main', isMainBranch: true, sonarCloudRepos, ruleEnrichmentMap, + wait, sonarCloudClient, sonarQubeClient, label: 'main', isMainBranch: true, sonarCloudRepos, ruleEnrichmentMap, }); if (journal) { diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-non-main-branches.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-non-main-branches.js index bb23976..8c38705 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-non-main-branches.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-non-main-branches.js @@ -4,7 +4,7 @@ import logger from '../../../../shared/utils/logger.js'; // -------- Transfer Non-Main Branches -------- -export async function transferNonMainBranches({ extractedData, excludeBranches, includeBranches, sonarCloudMainBranch, mainBranchCeTaskId, wait, sonarCloudClient, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker, performanceConfig }) { +export async function transferNonMainBranches({ extractedData, excludeBranches, includeBranches, sonarCloudMainBranch, mainBranchCeTaskId, wait, sonarCloudClient, sonarQubeClient, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker, performanceConfig }) { const allBranches = extractedData.project.branches || []; const nonMainBranches = allBranches.filter(b => { if (b.isMain) return false; @@ -23,7 +23,7 @@ export async function transferNonMainBranches({ extractedData, excludeBranches, return mapConcurrent(nonMainBranches, (branch) => transferOneBranch({ branch, extractedData, extractor, journal, cache, shutdownCheck, sonarcloudConfig, - sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarCloudRepos, + sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker, }), { concurrency: performanceConfig?.maxConcurrency || 4, settled: true }); } diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-one-branch.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-one-branch.js index 1c8794b..95ff31b 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-one-branch.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/transfer-one-branch.js @@ -4,7 +4,7 @@ import logger from '../../../../shared/utils/logger.js'; // -------- Transfer a Single Non-Main Branch -------- -export async function transferOneBranch({ branch, extractedData, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker }) { +export async function transferOneBranch({ branch, extractedData, extractor, journal, cache, shutdownCheck, sonarcloudConfig, sonarCloudProfiles, sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, sonarCloudRepos, ruleEnrichmentMap, isIncremental, stateTracker }) { const branchName = branch.name; if (shutdownCheck()) return { skipped: true, branchName, reason: 'shutdown' }; @@ -27,7 +27,7 @@ export async function transferOneBranch({ branch, extractedData, extractor, jour const branchResult = await transferBranch({ extractedData: branchData, sonarcloudConfig, sonarCloudProfiles, branchName, - referenceBranchName: sonarCloudMainBranch, wait, sonarCloudClient, label: branchName, + referenceBranchName: sonarCloudMainBranch, wait, sonarCloudClient, sonarQubeClient, label: branchName, sonarCloudRepos, ruleEnrichmentMap, }); diff --git a/src/pipelines/sq-9.9/transfer-pipeline/helpers/upload-report.js b/src/pipelines/sq-9.9/transfer-pipeline/helpers/upload-report.js index 8f59c1b..c9283b0 100644 --- a/src/pipelines/sq-9.9/transfer-pipeline/helpers/upload-report.js +++ b/src/pipelines/sq-9.9/transfer-pipeline/helpers/upload-report.js @@ -3,13 +3,13 @@ import logger from '../../../../shared/utils/logger.js'; // -------- Upload Report to SonarCloud -------- -export async function uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label) { +export async function uploadReport(encodedReport, sonarcloudConfig, sonarCloudClient, branchName, isMainBranch, wait, label, sourceProjectVersion) { logger.info(`[${label}] Uploading to SonarCloud...`); const uploader = new ReportUploader(sonarCloudClient); const metadata = { projectKey: sonarcloudConfig.projectKey, organization: sonarcloudConfig.organization, - version: '1.0.0', + version: sourceProjectVersion || '1.0.0', ...(!isMainBranch && branchName ? { branchName } : {}), }; diff --git a/src/shared/utils/source-version/resolve-source-project-version.js b/src/shared/utils/source-version/resolve-source-project-version.js new file mode 100644 index 0000000..0ff3762 --- /dev/null +++ b/src/shared/utils/source-version/resolve-source-project-version.js @@ -0,0 +1,33 @@ +import logger from '../logger.js'; + +/** + * Resolve the source SonarQube project version for a given branch. + * + * Calls `GET /api/navigation/component` on the source SQS to read the + * `version` of the project (or a specific branch). This lets the migration + * preserve real source versions like "3.20" instead of using the hardcoded + * "1.0.0" fallback when uploading to SonarCloud. + * + * @param {object} sqClient - SonarQube client (must expose `client.get`). + * @param {string} projectKey - Source project key. + * @param {string|null} [branchName] - Branch to resolve. Pass null/undefined for the main branch. + * @returns {Promise} The version string, or null when the setting + * is missing/blank, the branch is unknown, or the API call fails. + */ +export async function resolveSourceProjectVersion(sqClient, projectKey, branchName) { + if (!sqClient || !sqClient.client || !projectKey) return null; + + const params = { component: projectKey }; + if (branchName) params.branch = branchName; + + try { + const response = await sqClient.client.get('/api/navigation/component', { params }); + const version = response?.data?.version; + if (typeof version === 'string' && version.trim()) return version.trim(); + return null; + } catch (error) { + const branchLabel = branchName ? ` branch '${branchName}'` : ''; + logger.debug(`Failed to resolve source project version for '${projectKey}'${branchLabel}: ${error.message}`); + return null; + } +}