From eaa0819cec9e7b8639e4715da1b330cc513b2b90 Mon Sep 17 00:00:00 2001 From: Aaron Sulwer Date: Mon, 26 Oct 2026 14:46:45 -0700 Subject: [PATCH] ci: faster release builds Release builds compile from scratch, and fat LTO with one codegen unit made the final step over half of that: ~4 of ~7.5 minutes per OS. Thin LTO with the default codegen units cut a cold local build from 207 s to 118 s, for an exe about 3 MB bigger. The Linux package install now keeps the .deb files in the Actions cache, keyed on the exact files apt would download. The runners' Ubuntu mirror took 13-18 minutes for 64 MB twice today, against 20 s normally. release.yml no longer uses rust-cache: a release's cache never survived to the next release, so saving it only cost time and evicted the caches Build and Check reuse. --- .github/actions/linux-deps/action.yml | 49 ++++++++++++++++++++++----- .github/workflows/release.yml | 6 +++- Cargo.toml | 8 +++-- 3 files changed, 51 insertions(+), 12 deletions(-) diff --git a/.github/actions/linux-deps/action.yml b/.github/actions/linux-deps/action.yml index 62402fa4..62cf9d30 100644 --- a/.github/actions/linux-deps/action.yml +++ b/.github/actions/linux-deps/action.yml @@ -7,14 +7,45 @@ description: > runs: using: composite steps: - - shell: bash + # The .deb files are cached because the runners' Ubuntu mirror is sometimes + # very slow: the same 64 MB took 20 s on one run and 13-18 minutes on + # others. The key is the exact set of files apt would download, so a new + # package version or runner image makes a new cache instead of installing + # stale ones. + - id: debs + shell: bash + env: + PACKAGES: >- + libdbus-1-dev + libgtk-3-dev + libayatana-appindicator3-dev + librsvg2-dev + libsoup-3.0-dev + libjavascriptcoregtk-4.1-dev + libwebkit2gtk-4.1-dev run: | sudo apt-get update - sudo apt-get install -y \ - libdbus-1-dev \ - libgtk-3-dev \ - libayatana-appindicator3-dev \ - librsvg2-dev \ - libsoup-3.0-dev \ - libjavascriptcoregtk-4.1-dev \ - libwebkit2gtk-4.1-dev + # Field 2 is the file name (package_version_arch.deb); the URL in field + # 1 names whichever mirror this runner got, so it stays out of the key. + key=$(sudo apt-get install -y -qq --print-uris $PACKAGES | awk '{print $2}' | sort | sha256sum | cut -c1-16) + echo "packages=$PACKAGES" >> "$GITHUB_OUTPUT" + echo "key=$key" >> "$GITHUB_OUTPUT" + echo "dir=$RUNNER_TEMP/apt-debs" >> "$GITHUB_OUTPUT" + + - uses: actions/cache@v6 + with: + path: ${{ steps.debs.outputs.dir }} + key: apt-debs-${{ runner.os }}-${{ runner.arch }}-${{ steps.debs.outputs.key }} + + - shell: bash + env: + PACKAGES: ${{ steps.debs.outputs.packages }} + DEBS: ${{ steps.debs.outputs.dir }} + run: | + # apt reads and downloads its archives in our directory instead of + # /var/cache/apt/archives, so a restored cache is used as it is and a + # fresh download lands where the cache step saves from. + mkdir -p "$DEBS/partial" + sudo apt-get install -y -o Dir::Cache::Archives="$DEBS" $PACKAGES + # apt's lock file is root-only; the cache is saved as the runner user. + sudo rm -rf "$DEBS/lock" "$DEBS/partial" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d8d75ada..36654439 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -117,7 +117,11 @@ jobs: cache: pnpm cache-dependency-path: app/main/pnpm-lock.yaml - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + # No rust-cache. The version stamp rebuilds our crates anyway, and a + # release's cache was always evicted before the next release (the repo + # sits at GitHub's 10 GB cache limit), so saving one cost time and pushed + # out the caches Build and Check do reuse. + # protoc for prost-build. A composite action (no Node runtime): # arduino/setup-protoc is still on the deprecated Node 20. - uses: taiki-e/install-action@v2 diff --git a/Cargo.toml b/Cargo.toml index eca74dad..88ab7221 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,10 @@ version = "1.0.0" # Profiles must live at the workspace root (per-crate profiles are ignored). [profile.release] -lto = true +# Thin LTO with the default 16 codegen units, not fat LTO with one. A release +# always compiles from scratch, and fat LTO's single-threaded final step was +# over half of it: ~4 of ~7.5 minutes per OS in CI. A cold local build went +# from 207 s to 118 s. The exe grows about 3 MB (15.5 to 18.9 MB on Windows); +# transfers are bound by the network and Bluetooth, not the CPU. +lto = "thin" opt-level = 3 -codegen-units = 1