Skip to content

Commit 1a378bd

Browse files
committed
Merge remote-tracking branch 'origin/master' into codex/4480-stryd-steps-fix
# Conflicts: # src/qzsettings.cpp # src/settings-catalog.json # src/settings.qml
2 parents b130082 + 592c28e commit 1a378bd

160 files changed

Lines changed: 14161 additions & 344 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/main.yml

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -599,6 +599,20 @@ jobs:
599599
dir: '${{ github.workspace }}/output/android/'
600600
cache: 'false'
601601

602+
- name: Install custom 16K-page-size Qt (overlay onto stock Qt install)
603+
run: |
604+
QT_DIR="${{ github.workspace }}/output/android/Qt/5.15.0/android"
605+
curl -fsSL -o /tmp/qt5.15.0-android-page16.zip https://github.com/cagnulein/qt-5.15.0-android-page16k/releases/download/1.0.0/qt5.15.0-android-page16.zip
606+
unzip -q /tmp/qt5.15.0-android-page16.zip -d /tmp/qt-page16k
607+
# Overlay the 16K-page-aligned target libs/plugins/qml/mkspecs/jar/include onto the
608+
# stock Qt install; keep the stock Linux host tools (bin/) since the custom Qt's
609+
# host tools were built on macOS (Mach-O, not usable on this Linux runner).
610+
rsync -a --exclude='bin/' "/tmp/qt-page16k/qt5.15.0-android-page16/usr/local/Qt-5.15.0/" "$QT_DIR/"
611+
# qdevice.pri comes from the overlay carrying the macOS origin's NDK host value;
612+
# fix it back to linux so qmake resolves the NDK toolchain path correctly.
613+
sed -i 's/darwin-x86_64/linux-x86_64/' "$QT_DIR/mkspecs/qdevice.pri"
614+
rm -rf /tmp/qt5.15.0-android-page16.zip /tmp/qt-page16k
615+
602616
- name: Install Java
603617
uses: actions/setup-java@v4
604618
with:
@@ -615,13 +629,13 @@ jobs:
615629
id: cache-ndk
616630
uses: actions/cache@v4
617631
with:
618-
path: /usr/local/lib/android/sdk/ndk/21.4.7075529
619-
key: android-ndk-21.4.7075529-${{ runner.os }}
632+
path: /usr/local/lib/android/sdk/ndk/21.1.6352462
633+
key: android-ndk-21.1.6352462-${{ runner.os }}
620634

