Skip to content

Commit

Permalink
[WALL-3660] feature/switch to total size (binary-com#15483)
Browse files Browse the repository at this point in the history
* feat: improved bundle analysis

* feat: removed unnecessary table

* feat: added a bit of tolerance for orange, so it will not trigger orange for too minuscule changes
  • Loading branch information
wojciech-deriv committed Jun 5, 2024
1 parent 630659b commit dc78fdc
Show file tree
Hide file tree
Showing 2 changed files with 161 additions and 106 deletions.
39 changes: 23 additions & 16 deletions .github/actions/analyze/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,18 @@ runs:
GITHUB_TOKEN: ${{ inputs.GITHUB_TOKEN }}
shell: bash
run: |
# Fetching the artifacts and storing response for debugging
RESPONSE=$(curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
"https://api.github.com/repos/${{ github.repository }}/actions/artifacts?name=analyse")
# uncomment for debugging purposes
# echo "API Response: $RESPONSE"
# Extracting the artifact URL using jq
ARTIFACT_URL=$(echo $RESPONSE | jq -r '.artifacts[0].archive_download_url')
# Check if the artifact URL is empty
if [[ -z "$ARTIFACT_URL" ]]; then
echo "Error: No artifact URL found for the master branch with prefix 'analyse'."
exit 1
else
echo "Artifact URL: $ARTIFACT_URL"
fi
# Output artifact URL for other steps
echo "artifact_url=$ARTIFACT_URL" >> $GITHUB_OUTPUT
echo "artifact_url=$ARTIFACT_URL"
Expand Down Expand Up @@ -138,36 +131,50 @@ runs:
if: steps.get_artifact_url.outputs.artifact_url != 'null'
shell: bash
run: |
DIFF_OUTPUT=$(node .github/actions/analyze/compareReports.js)
echo "diff_output<<EOF" >> $GITHUB_OUTPUT
echo "$DIFF_OUTPUT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
echo $DIFF_OUTPUT
DIFF_OUTPUT_HTML=$(node .github/actions/analyze/compareReports.js --format=html)
ABOVE_THRESHOLD=$(node .github/actions/analyze/compareReports.js --format=boolean) || { echo "Threshold check failed"; exit 1; }
# Output results to GITHUB_OUTPUT
echo "diff_output_html=$DIFF_OUTPUT_HTML" >> $GITHUB_OUTPUT
echo "above_threshold=$ABOVE_THRESHOLD" >> $GITHUB_OUTPUT
- name: Comment on PR with Diff Output
if: steps.get_artifact_url.outputs.artifact_url != 'null' && inputs.ISSUE_NUMBER
uses: actions/github-script@v5
env:
DIFF_OUTPUT: ${{ steps.diff.outputs.diff_output }}
DIFF_OUTPUT_HTML: ${{ steps.diff.outputs.diff_output_html }}
ISSUE_NUMBER: ${{ inputs.ISSUE_NUMBER }}
with:
script: |
const diffOutput = process.env.DIFF_OUTPUT;
const diffOutputHtml = process.env.DIFF_OUTPUT_HTML; // Removed Buffer.from and base64 decoding
const issueNumber = process.env.ISSUE_NUMBER;
github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issueNumber,
body: `${diffOutput}`
body: `${diffOutputHtml}`
});
continue-on-error: true

