Skip to content

Commit be71989

Browse files
authored
feat: split checks into modules (#41)
1 parent 3c53681 commit be71989

File tree

9 files changed

+486
-396
lines changed

9 files changed

+486
-396
lines changed

build/main.js

Lines changed: 242 additions & 202 deletions
Large diffs are not rendered by default.

src/checks/bundle-size.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {formatBytes} from '../common.js';
2+
import {comparePackSizes, type PackInfo} from '../packs.js';
3+
4+
export async function scanForBundleSize(
5+
messages: string[],
6+
basePacks: PackInfo[],
7+
sourcePacks: PackInfo[],
8+
threshold: number
9+
): Promise<void> {
10+
if (basePacks.length === 0 && sourcePacks.length === 0) {
11+
return;
12+
}
13+
const comparison = comparePackSizes(basePacks, sourcePacks, threshold);
14+
const packWarnings = comparison.packChanges.filter(
15+
(change) => change.exceedsThreshold && change.sizeChange > 0
16+
);
17+
18+
if (packWarnings.length > 0) {
19+
const packRows = packWarnings
20+
.map((change) => {
21+
const baseSize = change.baseSize ? formatBytes(change.baseSize) : 'New';
22+
const sourceSize = change.sourceSize
23+
? formatBytes(change.sourceSize)
24+
: 'Removed';
25+
const sizeChange = formatBytes(change.sizeChange);
26+
return `| ${change.name} | ${baseSize} | ${sourceSize} | ${sizeChange} |`;
27+
})
28+
.join('\n');
29+
30+
messages.push(
31+
`## ⚠️ Package Size Increase
32+
33+
These packages exceed the size increase threshold of ${formatBytes(threshold)}:
34+
35+
| 📦 Package | 📏 Base Size | 📏 Source Size | 📈 Size Change |
36+
| --- | --- | --- | --- |
37+
${packRows}`
38+
);
39+
}
40+
}

src/checks/dependency-count.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import * as core from '@actions/core';
2+
3+
export function scanForDependencyCount(
4+
messages: string[],
5+
threshold: number,
6+
currentDeps: Map<string, Set<string>>,
7+
baseDeps: Map<string, Set<string>>
8+
): void {
9+
const currentDepCount = Array.from(currentDeps.values()).reduce(
10+
(sum, versions) => sum + versions.size,
11+
0
12+
);
13+
const baseDepCount = Array.from(baseDeps.values()).reduce(
14+
(sum, versions) => sum + versions.size,
15+
0
16+
);
17+
const depIncrease = currentDepCount - baseDepCount;
18+
core.info(`Base dependency count: ${baseDepCount}`);
19+
core.info(`Current dependency count: ${currentDepCount}`);
20+
core.info(`Dependency count increase: ${depIncrease}`);
21+
22+
if (depIncrease >= threshold) {
23+
messages.push(
24+
`## ⚠️ Dependency Count
25+
26+
This PR adds ${depIncrease} new dependencies (${baseDepCount}${currentDepCount}), which exceeds the threshold of ${threshold}.`
27+
);
28+
}
29+
}

src/checks/dependency-size.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as core from '@actions/core';
2+
import {calculateTotalDependencySizeIncrease} from '../npm.js';
3+
import {formatBytes} from '../common.js';
4+
5+
export async function scanForDependencySize(
6+
messages: string[],
7+
threshold: number,
8+
newVersions: Array<{name: string; version: string}>
9+
): Promise<void> {
10+
if (newVersions.length === 0) {
11+
return;
12+
}
13+
try {
14+
const sizeData = await calculateTotalDependencySizeIncrease(newVersions);
15+
16+
if (sizeData !== null && sizeData.totalSize >= threshold) {
17+
const packageRows = Array.from(sizeData.packageSizes.entries())
18+
.sort(([, a], [, b]) => b - a)
19+
.map(([pkg, size]) => `| ${pkg} | ${formatBytes(size)} |`)
20+
.join('\n');
21+
22+
messages.push(
23+
`## ⚠️ Large Dependency Size Increase
24+
25+
This PR adds ${formatBytes(sizeData.totalSize)} of new dependencies, which exceeds the threshold of ${formatBytes(threshold)}.
26+
27+
| 📦 Package | 📏 Size |
28+
| --- | --- |
29+
${packageRows}`
30+
);
31+
}
32+
} catch (err) {
33+
core.info(`Failed to calculate total dependency size increase: ${err}`);
34+
}
35+
}

src/checks/duplicates.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
function getLsCommand(
2+
lockfilePath: string,
3+
packageName: string
4+
): string | undefined {
5+
if (lockfilePath.endsWith('package-lock.json')) {
6+
return `npm ls ${packageName}`;
7+
}
8+
if (lockfilePath.endsWith('pnpm-lock.yaml')) {
9+
return `pnpm why ${packageName}`;
10+
}
11+
if (lockfilePath.endsWith('yarn.lock')) {
12+
return `yarn why ${packageName}`;
13+
}
14+
if (lockfilePath.endsWith('bun.lock')) {
15+
return `bun pm ls ${packageName}`;
16+
}
17+
return undefined;
18+
}
19+
20+
export function scanForDuplicates(
21+
messages: string[],
22+
threshold: number,
23+
dependencyMap: Map<string, Set<string>>,
24+
lockfilePath: string
25+
): void {
26+
const duplicateRows: string[] = [];
27+
for (const [packageName, currentVersionSet] of dependencyMap) {
28+
if (currentVersionSet.size > threshold) {
29+
const versions = Array.from(currentVersionSet).sort();
30+
duplicateRows.push(
31+
`| ${packageName} | ${currentVersionSet.size} versions | ${versions.join(', ')} |`
32+
);
33+
}
34+
}
35+
36+
if (duplicateRows.length > 0) {
37+
const exampleCommand = getLsCommand(lockfilePath, 'example-package');
38+
const helpMessage = exampleCommand
39+
? `\n\n💡 To find out what depends on a specific package, run: \`${exampleCommand}\``
40+
: '';
41+
messages.push(
42+
`## ⚠️ Duplicate Dependencies (threshold: ${threshold})
43+
44+
| 📦 Package | 🔢 Version Count | 📋 Versions |
45+
| --- | --- | --- |
46+
${duplicateRows.join('\n')}${helpMessage}`
47+
);
48+
}
49+
}

src/checks/provenance.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import * as core from '@actions/core';
2+
import {getMinTrustLevel, getProvenanceForPackageVersions} from '../npm.js';
3+
4+
export async function scanForProvenance(
5+
messages: string[],
6+
currentDeps: Map<string, Set<string>>,
7+
baseDeps: Map<string, Set<string>>
8+
): Promise<void> {
9+
const provenanceRows: string[] = [];
10+
11+
for (const [packageName, currentVersionSet] of currentDeps) {
12+
const baseVersionSet = baseDeps.get(packageName);
13+
14+
if (!baseVersionSet || baseVersionSet.size === 0) {
15+
continue;
16+
}
17+
18+
if (currentVersionSet.isSubsetOf(baseVersionSet)) {
19+
continue;
20+
}
21+
22+
try {
23+
const baseProvenances = await getProvenanceForPackageVersions(
24+
packageName,
25+
baseVersionSet
26+
);
27+
const currentProvenances = await getProvenanceForPackageVersions(
28+
packageName,
29+
currentVersionSet
30+
);
31+
32+
if (baseProvenances.size === 0 || currentProvenances.size === 0) {
33+
continue;
34+
}
35+
36+
const minBaseTrust = getMinTrustLevel(baseProvenances.values());
37+
const minCurrentTrust = getMinTrustLevel(currentProvenances.values());
38+
39+
if (minCurrentTrust.level < minBaseTrust.level) {
40+
provenanceRows.push(
41+
`| ${packageName} | ${minBaseTrust.status} | ${minCurrentTrust.status} |`
42+
);
43+
}
44+
} catch (err) {
45+
core.info(`Failed to check provenance for ${packageName}: ${err}`);
46+
}
47+
}
48+
49+
if (provenanceRows.length > 0) {
50+
messages.push(
51+
`## ⚠️ Package Trust Level Decreased
52+
53+
> [!CAUTION]
54+
> Decreased trust levels may indicate a higher risk of supply chain attacks. Please review these changes carefully.
55+
56+
| 📦 Package | 🔒 Before | 🔓 After |
57+
| --- | --- | --- |
58+
${provenanceRows.join('\n')}`
59+
);
60+
}
61+
}
File renamed without changes.

src/common.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
export function formatBytes(bytes: number): string {
2+
if (bytes === 0) return '0 B';
3+
const k = 1000;
4+
const sizes = ['B', 'kB', 'MB', 'GB'];
5+
const i = Math.floor(Math.log(bytes) / Math.log(k));
6+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
7+
}

0 commit comments

Comments
 (0)