forked from ctqvva/JugglucoNG
-
Notifications
You must be signed in to change notification settings - Fork 0
357 lines (307 loc) · 14.1 KB
/
Copy pathandroid-static-analysis.yml
File metadata and controls
357 lines (307 loc) · 14.1 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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
name: Static Analysis & Race Detection
on:
push:
branches: [glucodroid]
paths-ignore:
- '**.md'
- 'docs/**'
pull_request:
branches: [glucodroid]
paths-ignore:
- '**.md'
- 'docs/**'
workflow_dispatch:
schedule:
# Run nightly at 3 AM UTC (offset from UX QA at 2 AM)
- cron: '0 3 * * *'
env:
GRADLE_OPTS: -Xmx4g -XX:+HeapDumpOnOutOfMemoryError
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
jobs:
# ── 1. Android Lint ──────────────────────────────────────────────────────────
lint:
name: Android Lint (nogoogle release)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-
- name: Run lint
run: |
./gradlew :Common:lintMobileLibre3SiDexNogoogleRelease \
--no-daemon --warning-mode=all
# abortOnError is false in build.gradle so this always exits 0;
# we parse severity ourselves below.
- name: Summarise lint results
if: always()
run: |
XML=$(find Common/build/reports -name "lint-results-mobileLibre3SiDexNogoogleRelease.xml" | head -1)
if [[ -z "$XML" ]]; then
echo "No lint XML found." >> "$GITHUB_STEP_SUMMARY"
exit 0
fi
python3 - "$XML" << 'PYEOF'
import xml.etree.ElementTree as ET, sys, os, collections
tree = ET.parse(sys.argv[1])
issues = tree.getroot().findall("issue")
by_sev = collections.defaultdict(list)
for iss in issues:
by_sev[iss.get("severity","?")].append(iss)
summary = os.environ.get("GITHUB_STEP_SUMMARY","")
lines = ["## Android Lint Results\n"]
lines.append(f"| Severity | Count |")
lines.append(f"|---|---|")
for sev in ("Error","Warning","Information","Hint"):
if sev in by_sev:
lines.append(f"| {sev} | {len(by_sev[sev])} |")
lines.append("")
# Thread-safety focused categories
RACE_CATS = {"VisibleForTests","WrongThread","NotThreadSafe",
"SynchronizedOrAtomicFieldUpdater","ThreadSafe"}
race_hits = [i for i in issues if i.get("id","") in RACE_CATS]
if race_hits:
lines.append("### Thread-Safety Findings\n")
for iss in race_hits[:30]:
loc = iss.find("location")
f = os.path.basename(loc.get("file","?")) if loc is not None else "?"
l = loc.get("line","?") if loc is not None else "?"
msg = iss.get("message","")[:160]
sev = iss.get("severity","?")
lines.append(f"- **[{sev}]** `{f}:{l}` — {msg}")
if len(race_hits) > 30:
lines.append(f"- … and {len(race_hits)-30} more")
lines.append("")
# Top-10 error categories
errors = by_sev.get("Error",[])
if errors:
lines.append("### Errors (top categories)\n")
cat_counts = collections.Counter(i.get("category","?") for i in errors)
for cat, cnt in cat_counts.most_common(10):
lines.append(f"- **{cat}**: {cnt}")
lines.append("")
if summary:
with open(summary, "a") as fh:
fh.write("\n".join(lines) + "\n")
else:
print("\n".join(lines))
# Exit 1 if any Error-severity issues exist
if by_sev.get("Error"):
print(f"::error::{len(by_sev['Error'])} lint error(s) found — see report artifact")
sys.exit(1)
PYEOF
- name: Upload lint reports
if: always()
uses: actions/upload-artifact@v4
with:
name: lint-reports
path: |
Common/build/reports/lint-results-mobileLibre3SiDexNogoogleRelease.xml
Common/build/reports/lint-results-mobileLibre3SiDexNogoogleRelease.html
retention-days: 30
# ── 2. Race / Thread-Safety Detection ────────────────────────────────────────
race-detection:
name: Race & Thread-Safety Detection (API ${{ matrix.api-level }})
runs-on: ubuntu-latest
timeout-minutes: 45
strategy:
matrix:
api-level: [31, 34]
fail-fast: false
steps:
- uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '21'
- uses: actions/cache@v4
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-race-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: ${{ runner.os }}-gradle-
- 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
ls -l /dev/kvm
- name: Build debug + test APKs (nogoogle)
run: |
./gradlew \
:Common:assembleMobileLibre3SiDexNogoogleDebug \
:Common:assembleMobileLibre3SiDexNogoogleDebugAndroidTest \
--no-daemon --warning-mode=all \
-Pno_x86 -Pno_x86_64
- name: Run on emulator — capture thread violations
uses: reactivecircus/android-emulator-runner@v2
with:
api-level: ${{ matrix.api-level }}
arch: x86_64
profile: Nexus 6P
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
disk-size: 6000M
heap-size: 512M
script: bash .github/scripts/race-detection.sh
- name: Analyse logcat for race / thread violations
if: always()
run: |
python3 - << 'PYEOF'
import re, sys, os
logcat = open("logcat-full.log").read() if os.path.exists("logcat-full.log") else ""
instr = open("instrumentation.log").read() if os.path.exists("instrumentation.log") else ""
PATTERNS = {
"StrictMode violation": r"StrictMode policy violation",
"CalledFromWrongThread": r"CalledFromWrongThreadException",
"ConcurrentModification": r"ConcurrentModificationException",
"DeadObjectException": r"DeadObjectException",
"Main-thread IO": r"StrictMode.*disk (read|write)",
"ANR": r"Input dispatching timed out|ANR in",
"Looper not prepared": r"Can.t create handler inside thread.*Looper.prepare",
"IllegalStateException (thread)": r"IllegalStateException.*thread|thread.*IllegalStateException",
"Test failure": r"FAILURES!!|FAILED \(",
}
findings = {}
for label, pattern in PATTERNS.items():
hits = re.findall(f"(?m)^.*{pattern}.*$", logcat + "\n" + instr)
if hits:
findings[label] = hits
summary_path = os.environ.get("GITHUB_STEP_SUMMARY","")
lines = [f"## Race & Thread-Safety Scan — API ${{ matrix.api-level }}\n"]
if findings:
lines.append(f"**{len(findings)} category(ies) with findings:**\n")
for label, hits in findings.items():
lines.append(f"### {label} ({len(hits)} occurrence(s))")
for h in hits[:5]:
lines.append(f"```\n{h.strip()[:200]}\n```")
if len(hits) > 5:
lines.append(f"*… and {len(hits)-5} more — see logcat artifact*")
lines.append("")
else:
lines.append("No thread-safety violations or race indicators detected.\n")
# Note about StrictMode being commented out in Applic.java
lines.append("> **Note:** `StrictMode` is commented out in `Applic.java:792`.")
lines.append("> Uncommenting `startstrictmode()` would surface additional violations here.\n")
output = "\n".join(lines)
if summary_path:
with open(summary_path, "a") as fh:
fh.write(output + "\n")
print(output)
if findings:
critical = {k for k in findings if k not in ("Test failure",)}
if critical:
print(f"::warning::Thread-safety findings in {len(critical)} category(ies) — see step summary")
PYEOF
- name: Upload race-detection artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: race-detection-api${{ matrix.api-level }}
path: |
logcat-full.log
instrumentation.log
batterystats.txt
retention-days: 14
# ── 3. Redroid Kernel Probe (ARM64 runner) ───────────────────────────────────
# Checks whether GitHub's ubuntu-24.04-arm runners expose the binder and
# ashmem kernel modules required to run Redroid natively (no QEMU layer).
# This job never fails CI — it only reports findings so we know whether
# a Redroid-on-Actions path is viable.
redroid-kernel-probe:
name: Redroid Kernel Probe (ARM64)
runs-on: ubuntu-24.04-arm
timeout-minutes: 5
continue-on-error: true
steps:
- name: Check kernel modules and Docker capabilities
run: |
echo "## Redroid Kernel Probe — \$(uname -r) [\$(uname -m)]" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
check() {
local label="$1" result="$2" detail="$3"
if [[ "$result" == "ok" ]]; then
echo "| ✅ | $label | \`$detail\` |" >> "$GITHUB_STEP_SUMMARY"
else
echo "| ❌ | $label | $detail |" >> "$GITHUB_STEP_SUMMARY"
fi
}
echo "| | Check | Detail |" >> "$GITHUB_STEP_SUMMARY"
echo "|---|---|---|" >> "$GITHUB_STEP_SUMMARY"
# binder
binder=$(grep binder /proc/filesystems 2>/dev/null | awk '{print $2}')
[[ -n "$binder" ]] && check "binder in /proc/filesystems" ok "$binder" \
|| check "binder in /proc/filesystems" fail "not found"
# ashmem
ashmem=$(grep ashmem /proc/misc 2>/dev/null | awk '{print $2}')
[[ -n "$ashmem" ]] && check "ashmem in /proc/misc" ok "$ashmem" \
|| check "ashmem in /proc/misc" fail "not found"
# /dev/binder device node
[[ -e /dev/binder ]] \
&& check "/dev/binder device node" ok "$(ls -l /dev/binder)" \
|| check "/dev/binder device node" fail "not present"
# /dev/ashmem device node
[[ -e /dev/ashmem ]] \
&& check "/dev/ashmem device node" ok "$(ls -l /dev/ashmem)" \
|| check "/dev/ashmem device node" fail "not present"
# modprobe availability (can we load modules?)
modprobe_out=$(modprobe binder_linux 2>&1 || true)
[[ -z "$modprobe_out" || "$modprobe_out" == *"already loaded"* ]] \
&& check "modprobe binder_linux" ok "loaded or already present" \
|| check "modprobe binder_linux" fail "$modprobe_out"
# Docker privileged mode (required by Redroid)
docker_ok=$(docker run --rm --privileged alpine uname -m 2>/dev/null || echo "failed")
[[ "$docker_ok" == "aarch64" ]] \
&& check "Docker privileged containers" ok "aarch64 confirmed" \
|| check "Docker privileged containers" fail "$docker_ok"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Kernel:** \$(uname -r) " >> "$GITHUB_STEP_SUMMARY"
echo "**Arch:** \$(uname -m)" >> "$GITHUB_STEP_SUMMARY"
# Print raw details to the log for debugging
echo "=== /proc/filesystems (android-related) ==="
grep -E "binder|ashmem" /proc/filesystems /proc/misc 2>/dev/null || echo "(none)"
echo "=== /dev entries ==="
ls -l /dev/binder /dev/ashmem 2>/dev/null || echo "(none)"
echo "=== kernel modules ==="
lsmod 2>/dev/null | grep -E "binder|ashmem" || echo "(none loaded)"
# ── 4. Gate ───────────────────────────────────────────────────────────────────
analysis-gate:
name: Analysis Gate
runs-on: ubuntu-latest
needs: [lint, race-detection, redroid-kernel-probe]
if: always()
steps:
- name: Check results
run: |
lint="${{ needs.lint.result }}"
race="${{ needs.race-detection.result }}"
probe="${{ needs.redroid-kernel-probe.result }}"
echo "Lint: $lint"
echo "Race detection: $race"
echo "Redroid kernel probe: $probe (informational only)"
# Race detection uses ::warning:: not exit 1, so failures here are
# genuine infrastructure problems, not finding warnings.
# Probe is continue-on-error so it never blocks the gate.
for r in "$lint" "$race"; do
if [[ "$r" == "failure" ]]; then
echo "One or more analysis jobs failed — check artifacts and step summaries."
exit 1
fi
done
echo "All analysis jobs completed."