Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save namikingsoft/b8d553c64c9a0ff7409dd4db206d68f2 to your computer and use it in GitHub Desktop.
Save namikingsoft/b8d553c64c9a0ff7409dd4db206d68f2 to your computer and use it in GitHub Desktop.
Post reports of webpack-bundle-analyzer to GitHub statuses on CircleCI
#!/bin/sh -eu
# ## Post bundle analyzer to github statuses
#
# ### Environment Variables
#
# - `REPORT_JSON_PATH`: By webpack-bundle-analyzer
# - `REPORT_HTML_PATH`: By webpack-bundle-analyzer
# - `GITHUB_TOKEN`: GitHub Access Token
# - `CIRCLE_SHA1`: Defined by CircleCI
# - `CIRCLE_BUILD_NUM`: Defined by CircleCI
# - `CIRCLE_PROJECT_USERNAME`: Defined by CircleCI
# - `CIRCLE_PROJECT_REPONAME`: Defined by CircleCI
# - `CIRCLE_PULL_REQUEST`: Defined by CircleCI on pull request build
# ポーリング系を待つ秒数: ベースブランチと同時にビルドが走ると Artifact がなかったりするため
TIMEOUT_UNIXTIME="$(($(date +%s)+300))" # 5 分間
# GitHub Pull Request 番号を抽出
PR_NUMBER="$(echo "$CIRCLE_PULL_REQUEST" | sed -e 's@^.*/@@')"
# GitHub Pull Request からベースブランチのコミットハッシュを取得
base_branch_sha1() {
if [ "${PR_NUMBER:-undef}" != "undef" ]; then
branch="pull/${PR_NUMBER}/merge"
git fetch origin "${branch}:${branch}" > /dev/null
git log "${branch}" --oneline | head -n1 | sed -e 's/^.* into //'
else
git rev-list origin/master | head -n1
fi
}
# GitHub Statuses に添付する Bundle Analyzer の url を取得
report_html_url="$(
curl \
-X GET "https://circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${CIRCLE_BUILD_NUM}/artifacts" \
-H "Circle-Token: $CIRCLE_PERSONAL_TOKEN" \
| jq -r ".items | map(select(.url | endswith(\"${REPORT_HTML_PATH}\"))) | .[0].url"
)"
# GitHub Statuses に Bundle Analyzer の url を POST
curl \
-X POST "https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/statuses/${CIRCLE_SHA1}" \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(cat << EOS
{
"state": "success",
"description": "From CircleCI Job: ${CIRCLE_BUILD_NUM}",
"target_url": "${report_html_url}",
"context": "Bundle Analyzer"
}
EOS
)"
# ベースブランチの GitHub Statuses Description から CircleCI Job 番号を抽出
while [ "$(date +%s)" -lt "$TIMEOUT_UNIXTIME" ]; do # ベースブランチとの同時ビルド対策
description="$(
curl \
-X GET "https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/commits/$(base_branch_sha1)/statuses" \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
| jq -r '. | map(select(.context == "Bundle Analyzer")) | if length > 0 then .[0].description else "" end'
)"
base_build_num="$(echo "$description" | sed -E 's/^.* Job: (.*)$/\1/')"
if [ "${base_build_num:-none}" != "none" ]; then
break
fi
sleep 10 # sec
done
if [ "${base_build_num:-none}" = "none" ]; then
echo "Not found size-compare info from base branch"
exit 0
fi
# ベースブランチの Bundle Analyzer json を Artifact から取得
base_report_json_url="$(
curl \
-X GET "https://circleci.com/api/v2/project/gh/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/${base_build_num}/artifacts" \
-H "Circle-Token: $CIRCLE_PERSONAL_TOKEN" \
| jq -r '.items | map(select(.url | endswith("report.json"))) | .[0].url'
)"
curl -Lo /tmp/report-base.json "$base_report_json_url?circle-token=${CIRCLE_PERSONAL_TOKEN}"
cp "${REPORT_JSON_PATH}" /tmp/report-head.json
# Size compare を集計しやすいように json を整形
< /tmp/report-head.json jq 'map( . | { label: .label | sub("\\.[a-z0-9]{20,32}\\.";"."), gzipSize: .gzipSize })' > /tmp/report-headmod.json
< /tmp/report-base.json jq 'map( . | { label: .label | sub("\\.[a-z0-9]{20,32}\\.";"."), gzipSize: - .gzipSize })' > /tmp/report-basemod.json
# 前後の Bundle Analyzer json から Size compare 情報を集計
jq -s '
add
| group_by(.label)
| map({
label: .[0].label,
gzipSize: (.[0].gzipSize | fabs),
gzipSizeDiff: ((.[0].gzipSize // 0) + (.[1].gzipSize // 0)),
gzipSizePercent: (((.[0].gzipSize // 0) + (.[1].gzipSize // 0)) / (.[0].gzipSize | fabs) * 100),
})
| map(select(.gzipSizeDiff != 0))
' /tmp/report-headmod.json /tmp/report-basemod.json > /tmp/bundle-analyzer/size-compare.json # スクリプト外で store_artifact される TODO: 蜜
# Total, Entry の Gzip サイズ差分を集計
total_gzip_size_diff="$(< /tmp/bundle-analyzer/size-compare.json jq 'map(.gzipSizeDiff) | add // 0')"
entry_gzip_size_diff="$(
< /tmp/bundle-analyzer/size-compare.json jq '
map(
select(.label | contains("index.") or contains("startup."))
| .gzipSizeDiff
)
| add // 0
')"
# 明示的に `+` をつける & 三桁カンマ区切り refs. https://pooh.gr.jp/?p=9768
total_gzip_size_diff="$(perl -e "print '+' if $total_gzip_size_diff > 0")$(echo "${total_gzip_size_diff}" | awk '{printf"%\047d\n",$1}') B" # Byte
entry_gzip_size_diff="$(perl -e "print '+' if $entry_gzip_size_diff > 0")$(echo "${entry_gzip_size_diff}" | awk '{printf"%\047d\n",$1}') B" # Byte
# 他の Artifact url のファイル部を size-compare.json に書き換えて、流用する
# まだ store_artifact されていないため、API から直接 url が取得できない
size_compare_json_url="$(echo "$report_html_url" | sed -E 's|^(.+)/[^/]+$|\1/size-compare.json|')"
# Size compare 情報を GitHub Statuses に POST
curl \
-X POST "https://api.github.com/repos/${CIRCLE_PROJECT_USERNAME}/${CIRCLE_PROJECT_REPONAME}/statuses/${CIRCLE_SHA1}" \
-H "Authorization: token $GITHUB_TOKEN" \
-H "Accept: application/vnd.github.v3+json" \
-d "$(cat << EOS
{
"state": "success",
"description": "Entry: ${entry_gzip_size_diff} / Total: ${total_gzip_size_diff} (gzip)",
"target_url": "$size_compare_json_url",
"context": "Bundle Compare"
}
EOS
)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment