Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 160 additions & 0 deletions .github/actions/preview-emulator/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
name: Set up Android Emulator Preview
description: >
Installs the Android Emulator Preview package (emulators;latest), a
minor-versioned system image, and creates an AVD for it — plus the host
dependencies and console auth token the preview emulator needs on a bare
CI runner. Shared by the emulator-preview-* jobs so their setup cannot drift.

inputs:
api-level:
description: 'Minor-versioned platform, e.g. 37.0'
required: false
default: '37.0'
target:
description: 'System image target, e.g. google_apis_ps16k'
required: false
default: 'google_apis_ps16k'
abi:
description: 'System image ABI'
required: false
default: 'x86_64'
avd-name:
description: 'Name of the AVD to create'
required: false
default: 'test'
device:
description: 'Device profile passed to avdmanager'
required: false
default: 'pixel_6'
ram-size:
description: 'hw.ramSize in MB'
required: false
default: '4096'
cores:
description: 'hw.cpu.ncore'
required: false
default: '4'
disk-size:
description: 'disk.dataPartition.size'
required: false
default: '8192M'

runs:
using: composite
steps:
- name: Enable KVM
shell: bash
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

# The runner image ships an outdated avdmanager that writes target=android-0
# for minor-versioned packages (android-37.x); the emulator clamps that to
# API 3 and skips the Vulkan/GLDirectMem auto-enable the ps16k images need.
- name: Update SDK cmdline-tools
shell: bash
run: |
# Composite steps run with `bash -e -o pipefail`, unlike workflow `run:`
# steps (plain `bash -e`). Under pipefail `yes | sdkmanager` fails: yes
# takes SIGPIPE when sdkmanager exits. Turning pipefail off makes the
# pipeline report sdkmanager's status, so real failures still surface.
set +o pipefail
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --install "cmdline-tools;latest" > /dev/null
# sdkmanager won't overwrite the preinstalled dir; it installs to latest-2.
if [ -d "$SDK/cmdline-tools/latest-2" ]; then
rm -rf "$SDK/cmdline-tools/latest"
mv "$SDK/cmdline-tools/latest-2" "$SDK/cmdline-tools/latest"
fi
"$SDK/cmdline-tools/latest/bin/avdmanager" list device > /dev/null

# 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
shell: bash
run: |
set +o pipefail # see "Update SDK cmdline-tools" -- `yes |` vs pipefail
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
AVD='${{ inputs.avd-name }}'
PKG='system-images;android-${{ inputs.api-level }};${{ inputs.target }};${{ inputs.abi }}'
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install "$PKG" platform-tools > /dev/null
# 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" \
--abi '${{ inputs.target }}/${{ inputs.abi }}' --device '${{ inputs.device }}' \
--package "$PKG" 2>&1 || echo "avdmanager exit code: $?"
if [ ! -d "$HOME/.android/avd/$AVD.avd" ]; then
echo "avdmanager produced nothing; writing the AVD ini files by hand"
mkdir -p "$HOME/.android/avd/$AVD.avd"
printf 'avd.ini.encoding=UTF-8\npath=%s/.android/avd/%s.avd\npath.rel=avd/%s.avd\ntarget=android-%s\n' \
"$HOME" "$AVD" "$AVD" '${{ inputs.api-level }}' > "$HOME/.android/avd/$AVD.ini"
cat > "$HOME/.android/avd/$AVD.avd/config.ini" << CFG
AvdId=$AVD
avd.ini.displayname=$AVD
avd.ini.encoding=UTF-8
abi.type=${{ inputs.abi }}
hw.cpu.arch=${{ inputs.abi }}
image.sysdir.1=system-images/android-${{ inputs.api-level }}/${{ inputs.target }}/${{ inputs.abi }}/
tag.id=google_apis
tag.display=Google APIs
PlayStore.enabled=no
hw.lcd.density=420
hw.lcd.width=1080
hw.lcd.height=2400
hw.keyboard=yes
hw.gpu.enabled=yes
hw.gpu.mode=auto
disk.dataPartition.size=${{ inputs.disk-size }}
hw.ramSize=${{ inputs.ram-size }}
hw.cpu.ncore=${{ inputs.cores }}
CFG
fi
test -d "$HOME/.android/avd/$AVD.avd" || { echo "ERROR: AVD not created"; exit 1; }
# avdmanager defaults are too small for the ps16k image (the manual
# fallback already writes these keys).
grep -q '^hw.ramSize=' "$HOME/.android/avd/$AVD.avd/config.ini" || \
printf 'hw.ramSize=%s\nhw.cpu.ncore=%s\ndisk.dataPartition.size=%s\n' \
'${{ inputs.ram-size }}' '${{ inputs.cores }}' '${{ inputs.disk-size }}' \
>> "$HOME/.android/avd/$AVD.avd/config.ini"
echo "===== AVD root ini ====="; cat "$HOME/.android/avd/$AVD.ini"

