mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-09-08 04:08:32 +09:00
637 lines
25 KiB
YAML
637 lines
25 KiB
YAML
name: MobileGL APK
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- dev
|
|
- Feat/Backend-Direct-GLES
|
|
- Feat/Backend-Direct-Vulkan
|
|
workflow_dispatch:
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
CCACHE_BASEDIR: ${{ github.workspace }}
|
|
CCACHE_COMPRESS: "true"
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
CCACHE_MAXSIZE: 4G
|
|
CCACHE_NOHASHDIR: "true"
|
|
MOBILEGL_CMAKE_COMPILER_LAUNCHER: ccache
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
with:
|
|
submodules: recursive
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
|
|
- name: Set up JDK
|
|
uses: actions/setup-java@v5
|
|
with:
|
|
distribution: zulu
|
|
java-version: '17'
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v6
|
|
with:
|
|
gradle-version: 8.10.2
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: .ccache
|
|
key: ${{ runner.os }}-apk-${{ github.job }}-ccache-${{ github.ref_name }}-${{ github.run_id }}
|
|
restore-keys: |
|
|
${{ runner.os }}-apk-${{ github.job }}-ccache-${{ github.ref_name }}-
|
|
${{ runner.os }}-apk-${{ github.job }}-ccache-
|
|
|
|
- name: Install ccache
|
|
run: |
|
|
sudo apt-get update
|
|
sudo apt-get install -y ccache
|
|
ccache --version
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Install Android NDK
|
|
run: |
|
|
sdkmanager "ndk;27.3.13750724"
|
|
echo "ndk.dir=$ANDROID_HOME/ndk/27.3.13750724" >> android-plugin/local.properties
|
|
|
|
- name: Update glslang external sources
|
|
working-directory: 3rdparty/glslang
|
|
run: python update_glslang_sources.py
|
|
|
|
- name: Build plugin APK
|
|
run: gradle --no-daemon -p android-plugin :app:assemblePluginRelease -Pmobilegl.apkSuffix="${GITHUB_SHA}" -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)"
|
|
env:
|
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
|
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
|
|
|
- name: Download ANGLE x86_64 libraries
|
|
run: |
|
|
angle_dir="android-plugin/app/src/trace/jniLibs/x86_64"
|
|
rm -rf "${angle_dir}"
|
|
mkdir -p "${angle_dir}"
|
|
|
|
package_angle_variant() {
|
|
variant="$1"
|
|
commit="$2"
|
|
egl_sha="$3"
|
|
gles_sha="$4"
|
|
source_dir="${RUNNER_TEMP}/mobilegl-angle-${variant}"
|
|
base="https://raw.githubusercontent.com/FCL-Team/FoldCraftLauncher/${commit}/FCLauncher/src/main/jniLibs/x86_64"
|
|
mkdir -p "${source_dir}"
|
|
curl -L --fail --retry 3 -o "${source_dir}/libEGL_angle.so" "${base}/libEGL_angle.so"
|
|
curl -L --fail --retry 3 -o "${source_dir}/libGLESv2_angle.so" "${base}/libGLESv2_angle.so"
|
|
echo "${egl_sha} ${source_dir}/libEGL_angle.so" | sha256sum -c -
|
|
echo "${gles_sha} ${source_dir}/libGLESv2_angle.so" | sha256sum -c -
|
|
for library in libEGL_angle libGLESv2_angle; do
|
|
filename="${library}_${variant}.so"
|
|
cp "${source_dir}/${library}.so" "${angle_dir}/${filename}"
|
|
done
|
|
}
|
|
|
|
package_angle_variant \
|
|
ec889e6ea831 \
|
|
f2a3d510dffd8f6540a52e1a7d0c5787d151075b \
|
|
c41828768d089899fa058ec0bee711a91be88347f29bdb935223da6be1149c40 \
|
|
e4f820d99f94365c66df868c7740fef142fe5c0cd7c941790a9e30638857ca4d
|
|
package_angle_variant \
|
|
90a62123d794 \
|
|
bdcc96ac11c79001018ae4375eb73cb54a9f682f \
|
|
d0f4298ccc770cc801fc52e21733521646161e8a4adb3bd0052d9a1b57ee0ca8 \
|
|
66fdc867e552192d553d59095ea2e3cef4829de65c356f1fd826027b1905972e
|
|
|
|
- name: Build retrace APK
|
|
run: gradle --no-daemon -p android-plugin :app:assembleTraceRelease -Pmobilegl.apkSuffix="${GITHUB_SHA}" -Pmobilegl.abis=all -Pmobilegl.debuggableRelease=true -Pmobilegl.logLevel=MOBILEGL_LOG_LEVEL_INFO --parallel --max-workers "$(nproc)"
|
|
env:
|
|
SIGNING_STORE_PASSWORD: ${{ secrets.SIGNING_STORE_PASSWORD }}
|
|
SIGNING_KEY_ALIAS: ${{ secrets.SIGNING_KEY_ALIAS }}
|
|
SIGNING_KEY_PASSWORD: ${{ secrets.SIGNING_KEY_PASSWORD }}
|
|
|
|
- name: Show ccache stats
|
|
if: always()
|
|
run: ccache --show-stats
|
|
|
|
- name: Verify APK metadata and packaging
|
|
run: |
|
|
AAPT2="$(find "$ANDROID_HOME/build-tools" -name aapt2 -type f | sort -V | tail -n 1)"
|
|
plugin_apk="android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${GITHUB_SHA}.apk"
|
|
trace_apk="android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk"
|
|
test -f "${plugin_apk}"
|
|
test -f "${trace_apk}"
|
|
bash .github/scripts/validate-plugin-apks.sh "$AAPT2" "$plugin_apk" "$trace_apk"
|
|
|
|
- name: Verify signed APKs
|
|
run: |
|
|
APKSIGNER="$(find "$ANDROID_HOME/build-tools" -name apksigner -type f | sort -V | tail -n 1)"
|
|
mapfile -t APKS < <(printf '%s\n' \
|
|
"android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${GITHUB_SHA}.apk" \
|
|
"android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk")
|
|
for APK in "${APKS[@]}"; do
|
|
if [[ ! -f "$APK" ]]; then
|
|
echo "::error::Expected release APK was not produced: $APK"
|
|
exit 1
|
|
fi
|
|
done
|
|
|
|
for APK in "${APKS[@]}"; do
|
|
if [[ "$APK" == *-unsigned.apk ]]; then
|
|
echo "::error::Unsigned release APK produced: $APK"
|
|
exit 1
|
|
fi
|
|
"$APKSIGNER" verify --verbose "$APK"
|
|
done
|
|
|
|
- name: Upload plugin APK
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-plugin-${{ env.date_today }}-${{ github.sha }}
|
|
path: android-plugin/app/build/outputs/apk/plugin/release/MobileGL-plugin-release-${{ github.sha }}.apk
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
- name: Upload retrace APK
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-retrace-apk-${{ env.date_today }}-${{ github.sha }}
|
|
path: android-plugin/app/build/outputs/apk/trace/release/MobileGL-plugin-trace-release-${{ github.sha }}.apk
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
trace-cases:
|
|
name: trace case matrix
|
|
runs-on: ubuntu-latest
|
|
needs: build
|
|
outputs:
|
|
android: ${{ steps.trace-cases.outputs.android }}
|
|
names: ${{ steps.trace-cases.outputs.names }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Load trace cases
|
|
id: trace-cases
|
|
run: |
|
|
echo "android=$(python3 tools/trace_replay/trace_cases.py --ci --format github-apk)" >> "$GITHUB_OUTPUT"
|
|
echo "names=$(python3 tools/trace_replay/trace_cases.py --ci --format names)" >> "$GITHUB_OUTPUT"
|
|
|
|
trace-fixtures:
|
|
name: trace fixture (${{ matrix.case }})
|
|
runs-on: ubuntu-latest
|
|
needs: trace-cases
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 4
|
|
matrix:
|
|
case: ${{ fromJSON(needs.trace-cases.outputs.names) }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Derive trace fixture cache key
|
|
id: fixture-key
|
|
run: bash .github/scripts/trace-fixture-cache.sh key '${{ matrix.case }}'
|
|
|
|
- name: Restore trace fixture cache
|
|
id: fixture-cache
|
|
if: steps.fixture-key.outputs.cacheable == 'true'
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ steps.fixture-key.outputs.paths }}
|
|
key: ${{ steps.fixture-key.outputs.key }}
|
|
|
|
- name: Verify restored trace fixture
|
|
id: fixture-verify
|
|
if: steps.fixture-cache.outputs.cache-hit == 'true'
|
|
run: |
|
|
if bash .github/scripts/trace-fixture-cache.sh verify '${{ matrix.case }}'; then
|
|
echo "ok=true" >> "$GITHUB_OUTPUT"
|
|
else
|
|
echo "ok=false" >> "$GITHUB_OUTPUT"
|
|
echo "::warning::Cached fixture for ${{ matrix.case }} failed verification; falling back to the download path"
|
|
bash .github/scripts/trace-fixture-cache.sh reset '${{ matrix.case }}'
|
|
fi
|
|
|
|
- name: Fetch trace fixture
|
|
if: steps.fixture-verify.outputs.ok != 'true'
|
|
run: bash .github/scripts/fetch-trace-fixture-lfs.sh '${{ matrix.case }}'
|
|
|
|
- name: Save trace fixture cache
|
|
if: steps.fixture-key.outputs.cacheable == 'true' && steps.fixture-cache.outputs.cache-hit != 'true'
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: ${{ steps.fixture-key.outputs.paths }}
|
|
key: ${{ steps.fixture-key.outputs.key }}
|
|
|
|
- name: Stage trace fixture
|
|
run: |
|
|
safe_case="$(printf '%s' '${{ matrix.case }}' | sed 's/[^A-Za-z0-9._-]/_/g')"
|
|
stage_dir="trace-fixtures/${safe_case}"
|
|
mkdir -p "${stage_dir}"
|
|
python3 tools/trace_replay/trace_cases.py --format fixture-files --case '${{ matrix.case }}' |
|
|
while IFS= read -r file; do
|
|
cp "${file}" "${stage_dir}/"
|
|
done
|
|
|
|
- name: Upload trace fixture
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-trace-fixture-${{ matrix.case }}
|
|
path: trace-fixtures/**
|
|
if-no-files-found: error
|
|
|
|
android-avd:
|
|
name: android avd image
|
|
runs-on: ubuntu-latest
|
|
env:
|
|
AVD_NAME: mobilegl-ci
|
|
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
|
|
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
|
|
ANDROID_SDK_ROOT: ${{ github.workspace }}/.android/sdk
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Restore Android AVD cache
|
|
id: android-avd-cache
|
|
uses: actions/cache@v5
|
|
with:
|
|
path: |
|
|
${{ env.ANDROID_AVD_HOME }}
|
|
${{ env.ANDROID_SDK_ROOT }}/emulator
|
|
${{ env.ANDROID_SDK_ROOT }}/platform-tools
|
|
${{ env.ANDROID_SDK_ROOT }}/platforms/android-35
|
|
${{ env.ANDROID_SDK_ROOT }}/system-images/android-35/google_apis/x86_64
|
|
key: ${{ runner.os }}-mobilegl-avd-api35-google_apis-x86_64-pixel_6-v2-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
- name: Create AVD
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh create \
|
|
--api-level 35 \
|
|
--target google_apis \
|
|
--arch x86_64 \
|
|
--profile pixel_6 \
|
|
--avd-name "${AVD_NAME}"
|
|
|
|
retrace:
|
|
name: retrace (${{ matrix.backend.name }}, ${{ matrix.case.name }})
|
|
runs-on: ubuntu-latest
|
|
needs:
|
|
- build
|
|
- android-avd
|
|
- trace-cases
|
|
- trace-fixtures
|
|
if: ${{ always() && needs.build.result == 'success' && needs.android-avd.result == 'success' && needs.trace-cases.result == 'success' }}
|
|
timeout-minutes: 75
|
|
env:
|
|
AVD_NAME: mobilegl-ci
|
|
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
|
|
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
|
|
ANDROID_SDK_ROOT: ${{ github.workspace }}/.android/sdk
|
|
strategy:
|
|
fail-fast: false
|
|
max-parallel: 4
|
|
matrix:
|
|
backend:
|
|
- name: DirectGLES
|
|
gpu: software
|
|
- name: DirectVulkan
|
|
gpu: lavapipe
|
|
case: ${{ fromJSON(needs.trace-cases.outputs.android) }}
|
|
steps:
|
|
- name: Set Swap Space
|
|
uses: pierotofy/set-swap-space@v1.0
|
|
with:
|
|
swap-size-gb: 8
|
|
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Download trace fixture
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: MobileGL-trace-fixture-${{ matrix.case.name }}
|
|
path: trace-fixture-download
|
|
|
|
- name: Install trace fixture
|
|
run: |
|
|
mkdir -p tools/trace_replay/fixtures
|
|
find trace-fixture-download -type f -exec cp {} tools/trace_replay/fixtures/ \;
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
echo "EMULATOR_LOG=${RUNNER_TEMP}/mobilegl-emulator.log" >> "$GITHUB_ENV"
|
|
echo "EMULATOR_PID_FILE=${RUNNER_TEMP}/mobilegl-emulator.pid" >> "$GITHUB_ENV"
|
|
|
|
- name: Setup Android SDK
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Restore Android AVD cache
|
|
id: android-avd-cache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: |
|
|
${{ env.ANDROID_AVD_HOME }}
|
|
${{ env.ANDROID_SDK_ROOT }}/emulator
|
|
${{ env.ANDROID_SDK_ROOT }}/platform-tools
|
|
${{ env.ANDROID_SDK_ROOT }}/platforms/android-35
|
|
${{ env.ANDROID_SDK_ROOT }}/system-images/android-35/google_apis/x86_64
|
|
key: ${{ runner.os }}-mobilegl-avd-api35-google_apis-x86_64-pixel_6-v2-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
- name: Download retrace APK
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
name: MobileGL-plugin-trace-release-${{ github.sha }}.apk
|
|
path: android-retrace-apks
|
|
|
|
- name: Enable KVM
|
|
run: |
|
|
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
|
|
sudo udevadm control --reload-rules
|
|
sudo udevadm trigger --name-match=kvm
|
|
|
|
- name: Create AVD
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh create \
|
|
--api-level 35 \
|
|
--target google_apis \
|
|
--arch x86_64 \
|
|
--profile pixel_6 \
|
|
--avd-name "${AVD_NAME}"
|
|
|
|
- name: Launch Emulator
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh start \
|
|
--avd-name "${AVD_NAME}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}" \
|
|
--boot-timeout 300
|
|
|
|
- name: Retrace and validate
|
|
env:
|
|
MOBILEGL_USE_ANGLE: ${{ matrix.backend.name == 'DirectGLES' && '1' || '0' }}
|
|
MOBILEGL_TRACE_ANGLE_VARIANT: ${{ matrix.case.name == 'minecraft-1.21.4-fabric-iris-bliss-in-world' && '90a62123d794' || 'ec889e6ea831' }}
|
|
MOBILEGL_MAGMA_R11G11B10F_FALLBACK: ${{ matrix.backend.name == 'DirectVulkan' && '1' || '0' }}
|
|
run: |
|
|
apk_file="android-retrace-apks/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk"
|
|
test -f "${apk_file}"
|
|
extra_retrace_args=()
|
|
# Bliss needs the newer signed ANGLE variant plus sampler mipmap
|
|
# min-filter downgrading on ANGLE llvmpipe.
|
|
if [ "${{ matrix.backend.name }}" = "DirectGLES" ] && [ "${{ matrix.case.name }}" = "minecraft-1.21.4-fabric-iris-bliss-in-world" ]; then
|
|
extra_retrace_args+=(--avoid-angle-llvmpipe-sampler-mipmap-min-filter)
|
|
fi
|
|
if [ "${{ matrix.case.coherent_as_flush || false }}" = "true" ]; then
|
|
extra_retrace_args+=(--coherent-as-flush)
|
|
fi
|
|
|
|
run_retrace() {
|
|
timeout "$(( ${{ matrix.case.timeout_seconds }} + 300 ))" sh android-plugin/trace-replay-ci.sh \
|
|
--apk-file "${apk_file}" \
|
|
--package top.mobilegl.plugin.trace \
|
|
--backend "${{ matrix.backend.name }}" \
|
|
--result-root android-retrace-result \
|
|
--fixture-root android-retrace-fixture \
|
|
--case "${{ matrix.case.name }}" \
|
|
--trace-archive "${{ matrix.case.trace_archive }}" \
|
|
--trace-file "${{ matrix.case.trace_file }}" \
|
|
--golden "${{ matrix.case.golden }}" \
|
|
--alternate-golden "${{ matrix.case.alternate_golden || '' }}" \
|
|
--target-call "${{ matrix.case.target_call }}" \
|
|
--width "${{ matrix.case.width }}" \
|
|
--height "${{ matrix.case.height }}" \
|
|
--ssim-threshold "${{ matrix.case.ssim_threshold || '0.99' }}" \
|
|
--crop-x "${{ matrix.case.crop_x }}" \
|
|
--crop-y "${{ matrix.case.crop_y }}" \
|
|
--crop-width "${{ matrix.case.crop_width }}" \
|
|
--crop-height "${{ matrix.case.crop_height }}" \
|
|
--timeout-seconds "${{ matrix.case.timeout_seconds }}" \
|
|
"${extra_retrace_args[@]}"
|
|
}
|
|
|
|
retrace_status=0
|
|
run_retrace || retrace_status=$?
|
|
if [ "${retrace_status}" -eq 75 ]; then
|
|
echo "::warning::Android emulator infrastructure failed; restarting it and retrying this retrace once."
|
|
# Surface-lost is retried rather than failed, so it would otherwise
|
|
# be invisible. Report it per job - a healthy run prints nothing and
|
|
# a rate spike shows up as a row per affected case.
|
|
reason_file="android-retrace-result/infrastructure-failure-reason.txt"
|
|
surface_lost_retries=0
|
|
if [ -f "${reason_file}" ]; then
|
|
surface_lost_retries="$(grep -c 'angle-surface-lost' "${reason_file}" || true)"
|
|
fi
|
|
if [ "${surface_lost_retries}" -gt 0 ]; then
|
|
echo "surface-lost retries: ${surface_lost_retries} (${{ matrix.backend.name }}, ${{ matrix.case.name }})" \
|
|
>> "${GITHUB_STEP_SUMMARY}"
|
|
fi
|
|
# The restart truncates EMULATOR_LOG, and the attempt that lost the
|
|
# emulator is the one worth reading - the retry usually only shows
|
|
# the wreckage. Keep the first attempt's log before it is clobbered.
|
|
if [ -f "${EMULATOR_LOG}" ]; then
|
|
cp "${EMULATOR_LOG}" "${EMULATOR_LOG}.first-attempt" || true
|
|
fi
|
|
sh android-plugin/run-avd-ci.sh stop \
|
|
--avd-name "${AVD_NAME}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}"
|
|
adb kill-server || true
|
|
sleep 2
|
|
sh android-plugin/run-avd-ci.sh start \
|
|
--avd-name "${AVD_NAME}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}" \
|
|
--boot-timeout 300
|
|
run_retrace
|
|
elif [ "${retrace_status}" -ne 0 ]; then
|
|
exit "${retrace_status}"
|
|
fi
|
|
|
|
- name: Collect retrace summary inputs
|
|
if: always()
|
|
run: |
|
|
safe_case="$(printf '%s' '${{ matrix.case.name }}' | sed 's/[^A-Za-z0-9._-]/_/g')"
|
|
result_dir="android-retrace-result/${safe_case}-${{ matrix.backend.name }}"
|
|
mkdir -p "${result_dir}"
|
|
if [ -s "${{ matrix.case.golden }}" ]; then
|
|
cp "${{ matrix.case.golden }}" "${result_dir}/${safe_case}-${{ matrix.backend.name }}-golden.png"
|
|
fi
|
|
if [ -n "${{ matrix.case.alternate_golden || '' }}" ] && [ -s "${{ matrix.case.alternate_golden || '' }}" ]; then
|
|
cp "${{ matrix.case.alternate_golden || '' }}" "${result_dir}/${safe_case}-${{ matrix.backend.name }}-alternate-golden.png"
|
|
fi
|
|
|
|
- name: Collect emulator diagnostics
|
|
if: always()
|
|
run: |
|
|
mkdir -p android-retrace-result/diagnostics
|
|
adb devices -l > android-retrace-result/diagnostics/adb-devices.txt || true
|
|
timeout 30 adb logcat -d -t 1000 > android-retrace-result/diagnostics/logcat.txt || true
|
|
if [ -f "${EMULATOR_LOG}" ]; then
|
|
cp "${EMULATOR_LOG}" android-retrace-result/diagnostics/emulator.log
|
|
fi
|
|
if [ -f "${EMULATOR_LOG}.first-attempt" ]; then
|
|
cp "${EMULATOR_LOG}.first-attempt" android-retrace-result/diagnostics/emulator-first-attempt.log
|
|
fi
|
|
# A vanished emulator looks identical whether the host OOM killer took
|
|
# qemu or the renderer faulted. These two say which.
|
|
free -h > android-retrace-result/diagnostics/host-memory.txt 2>&1 || true
|
|
sudo dmesg -T 2>/dev/null | tail -300 > android-retrace-result/diagnostics/host-dmesg.txt || true
|
|
|
|
- name: Stop Emulator
|
|
if: always()
|
|
run: |
|
|
sh android-plugin/run-avd-ci.sh stop \
|
|
--avd-name "${AVD_NAME}" \
|
|
--emulator-log "${EMULATOR_LOG}" \
|
|
--pid-file "${EMULATOR_PID_FILE}"
|
|
|
|
- name: Upload Android retrace result
|
|
if: always()
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-retrace-result-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-${{ matrix.case.name }}
|
|
path: android-retrace-result/**
|
|
if-no-files-found: warn
|
|
|
|
retrace-summary:
|
|
name: retrace summary
|
|
runs-on: ubuntu-latest
|
|
needs: retrace
|
|
if: always()
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set artifact metadata
|
|
run: |
|
|
echo "date_today=$(date +'%Y-%m-%d')" >> "$GITHUB_ENV"
|
|
|
|
- name: Set up Node.js
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Download Android retrace results
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: MobileGL-android-retrace-result-*
|
|
path: retrace-artifacts
|
|
|
|
- name: Render retrace summary
|
|
run: |
|
|
node tools/trace_replay/render_retrace_summary.mjs \
|
|
--input retrace-artifacts \
|
|
--output-dir android-retrace-summary \
|
|
--title "MobileGL Android retrace overview" \
|
|
--group-label "Android Emulator" \
|
|
--html mobilegl-android-retrace-overview.html
|
|
|
|
- name: Upload Android retrace summary
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
path: android-retrace-summary/mobilegl-android-retrace-overview.html
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
remove-artifact-clutter:
|
|
name: remove artifact clutter
|
|
runs-on: ubuntu-latest
|
|
needs: retrace-summary
|
|
if: always()
|
|
permissions:
|
|
actions: write
|
|
steps:
|
|
- name: Delete intermediate Android retrace artifacts
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
declare -A failed_cases=()
|
|
while IFS= read -r job_name; do
|
|
case_name="${job_name#retrace (*, }"
|
|
case_name="${case_name%)}"
|
|
failed_cases["${case_name}"]=1
|
|
done < <(
|
|
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/jobs?per_page=100" \
|
|
--jq '.jobs[] | select(.name | startswith("retrace (")) | select(.conclusion == "failure" or .conclusion == "cancelled" or .conclusion == "timed_out" or .conclusion == "action_required") | .name'
|
|
)
|
|
|
|
if ((${#failed_cases[@]})); then
|
|
echo "Retaining fixtures and results for failed retrace case(s):"
|
|
printf ' %s\n' "${!failed_cases[@]}"
|
|
else
|
|
echo "All retrace jobs succeeded; nothing needs to be retained."
|
|
fi
|
|
|
|
deleted=0
|
|
retained=0
|
|
while IFS=$'\t' read -r artifact_id artifact_name; do
|
|
keep=0
|
|
if [[ "${artifact_name}" == MobileGL-trace-fixture-* ]]; then
|
|
case_name="${artifact_name#MobileGL-trace-fixture-}"
|
|
if [[ -v "failed_cases[${case_name}]" ]]; then
|
|
keep=1
|
|
fi
|
|
elif [[ "${artifact_name}" == MobileGL-android-retrace-result-* ]]; then
|
|
# The result artifact carries mobilegl.log, retrace.log, logcat,
|
|
# the emulator log and the actual/diff images - the only record of
|
|
# why a retrace failed. Its name ends in -<backend>-<case>, so a
|
|
# suffix match on the case name keeps both backends' results for a
|
|
# case that failed on either of them, which is what a comparison
|
|
# needs. The match is anchored at the end, so a case name that is a
|
|
# prefix of a longer one does not retain the longer one's results.
|
|
for case_name in "${!failed_cases[@]}"; do
|
|
if [[ "${artifact_name}" == *-"${case_name}" ]]; then
|
|
keep=1
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if ((keep)); then
|
|
echo "Retaining ${artifact_name} (${artifact_id}) for failed retrace."
|
|
((retained += 1))
|
|
continue
|
|
fi
|
|
|
|
echo "Deleting ${artifact_name} (${artifact_id})"
|
|
gh api --method DELETE "repos/${GITHUB_REPOSITORY}/actions/artifacts/${artifact_id}"
|
|
((deleted += 1))
|
|
done < <(
|
|
gh api --paginate "repos/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}/artifacts?per_page=100" \
|
|
--jq '.artifacts[] | select(.name | startswith("MobileGL-trace-fixture-") or startswith("MobileGL-android-retrace-result-") or startswith("trace-fixture-") or startswith("retrace-result-")) | [.id, .name] | @tsv'
|
|
)
|
|
|
|
echo "Deleted ${deleted} intermediate Android artifact(s); retained ${retained} failed-retrace fixture(s)."
|