Signing and Versioning

Expo 6,418 's template signs release builds with the debug key ("Caution! In production, you need to generate your own keystore file", its build.gradle says). Create the upload key with Release Signing's keytool command, here as ~/keys/booknest-rn-upload.jks with the alias upload. Release Signing then edited build.gradle by hand, but in a prebuild project that edit is lost whenever android/ is regenerated, by prebuild --clean or when prebuild judges the folder malformed, as BookNest's first prebuild here did. A config plugin (Config Plugins) makes the change part of the project:

plugins/withReleaseSigning.js: the upload key survives every prebuildJavaScript
const { withAppBuildGradle } = require('expo/config-plugins');
const RELEASE_CONFIG = `signingConfigs {
        release {
            if (findProperty('BOOKNEST_UPLOAD_STORE_FILE')) {
                storeFile file(findProperty('BOOKNEST_UPLOAD_STORE_FILE'))
                storePassword findProperty('BOOKNEST_UPLOAD_PASSWORD')
                keyAlias findProperty('BOOKNEST_UPLOAD_KEY_ALIAS')
                keyPassword findProperty('BOOKNEST_UPLOAD_PASSWORD')
            }
        }`;
const DEBUG_KEY = 'signingConfig signingConfigs.debug';
const USE_IT = `signingConfig findProperty('BOOKNEST_UPLOAD_STORE_FILE')
                ? signingConfigs.release : signingConfigs.debug`;
module.exports = (config) =>
  withAppBuildGradle(config, (c) => {
    let src = c.modResults.contents;
    if (!src.includes('BOOKNEST_UPLOAD_STORE_FILE')) {
      src = src.replace('signingConfigs {', RELEASE_CONFIG);
      // the template's release build type signs with the debug key: the last match
      const at = src.lastIndexOf(DEBUG_KEY);
      src = src.slice(0, at) + USE_IT + src.slice(at + DEBUG_KEY.length);
    }
    c.modResults.contents = src;
    return c;
  });

Add "./plugins/withReleaseSigning" to plugins. No secret enters the project: findProperty() reads Gradle 19,597 properties, and Gradle turns any environment variable named ORG_GRADLE_PROJECT_<name> into the property <name>, so a shell or CI job supplies the path, alias and password (Publishing BookNest). Without them a release falls back to the debug key, which suits Detox 12,028 (End-to-End Testing with Detox), while Play rejects a debug-signed upload.

Versions follow Versions and App IDs but live in app.json (Icons and Metadata), with android.versionCode and ios.buildNumber raised on every upload. With "appVersionSource": "remote" (EAS Build), EAS Build 6,418 ignores those two and keeps its own, raising them on each build.