The `payload-size.sh` script is mainly used on CI to calculate, check and potentially save (on non-PR builds) the sizes of the bundles for various apps (including angular.io). If everything goes well (i.e. the checks pass, meaning that the sizes did not increase above the specified threshold) nothing is shown in the CI logs. In some cases, it is useful to be able to see what the sizes were in a specific build; e.g. for debugging purposes or when investigating a gradual increase that happened over time. (Some of this info is available on https://size.angular.io/, but not all.) Previously, the only way to find out what the sizes were for a specific build was to checkout the corresponding commit locally and build the target app, which in turn requires building all Angular packages and can take some time. Given that the sizes are already calculated on CI, this was a waste. This commit makes it easy to find out the bundle sizes for a specific build/commit by always printing out the calculated sizes (thus making them show up in the CI logs). PR Close #33099
171 lines
5.5 KiB
Bash
171 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -eu -o pipefail
|
|
|
|
readonly PROJECT_NAME="angular-payload-size"
|
|
NODE_MODULES_BIN=$PROJECT_ROOT/node_modules/.bin/
|
|
|
|
# Get the gzip size of a file with the specified compression level.
|
|
# $1: string - The file path.
|
|
# $2: number - The level of compression.
|
|
getGzipSize() {
|
|
local filePath=$1
|
|
local compLevel=$2
|
|
local compPath=$1$2.gz
|
|
local size=-1
|
|
|
|
gzip -c -$compLevel "$filePath" >> "$compPath"
|
|
size=$(stat -c%s "$compPath")
|
|
rm "$compPath"
|
|
|
|
echo $size
|
|
}
|
|
|
|
# Calculate the size of target file uncompressed size, gzip7 size, gzip9 size
|
|
# Write to global variable $payloadData, $filename
|
|
calculateSize() {
|
|
label=$(echo "$filename" | sed "s/.*\///" | sed "s/\..*//")
|
|
|
|
rawSize=$(stat -c%s "$filename")
|
|
gzip7Size=$(getGzipSize "$filename" 7)
|
|
gzip9Size=$(getGzipSize "$filename" 9)
|
|
|
|
# Log the sizes (for information/debugging purposes).
|
|
printf "Size: %6d (gzip7: %6d, gzip9: %6d) %s\n" $rawSize $gzip7Size $gzip9Size $label
|
|
|
|
payloadData="$payloadData\"uncompressed/$label\": $rawSize, "
|
|
payloadData="$payloadData\"gzip7/$label\": $gzip7Size, "
|
|
payloadData="$payloadData\"gzip9/$label\": $gzip9Size, "
|
|
}
|
|
|
|
# Check whether the file size is under limit.
|
|
# Exit with an error if limit is exceeded.
|
|
# $1: string - The name in database.
|
|
# $2: string - The payload size limit file.
|
|
checkSize() {
|
|
name="$1"
|
|
limitFile="$2"
|
|
|
|
# In non-PR builds, `CI_BRANCH` is the branch being built (e.g. `pull/12345`), not the targeted branch.
|
|
# Thus, PRs will fall back to using the size limits for `master`.
|
|
node ${PROJECT_ROOT}/scripts/ci/payload-size.js $limitFile $name $CI_BRANCH $CI_COMMIT
|
|
}
|
|
|
|
# Write timestamp to global variable `$payloadData`.
|
|
addTimestamp() {
|
|
# Add Timestamp
|
|
timestamp=$(date +%s)
|
|
payloadData="$payloadData\"timestamp\": $timestamp, "
|
|
}
|
|
|
|
# Write the current CI build URL to global variable `$payloadData`.
|
|
# This allows mapping the data stored in the database to the CI build job that generated it, which
|
|
# might contain more info/context.
|
|
# $1: string - The CI build URL.
|
|
addBuildUrl() {
|
|
buildUrl="$1"
|
|
payloadData="$payloadData\"buildUrl\": \"$buildUrl\", "
|
|
}
|
|
|
|
# Write the commit message for the current CI commit range to global variable `$payloadData`.
|
|
# $1: string - The commit range for this build (in `<SHA-1>...<SHA-2>` format).
|
|
addMessage() {
|
|
commitRange="$1"
|
|
|
|
# Grab the set of SHAs for the message. This can fail when you force push or do initial build
|
|
# because $CI_COMMIT_RANGE may contain the previous SHA which will not be in the
|
|
# force push or commit, hence we default to last commit.
|
|
message=$(git log --oneline $commitRange -- || git log --oneline -n1)
|
|
message=$(echo $message | sed 's/\\/\\\\/g' | sed 's/"/\\"/g')
|
|
payloadData="$payloadData\"message\": \"$message\", "
|
|
}
|
|
|
|
# Add change source: `application`, `dependencies`, or `application+dependencies`
|
|
# Read from global variable `$parentDir`.
|
|
# Update the change source in global variable `$payloadData`.
|
|
# $1: string - The commit range for this build (in `<SHA-1>...<SHA-2>` format).
|
|
addChangeType() {
|
|
commitRange="$1"
|
|
|
|
yarnChanged=false
|
|
allChangedFiles=$(git diff --name-only $commitRange $parentDir | wc -l)
|
|
allChangedFileNames=$(git diff --name-only $commitRange $parentDir)
|
|
|
|
if [[ $allChangedFileNames == *"yarn.lock"* ]]; then
|
|
yarnChanged=true
|
|
fi
|
|
|
|
if [[ $allChangedFiles -eq 1 ]] && [[ "$yarnChanged" = true ]]; then
|
|
# only yarn.lock changed
|
|
change='dependencies'
|
|
elif [[ $allChangedFiles -gt 1 ]] && [[ "$yarnChanged" = true ]]; then
|
|
change='application+dependencies'
|
|
elif [[ $allChangedFiles -gt 0 ]]; then
|
|
change='application'
|
|
else
|
|
# Nothing changed in aio/
|
|
exit 0
|
|
fi
|
|
payloadData="$payloadData\"change\": \"$change\", "
|
|
}
|
|
|
|
# Convert the current `payloadData` value to a JSON string.
|
|
# (Basically remove trailing `,` and wrap in `{...}`.)
|
|
payloadToJson() {
|
|
echo "{$(sed -r 's|, *$||' <<< $payloadData)}"
|
|
}
|
|
|
|
# Upload data to firebase database if it's commit, print out data for pull requests.
|
|
# $1: string - The name in database.
|
|
uploadData() {
|
|
name="$1"
|
|
|
|
readonly safeBranchName=$(echo $CI_BRANCH | sed -e 's/\./_/g')
|
|
readonly dbPath=/payload/$name/$safeBranchName/$CI_COMMIT
|
|
readonly jsonPayload=$(payloadToJson)
|
|
|
|
# WARNING: CI_SECRET_PAYLOAD_FIREBASE_TOKEN should NOT be printed.
|
|
set +x
|
|
$NODE_MODULES_BIN/firebase database:update --data "$jsonPayload" --project $PROJECT_NAME --confirm --token "$CI_SECRET_PAYLOAD_FIREBASE_TOKEN" $dbPath
|
|
}
|
|
|
|
# Track payload size.
|
|
# $1: string - The name in database.
|
|
# $2: string - The file path.
|
|
# $3: true | false - Whether to check the payload size and fail the test if it exceeds limit.
|
|
# $4: true | false - Whether to record the type of changes.
|
|
# $5: [string] - The payload size limit file. Only necessary if `$3` is `true`.
|
|
trackPayloadSize() {
|
|
name="$1"
|
|
path="$2"
|
|
checkSize="$3"
|
|
trackChangeType="$4"
|
|
limitFile="${5:-}"
|
|
|
|
payloadData=""
|
|
|
|
# Calculate the file sizes.
|
|
for filename in $path; do
|
|
calculateSize
|
|
done
|
|
|
|
# Save the file sizes to be retrieved from `payload-size.js`.
|
|
echo "$(payloadToJson)" > /tmp/current.log
|
|
|
|
# If this is a non-PR build, upload the data to firebase.
|
|
if [[ "$CI_PULL_REQUEST" == "false" ]]; then
|
|
if [[ $trackChangeType = true ]]; then
|
|
addChangeType $CI_COMMIT_RANGE
|
|
fi
|
|
addTimestamp
|
|
addBuildUrl $CI_BUILD_URL
|
|
addMessage $CI_COMMIT_RANGE
|
|
uploadData $name
|
|
fi
|
|
|
|
# Check the file sizes against the specified limits.
|
|
if [[ $checkSize = true ]]; then
|
|
checkSize $name $limitFile
|
|
fi
|
|
}
|