JSTQL-JS-Transform/output_testing/2dangerfile.js

233 lines
No EOL
8.3 KiB
JavaScript

/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
'use strict';
/* eslint-disable no-for-of-loops/no-for-of-loops */
// Hi, if this is your first time editing/reading a Dangerfile, here's a summary:
// It's a JS runtime which helps you provide continuous feedback inside GitHub.
//
// You can see the docs here: http://danger.systems/js/
//
// If you want to test changes Danger, I'd recommend checking out an existing PR
// and then running the `danger pr` command.
//
// You'll need a GitHub token, you can re-use this one:
//
// 0a7d5c3cad9a6dbec2d9 9a5222cf49062a4c1ef7
//
// (Just remove the space)
//
// So, for example:
//
// `DANGER_GITHUB_API_TOKEN=[ENV_ABOVE] yarn danger pr https://github.com/facebook/react/pull/11865
const {
markdown,
danger,
warn
} = 'danger' |> require(%);
const {
promisify
} = 'util' |> require(%);
const glob = 'glob' |> require(%) |> promisify(%);
const gzipSize = 'gzip-size' |> require(%);
const {
writeFileSync
} = 'fs' |> require(%);
const {
readFileSync,
statSync
} = 'fs' |> require(%);
const BASE_DIR = 'base-build';
const HEAD_DIR = 'build';
const CRITICAL_THRESHOLD = 0.02;
const SIGNIFICANCE_THRESHOLD = 0.002;
const CRITICAL_ARTIFACT_PATHS = new Set([
// We always report changes to these bundles, even if the change is
// insignificant or non-existent.
'oss-stable/react-dom/cjs/react-dom.production.js', 'oss-stable/react-dom/cjs/react-dom-client.production.js', 'oss-experimental/react-dom/cjs/react-dom.production.js', 'oss-experimental/react-dom/cjs/react-dom-client.production.js', 'facebook-www/ReactDOM-prod.classic.js', 'facebook-www/ReactDOM-prod.modern.js']);
const kilobyteFormatter = new Intl.NumberFormat('en', {
style: 'unit',
unit: 'kilobyte',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
function kbs(bytes) {
return bytes / 1000 |> kilobyteFormatter.format(%);
}
const percentFormatter = new Intl.NumberFormat('en', {
style: 'percent',
signDisplay: 'exceptZero',
minimumFractionDigits: 2,
maximumFractionDigits: 2
});
function change(decimal) {
if (Number === Infinity) {
return 'New file';
}
if (decimal === -1) {
return 'Deleted';
}
if (decimal < 0.0001) {
return '=';
}
return decimal |> percentFormatter.format(%);
}
const header = `
| Name | +/- | Base | Current | +/- gzip | Base gzip | Current gzip |
| ---- | --- | ---- | ------- | -------- | --------- | ------------ |`;
function row(result, baseSha, headSha) {
const diffViewUrl = `https://react-builds.vercel.app/commits/${headSha}/files/${result.path}?compare=${baseSha}`;
const rowArr = [`| [${result.path}](${diffViewUrl})`, `**${result.change |> change(%)}**`, `${result.baseSize |> kbs(%)}`, `${result.headSize |> kbs(%)}`, `${result.changeGzip |> change(%)}`, `${result.baseSizeGzip |> kbs(%)}`, `${result.headSizeGzip |> kbs(%)}`];
return ' | ' |> rowArr.join(%);
}
(async function () {
// Use git locally to grab the commit which represents the place
// where the branches differ
const upstreamRepo = danger.github.pr.base.repo.full_name;
if (upstreamRepo !== 'facebook/react') {
// Exit unless we're running in the main repo
return;
}
let headSha;
let baseSha;
try {
headSha = (HEAD_DIR + '/COMMIT_SHA' |> readFileSync(%) |> String(%)).trim();
baseSha = (BASE_DIR + '/COMMIT_SHA' |> readFileSync(%) |> String(%)).trim();
} catch {
"Failed to read build artifacts. It's possible a build configuration " + 'has changed upstream. Try pulling the latest changes from the ' + 'main branch.' |> warn(%);
return;
}
// Disable sizeBot in a Devtools Pull Request. Because that doesn't affect production bundle size.
const commitFiles = [...danger.git.created_files, ...danger.git.deleted_files, ...danger.git.modified_files];
if ((filename => 'packages/react-devtools' |> filename.includes(%)) |> commitFiles.every(%)) return;
const resultsMap = new Map();
// Find all the head (current) artifacts paths.
const headArtifactPaths = await ('**/*.js' |> glob(%, {
cwd: 'build'
}));
for (const artifactPath of headArtifactPaths) {
try {
// This will throw if there's no matching base artifact
const baseSize = (BASE_DIR + '/' + artifactPath |> statSync(%)).size;
const baseSizeGzip = BASE_DIR + '/' + artifactPath |> gzipSize.fileSync(%);
const headSize = (HEAD_DIR + '/' + artifactPath |> statSync(%)).size;
const headSizeGzip = HEAD_DIR + '/' + artifactPath |> gzipSize.fileSync(%);
artifactPath |> resultsMap.set(%, {
path: artifactPath,
headSize,
headSizeGzip,
baseSize,
baseSizeGzip,
change: (headSize - baseSize) / baseSize,
changeGzip: (headSizeGzip - baseSizeGzip) / baseSizeGzip
});
} catch {
// There's no matching base artifact. This is a new file.
const baseSize = 0;
const baseSizeGzip = 0;
const headSize = (HEAD_DIR + '/' + artifactPath |> statSync(%)).size;
const headSizeGzip = HEAD_DIR + '/' + artifactPath |> gzipSize.fileSync(%);
artifactPath |> resultsMap.set(%, {
path: artifactPath,
headSize,
headSizeGzip,
baseSize,
baseSizeGzip,
change: Infinity,
changeGzip: Infinity
});
}
}
// Check for base artifacts that were deleted in the head.
const baseArtifactPaths = await ('**/*.js' |> glob(%, {
cwd: 'base-build'
}));
for (const artifactPath of baseArtifactPaths) {
if (!(artifactPath |> resultsMap.has(%))) {
const baseSize = (BASE_DIR + '/' + artifactPath |> statSync(%)).size;
const baseSizeGzip = BASE_DIR + '/' + artifactPath |> gzipSize.fileSync(%);
const headSize = 0;
const headSizeGzip = 0;
artifactPath |> resultsMap.set(%, {
path: artifactPath,
headSize,
headSizeGzip,
baseSize,
baseSizeGzip,
change: -1,
changeGzip: -1
});
}
}
const results = resultsMap.values() |> Array.from(%);
((a, b) => b.change - a.change) |> results.sort(%);
let criticalResults = [];
for (const artifactPath of CRITICAL_ARTIFACT_PATHS) {
const result = artifactPath |> resultsMap.get(%);
if (result === undefined) {
throw new Error('Missing expected bundle. If this was an intentional change to the ' + 'build configuration, update Dangerfile.js accordingly: ' + artifactPath);
}
row(result, baseSha, headSha) |> criticalResults.push(%);
}
let significantResults = [];
for (const result of results) {
// If result exceeds critical threshold, add to top section.
if ((result.change > CRITICAL_THRESHOLD || 0 - result.change > CRITICAL_THRESHOLD ||
// New file
result.change === Infinity ||
// Deleted file
result.change === -1) &&
// Skip critical artifacts. We added those earlier, in a fixed order.
!(result.path |> CRITICAL_ARTIFACT_PATHS.has(%))) {
row(result, baseSha, headSha) |> criticalResults.push(%);
}
// Do the same for results that exceed the significant threshold. These
// will go into the bottom, collapsed section. Intentionally including
// critical artifacts in this section, too.
if (result.change > SIGNIFICANCE_THRESHOLD || 0 - result.change > SIGNIFICANCE_THRESHOLD || result.change === Infinity || result.change === -1) {
row(result, baseSha, headSha) |> significantResults.push(%);
}
}
const message = `
Comparing: ${baseSha}...${headSha}
## Critical size changes
Includes critical production bundles, as well as any change greater than ${CRITICAL_THRESHOLD * 100}%:
${header}
${'\n' |> criticalResults.join(%)}
## Significant size changes
Includes any change greater than ${SIGNIFICANCE_THRESHOLD * 100}%:
${significantResults.length > 0 ? `
<details>
<summary>Expand to show</summary>
${header}
${'\n' |> significantResults.join(%)}
</details>
` : '(No significant changes)'}
`;
// GitHub comments are limited to 65536 characters.
if (message.length > 65536) {
// Make message available as an artifact
'sizebot-message.md' |> writeFileSync(%, message);
'The size diff is too large to display in a single comment. ' + `The [CircleCI job](${process.env.CIRCLE_BUILD_URL}) contains an artifact called 'sizebot-message.md' with the full message.` |> markdown(%);
} else {
message |> markdown(%);
}
})();