mirror of
https://github.com/MobileGL-Dev/MobileGL
synced 2026-11-16 06:23:23 +09:00
937 lines
43 KiB
YAML
937 lines
43 KiB
YAML
name: MobileGL APK
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- dev
|
|
- Feat/Backend-Direct-GLES
|
|
- Feat/Backend-Direct-Vulkan
|
|
# TEMPORARY, remove before merging the MGPipe work into dev: the disaggregation
|
|
# branch runs the full lane on every push so a phase's landing is not gated on
|
|
# someone remembering to dispatch the workflow by hand.
|
|
- feat/disaggregated
|
|
- codex/fix-github-ci
|
|
- cleanup
|
|
# A push that only touches these paths changes nothing this workflow builds or replays. The
|
|
# Test workflow keeps running for docs: it hosts the doc citation lint. The Android build
|
|
# compiles none of the unit, integration or benchmark trees, with one exception that keeps
|
|
# MobileGL/MG_Benchmark from being listed whole: MG_Util/SelfTest/DriverBenchJni.cpp
|
|
# includes MG_Benchmark/Driver/DriverBenchCases.inc.
|
|
paths-ignore:
|
|
- 'docs/**'
|
|
- '**/*.md'
|
|
- 'MobileGL/MG_Test/**'
|
|
- 'MobileGL/MG_IntegrationTest/**'
|
|
- 'MobileGL/MG_Benchmark/CMakeLists.txt'
|
|
- 'MobileGL/MG_Benchmark/SanityBench.cpp'
|
|
- 'MobileGL/MG_Benchmark/Buffer/**'
|
|
- 'MobileGL/MG_Benchmark/Container/**'
|
|
- 'MobileGL/MG_Benchmark/Program/**'
|
|
- 'MobileGL/MG_Benchmark/ShaderCache/**'
|
|
- 'MobileGL/MG_Benchmark/Transpile/**'
|
|
- 'tools/cts/**'
|
|
- 'tools/device_bench/**'
|
|
- '.github/workflows/test.yml'
|
|
- '.github/workflows/hygiene.yml'
|
|
- '.github/actions/setup-test-runner/**'
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
# Software-rendered CI submissions may exceed the interactive GPU hang budget.
|
|
MOBILEGL_GPU_HANG_BUDGET_MS: "0"
|
|
|
|
# ONE LIVE RUN PER BRANCH, as in test.yml: a superseded push is canceled rather than left queued
|
|
# behind its successor; a manual dispatch gets a group of its own; dev queues rather than cancels.
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event_name == 'workflow_dispatch' && github.run_id || github.ref }}
|
|
cancel-in-progress: ${{ github.event_name == 'push' && github.ref != 'refs/heads/dev' }}
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-26.04
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
env:
|
|
CCACHE_BASEDIR: ${{ github.workspace }}
|
|
CCACHE_COMPRESS: "true"
|
|
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
|
# One build's objects (three native configurations) take about 1 GB. The limit leaves
|
|
# half of that again for the objects a push replaces, and is what the cleanup before
|
|
# the save trims the entry to.
|
|
CCACHE_MAXSIZE: 1500M
|
|
CCACHE_NOHASHDIR: "true"
|
|
MOBILEGL_CMAKE_COMPILER_LAUNCHER: ccache
|
|
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'dev' || github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci' || github.ref_name == 'cleanup') && 'ON' || 'OFF' }}
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
# Only the submodules the two APKs are built from, shallow and in parallel, in place of a
|
|
# recursive checkout of every submodule in the tree: the ones the root CMakeLists.txt
|
|
# configures, and apitrace with the eight directories its thirdparty/CMakeLists.txt
|
|
# requires, for the trace APK. Each row names a submodule and one file the build reads
|
|
# from it, and a row whose file is missing afterwards fails the step: without
|
|
# 3rdparty/flatbuffers the root CMakeLists.txt would go on and build a library without
|
|
# MG_Remote instead of stopping. A submodule the build starts to use needs a row here.
|
|
- name: Check out build submodules
|
|
timeout-minutes: 15
|
|
run: |
|
|
top=(
|
|
"3rdparty/glslang CMakeLists.txt"
|
|
"3rdparty/SPIRV-Cross CMakeLists.txt"
|
|
"3rdparty/SPIRV-Reflect CMakeLists.txt"
|
|
"3rdparty/VulkanMemoryAllocator CMakeLists.txt"
|
|
"3rdparty/Vulkan-Headers CMakeLists.txt"
|
|
"3rdparty/Vulkan-Utility-Libraries CMakeLists.txt"
|
|
"3rdparty/xxHash build/cmake/CMakeLists.txt"
|
|
"3rdparty/asio include/asio.hpp"
|
|
"3rdparty/flatbuffers include/flatbuffers/flatbuffers.h"
|
|
"include/ska flat_hash_map.hpp"
|
|
"3rdparty/apitrace thirdparty/CMakeLists.txt"
|
|
)
|
|
apitrace=(
|
|
"thirdparty/brotli LICENSE"
|
|
"thirdparty/gtest LICENSE"
|
|
"thirdparty/libbacktrace LICENSE"
|
|
"thirdparty/libpng LICENSE"
|
|
"thirdparty/snappy COPYING"
|
|
"thirdparty/zlib README"
|
|
"thirdparty/zstd LICENSE"
|
|
"thirdparty/directxmath LICENSE"
|
|
)
|
|
# update <directory> <row>...: the update is idempotent, so a fetch that failed on
|
|
# the network is tried again, three times in all. Then every row's file must exist.
|
|
update() {
|
|
local dir="$1" paths=() row attempt updated=0 missing=0
|
|
shift
|
|
for row in "$@"; do
|
|
paths+=("${row%% *}")
|
|
done
|
|
for attempt in 1 2 3; do
|
|
if git -C "${dir}" submodule update --init --depth=1 --jobs 8 -- "${paths[@]}"; then
|
|
updated=1
|
|
break
|
|
fi
|
|
echo "the submodule update in ${dir} failed (attempt ${attempt} of 3)" >&2
|
|
sleep $((attempt * 5))
|
|
done
|
|
[ "${updated}" -eq 1 ] || return 1
|
|
for row in "$@"; do
|
|
if [ ! -f "${dir}/${row%% *}/${row#* }" ]; then
|
|
echo "::error::${dir}/${row%% *}/${row#* } is missing after the submodule update, so the APK build cannot use ${row%% *}"
|
|
missing=1
|
|
fi
|
|
done
|
|
return "${missing}"
|
|
}
|
|
update . "${top[@]}"
|
|
update 3rdparty/apitrace "${apitrace[@]}"
|
|
|
|
- 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: Android replay lifecycle regression
|
|
run: python3 tools/trace_replay/test_android_lifecycle.py
|
|
|
|
- name: Setup Gradle
|
|
uses: gradle/actions/setup-gradle@v6
|
|
with:
|
|
gradle-version: 8.10.2
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: .ccache
|
|
key: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
restore-keys: |
|
|
${{ runner.os }}-apk-${{ github.job }}-ccache-
|
|
|
|
- name: Install ccache
|
|
timeout-minutes: 15
|
|
run: |
|
|
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 update
|
|
sudo apt-get -o Acquire::Retries=3 -o Acquire::http::Timeout=30 install -y ccache
|
|
ccache --version
|
|
# The restored entry carries the counters of every earlier build; zero them so the
|
|
# statistics printed after the build are this run's.
|
|
ccache --zero-stats
|
|
|
|
- name: Setup Android SDK
|
|
id: netsetup84
|
|
continue-on-error: true
|
|
timeout-minutes: 15
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Setup Android SDK (retry)
|
|
timeout-minutes: 15
|
|
if: ${{ steps.netsetup84.outcome == 'failure' }}
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Accept Android SDK licenses
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- name: Install Android NDK
|
|
timeout-minutes: 30
|
|
run: |
|
|
for attempt in 1 2 3; do
|
|
sdkmanager "ndk;27.3.13750724" && break
|
|
[ "$attempt" = 3 ] && exit 1
|
|
echo "sdkmanager attempt $attempt failed; retrying"; sleep 20
|
|
done
|
|
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
|
|
-Pmobilegl.buildDisaggregated="${MOBILEGL_CI_DISAGGREGATED}"
|
|
-Pmobilegl.buildDisaggregatedInproc="${MOBILEGL_CI_DISAGGREGATED}"
|
|
--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
|
|
-Pmobilegl.buildDisaggregated="${MOBILEGL_CI_DISAGGREGATED}"
|
|
-Pmobilegl.buildDisaggregatedInproc="${MOBILEGL_CI_DISAGGREGATED}"
|
|
--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
|
|
|
|
# A build served from the cache stores nothing, so nothing would ever evict the objects
|
|
# of older source states. Trim to CCACHE_MAXSIZE, least recently used first, before the
|
|
# entry is saved; the objects this build hit were just touched and stay.
|
|
- name: Trim ccache to its size limit
|
|
run: |
|
|
ccache --cleanup
|
|
ccache --show-stats | grep -i 'cache size' || true
|
|
du -sh "${CCACHE_DIR}"
|
|
|
|
# Rewrite one rolling entry per job and branch. The upload carries the objects this
|
|
# build used plus what fits under the size limit, but Actions cache keys are
|
|
# immutable, so this branch's superseded blob is released first; --ref keeps the
|
|
# delete off other branches' entries. Running after the build means a failed build
|
|
# leaves the entry untouched.
|
|
- name: Release superseded ccache entry
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
CACHE_KEY: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
run: gh cache delete "${CACHE_KEY}" --ref "${GITHUB_REF}" || true
|
|
|
|
- name: Save ccache
|
|
continue-on-error: true
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: .ccache
|
|
key: ${{ runner.os }}-apk-${{ github.job }}-ccache-v1
|
|
|
|
- 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-26.04
|
|
env:
|
|
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'dev' || github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci' || github.ref_name == 'cleanup') && 'ON' || 'OFF' }}
|
|
# How much replay one emulator job aims for, in seconds as the duration hints count them.
|
|
# A (lane, transport, backend) cell is cut into as many shards as that takes, and its
|
|
# cases are spread so the shards of a cell come out even (tools/trace_replay/trace_cases.py,
|
|
# shard_matrix). A smaller value gives more and shorter jobs, each of which pays the
|
|
# emulator boot again; every case is replayed once per cell whatever the value.
|
|
RETRACE_SHARD_SECONDS: "720"
|
|
# The most a shard runner may spend on its cases. A shard gets the time in which every
|
|
# one of its cases could run to the end of its own time budget, up to this cap.
|
|
RETRACE_SHARD_DEADLINE_CAP_SECONDS: "7200"
|
|
outputs:
|
|
android: ${{ steps.trace-cases.outputs.android }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Load trace cases
|
|
id: trace-cases
|
|
run: |
|
|
python3 tools/trace_replay/trace_cases.py --self-test-transport-matrices
|
|
# The retrace jobs replay shards: several cases of one (lane, transport, backend) cell
|
|
# per emulator boot. The self-test proves every shard matrix replays exactly the rows
|
|
# of the per-case matrix it is built from, and trace_cases.py refuses to print one
|
|
# that does not.
|
|
python3 tools/trace_replay/trace_cases.py --self-test-shard-matrices
|
|
matrix_format=github-apk-shard-matrix
|
|
if [ "${MOBILEGL_CI_DISAGGREGATED}" = "ON" ]; then
|
|
matrix_format=github-apk-transport-shard-matrix
|
|
fi
|
|
android="$(python3 tools/trace_replay/trace_cases.py --ci --format "${matrix_format}" \
|
|
--shard-seconds "${RETRACE_SHARD_SECONDS}")"
|
|
# An empty `include` is not an error to GitHub: it skips the retrace job with no red.
|
|
shards="$(printf '%s' "${android}" | python3 -c 'import json, sys; print(len(json.load(sys.stdin)["include"]))')"
|
|
if [ "${shards}" -lt 1 ]; then
|
|
echo "::error::the Android retrace matrix is empty, so the retrace job would be skipped with no red"
|
|
exit 1
|
|
fi
|
|
# The cases of a shard share one job, so a case that hangs spends time the cases after
|
|
# it need. retrace_shard_budget.py gives every shard its own deadline_seconds, the
|
|
# time in which each of its cases could run to the end of its time budget, capped so
|
|
# that a shard whose replays all hang cannot hold a runner for hours, and the
|
|
# timeout_minutes of its job. The retrace job reads both from the matrix.
|
|
android="$(printf '%s' "${android}" | python3 android-plugin/retrace_shard_budget.py \
|
|
--cap-seconds "${RETRACE_SHARD_DEADLINE_CAP_SECONDS}")"
|
|
echo "android=${android}" >> "$GITHUB_OUTPUT"
|
|
echo "Android retrace: ${shards} shard(s) from ${matrix_format} at ${RETRACE_SHARD_SECONDS} s per shard"
|
|
|
|
# One cache entry holds every Git LFS fixture of the CI cases, and each retrace job restores
|
|
# that entry itself. This job only makes sure the entry exists. On a hit it downloads
|
|
# nothing. On a miss it starts from the newest older bundle, fetches from the mirror the
|
|
# cases whose pointers changed, saves the entry under the new key and confirms it is there,
|
|
# so that no retrace job has to download fixtures itself. The key, the path and the steps
|
|
# are .github/scripts/trace-fixture-bundle.sh's, and test.yml has the same job for the same
|
|
# entry: the two workflows of one push look the key up at the same time, and on a miss both
|
|
# fetch and both save. The save that loses is not an error, since the entry it wanted to
|
|
# write is the one the confirming step then finds. The timeout bounds a fixture download
|
|
# that stalls, which would otherwise hold every retrace job behind it.
|
|
trace-fixtures:
|
|
name: trace fixtures
|
|
runs-on: ubuntu-26.04
|
|
timeout-minutes: 30
|
|
permissions:
|
|
actions: read
|
|
contents: read
|
|
outputs:
|
|
key: ${{ steps.bundle.outputs.key }}
|
|
path: ${{ steps.bundle.outputs.path }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Derive the fixture bundle key
|
|
id: bundle
|
|
run: bash .github/scripts/trace-fixture-bundle.sh key
|
|
|
|
- name: Look up the fixture bundle
|
|
id: lookup
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ steps.bundle.outputs.path }}
|
|
key: ${{ steps.bundle.outputs.key }}
|
|
lookup-only: true
|
|
|
|
- name: Restore the newest older bundle
|
|
if: steps.lookup.outputs.cache-hit != 'true'
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ steps.bundle.outputs.path }}
|
|
key: ${{ steps.bundle.outputs.key }}
|
|
restore-keys: ${{ steps.bundle.outputs.restore-prefix }}
|
|
|
|
- name: Complete the fixture bundle
|
|
if: steps.lookup.outputs.cache-hit != 'true'
|
|
run: bash .github/scripts/trace-fixture-bundle.sh ensure
|
|
|
|
- name: Save the fixture bundle
|
|
if: steps.lookup.outputs.cache-hit != 'true'
|
|
continue-on-error: true
|
|
uses: actions/cache/save@v5
|
|
with:
|
|
path: ${{ steps.bundle.outputs.path }}
|
|
key: ${{ steps.bundle.outputs.key }}
|
|
|
|
# The Test workflow of the same push may be the one whose save won. Its entry is as good,
|
|
# once it is listed.
|
|
- name: Wait for the fixture bundle entry
|
|
if: steps.lookup.outputs.cache-hit != 'true'
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: bash .github/scripts/trace-fixture-bundle.sh wait '${{ steps.bundle.outputs.key }}'
|
|
|
|
- name: Confirm the fixture bundle entry
|
|
if: steps.lookup.outputs.cache-hit != 'true'
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ steps.bundle.outputs.path }}
|
|
key: ${{ steps.bundle.outputs.key }}
|
|
lookup-only: true
|
|
fail-on-cache-miss: true
|
|
|
|
android-avd:
|
|
name: android avd image
|
|
runs-on: ubuntu-26.04
|
|
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
|
|
|
|
# The emulator shard loop and the artifact cleanup, driven by fakes: what each case is
|
|
# run with, a verdict for every case, and what stays after a failure. It runs in this
|
|
# job because the retrace jobs wait for it and the APK build does not.
|
|
- name: Emulator shard runner regression
|
|
run: |
|
|
python3 android-plugin/test_trace_replay_shard.py
|
|
python3 android-plugin/test_retrace_artifact_cleanup.py
|
|
|
|
# This job only makes sure the cache entry exists; the retrace jobs restore it. A hit
|
|
# downloads nothing and skips every step below; a miss builds the AVD, and the post step
|
|
# of this action saves it.
|
|
- name: Look up Android AVD cache
|
|
id: android-avd-cache
|
|
uses: actions/cache@v5
|
|
with:
|
|
lookup-only: true
|
|
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-v4-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
- name: Setup Android SDK
|
|
id: netsetup336
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
continue-on-error: true
|
|
timeout-minutes: 15
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Setup Android SDK (retry)
|
|
timeout-minutes: 15
|
|
if: ${{ steps.netsetup336.outcome == 'failure' }}
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Accept Android SDK licenses
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- 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: Enable KVM
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
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
|
|
|
|
# Boot the new AVD once so the cached AVD is past its first-boot setup (measured 37 s first
|
|
# boot, 26 s for the next cold boot). A quick-boot snapshot was tried instead: it doubled the
|
|
# cache restore and tripled "device offline" retries, so retrace jobs cold-boot. If the
|
|
# pre-boot fails, the AVD is created afresh so the cache never holds a half-booted one.
|
|
- name: Pre-boot AVD
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: |
|
|
log="${RUNNER_TEMP}/avd-preboot.log"
|
|
pid="${RUNNER_TEMP}/avd-preboot.pid"
|
|
if ! sh android-plugin/run-avd-ci.sh preboot --avd-name "${AVD_NAME}" --gpu software \
|
|
--emulator-log "${log}" --pid-file "${pid}" --boot-timeout 300; then
|
|
echo "::warning::the AVD pre-boot failed; caching a never-booted AVD instead"
|
|
tail -50 "${log}" || true
|
|
sh android-plugin/run-avd-ci.sh stop --avd-name "${AVD_NAME}" --emulator-log "${log}" --pid-file "${pid}"
|
|
timeout 60 tail --pid="$(cat "${pid}")" -f /dev/null || true
|
|
sh android-plugin/run-avd-ci.sh create --api-level 35 --target google_apis --arch x86_64 \
|
|
--profile pixel_6 --avd-name "${AVD_NAME}"
|
|
fi
|
|
du -sh "${ANDROID_AVD_HOME}"
|
|
|
|
# One job replays a shard: some of the cases of one lane, transport and backend, one after
|
|
# another on one emulator boot (android-plugin/trace_replay_shard.py). The shard's step
|
|
# summary has a row per case, and a case that did not pass has an error annotation of its own.
|
|
retrace:
|
|
name: retrace (${{ matrix.backend.name }} / ${{ matrix.transport || 'monolith' }}, shard ${{ matrix.shard }}/${{ matrix.shards }})
|
|
runs-on: ubuntu-26.04
|
|
needs:
|
|
- build
|
|
- android-avd
|
|
- trace-cases
|
|
- trace-fixtures
|
|
if: ${{ !cancelled() && needs.build.result == 'success' && needs.android-avd.result == 'success' && needs.trace-cases.result == 'success' && needs.trace-fixtures.result == 'success' }}
|
|
# Paired with --deadline-seconds below, and far above what a shard takes when its replays
|
|
# end by themselves. Both come from the matrix. The deadline is the smaller of the time in
|
|
# which every case of the shard could run to the end of its own time budget
|
|
# (trace_replay_shard.py, needed_seconds) and RETRACE_SHARD_DEADLINE_CAP_SECONDS. With
|
|
# budgets of up to 1800 s a case the cap is what a shard of several cases gets: about
|
|
# three replays can hang to the end of their budgets before a later case is recorded as
|
|
# not-run. The minutes between the deadline and this limit are the setup before the
|
|
# replays and the uploads after them.
|
|
timeout-minutes: ${{ matrix.timeout_minutes }}
|
|
env:
|
|
MOBILEGL_CI_TRANSPORT: ${{ matrix.transport || 'monolith' }}
|
|
MOBILEGL_CI_LANE: ${{ matrix.lane || 'monolith-control' }}
|
|
AVD_NAME: mobilegl-ci
|
|
ANDROID_AVD_HOME: ${{ github.workspace }}/.android/avd
|
|
ANDROID_HOME: ${{ github.workspace }}/.android/sdk
|
|
ANDROID_SDK_ROOT: ${{ github.workspace }}/.android/sdk
|
|
# The shard's cases in run order, as the JSON array of case objects the shard runner
|
|
# takes: every field of the case in trace_cases.json, with the fixture paths in place.
|
|
MOBILEGL_SHARD_CASES: ${{ toJSON(matrix.cases) }}
|
|
# Set the repository variable to 1 to cold boot the emulator before every case of a
|
|
# shard, should one boot not survive a whole shard of software-rendered replays.
|
|
MOBILEGL_SHARD_RESTART_EMULATOR_EACH_CASE: ${{ vars.MOBILEGL_SHARD_RESTART_EMULATOR_EACH_CASE || '0' }}
|
|
# The emulator renders on the host with a software rasterizer that can keep compiled
|
|
# shaders on disk in the runner's home directory, where neither clearing the app's data
|
|
# nor restarting the emulator reaches. Off, so every case of a shard compiles its
|
|
# shaders as a job with a fresh machine does.
|
|
MESA_SHADER_CACHE_DISABLE: "true"
|
|
strategy:
|
|
fail-fast: false
|
|
# No max-parallel: the account's limit of 20 concurrent jobs, shared with Test, already
|
|
# bounds this matrix, and a cap below the number of shards only makes the round longer.
|
|
matrix: ${{ fromJSON(needs.trace-cases.outputs.android) }}
|
|
steps:
|
|
- name: Checkout repo
|
|
uses: actions/checkout@v6
|
|
|
|
# First, so the steps that run whatever happened (diagnostics, stopping the emulator, the
|
|
# result upload) have these even when a step before the emulator launch failed.
|
|
- 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: Set swap space
|
|
uses: ./.github/actions/set-swap-space
|
|
with:
|
|
swap-size-gb: 8
|
|
|
|
# A miss does not fail the job: the entry may have been evicted since trace-fixtures
|
|
# confirmed it (a re-run of failed jobs on an older run does not run that job again).
|
|
- name: Restore trace fixtures
|
|
uses: actions/cache/restore@v5
|
|
with:
|
|
path: ${{ needs.trace-fixtures.outputs.path }}
|
|
key: ${{ needs.trace-fixtures.outputs.key }}
|
|
|
|
# Moves this shard's fixtures into place and checks them against their Git LFS pointers.
|
|
# A case the restored bundle does not hold is downloaded here, with a warning annotation;
|
|
# the timeout bounds a download that stalls.
|
|
- name: Install trace fixtures
|
|
timeout-minutes: 15
|
|
run: bash .github/scripts/trace-fixture-bundle.sh install ${{ join(matrix.names, ' ') }}
|
|
|
|
- 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-v4-${{ hashFiles('android-plugin/run-avd-ci.sh') }}
|
|
|
|
# The cache holds everything a retrace job runs (emulator, adb, image, AVD); the SDK manager
|
|
# is only needed to create the AVD when the cache missed.
|
|
- name: Use the cached SDK tools
|
|
if: steps.android-avd-cache.outputs.cache-hit == 'true'
|
|
run: |
|
|
echo "${ANDROID_SDK_ROOT}/platform-tools" >> "$GITHUB_PATH"
|
|
echo "${ANDROID_SDK_ROOT}/emulator" >> "$GITHUB_PATH"
|
|
|
|
- name: Setup Android SDK
|
|
id: netsetup415
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
continue-on-error: true
|
|
timeout-minutes: 15
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Setup Android SDK (retry)
|
|
timeout-minutes: 15
|
|
if: ${{ steps.netsetup415.outcome == 'failure' }}
|
|
uses: android-actions/setup-android@v4
|
|
with:
|
|
accept-android-sdk-licenses: false
|
|
packages: platform-tools
|
|
cmdline-tools-version: 13114758
|
|
|
|
- name: Accept Android SDK licenses
|
|
if: steps.android-avd-cache.outputs.cache-hit != 'true'
|
|
run: yes | sdkmanager --licenses >/dev/null
|
|
|
|
- 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
|
|
|
|
# One emulator boot replays every case of the shard in order, each in a fresh app process
|
|
# with cleared app data and with the settings its case, backend and transport need. The
|
|
# runner keeps going after a failing case, on a cold boot so that case cannot touch the
|
|
# next one's verdict, retries a case once on a restarted emulator when the emulator
|
|
# itself failed, and exits non-zero when a case failed or a verdict is missing. The
|
|
# deadline stops it while the steps below can still upload what it has: the job timeout
|
|
# less the setup above and the uploads below. A case it stops or never starts is
|
|
# recorded as not-run, which is red.
|
|
- name: Retrace and validate
|
|
env:
|
|
# Lets the artifact cleanup tell which job a shard's verdicts came from.
|
|
MOBILEGL_SHARD_JOB_ID: ${{ job.check_run_id }}
|
|
run: |
|
|
sh android-plugin/trace-replay-shard.sh \
|
|
--apk-file "android-retrace-apks/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk" \
|
|
--backend "${{ matrix.backend.name }}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--transport "${MOBILEGL_CI_TRANSPORT}" \
|
|
--lane "${MOBILEGL_CI_LANE}" \
|
|
--cases-env MOBILEGL_SHARD_CASES \
|
|
--deadline-seconds "${{ matrix.deadline_seconds }}"
|
|
|
|
# Runs whatever happened above, without the device: every case of the shard gets its
|
|
# lane.json and golden copies for the summary, and a not-run verdict when the runner was
|
|
# killed or never started.
|
|
- name: Collect retrace summary inputs
|
|
if: always()
|
|
env:
|
|
MOBILEGL_SHARD_JOB_ID: ${{ job.check_run_id }}
|
|
run: |
|
|
sh android-plugin/trace-replay-shard.sh \
|
|
--apk-file "android-retrace-apks/MobileGL-plugin-trace-release-${GITHUB_SHA}.apk" \
|
|
--backend "${{ matrix.backend.name }}" \
|
|
--gpu "${{ matrix.backend.gpu }}" \
|
|
--transport "${MOBILEGL_CI_TRANSPORT}" \
|
|
--lane "${MOBILEGL_CI_LANE}" \
|
|
--cases-env MOBILEGL_SHARD_CASES \
|
|
--finalize
|
|
|
|
- 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
|
|
# What the guest was given; replay time inside the emulator depends on it.
|
|
timeout 30 adb shell 'nproc; grep -E "MemTotal|SwapTotal" /proc/meminfo' \
|
|
> android-retrace-result/diagnostics/guest-resources.txt 2>&1 || true
|
|
cp "${ANDROID_AVD_HOME}/${AVD_NAME}.avd/config.ini" android-retrace-result/diagnostics/avd-config.ini 2>/dev/null || true
|
|
# Expected to list nothing: the host rasterizer's on-disk shader cache is disabled.
|
|
du -sh "${XDG_CACHE_HOME:-${HOME}/.cache}"/mesa_shader_cache* \
|
|
> android-retrace-result/diagnostics/host-shader-cache.txt 2>&1 || true
|
|
# The log of the last boot. The shard runner keeps the log of every boot it ended
|
|
# as diagnostics/emulator-boot-<n>-*.log before a restart truncates it.
|
|
if [ -f "${EMULATOR_LOG}" ]; then
|
|
cp "${EMULATOR_LOG}" android-retrace-result/diagnostics/emulator.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
|
|
id: upload-retrace-result
|
|
if: always()
|
|
continue-on-error: true
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
# The summary job downloads by the lane and transport at the front of this name, and
|
|
# its renderer reads the backend and the case from the paths below it, so the name
|
|
# ends in the shard number and holds no case name.
|
|
name: MobileGL-android-retrace-result-${{ matrix.lane || 'monolith-control' }}-${{ matrix.transport || 'monolith' }}-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-shard${{ matrix.shard }}
|
|
path: android-retrace-result/**
|
|
if-no-files-found: warn
|
|
# A re-run of a failed job replaces the artifact its first attempt kept.
|
|
overwrite: true
|
|
|
|
# The artifact service sometimes times out (ETIMEDOUT on CreateArtifact, one job in 231 on
|
|
# 2026-10-18), which turned a passing case red; one delayed retry absorbs that.
|
|
- name: Wait before retrying the upload
|
|
if: always() && steps.upload-retrace-result.outcome == 'failure'
|
|
run: sleep 30
|
|
|
|
- name: Upload Android retrace result (retry)
|
|
if: always() && steps.upload-retrace-result.outcome == 'failure'
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-retrace-result-${{ matrix.lane || 'monolith-control' }}-${{ matrix.transport || 'monolith' }}-${{ env.date_today }}-${{ github.sha }}-${{ matrix.backend.name }}-shard${{ matrix.shard }}
|
|
path: android-retrace-result/**
|
|
if-no-files-found: warn
|
|
overwrite: true
|
|
|
|
retrace-summary:
|
|
name: retrace summary
|
|
runs-on: ubuntu-26.04
|
|
needs: retrace
|
|
# Also runs for a canceled run, where it only downloads the results and deletes the
|
|
# intermediate artifacts; the rendering steps are skipped then. The checkout and the
|
|
# downloads say so themselves, because a step with no condition is skipped once its job is
|
|
# canceled.
|
|
if: always()
|
|
permissions:
|
|
actions: write
|
|
contents: read
|
|
env:
|
|
MOBILEGL_CI_DISAGGREGATED: ${{ (github.ref_name == 'dev' || github.ref_name == 'feat/disaggregated' || github.ref_name == 'codex/fix-github-ci' || github.ref_name == 'cleanup') && 'ON' || 'OFF' }}
|
|
|
|
steps:
|
|
- name: Checkout repo
|
|
if: ${{ success() || cancelled() }}
|
|
uses: actions/checkout@v6
|
|
|
|
- name: Set up Node.js
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/setup-node@v7
|
|
with:
|
|
node-version: '22'
|
|
|
|
- name: Download Android monolith control results
|
|
if: ${{ success() || cancelled() }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: MobileGL-android-retrace-result-monolith-control-monolith-*
|
|
path: retrace-artifacts/monolith-control
|
|
|
|
- name: Download Android inproc acceptance results
|
|
if: ${{ (success() || cancelled()) && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: MobileGL-android-retrace-result-inproc-acceptance-inproc-*
|
|
path: retrace-artifacts/inproc-acceptance
|
|
|
|
- name: Download Android spawn acceptance results
|
|
if: ${{ (success() || cancelled()) && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
uses: actions/download-artifact@v8
|
|
with:
|
|
pattern: MobileGL-android-retrace-result-spawn-acceptance-spawn-*
|
|
path: retrace-artifacts/spawn-acceptance
|
|
|
|
- name: Render monolith control summary
|
|
if: ${{ !cancelled() }}
|
|
run: |
|
|
node tools/trace_replay/render_retrace_summary.mjs \
|
|
--input retrace-artifacts/monolith-control \
|
|
--output-dir android-retrace-summary/monolith-control \
|
|
--title "MobileGL Android monolith control" \
|
|
--group-label "Android Emulator / monolith control" \
|
|
--html mobilegl-android-monolith-control.html
|
|
|
|
- name: Render inproc acceptance summary
|
|
if: ${{ !cancelled() && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
run: |
|
|
node tools/trace_replay/render_retrace_summary.mjs \
|
|
--input retrace-artifacts/inproc-acceptance \
|
|
--output-dir android-retrace-summary/inproc-acceptance \
|
|
--title "MobileGL Android inproc acceptance" \
|
|
--group-label "Android Emulator / inproc acceptance" \
|
|
--html mobilegl-android-inproc-acceptance.html
|
|
|
|
- name: Render spawn acceptance summary
|
|
if: ${{ !cancelled() && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
run: |
|
|
node tools/trace_replay/render_retrace_summary.mjs \
|
|
--input retrace-artifacts/spawn-acceptance \
|
|
--output-dir android-retrace-summary/spawn-acceptance \
|
|
--title "MobileGL Android spawn acceptance" \
|
|
--group-label "Android Emulator / spawn acceptance" \
|
|
--html mobilegl-android-spawn-acceptance.html
|
|
|
|
- name: Upload Android monolith control summary
|
|
if: ${{ !cancelled() }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-monolith-control-summary-${{ github.sha }}
|
|
path: android-retrace-summary/monolith-control/mobilegl-android-monolith-control.html
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
- name: Upload Android inproc acceptance summary
|
|
if: ${{ !cancelled() && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-inproc-acceptance-summary-${{ github.sha }}
|
|
path: android-retrace-summary/inproc-acceptance/mobilegl-android-inproc-acceptance.html
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
- name: Upload Android spawn acceptance summary
|
|
if: ${{ !cancelled() && env.MOBILEGL_CI_DISAGGREGATED == 'ON' }}
|
|
uses: actions/upload-artifact@v7
|
|
with:
|
|
name: MobileGL-android-spawn-acceptance-summary-${{ github.sha }}
|
|
path: android-retrace-summary/spawn-acceptance/mobilegl-android-spawn-acceptance.html
|
|
archive: false
|
|
if-no-files-found: error
|
|
|
|
# The summaries embed every image they show, so the per-shard results are intermediates
|
|
# from here on. This is the last step of the workflow rather than a job of its own, which
|
|
# would wait for a runner and start from an empty machine. It keeps what a failure needs,
|
|
# read from the shard-status.json of the results downloaded above: the results of every
|
|
# shard that ran a case which did not pass somewhere, on all backends and transports, and
|
|
# every result when a job failed without saying which cases it ran. Deletes are paced
|
|
# below GitHub's limit for mutating requests, and a refused delete is a warning. Skipped
|
|
# when a summary step failed, so that re-running this job still finds the results, and
|
|
# when a retrace job did not succeed in a run that was not canceled: every result stays
|
|
# then, so a shard stopped by its job limit keeps its diagnostics and a re-run of the
|
|
# failed jobs renders complete summaries.
|
|
- name: Delete intermediate Android retrace artifacts
|
|
if: ${{ (success() && needs.retrace.result == 'success') || cancelled() }}
|
|
env:
|
|
GH_TOKEN: ${{ github.token }}
|
|
run: |
|
|
python3 android-plugin/retrace_artifact_cleanup.py \
|
|
--downloads retrace-artifacts/monolith-control \
|
|
--downloads retrace-artifacts/inproc-acceptance \
|
|
--downloads retrace-artifacts/spawn-acceptance
|