621635
- name: Install Android SDK packages
622636
run: |
623637
SDKMANAGER="/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager"
624-
echo "y" | $SDKMANAGER "ndk;21.4.7075529" "platforms;android-35" "build-tools;35.0.0"
638+
echo "y" | $SDKMANAGER "ndk;21.1.6352462" "platforms;android-35" "build-tools;35.0.0"
625639
626640
- name: Setup ccache
627641
run: |
@@ -638,13 +652,13 @@ jobs:
638652
uses: actions/cache@v4
639653
with:
640654
path: .ccache
641-
key: ccache-android-ndk21-${{ hashFiles('src/**/*.cpp', 'src/**/*.h', 'src/*.pro') }}
655+
key: ccache-android-ndk21_1-${{ hashFiles('src/**/*.cpp', 'src/**/*.h', 'src/*.pro') }}
642656
restore-keys: |
643-
ccache-android-ndk21-
657+
ccache-android-ndk21_1-
644658
645659
- name: Setup NDK environment with ccache
646660
run: |
647-
NDK_PATH=/usr/local/lib/android/sdk/ndk/21.4.7075529
661+
NDK_PATH=/usr/local/lib/android/sdk/ndk/21.1.6352462
648662
NDK_BIN=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin
649663
ln -sfn $NDK_PATH /usr/local/lib/android/sdk/ndk-bundle
650664
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
@@ -711,8 +725,12 @@ jobs:
711725
- name: Build QZ for Android (4 ABIs)
712726
run: |
713727
lrelease src/qdomyos-zwift.pri
714-
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.4.7075529' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
715-
sed -i '1s|{|{\n "android-extra-libs": "${{ github.workspace }}/android_openssl/no-asm/latest/arm/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/arm64/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86/libssl_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libcrypto_1_1.so,${{ github.workspace }}/android_openssl/no-asm/latest/x86_64/libssl_1_1.so",|' src/android-qdomyos-zwift-deployment-settings.json
728+
# CONFIG+=debug matches the real reference build validated against the Mac-built
729+
# APK: it keeps .so unstripped (matching the reference almost byte-for-byte) and,
730+
# with this custom Qt, naturally leaves android-extra-libs unset - the bundled
731+
# android_openssl libs are not 16K-page-aligned, so we must NOT inject them (see
732+
# below); Qt's own QtNetwork already has openssl linked in.
733+
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.1.6352462' 'CONFIG+=debug' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
716734
cat src/android-qdomyos-zwift-deployment-settings.json
717735
718736
- name: Cache Gradle
@@ -2304,6 +2322,11 @@ jobs:
23042322
sed -i 's/property string ${{ matrix.variant.setting_key }}: ""/property string ${{ matrix.variant.setting_key }}: "${{ matrix.variant.setting_value }}"/' settings.qml
23052323
cd ..
23062324
2325+
# Keep FitPro APKs usable on older 32-bit iFIT/ProForm consoles.
2326+
# qmake reads defaults.pri while generating the Android deployment JSON,
2327+
# so force all ABIs there before the build starts.
2328+
sed -i 's/^ANDROID_ABIS = .*/ANDROID_ABIS = armeabi-v7a arm64-v8a x86 x86_64/' defaults.pri
2329+
23072330
ln -sfn $ANDROID_SDK_ROOT/ndk/21.4.7075529 $ANDROID_NDK
23082331
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
23092332
lrelease src/qdomyos-zwift.pri
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
name: Release Android to Play Store (internal testing)
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
publish:
7+
description: 'Actually upload to Play Console internal track (unchecked = build + sign only, nothing leaves this workflow run)'
8+
type: boolean
9+
required: true
10+
default: false
11+
12+
concurrency:
13+
group: release-playstore
14+
cancel-in-progress: false
15+
16+
jobs:
17+
release-android:
18+
if: github.ref == 'refs/heads/master'
19+
runs-on: ubuntu-22.04
20+
21+
steps:
22+
- name: Checkout repository
23+
uses: actions/checkout@v3
24+
with:
25+
token: ${{ secrets.GITHUB_TOKEN }}
26+
submodules: 'false'
27+
28+
- name: Checkout submodules with specific branches
29+
run: |
30+
git submodule init
31+
git submodule update --init --recursive
32+
33+
- name: Install packages required to run QZ inside workflow
34+
run: sudo apt update -y && sudo apt-get install -y qtbase5-dev qtchooser qt5-qmake qtbase5-dev-tools qttools5-dev-tools qtquickcontrols2-5-dev libqt5bluetooth5 libqt5widgets5 libqt5positioning5 libqt5xml5 qtconnectivity5-dev qtpositioning5-dev libqt5charts5-dev libqt5charts5 libqt5networkauth5-dev libqt5websockets5* libxcb-randr0-dev libxcb-xtest0-dev libxcb-xinerama0-dev libxcb-shape0-dev libxcb-xkb-dev
35+
36+
- name: Install Qt Android
37+
uses: jdpurcell/install-qt-action@v5
38+
with:
39+
version: '5.15.0'
40+
host: 'linux'
41+
target: 'android'
42+
arch: 'android'
43+
modules: 'qtcharts qtnetworkauth qtpurchasing'
44+
dir: '${{ github.workspace }}/output/android/'
45+
cache: 'false'
46+
47+
- name: Install custom 16K-page-size Qt (overlay onto stock Qt install)
48+
run: |
49+
QT_DIR="${{ github.workspace }}/output/android/Qt/5.15.0/android"
50+
curl -fsSL -o /tmp/qt5.15.0-android-page16.zip https://github.com/cagnulein/qt-5.15.0-android-page16k/releases/download/1.0.0/qt5.15.0-android-page16.zip
51+
unzip -q /tmp/qt5.15.0-android-page16.zip -d /tmp/qt-page16k
52+
# Overlay the 16K-page-aligned target libs/plugins/qml/mkspecs/jar/include onto the
53+
# stock Qt install; keep the stock Linux host tools (bin/) since the custom Qt's
54+
# host tools were built on macOS (Mach-O, not usable on this Linux runner).
55+
rsync -a --exclude='bin/' "/tmp/qt-page16k/qt5.15.0-android-page16/usr/local/Qt-5.15.0/" "$QT_DIR/"
56+
# qdevice.pri comes from the overlay carrying the macOS origin's NDK host value;
57+
# fix it back to linux so qmake resolves the NDK toolchain path correctly.
58+
sed -i 's/darwin-x86_64/linux-x86_64/' "$QT_DIR/mkspecs/qdevice.pri"
59+
rm -rf /tmp/qt5.15.0-android-page16.zip /tmp/qt-page16k
60+
61+
- name: Install Java
62+
uses: actions/setup-java@v4
63+
with:
64+
distribution: 'temurin'
65+
java-version: '17'
66+
67+
- name: patching qt for bluetooth
68+
run: cp qt-patches/android/5.15.0/jar/*.* ${{ github.workspace }}/output/android/Qt/5.15.0/android/jar/
69+
70+
- name: download 3rd party files for qthttpserver
71+
run: cp qHttpServerBin/5.15.2/headers/* src/qthttpserver/src/3rdparty/http-parser/
72+
73+
- name: Cache Android NDK
74+
id: cache-ndk
75+
uses: actions/cache@v4
76+
with:
77+
path: /usr/local/lib/android/sdk/ndk/21.1.6352462
78+
key: android-ndk-21.1.6352462-${{ runner.os }}
79+
80+
- name: Install Android SDK packages
81+
run: |
82+
SDKMANAGER="/usr/local/lib/android/sdk/cmdline-tools/latest/bin/sdkmanager"
83+
echo "y" | $SDKMANAGER "ndk;21.1.6352462" "platforms;android-35" "build-tools;35.0.0"
84+
85+
- name: Setup ccache
86+
run: |
87+
sudo apt-get install -y ccache
88+
echo "CCACHE_DIR=${{ github.workspace }}/.ccache" >> $GITHUB_ENV
89+
ccache --set-config=max_size=2G
90+
ccache --set-config=compression=true
91+
ccache --set-config=compression_level=6
92+
ccache --set-config=base_dir=${{ github.workspace }}
93+
ccache --set-config=sloppiness=pch_defines,time_macros,include_file_mtime,file_macro
94+
ccache --zero-stats
95+
96+
- name: Restore ccache
97+
uses: actions/cache@v4
98+
with:
99+
path: .ccache
100+
key: ccache-android-ndk21_1-${{ hashFiles('src/**/*.cpp', 'src/**/*.h', 'src/*.pro') }}
101+
restore-keys: |
102+
ccache-android-ndk21_1-
103+
104+
- name: Setup NDK environment with ccache
105+
run: |
106+
NDK_PATH=/usr/local/lib/android/sdk/ndk/21.1.6352462
107+
NDK_BIN=$NDK_PATH/toolchains/llvm/prebuilt/linux-x86_64/bin
108+
ln -sfn $NDK_PATH /usr/local/lib/android/sdk/ndk-bundle
109+
rm -rf /usr/local/lib/android/sdk/ndk/25.1.8937393
110+
echo "ANDROID_NDK=/usr/local/lib/android/sdk/ndk-bundle" >> $GITHUB_ENV
111+
echo "ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk-bundle" >> $GITHUB_ENV
112+
if [ ! -f "$NDK_BIN/clang.real" ]; then
113+
mv $NDK_BIN/clang $NDK_BIN/clang.real
114+
mv $NDK_BIN/clang++ $NDK_BIN/clang++.real
115+
fi
116+
printf '#!/bin/bash\nexec /usr/bin/ccache %s/clang.real "$@"\n' "$NDK_BIN" > $NDK_BIN/clang
117+
printf '#!/bin/bash\nexec /usr/bin/ccache %s/clang++.real "$@"\n' "$NDK_BIN" > $NDK_BIN/clang++
118+
chmod +x $NDK_BIN/clang $NDK_BIN/clang++
119+
120+
- name: Generate secrets
121+
run: |
122+
cd src
123+
echo "#define STRAVA_SECRET_KEY ${{ secrets.strava_secret_key }}" > secret.h
124+
echo "#define PELOTON_SECRET_KEY ${{ secrets.peloton_secret_key }}" >> secret.h
125+
echo "#define SMTP_USERNAME ${{ secrets.smtp_username }}" >> secret.h
126+
echo "#define SMTP_PASSWORD ${{ secrets.smtp_password }}" >> secret.h
127+
echo "#define SMTP_SERVER ${{ secrets.smtp_server }}" >> secret.h
128+
echo "#define INTERVALSICU_CLIENT_ID ${{ secrets.intervalsicu_client_id }}" >> secret.h
129+
echo "#define INTERVALSICU_CLIENT_SECRET ${{ secrets.intervalsicu_client_secret }}" >> secret.h
130+
echo "${{ secrets.cesiumkey }}" >> inner_templates/googlemaps/cesium-key.js
131+
132+
- name: Prepare Android signing key
133+
env:
134+
SIGNING_KEY: ${{ secrets.SIGNING_KEY }}
135+
run: |
136+
if [ -z "$SIGNING_KEY" ] || [ -z "${{ secrets.ALIAS }}" ] || [ -z "${{ secrets.KEY_STORE_PASSWORD }}" ] || [ -z "${{ secrets.KEY_PASSWORD }}" ]; then
137+
echo "Android signing secrets are required to publish a release build; aborting."
138+
exit 1
139+
fi
140+
KEYSTORE="${{ github.workspace }}/src/android/ci-signing-key.jks"
141+
printf '%s' "$SIGNING_KEY" | base64 --decode > "$KEYSTORE"
142+
if [ ! -s "$KEYSTORE" ]; then
143+
echo "Failed to decode Android signing key."
144+
rm -f "$KEYSTORE"
145+
exit 1
146+
fi
147+
echo "CI_ANDROID_SIGNING_KEYSTORE=$KEYSTORE" >> "$GITHUB_ENV"
148+
149+
- name: Compute next Play Store versionCode
150+
id: next_version
151+
env:
152+
PLAY_SERVICE_ACCOUNT_JSON: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
153+
run: |
154+
echo "$PLAY_SERVICE_ACCOUNT_JSON" > /tmp/play-sa.json
155+
NEXT_VC=$(python3 scripts/play_next_version_code.py --sa-json /tmp/play-sa.json)
156+
rm -f /tmp/play-sa.json
157+
echo "version_code=$NEXT_VC" >> "$GITHUB_OUTPUT"
158+
echo "Computed next versionCode: $NEXT_VC"
159+
160+
- name: Bump Android versionCode/versionName for this release (ephemeral, never committed)
161+
run: |
162+
NEXT_VC="${{ steps.next_version.outputs.version_code }}"
163+
BASE_VERSION_NAME=$(grep -oE 'android:versionName="[^"]+"' src/android/AndroidManifest.xml | sed -E 's/.*"(.*)"/\1/')
164+
NEW_VERSION_NAME="${BASE_VERSION_NAME}-internal.${{ github.run_number }}"
165+
sed -i "s/android:versionCode=\"[0-9]*\"/android:versionCode=\"$NEXT_VC\"/" src/android/AndroidManifest.xml
166+
sed -i "s/android:versionName=\"[^\"]*\"/android:versionName=\"$NEW_VERSION_NAME\"/" src/android/AndroidManifest.xml
167+
echo "Using versionCode=$NEXT_VC versionName=$NEW_VERSION_NAME (this change is not committed back to the repo)"
168+
169+
- name: Build qthttpserver for Android
170+
run: |
171+
cd src/qthttpserver
172+
qmake
173+
make -j8
174+
make install
175+
176+
- name: Build QZ for Android (4 ABIs)
177+
run: |
178+
lrelease src/qdomyos-zwift.pri
179+
# CONFIG+=debug matches the validated reference build: keeps .so unstripped and,
180+
# with this custom Qt, naturally leaves android-extra-libs unset - the bundled
181+
# android_openssl libs are not 16K-page-aligned, so we must NOT inject them.
182+
qmake -spec android-clang 'ANDROID_ABIS=armeabi-v7a arm64-v8a x86 x86_64' 'ANDROID_NDK_ROOT=/usr/local/lib/android/sdk/ndk/21.1.6352462' 'CONFIG+=debug' && make -j4 && make INSTALL_ROOT=${{ github.workspace }}/output/android/ install
183+
cat src/android-qdomyos-zwift-deployment-settings.json
184+
185+
- name: Cache Gradle
186+
uses: actions/cache@v4
187+
with:
188+
path: |
189+
${{ github.workspace }}/.gradle-android/caches
190+
${{ github.workspace }}/.gradle-android/wrapper
191+
key: gradle-android-${{ runner.os }}-v2
192+
restore-keys: |
193+
gradle-android-${{ runner.os }}-
194+
195+
- name: Build Android package (release)
196+
run: |
197+
cd src
198+
mkdir -p ${{ github.workspace }}/output/android
199+
rsync -a --exclude build --exclude .gradle android/ ${{ github.workspace }}/output/android/
200+
androiddeployqt --input android-qdomyos-zwift-deployment-settings.json --output ${{ github.workspace }}/output/android/ --android-platform android-35 --gradle --aux-mode
201+
export GRADLE_USER_HOME="${{ github.workspace }}/.gradle-android"
202+
export GRADLE_OPTS="-Dgradle.user.home=$GRADLE_USER_HOME"
203+
export GRADLE_DIST_DIR="${{ github.workspace }}/gradle-8.13"
204+
export QT_ANDROID_DIR="${{ github.workspace }}/output/android/Qt/5.15.0/android"
205+
mkdir -p "$GRADLE_USER_HOME"
206+
rm -f "$GRADLE_USER_HOME/gradle.properties"
207+
rm -f "$HOME/.gradle/gradle.properties"
208+
find ${{ github.workspace }}/output/android \( -name '*.properties' -o -name '*.gradle' -o -name '*.gradle.kts' \) -exec sed -i '/android\.bundle\.enableUncompressedNativeLibs/d' {} +
209+
printf '\nqt5AndroidDir=%s/src/android/java\n' "$QT_ANDROID_DIR" >> ${{ github.workspace }}/output/android/gradle.properties
210+
if grep -RInE "android\\.bundle\\.enableUncompressedNativeLibs|enableUncompressedNativeLibs" ${{ github.workspace }}/output/android "$GRADLE_USER_HOME" "$HOME/.gradle" 2>/dev/null; then
211+
echo "Unexpected residual enableUncompressedNativeLibs matches found"
212+
exit 1
213+
fi
214+
if [ ! -x "$GRADLE_DIST_DIR/bin/gradle" ]; then
215+
curl -fsSL https://services.gradle.org/distributions/gradle-8.13-bin.zip -o /tmp/gradle-8.13-bin.zip
216+
unzip -q /tmp/gradle-8.13-bin.zip -d ${{ github.workspace }}
217+
fi
218+
"$GRADLE_DIST_DIR/bin/gradle" -g "$GRADLE_USER_HOME" --no-daemon -p ${{ github.workspace }}/output/android assembleRelease bundleRelease
219+
220+
- name: Sign release APK and AAB
221+
run: |
222+
APK_PATH="${{ github.workspace }}/output/android/build/outputs/apk/release/android-release-unsigned.apk"
223+
AAB_PATH="${{ github.workspace }}/output/android/build/outputs/bundle/release/android-release.aab"
224+
APKSIGNER=$(find /usr/local/lib/android/sdk/build-tools -name apksigner | sort | tail -n 1)
225+
"$APKSIGNER" sign \
226+
--ks "$CI_ANDROID_SIGNING_KEYSTORE" \
227+
--ks-key-alias "${{ secrets.ALIAS }}" \
228+
--ks-pass "pass:${{ secrets.KEY_STORE_PASSWORD }}" \
229+
--key-pass "pass:${{ secrets.KEY_PASSWORD }}" \
230+
--out "${{ github.workspace }}/output/android-release-signed.apk" \
231+
"$APK_PATH"
232+
jarsigner -sigalg SHA256withRSA -digestalg SHA-256 \
233+
-keystore "$CI_ANDROID_SIGNING_KEYSTORE" \
234+
-storepass "${{ secrets.KEY_STORE_PASSWORD }}" \
235+
-keypass "${{ secrets.KEY_PASSWORD }}" \
236+
-signedjar "${{ github.workspace }}/output/android-release-signed.aab" \
237+
"$AAB_PATH" "${{ secrets.ALIAS }}"
238+
rm -f "$CI_ANDROID_SIGNING_KEYSTORE"
239+
240+
- name: Verify 16K page-size alignment
241+
run: |
242+
NDK_LIBDIR=/usr/local/lib/android/sdk/ndk/21.1.6352462/toolchains/llvm/prebuilt/linux-x86_64/bin
243+
FAIL=0
244+
OK=0
245+
WORKDIR=$(mktemp -d)
246+
unzip -q "${{ github.workspace }}/output/android-release-signed.apk" -d "$WORKDIR"
247+
while IFS= read -r -d '' so; do
248+
ALIGN=$("$NDK_LIBDIR/llvm-readelf" -lW "$so" 2>/dev/null | awk '/LOAD/ {print $NF}' | sort -u | tail -1)
249+
if [ "$ALIGN" = "0x4000" ]; then
250+
OK=$((OK+1))
251+
else
252+
echo "FAIL (align=$ALIGN): $so"
253+
FAIL=$((FAIL+1))
254+
fi
255+
done < <(find "$WORKDIR/lib" -name '*.so' -print0)
256+
rm -rf "$WORKDIR"
257+
echo "-- Summary: OK=$OK, FAIL=$FAIL --"
258+
if [ "$FAIL" -ne 0 ]; then
259+
echo "16K page-size check failed - aborting before publish."
260+
exit 1
261+
fi
262+
263+
- name: Archive signed APK/AAB
264+
uses: actions/upload-artifact@v4
265+
with:
266+
name: release-playstore-${{ steps.next_version.outputs.version_code }}
267+
path: |
268+
${{ github.workspace }}/output/android-release-signed.apk
269+
${{ github.workspace }}/output/android-release-signed.aab
270+
271+
- name: Publish to Play Store (internal testing track)
272+
if: ${{ inputs.publish }}
273+
uses: r0adkll/upload-google-play@v1
274+
with:
275+
serviceAccountJsonPlainText: ${{ secrets.PLAY_SERVICE_ACCOUNT_JSON }}
276+
packageName: org.cagnulen.qdomyoszwift
277+
releaseFiles: ${{ github.workspace }}/output/android-release-signed.aab
278+
track: internal
279+
status: completed

0 commit comments

Comments
 (0)