From 52dee5fd8f1fc8cadbaa698ef7bb0fe79ccc4013 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Sat, 4 Jul 2026 11:25:43 +0300 Subject: [PATCH 1/2] Fix CI dependency caching: per-platform cache keys + fallbacks The macOS host, iOS and Android jobs all run on the same macos runner image and therefore computed identical cache keys. GitHub's cache is write-once per key, so the first job to save (the fast host build) owned the key forever and the iOS/Android saves failed ("Cache save failed." in the logs) - their cross-compiled dependencies (folly, protobuf, openssl, ...) were rebuilt from source on every run: measured ~23-31 min per Android run and ~35 min for iOS, versus ~14 min for the host build with a working cache. - The target platform (host / --ios / --android, from ARCHFLAGS) is now part of both cache keys, so each platform owns its own cache. - restore-keys prefix fallbacks: a manifest or overlay change now reuses the newest previous cache instead of rebuilding everything (vcpkg's per-package ABI hashing ignores stale archive entries, and vcpkg install reconciles a stale installed tree). - The vcpkg submodule commit is part of the exact key (a tool bump previously reused stale installed trees silently). - Saves are skipped when the exact key was already restored. The first run with the new keys is a cold build by design (it populates each platform's cache); the speedup shows from the next run onwards. Co-Authored-By: Claude Fable 5 --- .../reusable/cached-install/action.yml | 55 ++++++++++++------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/.github/workflows/reusable/cached-install/action.yml b/.github/workflows/reusable/cached-install/action.yml index 18a5a8af..f65467ee 100644 --- a/.github/workflows/reusable/cached-install/action.yml +++ b/.github/workflows/reusable/cached-install/action.yml @@ -4,18 +4,43 @@ description: "install dependencies, build and cache result, or restore from cach runs: using: "composite" steps: - - name: cache homedir + # The cache keys must distinguish the TARGET platform, not just the + # runner: the macOS host build, the iOS build and the Android build + # all run on the same macos runner image, and GitHub's cache is + # write-once per key — with a shared key the first job to save owns + # the cache forever and the other platforms' saves fail ("Cache save + # failed."), so their dependencies were rebuilt from source on every + # run (~30 min Android, ~35 min iOS). ARCHFLAGS is the workflow-level + # platform selector (empty = host, --ios, --android). The vcpkg + # submodule commit is part of the key because the tool version + # changes what it builds; the restore-keys prefix fallback reuses the + # newest previous cache on any key miss — vcpkg's own per-package ABI + # hashing makes stale archive entries harmless (they are ignored) and + # `vcpkg install` reconciles a stale installed tree. + - name: compute cache keys + id: keys + run: | + echo "platform=${ARCHFLAGS:-host}" >> "$GITHUB_OUTPUT" + echo "vcpkg_sha=$(git rev-parse HEAD:vcpkg)" >> "$GITHUB_OUTPUT" + shell: bash + - name: cache vcpkg binary archives id: cache-homedir uses: actions/cache/restore@v4 with: - key: ${{ runner.arch }}-${{ runner.os }}-cache-homedir2-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + key: ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-archives-${{ steps.keys.outputs.vcpkg_sha }}-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + restore-keys: | + ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-archives-${{ steps.keys.outputs.vcpkg_sha }}- + ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-archives- path: | ~/.cache/vcpkg/archives - name: cache vcpkg installed id: cache-vcpkg-installed uses: actions/cache/restore@v4 with: - key: ${{ runner.arch }}-${{ runner.os }}-cache-vcpkg-installed2-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + key: ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-installed-${{ steps.keys.outputs.vcpkg_sha }}-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + restore-keys: | + ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-installed-${{ steps.keys.outputs.vcpkg_sha }}- + ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-installed- path: | ./build/vcpkg_installed - name: install-prerequisities @@ -45,30 +70,22 @@ runs: exit "$EXITCODE" fi shell: bash - - name: cache homedir save + # Save even on failure (if: always()) so a partially built dependency + # set is reused by the retry. Saving is skipped when the exact key was + # already restored (nothing new to store). + - name: cache vcpkg binary archives save id: cache-homedir-save - if: always() + if: always() && steps.cache-homedir.outputs.cache-hit != 'true' uses: actions/cache/save@v4 with: - key: ${{ runner.arch }}-${{ runner.os }}-cache-homedir2-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + key: ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-archives-${{ steps.keys.outputs.vcpkg_sha }}-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} path: | ~/.cache/vcpkg/archives - name: cache vcpkg installed save id: cache-vcpkg-installed-save - if: always() + if: always() && steps.cache-vcpkg-installed.outputs.cache-hit != 'true' uses: actions/cache/save@v4 with: - key: ${{ runner.arch }}-${{ runner.os }}-cache-vcpkg-installed2-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} + key: ${{ runner.arch }}-${{ runner.os }}-${{ steps.keys.outputs.platform }}-installed-${{ steps.keys.outputs.vcpkg_sha }}-${{ hashFiles('./vcpkg.json', './overlaytriplets/**', './overlayports/**') }} path: | ./build/vcpkg_installed - #- name: Commit compiled binaries - # run: | - # git config --global user.name 'github-actions[bot]' - # git config --global user.email 'github-actions[bot]@users.noreply.github.com' - # git add packages/streamr-libstreamrproxyclient/dist - # git add packages/streamr-libstreamrproxyclient/wrappers/go - # git commit -m "Automatically compiled binaries" - # git pull --rebase --no-edit - # git push --no-verify - # shell: bash - From 66332c26733a3aa39cd70b21de2bf1878d1f1089 Mon Sep 17 00:00:00 2001 From: Petri Savolainen Date: Sat, 4 Jul 2026 11:32:53 +0300 Subject: [PATCH 2/2] Fix protoc plugin guard for iOS package builds The NOT CMAKE_CROSSCOMPILING guard (added when Android modules were enabled) is not enough on iOS: the iOS package builds keep the host CMAKE_SYSTEM_NAME by design (Homebrew compiler, only the SDK/sysroot is swapped), so CMake does not consider them cross-compiling and the host-only protoc plugin was configured against the arm64-ios vcpkg tree, which provides no protobuf::libprotoc. Restore the explicit IOS condition (set by toolchains/ios.toolchain.cmake) alongside the cross-compiling check. First iOS run since that commit caught it. Co-Authored-By: Claude Fable 5 --- packages/streamr-proto-rpc/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/streamr-proto-rpc/CMakeLists.txt b/packages/streamr-proto-rpc/CMakeLists.txt index 9abe80be..92bcd33c 100644 --- a/packages/streamr-proto-rpc/CMakeLists.txt +++ b/packages/streamr-proto-rpc/CMakeLists.txt @@ -96,7 +96,11 @@ file(WRITE "${CMAKE_BINARY_DIR}/streamr-proto-rpc-config.cmake" # and the plugin runs on the developer machine anyway. (Previously this # sat inside the modules block below and was skipped on Android only as # a side effect of the modules gate being OFF there.) -if(NOT CMAKE_CROSSCOMPILING) +# NOTE the explicit IOS check: iOS package builds keep the host +# CMAKE_SYSTEM_NAME (they use the Homebrew compiler and only swap the +# SDK/sysroot — see homebrewClang.cmake), so CMAKE_CROSSCOMPILING is +# FALSE there; IOS is set by toolchains/ios.toolchain.cmake. +if(NOT IOS AND NOT CMAKE_CROSSCOMPILING) add_executable(protobuf-streamr-plugin src/PluginCodeGeneratorMain.cpp include/streamr-proto-rpc/PluginCodeGenerator.hpp) target_include_directories(