Repository navigation
148 lines (128 loc) · 5.68 KB
/
Copy pathandroid-ui.yml
File metadata and controls
148 lines (128 loc) · 5.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
name: Android UI Tests
on:
workflow_run:
workflows: ["Build"]
types: [completed]
branches: ["main", "copilot/**"]
# An emulator run against a superseded commit answers a question nobody is
# still asking, and these are the longest jobs in the pipeline.
concurrency:
group: ui-tests-${{ github.event.workflow_run.head_branch }}
cancel-in-progress: true
# Default deny at the workflow level; each job grants itself the minimum it
# needs. A job added later then starts with no token scopes rather than
# silently inheriting whatever the workflow happened to declare.
permissions: {}
jobs:
ui-tests:
name: Instrumented UI Tests
runs-on: ubuntu-24.04
permissions:
contents: read
# Download artifacts from the triggering workflow run.
actions: read
timeout-minutes: 60
# Only run if the build workflow succeeded
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- name: Checkout
uses: actions/checkout@v7
with:
ref: ${{ github.event.workflow_run.head_sha }}
fetch-depth: 0 # Full git history for version calculation
fetch-tags: true
# No step here talks to the remote, and every job runs project
# build code. The token is not left in `.git/config` for that
# code to find.
persist-credentials: false
- name: Enable KVM group perms
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: Set up JDK
uses: actions/setup-java@v5
with:
distribution: temurin
java-version: "21"
- name: Set up Android SDK
uses: android-actions/setup-android@v4
- name: Install Android components
run: |
sdkmanager "platform-tools" "platforms;android-37.0" "build-tools;37.0.0"
sdkmanager "system-images;android-34;google_apis;x86_64"
sdkmanager "emulator"
# Bounded input rather than `yes |`, which dies of SIGPIPE once sdkmanager
# stops reading and would fail this step the moment anyone adds
# `set -o pipefail` to it.
- name: Accept Android licenses
run: printf 'y\n%.0s' {1..100} | sdkmanager --licenses > /dev/null
# Reuse the native libraries built once by the Build workflow instead of
# re-running the expensive Android cross-compile here. Only the x86_64 ABI
# is needed by the emulator, but downloading all ABIs is harmless and keeps
# the artifact a single shared unit.
- name: Download shared native libraries
uses: actions/download-artifact@v8
with:
name: letterbox-jnilibs
path: app/src/main/jniLibs
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
# The emulator is x86_64, so that is the ABI whose absence would actually
# break the run. Asserted directly: the previous form ended each check in
# `find ... | head -1 || (echo ERROR; exit 1)`, and `find` exits 0 when it
# matches nothing, so the `||` never fired.
- name: Verify native libraries are present
run: |
set -euo pipefail
missing=0
for lib in letterbox_core letterbox_proxy; do
path="app/src/main/jniLibs/x86_64/lib$lib.so"
if [[ -f "$path" ]]; then
printf ' ok %s (%s bytes)\n' "$path" "$(stat -c%s "$path")"
else
echo "::error::missing $path"
missing=1
fi
done
exit "$missing"
- name: Set up Gradle
uses: gradle/actions/setup-gradle@v6
with:
# Never written from here. This workflow runs in the default branch's
# context (`github.ref` is `refs/heads/main`) but checks out the
# triggering run's head, which may be a `copilot/**` branch — so a ref
# check would call it trusted when it is not. It only ever needs to
# consume what `build.yml` compiled.
cache-read-only: true
- name: Run instrumented UI tests (Gradle Managed Devices)
run: |
./gradlew pixel7Api34StagingDebugAndroidTest \
--no-daemon \
-Pandroid.testoptions.manageddevices.emulator.gpu=swiftshader_indirect \
-Pandroid.experimental.testOptions.managedDevices.setupTimeoutMinutes=30 \
-Pandroid.experimental.testOptions.managedDevices.maxConcurrentDevices=1
- name: Collect debug artifacts (always)
if: always()
run: |
mkdir -p artifacts
# Restart adb server to prevent hangs (best-effort)
timeout 20s adb kill-server || true
timeout 20s adb start-server || true
# Capture device state if any device is still connected (with timeout to prevent hangs)
timeout 10s adb devices || true
timeout 30s adb logcat -d > artifacts/logcat.txt 2>&1 || true
timeout 10s adb exec-out screencap -p > artifacts/final-screen.png 2>&1 || true
# Collect Gradle test reports
cp -r app/build/reports artifacts/ 2>/dev/null || true
cp -r app/build/outputs/androidTest-results artifacts/ 2>/dev/null || true
cp -r app/build/outputs/managed_device_android_test_additional_output artifacts/ 2>/dev/null || true
echo "Artifacts collected:"
ls -la artifacts/ || true
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v7
with:
name: android-ui-test-artifacts
path: artifacts/
retention-days: 14