diff --git a/.github/actions/preview-emulator/action.yml b/.github/actions/preview-emulator/action.yml index f1918ae..825702a 100644 --- a/.github/actions/preview-emulator/action.yml +++ b/.github/actions/preview-emulator/action.yml @@ -47,26 +47,26 @@ runs: shell: bash run: | echo "path=${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}" >> "$GITHUB_OUTPUT" - # actions/cache never re-saves on an exact key hit, so a fixed key would - # freeze the image at whatever revision was current when it was first - # cached: sdkmanager would re-fetch each new revision on every run and - # the cache would never catch up, while CI quietly tested a stale image. - # Rotating weekly bounds that. The restore-keys prefix means a rotation - # restores last week's copy and lets sdkmanager fetch just the delta, - # rather than paying for a full download. - echo "week=$(date -u +%G-W%V)" >> "$GITHUB_OUTPUT" # The system image is the one large download here (system.img alone is # ~4.1 GiB, ~2 GiB compressed). The preview emulator package is # deliberately NOT cached: it moves often and pinning a stale build would - # defeat the purpose of these jobs. sdkmanager still runs after a cache - # hit, so a newer image revision is still picked up. + # defeat the purpose of these jobs. + # + # Restore and save are split so the entry can be keyed on the image + # revision, which is only knowable after sdkmanager has run. A single + # actions/cache with a fixed key never re-saves on a hit, so it would pin + # whatever revision was cached first and re-download every subsequent + # revision on every run, forever. Here: restore whatever copy exists, + # let sdkmanager bring it up to date, then save under the revision that + # actually ended up on disk. A new revision lands in the cache on the + # first run that sees it. - name: Restore cached system image - id: sysimg-cache - uses: actions/cache@v4 + id: sysimg-restore + uses: actions/cache/restore@v4 with: path: ${{ steps.sdk.outputs.path }}/system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.abi }} - key: sysimg-v1-android-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.abi }}-${{ steps.sdk.outputs.week }} + key: sysimg-v1-android-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.abi }}-rev restore-keys: | sysimg-v1-android-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.abi }} @@ -100,6 +100,7 @@ runs: # The AVD must be created BEFORE emulators;latest is installed: with the # preview package present, avdmanager create silently no-ops. - name: Install system image and create AVD + id: sysimg shell: bash run: | set +o pipefail # see "Update SDK cmdline-tools" -- `yes |` vs pipefail @@ -108,11 +109,17 @@ runs: PKG='system-images;android-${{ inputs.api-level }};${{ inputs.target }};${{ inputs.abi }}' # Timed so the value of caching the image can be read straight off the # job log rather than inferred. - echo "SYSIMG-CACHE hit=${{ steps.sysimg-cache.outputs.cache-hit || 'false' }}" + echo "SYSIMG-CACHE restored-from='${{ steps.sysimg-restore.outputs.cache-matched-key }}'" T0=$(date +%s) yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install "$PKG" platform-tools > /dev/null echo "SYSIMG-INSTALL seconds=$(( $(date +%s) - T0 ))" - du -sh "$SDK/system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.abi }}" 2>/dev/null | sed 's/^/SYSIMG-SIZE /' || true + IMGDIR="$SDK/system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.abi }}" + du -sh "$IMGDIR" 2>/dev/null | sed 's/^/SYSIMG-SIZE /' || true + # Key the cache on the revision that actually ended up on disk. + REV="$(sed -n 's/^Pkg\.Revision=//p' "$IMGDIR/source.properties" 2>/dev/null | head -1 | tr -d ' \r')" + [ -n "$REV" ] || REV=unknown + echo "SYSIMG-REVISION $REV" + echo "revision=$REV" >> "$GITHUB_OUTPUT" # Mimic the emulator-runner action's invocation (--abi and --device # skip the auto-select/prompt path where bare creates silently no-op). echo no | "$SDK/cmdline-tools/latest/bin/avdmanager" create avd --force -n "$AVD" \ @@ -153,6 +160,15 @@ runs: >> "$HOME/.android/avd/$AVD.avd/config.ini" echo "===== AVD root ini ====="; cat "$HOME/.android/avd/$AVD.ini" + # Only writes when the restored entry is not already this revision, so a + # steady state costs nothing and a new revision is picked up once. + - name: Save system image to cache + if: steps.sysimg-restore.outputs.cache-matched-key != format('sysimg-v1-android-{0}-{1}-{2}-rev{3}', inputs.api-level, inputs.target, inputs.abi, steps.sysimg.outputs.revision) + uses: actions/cache/save@v4 + with: + path: ${{ steps.sdk.outputs.path }}/system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.abi }} + key: sysimg-v1-android-${{ inputs.api-level }}-${{ inputs.target }}-${{ inputs.abi }}-rev${{ steps.sysimg.outputs.revision }} + - name: Install preview emulator shell: bash run: |