- name: Install preview emulator
shell: bash
run: |
set +o pipefail # see "Update SDK cmdline-tools" -- `yes |` vs pipefail
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install 'emulators;latest' > /dev/null
echo "===== preview emulator version ====="
"$SDK/emulators/latest/emulator" -version | head -8 || true

# The preview package's qemu links libpulse.so.0 at load time.
- name: Install emulator host dependencies
shell: bash
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libpulse0

# The emulator console starts unauthenticated and then exposes only
# help/ping/auth/quit/avd -- 'kill' is not in that set, so 'adb emu kill'
# answers "KO: unknown command" and does nothing. The preview emulator
# currently READS $HOME/.emulator_console_auth_token but never creates it,
# and a fresh runner has no such file. Creating it here (both the emulator
# and adb read this same file, so the value is arbitrary) unlocks the full
# command set and makes 'adb emu kill' a real graceful shutdown.
#
# A fix has been proposed upstream so the emulator creates this file
# itself; this step already defers to it -- once a preview build ships
# that change, an existing token is left untouched and this becomes a
# no-op that can be deleted.
- name: Ensure emulator console auth token
shell: bash
run: |
TOKEN="$HOME/.emulator_console_auth_token"
if [ -s "$TOKEN" ]; then
echo "console auth token already present; leaving it untouched"
else
printf 'ciConsoleTokenForHeadlessRuns' > "$TOKEN"
chmod 600 "$TOKEN"
echo "created console auth token (workaround: preview emulator does not create one)"
fi
163 changes: 10 additions & 153 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -432,83 +432,10 @@ jobs:
echo "y" | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager \
"ndk;29.0.14206865" 2>&1 | tail -3

- 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

# The runner image ships an outdated avdmanager that writes target=android-0
# for minor-versioned packages (android-37.x); the emulator clamps that to
# API 3 and skips the Vulkan/GLDirectMem auto-enable the ps16k images need.
- name: Update SDK cmdline-tools
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --install "cmdline-tools;latest" > /dev/null
# sdkmanager won't overwrite the preinstalled dir; it installs to latest-2.
if [ -d "$SDK/cmdline-tools/latest-2" ]; then
rm -rf "$SDK/cmdline-tools/latest"
mv "$SDK/cmdline-tools/latest-2" "$SDK/cmdline-tools/latest"
fi
"$SDK/cmdline-tools/latest/bin/avdmanager" list device > /dev/null

