From 4f1c2ac25585000f51afa9b0252977eb04b54e3c Mon Sep 17 00:00:00 2001 From: Osei Fortune Date: Thu, 12 Nov 2026 09:39:25 -0400 Subject: [PATCH] ci: keep the Skia cache across unrelated changes and failing later steps The Skia cache key hashed every Cargo.toml, all build scripts and the Makefile, so any dependency or script change dropped every entry. Key it on the skia lines and [features] chains of the Cargo.toml files plus the scripts and setup that feed cargo's environment. Each job saved the cache as its last step, so a failing test, AAR or xcframework step lost a Skia build that had succeeded. Save it right after the Rust build. --- .github/actions/skia-cache/action.yml | 22 ++++++++- .github/workflows/build-native.yml | 69 +++++++++++++++------------ 2 files changed, 58 insertions(+), 33 deletions(-) diff --git a/.github/actions/skia-cache/action.yml b/.github/actions/skia-cache/action.yml index 98a2fde1..8d3ca32a 100644 --- a/.github/actions/skia-cache/action.yml +++ b/.github/actions/skia-cache/action.yml @@ -11,7 +11,7 @@ description: > for the whole job: skia-bindings rebuilds Skia when one of them changes, and the Xcode pre-build phases call cargo directly. - Save (save: 'true', as the job's last step): only on pushes to master, so pull requests and + Save (save: 'true', right after the job's Rust build): only on pushes to master, so pull requests and releases read master's entries and never write the ones master restores. Also saves the cargo registry the setup-*-native actions restored, under the same rule. @@ -38,12 +38,30 @@ runs: cp "$GITHUB_ACTION_PATH/cargo-shim.sh" "$tmp/skia-cache-bin/cargo" chmod +x "$tmp/skia-cache-bin/cargo" echo "$tmp/skia-cache-bin" >> "$GITHUB_PATH" + # Key on what the Skia build depends on: the rust-skia rev and features (every Cargo.toml + # line naming skia, plus the [features] chains that reach skia-safe), the env the build + # scripts and Makefile give cargo, and the toolchain setup. Unrelated dependency or + # script changes keep the entries. + sha() { if command -v sha256sum > /dev/null; then sha256sum; else shasum -a 256; fi; } + inputs_hash="$( + for f in $(git ls-files Cargo.toml 'crates/*/Cargo.toml'); do + echo "## $f" + tr -d '\r' < "$f" | awk '/^\[/ { in_features = ($0 == "[features]") } in_features' + tr -d '\r' < "$f" | grep -i skia || true + done | sha | cut -c1-40 + )" + files_hash="$( + git ls-files .cargo/config.toml Makefile 'packages/*/src-native/**/pre-build.sh' \ + '.github/actions/setup-*/action.yml' tools/scripts/build-napi.sh tools/scripts/build-linux.sh \ + tools/scripts/build-android.sh tools/scripts/build-svg-android.sh tools/scripts/android-build.sh \ + tools/scripts/build-ios.sh | xargs cat | sha | cut -c1-40 + )" { echo "SKIA_CACHE_REAL_CARGO=$real_cargo" echo "SKIA_CACHE_DIR=$tmp/skia-cache" echo "BUILD_ARTIFACTSTAGINGDIRECTORY=$tmp/skia-export" # bump to drop every Skia cache entry - echo "SKIA_CACHE_KEY=skia-v1-${{ inputs.name }}-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('Cargo.lock', 'Cargo.toml', 'crates/*/Cargo.toml', '.cargo/config.toml', 'Makefile', 'tools/scripts/*.sh', 'packages/*/src-native/**/pre-build.sh', '.github/actions/setup-*/action.yml') }}" + echo "SKIA_CACHE_KEY=skia-v2-${{ inputs.name }}-${{ runner.os }}-${{ runner.arch }}-$inputs_hash-$files_hash" } >> "$GITHUB_ENV" # Exact keys only: a partial match could lack a target, and a forced install that finds no diff --git a/.github/workflows/build-native.yml b/.github/workflows/build-native.yml index e258810c..9c7cb4a5 100644 --- a/.github/workflows/build-native.yml +++ b/.github/workflows/build-native.yml @@ -72,15 +72,16 @@ jobs: run: ./tools/scripts/build-android-v8-stub.sh - name: Build Rust libraries run: make android + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + with: + name: canvas-android + save: 'true' - name: Build canvas AAR working-directory: packages/canvas/src-native/canvas-android run: ./gradlew :canvas:assembleRelease - name: Copy AAR into platforms/android run: cp packages/canvas/src-native/canvas-android/canvas/build/outputs/aar/canvas-release.aar packages/canvas/platforms/android/canvas-release.aar - - uses: ./.github/actions/skia-cache - with: - name: canvas-android - save: 'true' - uses: actions/upload-artifact@v4 with: name: canvas-android @@ -109,15 +110,16 @@ jobs: - name: Build Rust libraries (Mac Catalyst) run: make catalyst # same as `npm run build.canvas.ios.framework.release`, without needing node_modules + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + with: + name: canvas-ios + save: 'true' - name: Build CanvasNative.xcframework working-directory: tools/scripts run: sh canvas-build.sh - name: Pack xcframework run: tar -czf canvas-ios.tar.gz -C packages/canvas/platforms/ios/CanvasNativeBinary CanvasNative.xcframework - - uses: ./.github/actions/skia-cache - with: - name: canvas-ios - save: 'true' - uses: actions/upload-artifact@v4 with: name: canvas-ios @@ -151,6 +153,12 @@ jobs: - name: Build Rust libraries if: steps.svg-aar.outputs.cache-hit != 'true' run: make android-svg + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + if: steps.svg-aar.outputs.cache-hit != 'true' + with: + name: canvas-svg-android + save: 'true' - name: Copy .so files into jniLibs if: steps.svg-aar.outputs.cache-hit != 'true' run: sh tools/scripts/copy-svg-android.sh @@ -161,11 +169,6 @@ jobs: - name: Copy AAR into platforms/android if: steps.svg-aar.outputs.cache-hit != 'true' run: cp packages/canvas-svg/src-native/android/canvassvg/build/outputs/aar/canvassvg-release.aar packages/canvas-svg/platforms/android/canvassvg-release.aar - - uses: ./.github/actions/skia-cache - if: steps.svg-aar.outputs.cache-hit != 'true' - with: - name: canvas-svg-android - save: 'true' - uses: actions/upload-artifact@v4 with: name: canvas-svg-android @@ -193,6 +196,12 @@ jobs: if: steps.svg-xcframework.outputs.cache-hit != 'true' run: make svg # same as `npm run build.canvas.svg.ios.framework.release` + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + if: steps.svg-xcframework.outputs.cache-hit != 'true' + with: + name: canvas-svg-ios + save: 'true' - name: Build CanvasSVG.xcframework if: steps.svg-xcframework.outputs.cache-hit != 'true' working-directory: tools/scripts @@ -200,11 +209,6 @@ jobs: - name: Pack xcframework if: steps.svg-xcframework.outputs.cache-hit != 'true' run: tar -czf canvas-svg-ios.tar.gz -C packages/canvas-svg/platforms/ios/CanvasSVGBinary CanvasSVG.xcframework - - uses: ./.github/actions/skia-cache - if: steps.svg-xcframework.outputs.cache-hit != 'true' - with: - name: canvas-svg-ios - save: 'true' - uses: actions/upload-artifact@v4 with: name: canvas-svg-ios @@ -321,15 +325,16 @@ jobs: run: ./tools/scripts/build-napi.sh ${{ matrix.target }} release-napi # arm64 is cross-compiled, so only x64 runs them. The runners have no GPU: D3D12 and ANGLE # fall back to WARP, and the WebGPU suites skip without an adapter. + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + with: + name: canvas-windows-${{ matrix.arch }} + save: 'true' - name: Node-API tests if: matrix.arch == 'x64' env: CANVAS_NAPI_ADDON: ${{ github.workspace }}\packages\canvas\platforms\windows\x64\canvasnative.node run: node --expose-gc --test "crates/canvas-napi/__test__/*.test.mjs" - - uses: ./.github/actions/skia-cache - with: - name: canvas-windows-${{ matrix.arch }} - save: 'true' - uses: actions/upload-artifact@v4 with: name: canvas-windows-${{ matrix.arch }} @@ -409,17 +414,18 @@ jobs: run: ./tools/scripts/build-napi.sh ${{ matrix.target }} release-napi ${{ matrix.module.crate }} # arm64 is cross-compiled, so only x64 runs them. The timeout bounds a test process that # hangs on exit (an addon releasing Media Foundation / D3D objects during process shutdown). + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + if: steps.cache.outputs.cache-hit != 'true' && matrix.module.skia + with: + name: ${{ matrix.module.name }}-windows-${{ matrix.arch }} + save: 'true' - name: Node-API tests if: steps.cache.outputs.cache-hit != 'true' && matrix.arch == 'x64' && matrix.module.addon-env timeout-minutes: 5 run: | export ${{ matrix.module.addon-env }}="$(cygpath -w "$PWD/$MODULE_DIR/${{ matrix.module.file }}")" node --expose-gc --test "crates/${{ matrix.module.crate }}/__test__/*.test.mjs" - - uses: ./.github/actions/skia-cache - if: steps.cache.outputs.cache-hit != 'true' && matrix.module.skia - with: - name: ${{ matrix.module.name }}-windows-${{ matrix.arch }} - save: 'true' - uses: actions/upload-artifact@v4 with: name: ${{ matrix.module.name }}-windows-${{ matrix.arch }} @@ -452,6 +458,11 @@ jobs: run: ./tools/scripts/build-linux.sh # Mesa's surfaceless EGL (llvmpipe) for 2D / WebGL and lavapipe for WebGPU: no GPU here. # canvas-media's suite drives Media Foundation, so it is Windows only. + # Saved right after the Rust build, so a later failing step cannot drop the cache. + - uses: ./.github/actions/skia-cache + with: + name: linux-${{ matrix.arch }} + save: 'true' - name: Node-API tests env: CANVAS_NAPI_ADDON: ${{ github.workspace }}/packages/canvas/platforms/linux/lib/${{ matrix.arch }}/canvasnative.node @@ -462,10 +473,6 @@ jobs: node --expose-gc --test "crates/canvas-napi/__test__/*.test.mjs" node --expose-gc --test "crates/canvas-svg-napi/__test__/*.test.mjs" node --expose-gc --test "crates/audio-context-napi/__test__/*.test.mjs" - - uses: ./.github/actions/skia-cache - with: - name: linux-${{ matrix.arch }} - save: 'true' - uses: actions/upload-artifact@v4 with: name: linux-${{ matrix.arch }}