From abe1a24bb400f5a66586381b90056310f5f0aaeb Mon Sep 17 00:00:00 2001 From: chris Date: Tue, 6 Oct 2026 13:09:22 -0400 Subject: [PATCH] Android: sign release builds with our own upload key A config plugin adds a release signing config that reads the key from MUSICSHARE_UPLOAD_* in ~/.gradle/gradle.properties (kept out of the repo), and falls back to the debug key where that isn't set up. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01MzFywujuH9TiuXbr9vjoX8 --- apps/mobile/app.json | 3 +- apps/mobile/plugins/withReleaseSigning.js | 41 +++++++++++++++++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) create mode 100644 apps/mobile/plugins/withReleaseSigning.js diff --git a/apps/mobile/app.json b/apps/mobile/app.json index 0816c53..54d1735 100644 --- a/apps/mobile/app.json +++ b/apps/mobile/app.json @@ -50,7 +50,8 @@ "cameraPermission": "musicshare uses the camera to scan friends' QR codes.", "microphonePermission": false } - ] + ], + "./plugins/withReleaseSigning" ], "experiments": { "reactCompiler": true diff --git a/apps/mobile/plugins/withReleaseSigning.js b/apps/mobile/plugins/withReleaseSigning.js new file mode 100644 index 0000000..b5129cb --- /dev/null +++ b/apps/mobile/plugins/withReleaseSigning.js @@ -0,0 +1,41 @@ +// Signs release builds with the musicshare upload key when it's configured in +// ~/.gradle/gradle.properties (MUSICSHARE_UPLOAD_STORE_FILE, _STORE_PASSWORD, +// _KEY_ALIAS, _KEY_PASSWORD); otherwise falls back to the debug key so builds +// still work on other machines. The key itself never lives in the repo. +const { withAppBuildGradle } = require('expo/config-plugins'); + +const MARKER = '// musicshare release signing'; + +const SIGNING_CONFIG = ` + ${MARKER} + release { + if (project.hasProperty('MUSICSHARE_UPLOAD_STORE_FILE')) { + storeFile file(MUSICSHARE_UPLOAD_STORE_FILE) + storePassword MUSICSHARE_UPLOAD_STORE_PASSWORD + keyAlias MUSICSHARE_UPLOAD_KEY_ALIAS + keyPassword MUSICSHARE_UPLOAD_KEY_PASSWORD + } + }`; + +module.exports = function withReleaseSigning(config) { + return withAppBuildGradle(config, (cfg) => { + let gradle = cfg.modResults.contents; + if (gradle.includes(MARKER)) return cfg; + + // Add a release signing config next to the debug one. + gradle = gradle.replace(/signingConfigs\s*\{/, (m) => `${m}${SIGNING_CONFIG}`); + + // In the release build type, use it when the key is configured. + const release = gradle.indexOf('release {', gradle.indexOf('buildTypes {')); + if (release === -1) throw new Error('withReleaseSigning: no release build type in app/build.gradle'); + const line = gradle.indexOf('signingConfig signingConfigs.debug', release); + if (line === -1) throw new Error('withReleaseSigning: release build type has no signingConfig line'); + gradle = + gradle.slice(0, line) + + "signingConfig project.hasProperty('MUSICSHARE_UPLOAD_STORE_FILE') ? signingConfigs.release : signingConfigs.debug" + + gradle.slice(line + 'signingConfig signingConfigs.debug'.length); + + cfg.modResults.contents = gradle; + return cfg; + }); +};