forked from ppy/osu
-
Notifications
You must be signed in to change notification settings - Fork 0
334 lines (297 loc) Β· 14 KB
/
Copy pathrelease.yml
File metadata and controls
334 lines (297 loc) Β· 14 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
name: Build Android APK
on:
push:
tags:
- '*.*.*'
- '!*-*'
workflow_dispatch:
inputs:
version:
description: "Version number (e.g. 1.0.0). Leave empty for auto-increment."
required: false
default: ""
type: string
create_release:
description: "Create a GitHub Release with the APK?"
required: false
default: true
type: boolean
jobs:
build-android:
name: Build Android APK
runs-on: ubuntu-latest
timeout-minutes: 60
permissions:
contents: write
steps:
- name: Checkout
uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0
- name: Setup JDK 17
uses: actions/setup-java@v5
with:
distribution: microsoft
java-version: 17
- name: Install .NET 10.0.x
uses: actions/setup-dotnet@v5
with:
dotnet-version: "10.0.x"
- name: Install .NET Android workload
run: dotnet workload install android
- name: Ensure Android NDK and CMake are available
run: |
# The ubuntu-latest runner has $ANDROID_HOME pre-installed.
# sdkmanager is not on PATH; use the full path.
SDKMANAGER="$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager"
# Install latest stable NDK (r29) + CMake.
yes | "$SDKMANAGER" --licenses > /dev/null 2>&1 || true
"$SDKMANAGER" --install "ndk;29.0.14206865" "cmake;3.22.1" > /dev/null 2>&1
- name: Build native library (libosu_native.so)
run: |
NDK_HOME="$ANDROID_HOME/ndk/29.0.14206865"
CMAKE_BIN="$ANDROID_HOME/cmake/3.22.1/bin/cmake"
# arm64 only β matches RuntimeIdentifiers in osu.Android.props.
for ABI in arm64-v8a; do
echo "::group::Building osu_native for $ABI"
"$CMAKE_BIN" -B "build-native/$ABI" -S osu.Android/Native \
-DCMAKE_TOOLCHAIN_FILE="$NDK_HOME/build/cmake/android.toolchain.cmake" \
-DANDROID_ABI="$ABI" \
-DANDROID_PLATFORM=android-33 \
-DCMAKE_BUILD_TYPE=Release
"$CMAKE_BIN" --build "build-native/$ABI" --config Release -j "$(nproc)"
mkdir -p "osu.Android/libs/$ABI"
cp "build-native/$ABI/libosu_native.so" "osu.Android/libs/$ABI/"
echo "::endgroup::"
done
echo "Native libraries built successfully:"
find osu.Android/libs -name "*.so" -exec ls -lh {} \;
- name: Decode or generate keystore
id: keystore
run: |
KS_PATH="${{ github.workspace }}/osu.Android/osu.keystore"
if [ -n "$KEYSTORE_BASE64" ]; then
# ββ User provided a persistent keystore secret ββββββββββββββ
echo "$KEYSTORE_BASE64" | base64 --decode > "$KS_PATH"
echo "has_keystore=true" >> "$GITHUB_OUTPUT"
echo "generated=false" >> "$GITHUB_OUTPUT"
echo "key_alias=$KEY_ALIAS_SECRET" >> "$GITHUB_OUTPUT"
echo "key_pass=$KEY_PASS_SECRET" >> "$GITHUB_OUTPUT"
echo "store_pass=$STORE_PASS_SECRET" >> "$GITHUB_OUTPUT"
echo "β
Using saved keystore from repository secrets."
else
# ββ No secret β auto-generate a keystore for this build βββββ
# The APK will install fine on any device, but UPDATING from a
# previous build signed with a DIFFERENT key will fail.
# To avoid that, save the generated keystore as a secret
# (instructions are printed at the end of the build).
AUTO_PASS="osu-$(openssl rand -hex 12)"
echo "::add-mask::$AUTO_PASS"
keytool -genkeypair \
-keystore "$KS_PATH" \
-storepass "$AUTO_PASS" \
-keypass "$AUTO_PASS" \
-alias osu-release \
-keyalg RSA -keysize 2048 -validity 10000 \
-dname "CN=osu! Android,O=osu,C=US" 2>/dev/null
echo "has_keystore=true" >> "$GITHUB_OUTPUT"
echo "generated=true" >> "$GITHUB_OUTPUT"
echo "key_alias=osu-release" >> "$GITHUB_OUTPUT"
echo "key_pass=$AUTO_PASS" >> "$GITHUB_OUTPUT"
echo "store_pass=$AUTO_PASS" >> "$GITHUB_OUTPUT"
# Export base64 and password into a single instructions file
# so the user only needs to download one artifact.
{
echo "=== osu! Android Signing Keystore ==="
echo ""
echo "ANDROID_KEYSTORE_BASE64 value (copy everything on the next line):"
base64 -w 0 "$KS_PATH"
echo ""
echo ""
echo "ANDROID_SIGNING_KEY_ALIAS value:"
echo "osu-release"
echo ""
echo "ANDROID_SIGNING_KEY_PASSWORD value:"
echo "$AUTO_PASS"
echo ""
echo "ANDROID_SIGNING_STORE_PASSWORD value:"
echo "$AUTO_PASS"
} > "${{ github.workspace }}/SAVE-THESE-SECRETS.txt"
echo "::warning::No signing keystore secret found β auto-generated one for this build."
echo "::warning::See the end of this job for instructions to save it for future builds."
fi
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
KEY_ALIAS_SECRET: ${{ secrets.ANDROID_SIGNING_KEY_ALIAS }}
KEY_PASS_SECRET: ${{ secrets.ANDROID_SIGNING_KEY_PASSWORD }}
STORE_PASS_SECRET: ${{ secrets.ANDROID_SIGNING_STORE_PASSWORD }}
- name: Determine version
id: version
run: |
# Priority: manual input > tag name > auto-increment from run number
INPUT_VERSION="${{ inputs.version }}"
REF="${{ github.ref_name }}"
if [[ -n "$INPUT_VERSION" && "$INPUT_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
VERSION="$INPUT_VERSION"
echo "π¦ Using manually specified version: $VERSION"
elif [[ "$REF" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
VERSION="$REF"
echo "π¦ Using version from tag: $VERSION"
else
# Auto-generate: YYYY.MMDD.run_number
VERSION="$(date -u +%Y).$(date -u +%-m%d).${{ github.run_number }}"
echo "π¦ Auto-generated version: $VERSION"
fi
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
echo "tag=v$VERSION" >> "$GITHUB_OUTPUT"
- name: Build Android APK
run: >
dotnet publish -c Release
osu.Android/osu.Android.csproj
-f net10.0-android
-p:Version="${{ steps.version.outputs.version }}"
-p:ApplicationDisplayVersion="${{ steps.version.outputs.version }}"
-p:ApplicationVersion="${{ github.run_number }}"
-p:AndroidKeyStore=true
-p:AndroidSigningKeyStore="${{ github.workspace }}/osu.Android/osu.keystore"
-p:AndroidSigningKeyAlias="${{ steps.keystore.outputs.key_alias }}"
-p:AndroidSigningKeyPass="${{ steps.keystore.outputs.key_pass }}"
-p:AndroidSigningStorePass="${{ steps.keystore.outputs.store_pass }}"
-p:CustomBeforeMicrosoftCommonTargets="${{ github.workspace }}/build/SuppressSubmoduleWarnings.targets"
- name: Find and rename APK
id: find_apk
run: |
APK=$(find "osu.Android/bin/Release/net10.0-android/publish" -maxdepth 1 -name "*-Signed.apk" 2>/dev/null | head -1)
if [ -z "$APK" ]; then
APK=$(find "osu.Android/bin/Release" -name "*-Signed.apk" | head -1)
fi
if [ -z "$APK" ]; then
echo "::error::Failed to locate signed APK. Listing bin directory:"
find osu.Android/bin -name "*.apk" -o -name "*.aab" 2>/dev/null || true
exit 1
fi
# Rename to a clean versioned filename for the release
VERSION="${{ steps.version.outputs.version }}"
FINAL_APK="$(dirname "$APK")/osu-lazer-$VERSION.apk"
cp "$APK" "$FINAL_APK"
APK_SIZE=$(stat -c %s "$FINAL_APK" 2>/dev/null || stat -f %z "$FINAL_APK" 2>/dev/null || wc -c < "$FINAL_APK")
APK_SIZE_MB=$((APK_SIZE / 1048576))
echo "Found APK: $FINAL_APK ($APK_SIZE_MB MB)"
echo "apk_path=$FINAL_APK" >> "$GITHUB_OUTPUT"
- name: Verify APK signature
run: |
APK="${{ steps.find_apk.outputs.apk_path }}"
APKSIGNER="$ANDROID_HOME/build-tools/$(ls "$ANDROID_HOME/build-tools" | sort -V | tail -1)/apksigner"
if "$APKSIGNER" verify --print-certs "$APK"; then
echo "APK signature verified β"
else
echo "::error::APK signature verification failed. The APK may not install correctly."
exit 1
fi
- name: Verify native libraries in APK
run: |
APK="${{ steps.find_apk.outputs.apk_path }}"
SUBMOD="submodules/osu-framework/osu.Framework.Android/arm64-v8a"
echo "Checking APK for required native libraries..."
NATIVE_LIBS=$(unzip -l "$APK" | grep "lib/arm64-v8a/.*\.so" || true)
echo "$NATIVE_LIBS"
echo ""
MISSING=0
for LIB in libbass.so libbass_fx.so libbassmix.so; do
if echo "$NATIVE_LIBS" | grep -q "$LIB"; then
echo "β
$LIB found"
else
echo "::error::$LIB is MISSING from the APK β the app will crash at startup (DllNotFoundException)."
MISSING=1
fi
done
if [ "$MISSING" -ne 0 ]; then
echo ""
echo "::error::One or more required native libraries are missing. Check that the osu-framework submodule is initialised and the AndroidNativeLibrary glob in osu.Android.props resolves correctly."
exit 1
fi
echo ""
echo "All required native libraries present β"
# Verify the BASS libraries are the Android-built versions, not the Linux desktop
# ones from the NativeLibs NuGet package. The Android libbass.so links against
# libOpenSLES.so (Android audio API); the Linux one links against libc.so.6.
echo ""
echo "Verifying native library ABI compatibility..."
TMPDIR=$(mktemp -d)
unzip -q -o "$APK" "lib/arm64-v8a/libbass.so" -d "$TMPDIR"
if readelf -d "$TMPDIR/lib/arm64-v8a/libbass.so" | grep -q "libOpenSLES.so"; then
echo "β
libbass.so is the correct Android build (links libOpenSLES.so)"
else
echo "::error::libbass.so in APK is NOT the Android build β it appears to be the Linux desktop version from ppy.osu.Framework.NativeLibs NuGet. The app will crash with DllNotFoundException."
echo "::error::Ensure ppy.osu.Framework.NativeLibs has ExcludeAssets=native in osu.Android.props."
readelf -d "$TMPDIR/lib/arm64-v8a/libbass.so" | grep NEEDED || true
rm -rf "$TMPDIR"
exit 1
fi
rm -rf "$TMPDIR"
- name: Upload APK artifact
uses: actions/upload-artifact@v7
with:
name: osu-android-${{ steps.version.outputs.version }}
path: ${{ steps.find_apk.outputs.apk_path }}
if-no-files-found: error
# When the keystore was auto-generated, upload it so the user can save it
# as a repository secret for consistent signing across builds.
- name: Upload generated keystore
if: steps.keystore.outputs.generated == 'true'
uses: actions/upload-artifact@v7
with:
name: osu-signing-keystore
path: |
${{ github.workspace }}/osu.Android/osu.keystore
${{ github.workspace }}/SAVE-THESE-SECRETS.txt
retention-days: 7
- name: Print keystore setup instructions
if: steps.keystore.outputs.generated == 'true'
run: |
echo ""
echo "=================================================================="
echo " β οΈ YOUR APK WAS SIGNED WITH AN AUTO-GENERATED KEYSTORE"
echo "=================================================================="
echo ""
echo " β
The APK will install fine on any device."
echo ""
echo " β οΈ BUT β if you build again without saving this keystore,"
echo " Android will REFUSE to update (different signing certificate)."
echo ""
echo " To keep your APK updatable across builds:"
echo ""
echo " 1. Download the 'osu-signing-keystore' artifact from this run"
echo " 2. Open 'SAVE-THESE-SECRETS.txt' β it contains all 4 values"
echo " 3. Go to: Settings β Secrets and variables β Actions"
echo " 4. Create these 4 secrets with the values from the file:"
echo ""
echo " β’ ANDROID_KEYSTORE_BASE64"
echo " β’ ANDROID_SIGNING_KEY_ALIAS"
echo " β’ ANDROID_SIGNING_KEY_PASSWORD"
echo " β’ ANDROID_SIGNING_STORE_PASSWORD"
echo ""
echo " β‘ After saving the secrets, all future builds will use the"
echo " same keystore automatically β no more setup needed."
echo "=================================================================="
# Create a GitHub Release with the APK attached.
# Works for both tag pushes and manual workflow_dispatch runs.
# For manual runs: creates a tag automatically.
- name: Create GitHub Release
if: inputs.create_release != false
uses: softprops/action-gh-release@v3
with:
tag_name: ${{ steps.version.outputs.tag }}
name: "osu! Android ${{ steps.version.outputs.version }}"
files: ${{ steps.find_apk.outputs.apk_path }}
generate_release_notes: true
body: |
## osu! Android v${{ steps.version.outputs.version }}
### Download
Grab the APK below and install it on your Android device.
> **Minimum Android version:** 13 (API 33)
> **Architecture:** arm64-v8a
make_latest: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}