- name: Print out differences on console
id: print_diff
if: steps.get_artifact_url.outputs.artifact_url != 'null'
shell: bash
run: |
node .github/actions/analyze/compareReports.js --format=console
- name: Validate size changes
if: ${{ steps.diff.outputs.above_threshold == 'true' }}
uses: actions/github-script@v5
with:
script: |
core.setFailed('Size changes exceed the defined threshold. Check above logs for details.');
- name: Zip all report.json files
shell: bash
run: |
zip -r analyse.zip packages/*/report.json
# Step for the master branch
- name: Upload analyse.zip for Master Branch
if: github.ref == 'refs/heads/master'
uses: actions/upload-artifact@v4
Expand Down
228 changes: 138 additions & 90 deletions .github/actions/analyze/compareReports.js
Original file line number Diff line number Diff line change
@@ -1,122 +1,170 @@
const fs = require('fs');
const path = require('path');

function roundUpToDecimals(num, decimals) {
const factor = Math.pow(10, decimals);
return Math.ceil(num * factor) / factor;
}

function readJsonFile(filePath) {
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(data);
// read and use parameters from command line
const args = process.argv.slice(2); // Skip the first two elements
let format = args.find(arg => arg.startsWith('--format='))?.split('=')[1] || 'html';
let orangeThreshold = +(args.find(arg => arg.startsWith('--orangeThreshold='))?.split('=')[1] || 0.5);
let redThreshold = +(args.find(arg => arg.startsWith('--redThreshold='))?.split('=')[1] || 5);

// main function execution
main();

function main() {
// format: [package]: { oldSize, newSize, diff, percentage }
const sizes = analyse();

// format to different output based on the format parameter
// nice table in html if its for comment, nice table in console if its for console, or just true/false if its just to check validity
if (format === 'html') {
let formattedOutput = formatToTable(sizes);
console.log(formattedOutput);
} else if (format === 'console') {
let formattedOutput = formatToConsole(sizes);
console.table(formattedOutput, ['oldSize', 'newSize', 'diff', 'percentage', 'alert']);
} else if (format === 'boolean') {
const aboveRedThreshold = Object.values(sizes).some(pkg => pkg.percentage > redThreshold);
if (aboveRedThreshold) {
console.log('true');
} else {
console.log('false');
}
}
return null;
}

function calculateDiff(oldSize, newSize) {
return newSize - oldSize;
}
function analyse() {
const packagesDir = './packages';
const oldPackagesDir = './old/packages';

function calculatePercentage(oldSize, newSize) {
if (oldSize === 0) {
return newSize === 0 ? 0 : 100;
}
return ((newSize - oldSize) / oldSize) * 100;
}
const packages = [...new Set([...fs.readdirSync(packagesDir), ...fs.readdirSync(oldPackagesDir)])];

function formatSize(size, roundUp) {
if (size === null) {
return '-';
}
const result = {};

const formattedSize = roundUp ? roundUpToDecimals(size / 1024, 2) + 'kb' : (size / 1024).toFixed(2) + 'kb';
return formattedSize;
}
for (const pkg of packages) {
const oldReport = readJsonFile(path.join(oldPackagesDir, pkg, 'report.json'));
const newReport = readJsonFile(path.join(packagesDir, pkg, 'report.json'));

if (!newReport) {
continue;
}

const packagesDir = './packages';
const oldPackagesDir = './old/packages';
const packages = [...new Set([...fs.readdirSync(packagesDir), ...fs.readdirSync(oldPackagesDir)])];
const oldSize = oldReport ? oldReport.reduce((acc, item) => acc + item.gzipSize, 0) : null;
const newSize = newReport ? newReport.reduce((acc, item) => acc + item.gzipSize, 0) : null;

let tableRows = '';
let diff = oldSize && newSize ? newSize - oldSize : oldSize || newSize;
let percentage = oldSize && newSize ? calculatePercentage(oldSize, newSize) : null;

for (const pkg of packages) {
const oldReport = readJsonFile(path.join(oldPackagesDir, pkg, 'report.json'));
const newReport = readJsonFile(path.join(packagesDir, pkg, 'report.json'));
result[pkg] = {
oldSize,
newSize,
diff,
percentage,
};
}

const oldSize = oldReport ? oldReport[0].gzipSize : null;
const newSize = newReport ? newReport[0].gzipSize : null;
let diff = oldSize && newSize ? calculateDiff(oldSize, newSize) : null;
return result;
}

if (oldSize === null) {
diff = newSize;
function formatToTable(sizes) {
const GREEN_SIGN = '🟢';
const YELLOW_SIGN = '🟡';
const RED_SIGN = '🔴';

let tableRows = '';
for (const [pkg, { oldSize, newSize, diff, percentage }] of Object.entries(sizes)) {
const formattedPercentage = formatPercentageWithSign(percentage);
const lightSign =
percentage > redThreshold ? RED_SIGN : percentage > orangeThreshold ? YELLOW_SIGN : GREEN_SIGN;

tableRows += `
<tr>
<td>${pkg}</td>
<td>${formatBytes(oldSize)}</td>
<td>${formatBytes(newSize)}</td>
<td>${formatBytes(diff, true)}</td>
<td>${formattedPercentage} ${lightSign}</td>
</tr>
`.trim();
}

if (newSize === null) {
diff = oldSize;
return `
<table>
<thead>
<th>package</th>
<th>old</th>
<th>new</th>
<th>diff</th>
<th>pct change</th>
</thead>
<tbody>
${tableRows}
</tbody>
</table>`
.replace(/[\n\t]/g, '')
.trim();
}

function formatToConsole(sizes) {
Object.keys(sizes).forEach(key => {
const pkg = sizes[key];
pkg.oldSize = formatBytes(pkg.oldSize);
pkg.newSize = formatBytes(pkg.newSize);
pkg.diff = formatBytes(pkg.diff, true);
pkg.alert = pkg.percentage > redThreshold ? 'FAIL' : pkg.percentage > orangeThreshold ? 'WARN' : 'OK';
pkg.percentage = formatPercentageWithSign(pkg.percentage);
});
return sizes;
}

function readJsonFile(filePath) {
if (fs.existsSync(filePath)) {
const data = fs.readFileSync(filePath, 'utf-8');
return JSON.parse(data);
}
return null;
}

let diffText = '-';
function calculatePercentage(oldSize, newSize) {
return ((newSize - oldSize) / oldSize) * 100;
}

if (diff !== 0) {
diffText = diff < 0 ? '-' : '+' + formatSize(diff, true);
} else {
diffText = '+0kb';
function formatBytes(bytes, sign = false) {
if (bytes === null || isNaN(bytes)) {
return 'n/a';
}

let percentage = oldSize && newSize ? calculatePercentage(oldSize, newSize) : null;
let formattedValue = '';

if (oldSize === null) {
percentage = 100;
if (bytes < 1024) {
formattedValue = bytes + ' B'; // Bytes
} else if (bytes < 1048576) {
formattedValue = Math.round(bytes / 1024) + ' KB'; // Kilobytes
} else {
formattedValue = (bytes / 1048576).toFixed(1) + ' MB'; // Megabytes
}

if (newSize === null) {
percentage = -100;
if (sign) {
if (bytes === 0) {
return '0 B';
}
formattedValue = bytes >= 0 ? '+' + formattedValue : '-' + formattedValue;
}

let percentageText = '-';
let percentageEmoji;
return formattedValue;
}

if (percentage === 0) {
percentageEmoji = '';
} else if (percentage < 0) {
percentageEmoji = '🟢'; // green for decrease
} else if (percentage >= 0 && percentage <= 5) {
percentageEmoji = '🟡'; // yellow for small increase
} else {
percentageEmoji = '🔴'; // red for larger increase
function formatPercentageWithSign(percentage) {
if (percentage === null || isNaN(percentage)) {
return 'n/a';
}

if (percentage !== 0) {
percentageText = percentage.toFixed(2) + '%';
} else {
percentageText = '0%';
const absPercentage = Math.abs(percentage);
const decimalPoints = absPercentage < 10 ? 1 : 2;
let formattedValue = percentage.toFixed(decimalPoints) + '%';

if (percentage === 0) {
return '0%';
}

tableRows += `
<tr>
<td>${pkg}</td>
<td>${formatSize(oldSize)}</td>
<td>${formatSize(newSize)}</td>
<td>${diffText}</td>
<td>${percentageText} ${percentageEmoji}</td>
</tr>
`.trim();
return percentage >= 0 ? '+' + formattedValue : '−' + formattedValue;
}

console.log(
`
<table>
<thead>
<th>package</th>
<th>old</th>
<th>new</th>
<th>diff</th>
<th>percentage</th>
</thead>
<tbody>
${tableRows}
</tbody>
</table>
`.trim()
);

0 comments on commit dc78fdc

Please sign in to comment.