Oore CI
Build and shipSign buildsAndroid signing

Keep local Gradle signing alongside Oore

How Oore hands the pipeline keystore to Gradle, and what a local key.properties setup needs to keep working.

Oore signs Android builds in Gradle itself, so most apps need no Gradle change. This page explains how, and what to check when the repository also signs release builds on developer machines.

What you need

How Oore signs the build

For each flutter build apk or flutter build appbundle command, the runner:

  1. Writes the pipeline’s keystore to a private folder outside the checkout.
  2. Adds the Android Gradle plugin’s injected signing properties, android.injected.signing.store.file, store.password, key.alias and key.password, to the workspace’s android/gradle.properties.
  3. Runs the command. The Android Gradle plugin signs every variant it builds with those values, in place of the app’s own signingConfig.
  4. Restores android/gradle.properties and deletes the keystore copy as soon as the command ends.

These are the properties Android Studio uses for Generate Signed Bundle / APK, and the ones fastlane’s gradle action passes. After the build, the android-sign step checks every APK or App Bundle under build/app/outputs. An artifact Gradle signed with the pipeline key is left as it is. Anything else is signed again with the pipeline key, so the result is the same either way.

The build command can read the keystore and its passwords while Gradle runs. Oore does not put them in the step’s environment, its arguments, or the build log.

1. Keep local signing from failing in CI

A local setup that loads key.properties keeps working on developer machines. In CI the file usually does not exist, so Gradle must still configure without it. Guard the file read, and do not call file() on a missing path:

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
    keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android {
    signingConfigs {
        release {
            keyAlias keystoreProperties["keyAlias"]
            keyPassword keystoreProperties["keyPassword"]
            storeFile keystoreProperties["storeFile"] ? file(keystoreProperties["storeFile"]) : null
            storePassword keystoreProperties["storePassword"]
        }
    }

    buildTypes {
        release {
            signingConfig signingConfigs.release
        }
    }
}

With this setup the repository does not need to commit key.properties or the keystore. Adapt the syntax to a Kotlin build.gradle.kts file as needed.

2. Keep secrets out of repository configuration

Do not add keystore content or passwords to .oore.yaml, UI environment variables, or Gradle source. Do not read OORE_ANDROID_* variables from Gradle: the runner removes them from build commands.

3. Run one Android variant

Use a supported Flutter APK or App Bundle build command for one variant in the pipeline, then trigger a build.

Verify the result

The build log shows [oore-signing] Gradle signs this build with the managed keystore before the build command, and [oore-signing] Gradle signed 1 Android artifact(s) with the managed key; verified in the android-sign step. The downloaded artifact passes apksigner verify --print-certs or keytool -printcert -jarfile, with your key’s certificate.

Troubleshooting

Gradle fails because key.properties is missing

The Gradle file reads the file or calls file() on its storeFile without checking that it exists. Guard both as in step 1.

The log says Gradle did not sign with the managed key

The build still succeeds, because the android-sign step signed the output with the pipeline key. The build does not apply the injected properties, for example because the repository has no android/gradle.properties project at its root, the file is a symbolic link, or the Gradle build sets signing in a way that ignores them.

No Android artifact is found for signing

Confirm the Flutter build command produces an APK under build/app/outputs or an App Bundle under the corresponding bundle output, and that the pipeline artifact patterns include it.

The runner reports both debug and release

Separate those commands into different pipeline runs. Managed signing selects one Android signing profile per run.

Next step

Trigger the release build and verify its downloaded artifact.