Repository navigation
218 lines (213 loc) · 11.1 KB
/
Copy pathhealth-android.yml
File metadata and controls
218 lines (213 loc) · 11.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
---
name: Health (Health Connect) integration
# Scoped to the health feature. The load-bearing artifact here is
# CN1HealthConnectBridge.kt: it is a plugin *resource* compiled by the app's
# own Gradle build, so no job in this repository compiles it. Without this
# workflow a Kotlin syntax error, a wrong androidx.health.connect API or a
# record class that does not exist ships straight to customers.
#
# The health smoke source deliberately lives outside the Hello Codename One
# source root and is copied in here. Referencing com.codename1.health raises
# minSdkVersion to 26, and at 26 the platform supplies java.time instead of
# the desugared library -- which changes behaviour for unrelated tests. The
# shared conformance suite must keep running at the framework's normal floor.
'on':
pull_request:
paths:
# The application these jobs build compiles a Unity project, from an
# archive that unity-gallery-prebuilt.yml makes out of these.
- 'scripts/unity-compat-samples/gallery/**'
- 'maven/cil-translator/**'
- 'maven/unity-compat/**'
- 'maven/build-engine/src/main/java/com/codename1/maven/Unity*'
- 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*'
- 'scripts/unity-gallery-prebuilt.sh'
- '.github/workflows/unity-gallery-prebuilt.yml'
- '.github/actions/unity-gallery-prebuilt/**'
- '.github/workflows/health-android.yml'
- 'CodenameOne/src/com/codename1/health/**'
- 'CodenameOne/src/com/codename1/impl/health/**'
- 'Ports/Android/src/com/codename1/impl/android/AndroidHealth*.java'
- 'Ports/Android/src/com/codename1/impl/android/HealthConnectDelegate.java'
- 'Ports/Android/src/com/codename1/health/**'
- 'maven/build-engine/src/main/resources/com/codename1/builders/health/**'
- 'maven/build-engine/src/main/java/com/codename1/builders/HealthManifestFragments.java'
- 'maven/build-engine/src/main/java/com/codename1/builders/HealthListenerBindings.java'
- 'maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java'
- 'scripts/hellocodenameone/health-smoke/**'
push:
branches:
- master
paths:
# The application these jobs build compiles a Unity project, from an
# archive that unity-gallery-prebuilt.yml makes out of these.
- 'scripts/unity-compat-samples/gallery/**'
- 'maven/cil-translator/**'
- 'maven/unity-compat/**'
- 'maven/build-engine/src/main/java/com/codename1/maven/Unity*'
- 'maven/codenameone-maven-plugin/src/main/java/com/codename1/maven/*Unity*'
- 'scripts/unity-gallery-prebuilt.sh'
- '.github/workflows/unity-gallery-prebuilt.yml'
- '.github/actions/unity-gallery-prebuilt/**'
- '.github/workflows/health-android.yml'
- 'CodenameOne/src/com/codename1/health/**'
- 'CodenameOne/src/com/codename1/impl/health/**'
- 'Ports/Android/src/com/codename1/impl/android/AndroidHealth*.java'
- 'Ports/Android/src/com/codename1/impl/android/HealthConnectDelegate.java'
- 'Ports/Android/src/com/codename1/health/**'
- 'maven/build-engine/src/main/resources/com/codename1/builders/health/**'
- 'maven/build-engine/src/main/java/com/codename1/builders/HealthManifestFragments.java'
- 'maven/build-engine/src/main/java/com/codename1/builders/HealthListenerBindings.java'
- 'maven/build-engine/src/main/java/com/codename1/builders/AndroidGradleBuilder.java'
- 'scripts/hellocodenameone/health-smoke/**'
concurrency:
# Only the latest revision needs validation. PR numbers keep forks isolated;
# event names keep a manual/scheduled run from cancelling a push or PR run.
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
jobs:
# The application has a Unity project in it. One Linux job compiles it and
# hands the result to the jobs below as an artifact; none of them installs
# the .NET SDK or builds the Unity modules.
unity-gallery:
uses: ./.github/workflows/unity-gallery-prebuilt.yml
health-android:
needs: unity-gallery
name: Health Connect Android
permissions:
contents: read
runs-on: ubuntu-24.04
timeout-minutes: 60
steps:
- uses: actions/checkout@v6
- name: Set TMPDIR
run: echo "TMPDIR=${{ runner.temp }}" >> $GITHUB_ENV
- name: Cache codenameone-tools
uses: actions/cache@v5
with:
path: ${{ runner.temp }}/codenameone-tools
key: ${{ runner.os }}-cn1-tools-${{ hashFiles('scripts/setup-workspace.sh') }}
restore-keys: |
${{ runner.os }}-cn1-tools-
- name: Cache Maven repository
uses: actions/cache@v5
with:
path: ~/.m2/repository
key: ${{ runner.os }}-m2-health-android-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-m2-health-android-
${{ runner.os }}-m2-
- name: Cache Gradle
uses: actions/cache@v5
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-health-${{ hashFiles('scripts/hellocodenameone/**/build.gradle*', 'Ports/Android/build.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-health-
${{ runner.os }}-gradle-
- name: Setup workspace
run: ./scripts/setup-workspace.sh -q -DskipTests
- name: Build Android port
run: ./scripts/build-android-port.sh -q -DskipTests
# After everything that fills the local Maven repository and before the
# application is built; this job has no .NET SDK (see the action).
- name: Unpack the Unity gallery prebuilt
uses: ./.github/actions/unity-gallery-prebuilt
- name: Make the smoke app a health app
run: |
set -euo pipefail
dest=scripts/hellocodenameone/common/src/main/java/com/codenameone/examples/hellocodenameone
mkdir -p "$dest"
cp scripts/hellocodenameone/health-smoke/HealthSmokeDemo.java "$dest/"
# The android.health.* hints are already in the checked-in
# settings and stay inert until this file is copied in: the
# builder only demands them once the class scanner sees a
# com.codename1.health reference. They cannot be derived from
# bytecode -- the scanner never sees a field read, so
# HealthDataType.STEPS is invisible to it.
# This is the step that earns the workflow. build-android-app.sh runs
# gradlew assembleDebug, which is the only place
# CN1HealthConnectBridge.kt is ever compiled against a real
# androidx.health.connect artifact.
- name: Build Hello Codename One Android app (health-enabled)
id: build-android-app
run: |
mkdir -p ~/.codenameone
cp maven/UpdateCodenameOne.jar ~/.codenameone/
./scripts/build-android-app.sh -q -DskipTests
- name: Assert Health Connect wiring was injected
run: |
set -euo pipefail
dir="${{ steps.build-android-app.outputs.gradle_project_dir }}"
echo "Gradle project: $dir"
# Same pipefail/SIGPIPE hazard as the dexdump check below, and this
# one fires more readily: `grep -q .` matches the very first line, so
# find is almost always killed before it exits cleanly.
bridges="$(find "$dir" -name 'CN1HealthConnectBridge.kt')"
[ -n "$bridges" ] \
|| { echo "::error::Health Connect bridge not copied into project"; exit 1; }
grep -Rqs --include='build.gradle*' "androidx.health.connect:connect-client" "$dir" \
|| { echo "::error::connect-client dependency not injected"; exit 1; }
grep -Rqs --include=AndroidManifest.xml \
"android.permission.health.READ_STEPS" "$dir" \
|| { echo "::error::per-type read permission missing from manifest"; exit 1; }
grep -Rqs --include=AndroidManifest.xml \
"android.permission.health.WRITE_STEPS" "$dir" \
|| { echo "::error::per-type write permission missing from manifest"; exit 1; }
grep -Rqs --include=AndroidManifest.xml \
"com.google.android.apps.healthdata" "$dir" \
|| { echo "::error::Health Connect package query missing from manifest"; exit 1; }
# READ_HEART_RATE is a prefix of READ_HEART_RATE_VARIABILITY, so a
# substring match would pass even when dedup is broken. Match the
# closing quote to prove the exact permission was emitted.
grep -Rqs --include=AndroidManifest.xml \
'android.permission.health.READ_HEART_RATE"' "$dir" \
|| { echo "::error::exact READ_HEART_RATE permission missing"; exit 1; }
echo "Health Connect wiring present."
- name: Assert the bridge reached the dex
run: |
set -euo pipefail
dir="${{ steps.build-android-app.outputs.gradle_project_dir }}"
apk="$(find "$dir" -name '*debug*.apk' | head -1)"
[ -n "$apk" ] || { echo "::error::no debug APK produced"; exit 1; }
work="$(mktemp -d)"
unzip -qo "$apk" -d "$work"
dexdump="$(find "$ANDROID_HOME/build-tools" -name dexdump | sort | tail -1)"
found=0
for dex in "$work"/*.dex; do
# Captured before it is searched, not piped into grep. With pipefail
# set, `grep -q` exits at the first match and SIGPIPEs dexdump
# (status 141), and the pipeline status was then read as "class
# absent" -- so a dex that did contain the bridge failed the build.
# Whether it fired depended on where the descriptor sat in the dump:
# early enough to short-circuit grep and it failed, late enough that
# grep drained the output and it passed. Same fix as
# identity-stack.yml's bundle listing.
dump="$("$dexdump" -f "$dex" 2>/dev/null || true)"
# A here-string, not a pipe. Piping the captured dump into grep -q
# just moves the SIGPIPE onto whatever feeds it -- verified: grep
# short-circuits, the writer dies with 141, and pipefail reports the
# class as absent all over again. identity-stack.yml uses <<< for the
# same reason.
if grep -qF "Lcom/codename1/health/CN1HealthConnectBridge;" \
<<<"$dump"; then
found=1
break
fi
done
[ "$found" = 1 ] \
|| { echo "::error::bridge compiled but was shrunk out of the dex"; exit 1; }
echo "Bridge present in the dex."
- name: Upload build output
if: ${{ !cancelled() }}
uses: actions/upload-artifact@v7
with:
name: health-android-artifacts
path: |
${{ steps.build-android-app.outputs.gradle_project_dir }}/app/build/outputs/apk/debug/*.apk
${{ steps.build-android-app.outputs.gradle_project_dir }}/app/src/main/AndroidManifest.xml
${{ steps.build-android-app.outputs.gradle_project_dir }}/app/src/main/java/com/codename1/health/CN1HealthConnectBridge.kt
if-no-files-found: warn
retention-days: 14