# The AVD must be created BEFORE emulators;latest is installed: with the
# preview package's new-generation metadata in the SDK, avdmanager create
# silently produces nothing (exit 0) — same trap as the Android CLI
# packages.
- name: Install API 37.0 ps16k image and create AVD
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install 'system-images;android-37.0;google_apis_ps16k;x86_64' platform-tools > /dev/null
# 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 test --abi 'google_apis_ps16k/x86_64' --device 'pixel_6' --package 'system-images;android-37.0;google_apis_ps16k;x86_64' 2>&1 || echo "avdmanager exit code: $?"
if [ ! -d "$HOME/.android/avd/test.avd" ]; then
echo "avdmanager produced nothing; writing the AVD ini files by hand"
mkdir -p "$HOME/.android/avd/test.avd"
printf 'avd.ini.encoding=UTF-8\npath=%s/.android/avd/test.avd\npath.rel=avd/test.avd\ntarget=android-37.0\n' "$HOME" > "$HOME/.android/avd/test.ini"
cat > "$HOME/.android/avd/test.avd/config.ini" << 'CFG'
AvdId=test
avd.ini.displayname=test
avd.ini.encoding=UTF-8
abi.type=x86_64
hw.cpu.arch=x86_64
image.sysdir.1=system-images/android-37.0/google_apis_ps16k/x86_64/
tag.id=google_apis
tag.display=Google APIs
PlayStore.enabled=no
hw.lcd.density=420
hw.lcd.width=1080
hw.lcd.height=2400
hw.keyboard=yes
hw.gpu.enabled=yes
hw.gpu.mode=auto
disk.dataPartition.size=8192M
hw.ramSize=4096
hw.cpu.ncore=4
CFG
sed -i 's/^ //' "$HOME/.android/avd/test.avd/config.ini"
fi
test -d "$HOME/.android/avd/test.avd" || { echo "ERROR: AVD not created"; exit 1; }
# avdmanager defaults are too small for the ps16k image (the manual
# fallback already writes these keys).
grep -q '^hw.ramSize=' "$HOME/.android/avd/test.avd/config.ini" || printf 'hw.ramSize=4096\nhw.cpu.ncore=4\ndisk.dataPartition.size=8192M\n' >> "$HOME/.android/avd/test.avd/config.ini"
echo "===== AVD root ini ====="; cat "$HOME/.android/avd/test.ini"

- name: Install preview emulator
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install 'emulators;latest' > /dev/null
echo "===== preview emulator version ====="
"$SDK/emulators/latest/emulator" -version | head -8 || true

# The preview package's qemu links libpulse.so.0 at load time even with
# -noaudio (its command line already uses -audiodev none); the runner
# image doesn't ship it.
- name: Install emulator host dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libpulse0
# Shared setup: KVM, cmdline-tools, ps16k image + AVD, the preview
# emulator package, libpulse, and the console auth token.
- name: Set up Android Emulator Preview
uses: ./.github/actions/preview-emulator

- name: Boot preview emulator
timeout-minutes: 12
Expand Down Expand Up @@ -623,76 +550,16 @@ jobs:
echo "y" | ${ANDROID_HOME}/cmdline-tools/latest/bin/sdkmanager \
"ndk;29.0.14206865" 2>&1 | tail -3

- 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: Build debug APK
run: ./gradlew assembleDebug --no-daemon

# The runner image ships an outdated avdmanager that writes target=android-0
# for minor-versioned packages (android-37.x); the emulator clamps that to
# API 3 and skips the Vulkan/GLDirectMem auto-enable the ps16k images need.
- name: Update SDK cmdline-tools
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --install "cmdline-tools;latest" > /dev/null
# sdkmanager won't overwrite the preinstalled dir; it installs to latest-2.
if [ -d "$SDK/cmdline-tools/latest-2" ]; then
rm -rf "$SDK/cmdline-tools/latest"
mv "$SDK/cmdline-tools/latest-2" "$SDK/cmdline-tools/latest"
fi
"$SDK/cmdline-tools/latest/bin/avdmanager" list device > /dev/null

