From 0d9364b539c3f85af57b89375b113b2ef0651b4b Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 25 Sep 2026 08:19:13 -0400 Subject: [PATCH 1/2] ci: store each app-host product file once in the product archive Build/Products reaches every framework binary through Versions/Current symlinks. Product extractors accept no symlinks, so packaging used `tar -h`, which stored each framework binary three times: 1.9 GB on disk became 2.7 GB of tar and 830 MB gzipped. On an owned Mac the upload runs at about 7.5 MB/s (a per-host uplink cap), so the duplicates cost about 35 s of every compile admission and as much again on every owned consumer download. Stage the tree as hard links with the symlinks followed (pax -rwlL, the same tree `tar -h` wrote) beside the DerivedData, and tar the stage. tar stores each file once and the other paths as hard links, which tar -x, aa and reuse_app_host_products.py all extract to the same files. Measured on a kept cmux10s product: 830 MB -> 572 MB, 41 s -> 28 s to pack. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-macos.yml | 14 +++++++++++++- .github/workflows/seed-derived-data.yml | 14 +++++++++++++- .github/workflows/test-e2e.yml | 14 +++++++++++++- 3 files changed, 39 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index c31104eb983e..e959b84c7f7c 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -942,7 +942,19 @@ jobs: # COPYFILE_DISABLE: see the matching note in test-e2e.yml. gzip -1 # takes about a third of the default level's time on this product # for an archive about 11% larger. - COPYFILE_DISABLE=1 tar -chf - -C "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" Build/Products | gzip -1 > "$archive" + # Build/Products reaches each framework binary through Versions/Current + # symlinks, and product extractors accept no symlinks, so `tar -h` stored + # every framework binary three times: 1.9 GB on disk became 2.7 GB of + # tar and 830 MB gzipped. Stage the tree as hard links with the symlinks + # followed (the same tree `tar -h` wrote, beside the DerivedData so the + # links stay on one volume); tar then stores each file once and every + # other path as a hard link, which tar -x, aa and + # reuse_app_host_products.py all extract. Same files, 572 MB, packed + # faster. + stage="$(mktemp -d "${CMUX_COMPILE_ADMISSION_DERIVED_DATA%/*}/.product-pack.XXXXXX")" + (cd "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" && pax -rwlL Build/Products "$stage") + COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" + rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" - name: Upload compiled app-host test product diff --git a/.github/workflows/seed-derived-data.yml b/.github/workflows/seed-derived-data.yml index 316e24b0f389..acb554b34101 100644 --- a/.github/workflows/seed-derived-data.yml +++ b/.github/workflows/seed-derived-data.yml @@ -396,7 +396,19 @@ jobs: python3 scripts/ci/app_host_test_products.py stamp "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" python3 scripts/ci/reuse_app_host_products.py seal "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" archive="$RUNNER_TEMP/app-host-products.tar.gz" - COPYFILE_DISABLE=1 tar -chf - -C "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" Build/Products | gzip -1 > "$archive" + # Build/Products reaches each framework binary through Versions/Current + # symlinks, and product extractors accept no symlinks, so `tar -h` stored + # every framework binary three times: 1.9 GB on disk became 2.7 GB of + # tar and 830 MB gzipped. Stage the tree as hard links with the symlinks + # followed (the same tree `tar -h` wrote, beside the DerivedData so the + # links stay on one volume); tar then stores each file once and every + # other path as a hard link, which tar -x, aa and + # reuse_app_host_products.py all extract. Same files, 572 MB, packed + # faster. + stage="$(mktemp -d "${CMUX_COMPILE_ADMISSION_DERIVED_DATA%/*}/.product-pack.XXXXXX")" + (cd "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" && pax -rwlL Build/Products "$stage") + COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" + rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" # Three days, as admission keeps its own: a pull request only matches diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index c581b9a1e24f..8d1f59fde351 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -644,7 +644,19 @@ jobs: # (Build/._Products) for Xcode's xattrs. macOS tar folds them back # in on extract, so the test job never noticed, but cross-run reuse # extracts with Python and rejects them as unscoped paths. - COPYFILE_DISABLE=1 tar -chf - -C "$CMUX_DERIVED_DATA_PATH" Build/Products | gzip -1 > "$archive" + # Build/Products reaches each framework binary through Versions/Current + # symlinks, and product extractors accept no symlinks, so `tar -h` stored + # every framework binary three times: 1.9 GB on disk became 2.7 GB of + # tar and 830 MB gzipped. Stage the tree as hard links with the symlinks + # followed (the same tree `tar -h` wrote, beside the DerivedData so the + # links stay on one volume); tar then stores each file once and every + # other path as a hard link, which tar -x, aa and + # reuse_app_host_products.py all extract. Same files, 572 MB, packed + # faster. + stage="$(mktemp -d "${CMUX_DERIVED_DATA_PATH%/*}/.product-pack.XXXXXX")" + (cd "$CMUX_DERIVED_DATA_PATH" && pax -rwlL Build/Products "$stage") + COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" + rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" echo "bytes=$(wc -c < "$archive" | tr -d ' ')" >> "$GITHUB_OUTPUT" From 7a2870f5c58b33e6425309c08d852378c56517d8 Mon Sep 17 00:00:00 2001 From: Leo Li Date: Fri, 25 Sep 2026 08:25:35 -0400 Subject: [PATCH 2/2] ci: remove the product pack stage on any exit Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci-macos.yml | 2 +- .github/workflows/seed-derived-data.yml | 2 +- .github/workflows/test-e2e.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci-macos.yml b/.github/workflows/ci-macos.yml index e959b84c7f7c..6b438375cb88 100644 --- a/.github/workflows/ci-macos.yml +++ b/.github/workflows/ci-macos.yml @@ -952,9 +952,9 @@ jobs: # reuse_app_host_products.py all extract. Same files, 572 MB, packed # faster. stage="$(mktemp -d "${CMUX_COMPILE_ADMISSION_DERIVED_DATA%/*}/.product-pack.XXXXXX")" + trap 'rm -rf "$stage"' EXIT (cd "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" && pax -rwlL Build/Products "$stage") COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" - rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" - name: Upload compiled app-host test product diff --git a/.github/workflows/seed-derived-data.yml b/.github/workflows/seed-derived-data.yml index acb554b34101..c313aa1a9017 100644 --- a/.github/workflows/seed-derived-data.yml +++ b/.github/workflows/seed-derived-data.yml @@ -406,9 +406,9 @@ jobs: # reuse_app_host_products.py all extract. Same files, 572 MB, packed # faster. stage="$(mktemp -d "${CMUX_COMPILE_ADMISSION_DERIVED_DATA%/*}/.product-pack.XXXXXX")" + trap 'rm -rf "$stage"' EXIT (cd "$CMUX_COMPILE_ADMISSION_DERIVED_DATA" && pax -rwlL Build/Products "$stage") COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" - rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" # Three days, as admission keeps its own: a pull request only matches diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 8d1f59fde351..f538e912b779 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -654,9 +654,9 @@ jobs: # reuse_app_host_products.py all extract. Same files, 572 MB, packed # faster. stage="$(mktemp -d "${CMUX_DERIVED_DATA_PATH%/*}/.product-pack.XXXXXX")" + trap 'rm -rf "$stage"' EXIT (cd "$CMUX_DERIVED_DATA_PATH" && pax -rwlL Build/Products "$stage") COPYFILE_DISABLE=1 tar -cf - -C "$stage" Build/Products | gzip -1 > "$archive" - rm -rf "$stage" echo "sha256=$(shasum -a 256 "$archive" | awk '{print $1}')" >> "$GITHUB_OUTPUT" echo "bytes=$(wc -c < "$archive" | tr -d ' ')" >> "$GITHUB_OUTPUT"