Improve workflow reliability and code clarity

- Update GitHub Actions to use latest versions and full clone - Add
retry logic to Gradle build steps in workflows - Refactor BridgeModule
for clearer variable naming and method usage - Simplify UpdaterModule
timeout assignment
This commit is contained in:
kmmiio99o 2025-10-11 21:45:18 +02:00
parent ce244b6e58
commit 25cadd08a8
4 changed files with 160 additions and 121 deletions

View file

@ -7,21 +7,33 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: "17"
- name: "Setup Gradle"
uses: gradle/gradle-build-action@v2
- name: "Grant execution permission"
run: chmod +x ./gradlew
- name: "Build APKs"
run: ./gradlew build
run: |
# Retry the Gradle build up to 3 times to mitigate transient cache/service errors
ATTEMPT=0
until [ $ATTEMPT -ge 3 ]
do
./gradlew build --no-daemon --stacktrace && break
ATTEMPT=$((ATTEMPT+1))
echo "Gradle build failed, retrying ($ATTEMPT/3)..."
sleep 5
done
if [ $ATTEMPT -ge 3 ]; then
echo "Gradle build failed after 3 attempts"
exit 1
fi
- name: "Setup directories"
run: |

View file

@ -1,77 +1,99 @@
name: Build Release
on:
workflow_dispatch:
inputs:
versionName:
required: true
description: This releases version name
default: "1.0.0"
versionCode:
required: true
description: This releases version code
default: "1000"
workflow_dispatch:
inputs:
versionName:
required: true
description: This releases version name
default: "1.0.0"
versionCode:
required: true
description: This releases version code
default: "1000"
permissions:
contents: write
contents: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
- name: Setup JDK 17
uses: actions/setup-java@v2
with:
java-version: 17
distribution: 'temurin'
cache: 'gradle'
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Android SDK
uses: android-actions/setup-android@v2.0.10
- name: Setup JDK 17
uses: actions/setup-java@v3
with:
distribution: "temurin"
java-version: 17
- name: Set Version
uses: chkfung/android-version-actions@v1.1
with:
gradlePath: app/build.gradle.kts
versionCode: ${{github.event.inputs.versionCode}}
versionName: ${{github.event.inputs.versionName}}
- name: Bump Version
continue-on-error: true
run: |
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add app/build.gradle.kts
if ! git diff --quiet app/build.gradle.kts; then
git commit -m "chore: Bump version to ${{github.event.inputs.versionName}} (${{github.event.inputs.versionCode}})"
git push
else
echo "No changes to version file, skipping commit"
fi
- name: Build Signed APK
run: |
echo "${{ secrets.keystore }}" | base64 -d > $GITHUB_WORKSPACE/signing-key.jks
chmod +x ./gradlew
./gradlew packageReleaseUniversalApk \
-Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/signing-key.jks \
-Pandroid.injected.signing.store.password=${{ secrets.keystore_password }} \
-Pandroid.injected.signing.key.alias=${{ secrets.key_alias }} \
-Pandroid.injected.signing.key.password=${{ secrets.key_password }}
- name: Release
run: |
mv app/build/outputs/apk_from_bundle/release/app-release-universal.apk ./app-release.apk
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
tag="${{ github.event.inputs.versionCode }}"
git tag "$tag"
git push origin "$tag"
gh release create "$tag" \
--title "${{ github.event.inputs.versionName }}" \
--generate-notes \
./*.apk
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
- name: Setup Android SDK
uses: android-actions/setup-android@v2.0.10
- name: Set Version
uses: chkfung/android-version-actions@v1.1
with:
gradlePath: app/build.gradle.kts
versionCode: ${{github.event.inputs.versionCode}}
versionName: ${{github.event.inputs.versionName}}
- name: Bump Version
continue-on-error: true
run: |
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
git add app/build.gradle.kts
if ! git diff --quiet app/build.gradle.kts; then
git commit -m "chore: Bump version to ${{github.event.inputs.versionName}} (${{github.event.inputs.versionCode}})"
git push
else
echo "No changes to version file, skipping commit"
fi
- name: Build Signed APK
run: |
# Ensure keystore secret is present
if [ -z "${{ secrets.keystore }}" ]; then
echo "Keystore secret missing, aborting"
exit 1
fi
echo "${{ secrets.keystore }}" | base64 -d > $GITHUB_WORKSPACE/signing-key.jks
chmod +x ./gradlew
# Retry gradle up to 3 times to mitigate transient network/cache service issues
ATTEMPT=0
until [ $ATTEMPT -ge 3 ]
do
./gradlew packageReleaseUniversalApk \
-Pandroid.injected.signing.store.file=$GITHUB_WORKSPACE/signing-key.jks \
-Pandroid.injected.signing.store.password=${{ secrets.keystore_password }} \
-Pandroid.injected.signing.key.alias=${{ secrets.key_alias }} \
-Pandroid.injected.signing.key.password=${{ secrets.key_password }} --no-daemon --stacktrace && break
ATTEMPT=$((ATTEMPT+1))
echo "Gradle build failed, retrying ($ATTEMPT/3)..."
sleep 5
done
if [ $ATTEMPT -ge 3 ]; then
echo "Gradle build failed after 3 attempts"
exit 1
fi
- name: Release
run: |
mv app/build/outputs/apk_from_bundle/release/app-release-universal.apk ./app-release.apk
git config --local user.email "actions@github.com"
git config --local user.name "GitHub Actions"
tag="${{ github.event.inputs.versionCode }}"
git tag "$tag"
git push origin "$tag"
gh release create "$tag" \
--title "${{ github.event.inputs.versionName }}" \
--generate-notes \
./*.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -98,11 +98,10 @@ class UpdaterModule : Module() {
// Retries don't need timeout
if (activity != null) {
timeout {
requestTimeoutMillis =
if (!bundle.exists()) TIMEOUT
else TIMEOUT_CACHED
}
val timeoutMs =
if (!bundle.exists()) TIMEOUT
else TIMEOUT_CACHED
timeout { requestTimeoutMillis = timeoutMs }
}
}

View file

@ -49,7 +49,7 @@ typealias BridgeMethodArgs = ArrayList<Any>
*/
class BridgeModule : Module() {
private lateinit var readableMapGetString: Method
private lateinit var readableMapToHashMap: Method
private lateinit var readableMapToHashMapMethod: Method
private lateinit var argumentsMakeNative: Method
companion object {
@ -126,43 +126,46 @@ class BridgeModule : Module() {
}
}
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) =
with(packageParam) {
val arguments = classLoader.loadClass("com.facebook.react.bridge.Arguments")
val readableMap = classLoader.loadClass("com.facebook.react.bridge.ReadableMap")
val promise = classLoader.loadClass("com.facebook.react.bridge.Promise")
override fun onLoad(packageParam: XC_LoadPackage.LoadPackageParam) {
with(packageParam) {
val arguments = classLoader.loadClass("com.facebook.react.bridge.Arguments")
val readableMap = classLoader.loadClass("com.facebook.react.bridge.ReadableMap")
val promise = classLoader.loadClass("com.facebook.react.bridge.Promise")
val promiseResolve = promise.method("resolve", Object::class.java)
argumentsMakeNative = arguments.method("makeNativeObject", Object::class.java)
readableMapGetString = readableMap.method("getString", String::class.java)
readableMapToHashMap = readableMap.method("toHashMap")
val promiseResolve = promise.method("resolve", Object::class.java)
argumentsMakeNative = arguments.method("makeNativeObject", Object::class.java)
readableMapGetString = readableMap.method("getString", String::class.java)
readableMapToHashMapMethod = readableMap.method("toHashMap")
classLoader.loadClass("com.horcrux.svg.RNSVGRenderableManager").hookMethod(
"getBBox",
Double::class.javaObjectType,
readableMap
) {
before {
callBridgeMethod(readableMapToHashMap(args[1]!!))?.let {
result = it.toNativeObject()
}
classLoader.loadClass("com.horcrux.svg.RNSVGRenderableManager").hookMethod(
"getBBox",
Double::class.javaObjectType,
readableMap
) {
before {
callBridgeMethod(readableMapToHashMapConverter(args[1]!!))?.let {
result = it.toNativeObject()
}
}
classLoader.loadClass("com.facebook.react.modules.blob.FileReaderModule")
.hookMethod("readAsDataURL", readableMap, promise) {
before {
val (readableMap, promise) = args
callBridgeMethod(readableMapToHashMap(readableMap!!))?.let {
promiseResolve.invoke(promise, it.toNativeObject())
result = null
}
}
}
return@with
}
classLoader.loadClass("com.facebook.react.modules.blob.FileReaderModule").hookMethod(
"readAsDataURL",
readableMap,
promise
) {
before {
// Avoid shadowing the outer `readableMap` and `promise` variables
val (readableMapArg, promiseArg) = args
callBridgeMethod(readableMapToHashMapConverter(readableMapArg!!))?.let {
promiseResolve.invoke(promiseArg, it.toNativeObject())
result = null
}
}
}
}
}
private fun Any?.toNativeObject(): Any? =
argumentsMakeNative.invoke(
null,
@ -172,18 +175,21 @@ class BridgeModule : Module() {
}
)
private fun readableMapToHashMap(map: Any): HashMap<String, Any?> {
@Suppress("UNCHECKED_CAST") return readableMapToHashMap.invoke(map) as HashMap<String, Any?>
private fun readableMapToHashMapConverter(map: Any): HashMap<String, Any?> {
@Suppress("UNCHECKED_CAST")
return readableMapToHashMapMethod.invoke(map) as HashMap<String, Any?>
}
private fun callBridgeMethod(hashMap: HashMap<String, Any?>): Map<String, Any?>? =
try {
val (method, args) = getBridgeCallData(hashMap) ?: return null
val ret = method(args).toNativeObject()
mapOf("result" to ret)
} catch (e: Throwable) {
mapOf("error" to e.toString())
}
private fun callBridgeMethod(hashMap: HashMap<String, Any?>): Map<String, Any?>? {
try {
val pair = getBridgeCallData(hashMap) ?: return null
val (method, args) = pair
val ret = method(args).toNativeObject()
return mapOf("result" to ret)
} catch (e: Throwable) {
return mapOf("error" to e.toString())
}
}
private fun getBridgeCallData(
hashMap: HashMap<String, Any?>