Last active
July 14, 2026 18:28
-
-
Save alexeagle/80bef82e244f4711fb3e5d8dafce50f5 to your computer and use it in GitHub Desktop.
Rank a Bazel test target's action inputs by on-disk size, grouped by owning package/repo — find which deps bloat runfiles (slow remote-cache downloads)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| # Rank a Bazel target's output artifacts by size, grouped by owning package/repo, | |
| # so a product engineer sees WHICH deps bloat their test's inputs/runfiles — the | |
| # thing that makes EngFlow remote-cache downloads slow. | |
| # | |
| # Sizes come from the CAS digests in the Build Event Protocol, NOT from disk: | |
| # every BEP file entry has a `bytestream://<host>/blobs/<hash>/<SIZE>` URI, and a | |
| # Bazel Digest is {hash, size_bytes} — the size IS the last path segment. So this | |
| # works under `--remote_download_minimal` (build-without-the-bytes) with zero byte | |
| # downloads and no local stat. (CAS has no "size of hash X" RPC; you read the size | |
| # from the digest wherever Bazel recorded it — here, the BEP.) | |
| # | |
| # Usage: ./bazel-input-weight.sh //pkg:target [extra bazel build flags...] | |
| set -euo pipefail | |
| TARGET="${1:?usage: $0 //pkg:target}"; shift || true | |
| BEP="$(mktemp -t bep.XXXXXX.json)" | |
| # Build (or cache-hit) to emit the BEP. --remote_download_minimal keeps bytes remote; | |
| # we only want the digest metadata, so nothing is fetched. | |
| bazel build "$TARGET" \ | |
| --remote_download_minimal \ | |
| --build_event_json_file="$BEP" \ | |
| "$@" >/dev/null 2>&1 || true # keep going even if some action fails; BEP still has files | |
| # Parse namedSetOfFiles: pull (size, group) from each file's bytestream URI. | |
| # group key = external repo (external/<repo>) else the bazel-out package path. | |
| jq -r ' | |
| select(.namedSetOfFiles) | .namedSetOfFiles.files[]? | |
| | .uri as $u | |
| | ($u | capture("/blobs/[0-9a-f]+/(?<sz>[0-9]+)$").sz | tonumber) as $sz | |
| | ((.pathPrefix // []) + [.name] | join("/")) as $p | |
| | (if ($p|test("(^|/)external/")) then ($p | capture("external/(?<r>[^/]+)").r) | |
| elif ($p|test("(^|/)bin/")) then ($p | sub(".*/bin/";"") | split("/")[0:3] | join("/")) | |
| else ($p | split("/")[0:3] | join("/")) end) as $g | |
| | "\($sz)\t\($g)" | |
| ' "$BEP" \ | |
| | awk -F'\t' '{s[$2]+=$1} END{for(k in s) printf "%d\t%s\n", s[k], k}' \ | |
| | sort -rn \ | |
| | awk 'BEGIN{print "SIZE\tGROUP"} | |
| {h=$1;u="B"; if(h>1073741824){h/=1073741824;u="G"} else if(h>1048576){h/=1048576;u="M"} else if(h>1024){h/=1024;u="K"} | |
| printf "%.1f%s\t%s\n", h, u, $2}' \ | |
| | head -30 | |
| rm -f "$BEP" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment