-
Notifications
You must be signed in to change notification settings - Fork 6
Description
Keystore file not found for signing config 'externalOverride'
When running Rock Remote Build - Android with sign: true, the build fails because the Android Gradle Plugin (AGP) cannot find the keystore file. It looks for it in the Gradle daemon directory instead of where the action actually places it.
Error from logs
> Keystore file '/home/runner/.gradle/daemon/9.0.0/release.keystore' not found for signing config 'externalOverride'.
The exact daemon path may vary (e.g. 9.0.0 depends on the Gradle version). The filename (release.keystore) comes from the keystore-store-file input.
Steps to reproduce
-
Use the Rock Remote Build - Android action with signing enabled:
sign: truekeystore-base64(orkeystore-file) andkeystore-store-file(e.g.release.keystore) setkeystore-store-password,keystore-key-alias,keystore-key-passwordsetkeystore-pathnot set explicitly (defaultrelease.keystoreis used)
-
Run the workflow.
-
The "Build Android" step fails with the error above.
Expected behavior
The build should succeed and produce a signed APK/AAB. The keystore is decoded and written by the "Decode and store keystore" step to a path like android/app/release.keystore (or $ANDROID_SOURCE_DIR/$APP_NAME/release.keystore). AGP should use that file for the externalOverride signing config.
Actual behavior
AGP resolves android.injected.signing.store.file as a path. When it receives a relative value (e.g. release.keystore), it resolves it from the Gradle daemon working directory (e.g. /home/runner/.gradle/daemon/9.0.0/), so it tries to open:
/home/runner/.gradle/daemon/9.0.0/release.keystore
That file does not exist. The real keystore is at e.g.:
$GITHUB_WORKSPACE/android/app/release.keystore(or similar, depending onkeystore-pathand project layout).
Root cause
-
The action writes
android.injected.signing.*properties to$HOME/.gradle/gradle.properties. AGP reads these and creates anexternalOverridesigning config. -
For
android.injected.signing.store.file, the action was writing the bare filename from thekeystore-store-fileinput (e.g.release.keystore), not the full path where the keystore is stored. -
AGP treats
android.injected.signing.store.fileas a path. For relative values, it resolves them from the Gradle daemon directory, not from the project orandroid/app/. -
Relative path + wrong base directory → "Keystore file not found" for
externalOverride.
The action also sets ROCK_UPLOAD_STORE_FILE and similar; apps using file(ROCK_UPLOAD_STORE_FILE) in build.gradle can resolve correctly relative to the project. However, android.injected.signing.* takes precedence, so AGP uses externalOverride with the wrong path.
So commenting out the android.injected.signing.* parts also fixes the problem for me.
Suggested fix
-
Run "Create local gradle.properties" after "Decode and store keystore" so the absolute path (
KEYSTORE_TARGET_PATH) is available when writinggradle.properties. -
Set
android.injected.signing.store.fileto that absolute path instead of the filename:android.injected.signing.store.file=$KEYSTORE_TARGET_PATHKEYSTORE_TARGET_PATHis the absolute path produced by the "Decode and store keystore" step (e.g./home/runner/work/.../android/app/release.keystore). -
Leave
android.injected.signing.store.password,key.alias,key.password, and allROCK_UPLOAD_*properties unchanged.
With an absolute path, AGP’s externalOverride finds the keystore correctly. No changes are required in build.gradle or app code.
Environment
- Action: Rock Remote Build - Android (composite action)
- Trigger:
sign: truewith valid keystore inputs - Runner: GitHub-hosted (e.g.
ubuntu-latest); the daemon path is under/home/runner/.gradle/daemon/ - AGP: reads
android.injected.signing.*from~/.gradle/gradle.propertiesand createsexternalOverridesigning config