# The AVD must be created BEFORE emulators;latest is installed (its
# metadata makes avdmanager create silently no-op).
- name: Install API 37.0 ps16k image and create AVD
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install 'system-images;android-37.0;google_apis_ps16k;x86_64' platform-tools > /dev/null
echo no | "$SDK/cmdline-tools/latest/bin/avdmanager" create avd --force -n test --abi 'google_apis_ps16k/x86_64' --device 'pixel_6' --package 'system-images;android-37.0;google_apis_ps16k;x86_64' 2>&1 || echo "avdmanager exit code: $?"
if [ ! -d "$HOME/.android/avd/test.avd" ]; then
echo "avdmanager produced nothing; writing the AVD ini files by hand"
mkdir -p "$HOME/.android/avd/test.avd"
printf 'avd.ini.encoding=UTF-8\npath=%s/.android/avd/test.avd\npath.rel=avd/test.avd\ntarget=android-37.0\n' "$HOME" > "$HOME/.android/avd/test.ini"
cat > "$HOME/.android/avd/test.avd/config.ini" << 'CFG'
AvdId=test
avd.ini.displayname=test
avd.ini.encoding=UTF-8
abi.type=x86_64
hw.cpu.arch=x86_64
image.sysdir.1=system-images/android-37.0/google_apis_ps16k/x86_64/
tag.id=google_apis
tag.display=Google APIs
PlayStore.enabled=no
hw.lcd.density=420
hw.lcd.width=1080
hw.lcd.height=2400
hw.keyboard=yes
hw.gpu.enabled=yes
hw.gpu.mode=auto
disk.dataPartition.size=8192M
hw.ramSize=4096
hw.cpu.ncore=4
CFG
sed -i 's/^ //' "$HOME/.android/avd/test.avd/config.ini"
fi
test -d "$HOME/.android/avd/test.avd" || { echo "ERROR: AVD not created"; exit 1; }
grep -q '^hw.ramSize=' "$HOME/.android/avd/test.avd/config.ini" || printf 'hw.ramSize=4096\nhw.cpu.ncore=4\ndisk.dataPartition.size=8192M\n' >> "$HOME/.android/avd/test.avd/config.ini"

- name: Install preview emulator
run: |
SDK="${ANDROID_HOME:-${ANDROID_SDK_ROOT:-/usr/local/lib/android/sdk}}"
yes | "$SDK/cmdline-tools/latest/bin/sdkmanager" --channel=3 --install 'emulators;latest' > /dev/null
"$SDK/emulators/latest/emulator" -version | head -8 || true

# The preview package's qemu links libpulse.so.0 at load time.
- name: Install emulator host dependencies
run: |
sudo apt-get update -qq
sudo apt-get install -y -qq libpulse0
# Shared setup: KVM, cmdline-tools, ps16k image + AVD, the preview
# emulator package, libpulse, and the console auth token.
- name: Set up Android Emulator Preview
uses: ./.github/actions/preview-emulator

- name: Snapshot multi-run cycle
timeout-minutes: 25
Expand All @@ -703,18 +570,8 @@ jobs:
PKG=com.jpcottin.vulkanspaceinvaders
mkdir -p "${{ github.workspace }}/screenshots"

# The emulator console starts unauthenticated and then exposes only
# help/ping/auth/quit/avd -- 'kill' is not in that set, which is why
# 'adb emu kill' used to answer "KO: unknown command" and act as a
# no-op. The preview emulator READS $HOME/.emulator_console_auth_token
# but never creates it, and a fresh runner has no such file. Creating
# it before the first boot (both the emulator and adb read this same
# file, so the value is arbitrary) unlocks the full command set and
# makes 'adb emu kill' a real graceful shutdown: the emulator exits in
# ~2s having saved its snapshot, instead of being SIGTERMed minutes
# later while the game keeps playing.
printf 'ciConsoleTokenForHeadlessRuns' > "$HOME/.emulator_console_auth_token"
chmod 600 "$HOME/.emulator_console_auth_token"
# The console auth token that makes 'adb emu kill' work is created by
# the Set up Android Emulator Preview action; see it for the details.

SELF_PGID="$(ps -o pgid= -p $$ | tr -d ' ')"

Expand Down