@@ -44,6 +44,49 @@ record_artifact() {
4444 printf " %s\t%s\t%s\n" " $platform " " $artifact " " $bytes " >> " $output_file "
4545}
4646
47+ # Emit one detail row per file inside a packed artifact. Detail rows carry a
48+ # fourth column (the file path) so the renderer can tell them apart from the
49+ # artifact summary rows above and group them into a collapsible breakdown.
50+ # Sizes are the uncompressed on-disk sizes of each entry.
51+ record_tarball_breakdown () {
52+ local platform=$1
53+ local artifact=$2
54+ local tarball=$3
55+ local dir
56+ local file
57+ local rel
58+
59+ dir=$( mktemp -d " ${TMPDIR:-/ tmp} /checkout-kit-explode.XXXXXX" )
60+ tar -xzf " $tarball " -C " $dir "
61+
62+ while IFS= read -r file; do
63+ rel=${file# " $dir /" }
64+ rel=${rel# package/ }
65+ printf " %s\t%s\t%s\t%s\n" " $platform " " $artifact " " $( size_bytes " $file " ) " " $rel " >> " $output_file "
66+ done < <( find " $dir " -type f | sort)
67+
68+ rm -rf " $dir "
69+ }
70+
71+ record_zip_breakdown () {
72+ local platform=$1
73+ local artifact=$2
74+ local archive=$3
75+ local dir
76+ local file
77+ local rel
78+
79+ dir=$( mktemp -d " ${TMPDIR:-/ tmp} /checkout-kit-explode.XXXXXX" )
80+ unzip -qq " $archive " -d " $dir "
81+
82+ while IFS= read -r file; do
83+ rel=${file# " $dir /" }
84+ printf " %s\t%s\t%s\t%s\n" " $platform " " $artifact " " $( size_bytes " $file " ) " " $rel " >> " $output_file "
85+ done < <( find " $dir " -type f | sort)
86+
87+ rm -rf " $dir "
88+ }
89+
4790measure_web_package () {
4891 local pack_dir
4992 local tarball
@@ -57,6 +100,7 @@ measure_web_package() {
57100
58101 tarball=$( find " $pack_dir " -name " *.tgz" -type f -print -quit)
59102 record_artifact " Web" " npm tarball" " $tarball "
103+ record_tarball_breakdown " Web" " npm tarball" " $tarball "
60104}
61105
62106measure_react_native_package () {
@@ -74,6 +118,7 @@ measure_react_native_package() {
74118
75119 tarball=$( find " $pack_dir " -name " *.tgz" -type f -print -quit)
76120 record_artifact " React Native" " npm tarball" " $tarball "
121+ record_tarball_breakdown " React Native" " npm tarball" " $tarball "
77122}
78123
79124measure_android_package () {
@@ -85,6 +130,7 @@ measure_android_package() {
85130 )
86131
87132 record_artifact " Android" " release AAR" " $aar "
133+ record_zip_breakdown " Android" " release AAR" " $aar "
88134}
89135
90136collect_measurements () {
@@ -110,7 +156,7 @@ render_comment() {
110156 local comment_file=${3:? usage: measure-package-size render <base-file> <head-file> <output-file>}
111157
112158 awk -F ' \t' -v base_path=" $base_file " '
113- function human(bytes, units, value, unit) {
159+ function human(bytes, units, value, unit) {
114160 if (bytes == "") {
115161 return "unavailable";
116162 }
@@ -130,7 +176,11 @@ render_comment() {
130176 return sprintf("%.1f %s", value, units[unit]);
131177 }
132178
133- function delta(head, base, diff) {
179+ function cell(bytes) {
180+ return (bytes == "") ? "—" : human(bytes);
181+ }
182+
183+ function delta(head, base, diff) {
134184 if (head == "" || base == "") {
135185 return "unavailable";
136186 }
@@ -146,7 +196,23 @@ render_comment() {
146196 return "0 B";
147197 }
148198
149- function remember(platform, artifact, key) {
199+ # File-level delta: a file missing on one side is an add/remove, not
200+ # "unavailable", so treat the absent side as zero.
201+ function file_delta(head, base, h, b, diff) {
202+ h = (head == "") ? 0 : head + 0;
203+ b = (base == "") ? 0 : base + 0;
204+ diff = h - b;
205+ if (diff > 0) {
206+ return "+" human(diff);
207+ }
208+ if (diff < 0) {
209+ return "-" human(-diff);
210+ }
211+
212+ return "0 B";
213+ }
214+
215+ function remember(platform, artifact, key) {
150216 key = platform SUBSEP artifact;
151217 if (!(key in seen)) {
152218 seen[key] = 1;
@@ -157,21 +223,45 @@ render_comment() {
157223 return key;
158224 }
159225
226+ function remember_detail(platform, path, key) {
227+ key = platform SUBSEP path;
228+ if (!(key in detail_seen)) {
229+ detail_seen[key] = 1;
230+ detail_paths[platform, ++detail_n[platform]] = path;
231+ if (!(platform in dplat_seen)) {
232+ dplat_seen[platform] = 1;
233+ dplat_order[++dplat_count] = platform;
234+ }
235+ }
236+ return key;
237+ }
238+
160239 FILENAME == base_path {
161- if (NF >= 3) {
240+ if (NF >= 4 && $4 != "") {
241+ remember_detail($1, $4);
242+ detail_base[$1, $4] = $3;
243+ } else if (NF >= 3) {
162244 key = remember($1, $2);
163245 base_bytes[key] = $3;
164246 }
165247 next;
166248 }
167249
250+ NF >= 4 && $4 != "" {
251+ remember_detail($1, $4);
252+ detail_head[$1, $4] = $3;
253+ next;
254+ }
255+
168256 NF >= 3 {
169257 key = remember($1, $2);
170258 head_bytes[key] = $3;
171259 next;
172260 }
173261
174262 END {
263+ cap = 20;
264+
175265 print "<!-- checkout-kit-package-size -->";
176266 print "## Package Size";
177267 print "";
@@ -192,8 +282,58 @@ render_comment() {
192282 }
193283 }
194284
285+ for (p = 1; p <= dplat_count; p++) {
286+ platform = dplat_order[p];
287+ n = detail_n[platform];
288+ if (n == 0) {
289+ continue;
290+ }
291+
292+ for (i = 1; i <= n; i++) {
293+ idx[i] = detail_paths[platform, i];
294+ b = detail_base[platform, idx[i]] + 0;
295+ h = detail_head[platform, idx[i]] + 0;
296+ sortval[i] = (h > b) ? h : b;
297+ }
298+
299+ for (i = 1; i <= n; i++) {
300+ maxpos = i;
301+ for (j = i + 1; j <= n; j++) {
302+ if (sortval[j] > sortval[maxpos]) {
303+ maxpos = j;
304+ }
305+ }
306+ if (maxpos != i) {
307+ tmpv = sortval[i]; sortval[i] = sortval[maxpos]; sortval[maxpos] = tmpv;
308+ tmps = idx[i]; idx[i] = idx[maxpos]; idx[maxpos] = tmps;
309+ }
310+ }
311+
312+ print "";
313+ print "<details><summary>" platform " file breakdown</summary>";
314+ print "";
315+ print "| File | Base | Head | Delta |";
316+ print "| --- | ---: | ---: | ---: |";
317+
318+ shown = (n > cap) ? cap : n;
319+ for (i = 1; i <= shown; i++) {
320+ path = idx[i];
321+ printf "| `%s` | %s | %s | %s |\n",
322+ path,
323+ cell(detail_base[platform, path]),
324+ cell(detail_head[platform, path]),
325+ file_delta(detail_head[platform, path], detail_base[platform, path]);
326+ }
327+ if (n > cap) {
328+ printf "| _…and %d smaller files_ | | | |\n", n - cap;
329+ }
330+
331+ print "";
332+ print "</details>";
333+ }
334+
195335 print "";
196- print "_Measured from the PR base SHA and PR head SHA. This comment reports package artifact sizes only; it is not a final app binary-size report._";
336+ print "_Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report._";
197337 }
198338 ' " $base_file " " $head_file " > " $comment_file "
199339}
0 commit comments