Address review feedback: deterministic tests, cached normalization, simplified promisePool (#9)

* Initial plan

* Apply PR review comments: deterministic delays, cached normalization, simplified promisePool

Co-authored-by: felickz <[email protected]>

* Improve comment clarity for ecoLower field

Co-authored-by: felickz <[email protected]>

---------

Co-authored-by: copilot-swe-agent[bot] <[email protected]>
Co-authored-by: felickz <[email protected]>
This commit is contained in:
Copilot
2026-02-18 06:33:39 -05:00
committed by GitHub
co-authored by felickz
parent 539c79be65
commit a6c34d8785
4 changed files with 45 additions and 39 deletions
+2 -2
View File
@@ -747,8 +747,8 @@ test('addChangeVulnerabilitiesToSummary() - respects concurrency limit for API c
currentConcurrent++ currentConcurrent++
maxConcurrent = Math.max(maxConcurrent, currentConcurrent) maxConcurrent = Math.max(maxConcurrent, currentConcurrent)
// Simulate async API call with variable delay // Simulate async API call with a small deterministic delay
await new Promise(resolve => setTimeout(resolve, Math.random() * 10)) await new Promise(resolve => setTimeout(resolve, 5))
currentConcurrent-- currentConcurrent--
Generated Vendored
+15 -15
View File
@@ -1820,15 +1820,11 @@ function countScorecardWarnings(scorecard, config) {
*/ */
function promisePool(tasks, limit) { function promisePool(tasks, limit) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const results = [];
const executing = new Set(); const executing = new Set();
for (let i = 0; i < tasks.length; i++) { for (const task of tasks) {
const task = tasks[i]; // Execute task and clean up
const index = i;
// Wrap task execution to store result and clean up
const wrappedPromise = (() => __awaiter(this, void 0, void 0, function* () { const wrappedPromise = (() => __awaiter(this, void 0, void 0, function* () {
const result = yield task(); yield task();
results[index] = result;
}))(); }))();
executing.add(wrappedPromise); executing.add(wrappedPromise);
// When promise completes, remove it from the executing set // When promise completes, remove it from the executing set
@@ -1842,7 +1838,6 @@ function promisePool(tasks, limit) {
} }
// Wait for all remaining promises // Wait for all remaining promises
yield Promise.all(executing); yield Promise.all(executing);
return results;
}); });
} }
function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) { function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) {
@@ -1860,6 +1855,7 @@ function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) {
} }
// Query GitHub API for patch info with concurrency limiting // Query GitHub API for patch info with concurrency limiting
// Store all vulnerability entries (may be multiple per package with different ranges) // Store all vulnerability entries (may be multiple per package with different ranges)
// Pre-normalize ecosystem, package name, and range to avoid repeated work in rendering
const patchInfo = {}; const patchInfo = {};
const apiClient = (0, utils_1.octokitClient)(); const apiClient = (0, utils_1.octokitClient)();
// Create tasks for promise pool // Create tasks for promise pool
@@ -1879,11 +1875,17 @@ function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) {
const vulnRange = v.vulnerable_version_range || ''; const vulnRange = v.vulnerable_version_range || '';
const patchVerId = extractPatchVersionId(v.first_patched_version); const patchVerId = extractPatchVersionId(v.first_patched_version);
if (patchVerId) { if (patchVerId) {
// Pre-normalize and cache values to avoid repeated work in rendering loop
const trimmedRange = vulnRange.trim();
const normalizedRange = trimmedRange.replace(/,\s*/g, ' ');
patchInfo[advId].push({ patchInfo[advId].push({
eco: normalizedEco, eco: normalizedEco,
pkg: pkgName, pkg: pkgName,
range: vulnRange, range: vulnRange,
patch: patchVerId patch: patchVerId,
ecoLower: normalizedEco, // Ecosystem already normalized to lowercase
pkgLower: pkgName.toLowerCase(),
normalizedRange
}); });
core.debug(`Added patch info for ${pkgName} (${normalizedEco}): ${patchVerId} for range ${vulnRange}`); core.debug(`Added patch info for ${pkgName} (${normalizedEco}): ${patchVerId} for range ${vulnRange}`);
} }
@@ -1920,19 +1922,17 @@ function addChangeVulnerabilitiesToSummary(vulnerableChanges, severity) {
const normalizedChangeVersion = change.version.trim(); const normalizedChangeVersion = change.version.trim();
core.debug(`Looking up patch for ${change.name}@${change.version} (${ecoLowercase}) in ${vuln.advisory_ghsa_id}`); core.debug(`Looking up patch for ${change.name}@${change.version} (${ecoLowercase}) in ${vuln.advisory_ghsa_id}`);
// Find matching entry by ecosystem, package name (case-insensitive), and version range // Find matching entry by ecosystem, package name (case-insensitive), and version range
// Use pre-normalized values from cache to avoid repeated lowercasing and range conversion
let foundEntry; let foundEntry;
for (const vulnEntry of advisoryEntries) { for (const vulnEntry of advisoryEntries) {
if (vulnEntry.eco.toLowerCase() !== ecoLowercase) if (vulnEntry.ecoLower !== ecoLowercase)
continue; continue;
if (vulnEntry.pkg.toLowerCase() !== packageLowercase) if (vulnEntry.pkgLower !== packageLowercase)
continue; continue;
// Normalize range once for both cache key and function call
const trimmedRangeOnce = vulnEntry.range.trim();
const normalizedRange = trimmedRangeOnce.replace(/,\s*/g, ' ');
// Use fail-open (failClosed: false) for patch selection to avoid // Use fail-open (failClosed: false) for patch selection to avoid
// incorrectly matching on invalid ranges // incorrectly matching on invalid ranges
// Use preTrimmed and preNormalized optimizations since we've done both // Use preTrimmed and preNormalized optimizations since we've done both
const isInRange = versionInRange(normalizedChangeVersion, normalizedRange, { preTrimmed: true, preNormalized: true, failClosed: false }); const isInRange = versionInRange(normalizedChangeVersion, vulnEntry.normalizedRange, { preTrimmed: true, preNormalized: true, failClosed: false });
if (isInRange) { if (isInRange) {
foundEntry = vulnEntry; foundEntry = vulnEntry;
break; break;
Generated Vendored
+1 -1
View File
File diff suppressed because one or more lines are too long
+27 -21
View File
@@ -246,21 +246,16 @@ function countScorecardWarnings(
* @param tasks - Array of functions that return promises * @param tasks - Array of functions that return promises
* @param limit - Maximum number of concurrent promises * @param limit - Maximum number of concurrent promises
*/ */
async function promisePool<T>( async function promisePool(
tasks: (() => Promise<T>)[], tasks: (() => Promise<void>)[],
limit: number limit: number
): Promise<T[]> { ): Promise<void> {
const results: T[] = []
const executing: Set<Promise<void>> = new Set() const executing: Set<Promise<void>> = new Set()
for (let i = 0; i < tasks.length; i++) { for (const task of tasks) {
const task = tasks[i] // Execute task and clean up
const index = i
// Wrap task execution to store result and clean up
const wrappedPromise = (async () => { const wrappedPromise = (async () => {
const result = await task() await task()
results[index] = result
})() })()
executing.add(wrappedPromise) executing.add(wrappedPromise)
@@ -278,7 +273,6 @@ async function promisePool<T>(
// Wait for all remaining promises // Wait for all remaining promises
await Promise.all(executing) await Promise.all(executing)
return results
} }
export async function addChangeVulnerabilitiesToSummary( export async function addChangeVulnerabilitiesToSummary(
@@ -301,9 +295,18 @@ export async function addChangeVulnerabilitiesToSummary(
// Query GitHub API for patch info with concurrency limiting // Query GitHub API for patch info with concurrency limiting
// Store all vulnerability entries (may be multiple per package with different ranges) // Store all vulnerability entries (may be multiple per package with different ranges)
// Pre-normalize ecosystem, package name, and range to avoid repeated work in rendering
const patchInfo: Record< const patchInfo: Record<
string, string,
{eco: string; pkg: string; range: string; patch: string}[] {
eco: string
pkg: string
range: string
patch: string
ecoLower: string
pkgLower: string
normalizedRange: string
}[]
> = {} > = {}
const apiClient = octokitClient() const apiClient = octokitClient()
@@ -326,11 +329,17 @@ export async function addChangeVulnerabilitiesToSummary(
const vulnRange = v.vulnerable_version_range || '' const vulnRange = v.vulnerable_version_range || ''
const patchVerId = extractPatchVersionId(v.first_patched_version) const patchVerId = extractPatchVersionId(v.first_patched_version)
if (patchVerId) { if (patchVerId) {
// Pre-normalize and cache values to avoid repeated work in rendering loop
const trimmedRange = vulnRange.trim()
const normalizedRange = trimmedRange.replace(/,\s*/g, ' ')
patchInfo[advId].push({ patchInfo[advId].push({
eco: normalizedEco, eco: normalizedEco,
pkg: pkgName, pkg: pkgName,
range: vulnRange, range: vulnRange,
patch: patchVerId patch: patchVerId,
ecoLower: normalizedEco, // Ecosystem already normalized to lowercase
pkgLower: pkgName.toLowerCase(),
normalizedRange
}) })
core.debug( core.debug(
`Added patch info for ${pkgName} (${normalizedEco}): ${patchVerId} for range ${vulnRange}` `Added patch info for ${pkgName} (${normalizedEco}): ${patchVerId} for range ${vulnRange}`
@@ -380,23 +389,20 @@ export async function addChangeVulnerabilitiesToSummary(
) )
// Find matching entry by ecosystem, package name (case-insensitive), and version range // Find matching entry by ecosystem, package name (case-insensitive), and version range
// Use pre-normalized values from cache to avoid repeated lowercasing and range conversion
let foundEntry: let foundEntry:
| {eco: string; pkg: string; range: string; patch: string} | {eco: string; pkg: string; range: string; patch: string}
| undefined | undefined
for (const vulnEntry of advisoryEntries) { for (const vulnEntry of advisoryEntries) {
if (vulnEntry.eco.toLowerCase() !== ecoLowercase) continue if (vulnEntry.ecoLower !== ecoLowercase) continue
if (vulnEntry.pkg.toLowerCase() !== packageLowercase) continue if (vulnEntry.pkgLower !== packageLowercase) continue
// Normalize range once for both cache key and function call
const trimmedRangeOnce = vulnEntry.range.trim()
const normalizedRange = trimmedRangeOnce.replace(/,\s*/g, ' ')
// Use fail-open (failClosed: false) for patch selection to avoid // Use fail-open (failClosed: false) for patch selection to avoid
// incorrectly matching on invalid ranges // incorrectly matching on invalid ranges
// Use preTrimmed and preNormalized optimizations since we've done both // Use preTrimmed and preNormalized optimizations since we've done both
const isInRange = versionInRange( const isInRange = versionInRange(
normalizedChangeVersion, normalizedChangeVersion,
normalizedRange, vulnEntry.normalizedRange,
{preTrimmed: true, preNormalized: true, failClosed: false} {preTrimmed: true, preNormalized: true, failClosed: false}
) )