2020-03-20 17:11:30 +03:00
|
|
|
/**
|
2021-12-31 02:08:43 +03:00
|
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
2020-03-20 17:11:30 +03:00
|
|
|
*
|
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
|
|
|
*
|
|
|
|
* @format
|
|
|
|
*/
|
|
|
|
|
|
|
|
'use strict';
|
|
|
|
|
2021-11-03 22:09:13 +03:00
|
|
|
const {
|
|
|
|
GITHUB_TOKEN,
|
|
|
|
GITHUB_OWNER,
|
|
|
|
GITHUB_REPO,
|
|
|
|
GITHUB_PR_NUMBER,
|
|
|
|
GITHUB_REF,
|
|
|
|
GITHUB_SHA,
|
|
|
|
} = process.env;
|
2020-03-20 17:11:30 +03:00
|
|
|
|
|
|
|
const fs = require('fs');
|
|
|
|
const datastore = require('./datastore');
|
2021-11-03 22:09:13 +03:00
|
|
|
const {
|
|
|
|
createOrUpdateComment,
|
|
|
|
validateEnvironment: validateEnvironmentForMakeComment,
|
|
|
|
} = require('./make-comment');
|
2020-03-20 17:11:30 +03:00
|
|
|
|
|
|
|
/**
|
2021-10-18 21:04:22 +03:00
|
|
|
* Generates and submits a comment. If this is run on the main or release branch, data is
|
2020-03-20 17:11:30 +03:00
|
|
|
* committed to the store instead.
|
|
|
|
* @param {{
|
|
|
|
'android-hermes-arm64-v8a'?: number;
|
|
|
|
'android-hermes-armeabi-v7a'?: number;
|
|
|
|
'android-hermes-x86'?: number;
|
|
|
|
'android-hermes-x86_64'?: number;
|
|
|
|
'android-jsc-arm64-v8a'?: number;
|
|
|
|
'android-jsc-armeabi-v7a'?: number;
|
|
|
|
'android-jsc-x86'?: number;
|
|
|
|
'android-jsc-x86_64'?: number;
|
|
|
|
'ios-universal'?: number;
|
|
|
|
}} stats
|
|
|
|
*/
|
|
|
|
async function reportSizeStats(stats, replacePattern) {
|
|
|
|
const {FIREBASE_APP_EMAIL, FIREBASE_APP_PASS} = process.env;
|
|
|
|
const store = await datastore.initializeStore(
|
|
|
|
FIREBASE_APP_EMAIL,
|
|
|
|
FIREBASE_APP_PASS,
|
|
|
|
);
|
|
|
|
const collection = datastore.getBinarySizesCollection(store);
|
|
|
|
|
2021-11-03 22:09:13 +03:00
|
|
|
if (!isPullRequest(GITHUB_REF)) {
|
2020-03-20 17:11:30 +03:00
|
|
|
// Ensure we only store numbers greater than zero.
|
|
|
|
const validatedStats = Object.keys(stats).reduce((validated, key) => {
|
|
|
|
const value = stats[key];
|
|
|
|
if (typeof value !== 'number' || value <= 0) {
|
|
|
|
return validated;
|
|
|
|
}
|
|
|
|
|
|
|
|
validated[key] = value;
|
|
|
|
return validated;
|
|
|
|
}, {});
|
2021-10-18 21:04:22 +03:00
|
|
|
|
2020-03-20 17:11:30 +03:00
|
|
|
if (Object.keys(validatedStats).length > 0) {
|
2021-10-18 21:04:22 +03:00
|
|
|
// Print out the new stats
|
|
|
|
const document =
|
|
|
|
(await datastore.getLatestDocument(collection, GITHUB_REF)) || {};
|
|
|
|
const formattedStats = formatBundleStats(document, validatedStats);
|
|
|
|
console.log(formattedStats);
|
|
|
|
|
2020-03-20 17:11:30 +03:00
|
|
|
await datastore.createOrUpdateDocument(
|
|
|
|
collection,
|
|
|
|
GITHUB_SHA,
|
|
|
|
validatedStats,
|
2021-10-18 21:04:22 +03:00
|
|
|
GITHUB_REF,
|
2020-03-20 17:11:30 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
2021-11-03 22:09:13 +03:00
|
|
|
const params = {
|
|
|
|
auth: GITHUB_TOKEN,
|
|
|
|
owner: GITHUB_OWNER,
|
|
|
|
repo: GITHUB_REPO,
|
|
|
|
issue_number: GITHUB_PR_NUMBER,
|
|
|
|
};
|
|
|
|
|
2021-10-18 21:04:22 +03:00
|
|
|
// For PRs, always compare vs main.
|
|
|
|
const document =
|
|
|
|
(await datastore.getLatestDocument(collection, 'main')) || {};
|
|
|
|
const comment = formatBundleStats(document, stats);
|
2021-11-03 22:09:13 +03:00
|
|
|
createOrUpdateComment(params, comment, replacePattern);
|
2020-03-20 17:11:30 +03:00
|
|
|
}
|
|
|
|
|
2021-09-16 23:19:01 +03:00
|
|
|
await datastore.terminateStore(store);
|
2020-03-20 17:11:30 +03:00
|
|
|
}
|
|
|
|
|
2021-10-18 21:04:22 +03:00
|
|
|
/**
|
|
|
|
* Format the new bundle stats as compared to the latest stored entry.
|
|
|
|
* @param {firebase.firestore.DocumentData} document the latest entry to compare against
|
|
|
|
* @param {firebase.firestore.UpdateData} stats The stats to be formatted
|
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
function formatBundleStats(document, stats) {
|
|
|
|
const diffFormatter = new Intl.NumberFormat('en', {signDisplay: 'always'});
|
|
|
|
const sizeFormatter = new Intl.NumberFormat('en', {});
|
|
|
|
|
|
|
|
// | Platform | Engine | Arch | Size (bytes) | Diff |
|
|
|
|
// |:---------|:-------|:------------|-------------:|-----:|
|
|
|
|
// | android | hermes | arm64-v8a | 9437184 | ±0 |
|
|
|
|
// | android | hermes | armeabi-v7a | 9015296 | ±0 |
|
|
|
|
// | android | hermes | x86 | 9498624 | ±0 |
|
|
|
|
// | android | hermes | x86_64 | 9965568 | ±0 |
|
|
|
|
// | android | jsc | arm64-v8a | 9236480 | ±0 |
|
|
|
|
// | android | jsc | armeabi-v7a | 8814592 | ±0 |
|
|
|
|
// | android | jsc | x86 | 9297920 | ±0 |
|
|
|
|
// | android | jsc | x86_64 | 9764864 | ±0 |
|
|
|
|
// | android | jsc | x86_64 | 9764864 | ±0 |
|
|
|
|
// | ios | - | universal | 10715136 | ±0 |
|
|
|
|
const formatted = [
|
|
|
|
'| Platform | Engine | Arch | Size (bytes) | Diff |',
|
|
|
|
'|:---------|:-------|:-----|-------------:|-----:|',
|
|
|
|
...Object.keys(stats).map(identifier => {
|
|
|
|
const [size, diff] = (() => {
|
|
|
|
const statSize = stats[identifier];
|
|
|
|
if (!statSize) {
|
|
|
|
return ['n/a', '--'];
|
|
|
|
} else if (!(identifier in document)) {
|
|
|
|
return [statSize, 'n/a'];
|
|
|
|
} else {
|
|
|
|
return [
|
|
|
|
sizeFormatter.format(statSize),
|
|
|
|
diffFormatter.format(statSize - document[identifier]),
|
|
|
|
];
|
|
|
|
}
|
|
|
|
})();
|
|
|
|
|
|
|
|
const [platform, engineOrArch, ...archParts] = identifier.split('-');
|
|
|
|
const arch = archParts.join('-') || engineOrArch;
|
|
|
|
const engine = arch === engineOrArch ? '-' : engineOrArch; // e.g. 'ios-universal'
|
|
|
|
return `| ${platform} | ${engine} | ${arch} | ${size} | ${diff} |`;
|
|
|
|
}),
|
|
|
|
'',
|
|
|
|
`Base commit: ${document.commit || '<unknown>'}`,
|
|
|
|
`Branch: ${document.branch || '<unknown>'}`,
|
|
|
|
].join('\n');
|
|
|
|
|
|
|
|
return formatted;
|
|
|
|
}
|
|
|
|
|
2020-03-20 17:11:30 +03:00
|
|
|
/**
|
|
|
|
* Returns the size of the file at specified path in bytes.
|
|
|
|
* @param {fs.PathLike} path
|
|
|
|
* @returns {number}
|
|
|
|
*/
|
|
|
|
function getFileSize(path) {
|
|
|
|
try {
|
|
|
|
const stats = fs.statSync(path);
|
|
|
|
return stats.size;
|
|
|
|
} catch {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the size of the APK for specified JS engine and architecture.
|
|
|
|
* @param {'hermes' | 'jsc'} engine
|
|
|
|
* @param {'arm64-v8a' | 'armeabi-v7a' | 'x86' | 'x86_64'} arch
|
|
|
|
*/
|
|
|
|
function android_getApkSize(engine, arch) {
|
|
|
|
return getFileSize(
|
2020-08-20 03:54:30 +03:00
|
|
|
`packages/rn-tester/android/app/build/outputs/apk/${engine}/release/app-${engine}-${arch}-release.apk`,
|
2020-03-20 17:11:30 +03:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:09:13 +03:00
|
|
|
/**
|
|
|
|
* Returns whether the specified ref points to a pull request.
|
|
|
|
*/
|
|
|
|
function isPullRequest(ref) {
|
|
|
|
return ref !== 'main' && !/^\d+\.\d+-stable$/.test(ref);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates that required environment variables are set.
|
|
|
|
* @returns {boolean} `true` if everything is in order; `false` otherwise.
|
|
|
|
*/
|
|
|
|
function validateEnvironment() {
|
|
|
|
if (!GITHUB_REF) {
|
|
|
|
console.error("Missing GITHUB_REF. This should've been set by the CI.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isPullRequest(GITHUB_REF)) {
|
|
|
|
if (!validateEnvironmentForMakeComment()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else if (!GITHUB_SHA) {
|
|
|
|
// To update the data store, we need the SHA associated with the build
|
|
|
|
console.error("Missing GITHUB_SHA. This should've been set by the CI.");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log(` GITHUB_SHA=${GITHUB_SHA}`);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-20 17:11:30 +03:00
|
|
|
/**
|
|
|
|
* Reports app bundle size.
|
|
|
|
* @param {string} target
|
|
|
|
*/
|
2021-09-16 23:19:01 +03:00
|
|
|
async function report(target) {
|
2020-03-20 17:11:30 +03:00
|
|
|
switch (target) {
|
|
|
|
case 'android':
|
2021-09-16 23:19:01 +03:00
|
|
|
await reportSizeStats(
|
2020-03-20 17:11:30 +03:00
|
|
|
{
|
|
|
|
'android-hermes-arm64-v8a': android_getApkSize('hermes', 'arm64-v8a'),
|
|
|
|
'android-hermes-armeabi-v7a': android_getApkSize(
|
|
|
|
'hermes',
|
|
|
|
'armeabi-v7a',
|
|
|
|
),
|
|
|
|
'android-hermes-x86': android_getApkSize('hermes', 'x86'),
|
|
|
|
'android-hermes-x86_64': android_getApkSize('hermes', 'x86_64'),
|
|
|
|
'android-jsc-arm64-v8a': android_getApkSize('jsc', 'arm64-v8a'),
|
|
|
|
'android-jsc-armeabi-v7a': android_getApkSize('jsc', 'armeabi-v7a'),
|
|
|
|
'android-jsc-x86': android_getApkSize('jsc', 'x86'),
|
|
|
|
'android-jsc-x86_64': android_getApkSize('jsc', 'x86_64'),
|
|
|
|
},
|
2020-03-24 21:13:25 +03:00
|
|
|
'\\| android \\| hermes \\| arm',
|
2020-03-20 17:11:30 +03:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'ios':
|
2021-09-16 23:19:01 +03:00
|
|
|
await reportSizeStats(
|
2020-03-20 17:11:30 +03:00
|
|
|
{
|
|
|
|
'ios-universal': getFileSize(
|
2020-08-20 03:54:30 +03:00
|
|
|
'packages/rn-tester/build/Build/Products/Release-iphonesimulator/RNTester.app/RNTester',
|
2020-03-20 17:11:30 +03:00
|
|
|
),
|
|
|
|
},
|
2020-03-24 21:13:25 +03:00
|
|
|
'\\| ios \\| - \\| universal \\|',
|
2020-03-20 17:11:30 +03:00
|
|
|
);
|
|
|
|
break;
|
|
|
|
|
|
|
|
default: {
|
|
|
|
const path = require('path');
|
|
|
|
console.log(`Syntax: ${path.basename(process.argv[1])} [android | ios]`);
|
2021-09-16 23:19:01 +03:00
|
|
|
process.exitCode = 2;
|
2020-03-20 17:11:30 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-11-03 22:09:13 +03:00
|
|
|
if (!validateEnvironment()) {
|
|
|
|
process.exit(1);
|
|
|
|
}
|
|
|
|
|
2020-03-20 17:11:30 +03:00
|
|
|
const {[2]: target} = process.argv;
|
2021-09-16 23:19:01 +03:00
|
|
|
report(target).catch(error => {
|
|
|
|
console.error(error);
|
|
|
|
process.exitCode = 1;
|
|
|
|
});
|