From cbaa7138cea826c95aeb9baa28ae70dbf728f0d4 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Mon, 29 Sep 2025 19:17:51 +0200 Subject: [PATCH 01/22] feat: android aab signing --- .../src/lib/commands/signAndroid/command.ts | 12 +- .../lib/commands/signAndroid/signAndroid.ts | 146 +++++++++++++----- 2 files changed, 110 insertions(+), 48 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index 0da6e63ab..296bb8646 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -3,7 +3,7 @@ import { signAndroid } from './signAndroid.js'; export type SignFlags = { verbose?: boolean; - apk: string; + path: string; output?: string; keystore?: string; keystorePassword?: string; @@ -16,8 +16,8 @@ export type SignFlags = { const ARGUMENTS = [ { - name: 'apk', - description: 'APK file path', + name: 'path', + description: 'Archive (apk or aab) file path', }, ]; @@ -44,7 +44,7 @@ const OPTIONS = [ }, { name: '--output ', - description: 'Path to the output APK file.', + description: 'Path to the output APK/AAB file.', }, { name: '--build-jsbundle', @@ -66,9 +66,9 @@ export const registerSignCommand = (api: PluginApi) => { description: 'Sign the Android app with modified JS bundle.', args: ARGUMENTS, options: OPTIONS, - action: async (apkPath, flags: SignFlags) => { + action: async (path, flags: SignFlags) => { await signAndroid({ - apkPath, + path, keystorePath: flags.keystore, keystorePassword: flags.keystorePassword, keyAlias: flags.keyAlias, diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 1f642a0d8..95fad4e27 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -16,7 +16,7 @@ import { findAndroidBuildTool, getAndroidBuildToolsPath } from '../../paths.js'; import { buildJsBundle } from './bundle.js'; export type SignAndroidOptions = { - apkPath: string; + path: string; keystorePath?: string; keystorePassword?: string; keyAlias?: string; @@ -30,7 +30,9 @@ export type SignAndroidOptions = { export async function signAndroid(options: SignAndroidOptions) { validateOptions(options); - intro(`Modifying APK file`); + const extension = path.extname(options.path).slice(1); + + intro(`Modifying ${extension.toUpperCase()} file`); const tempPath = getSignOutputPath(); if (fs.existsSync(tempPath)) { @@ -60,28 +62,28 @@ export async function signAndroid(options: SignAndroidOptions) { options.jsBundlePath = bundleOutputPath; } - // 2. Initialize temporary APK file - const tempApkPath = path.join(tempPath, 'output-app.apk'); + // 2. Initialize temporary archive file + const tempArchivePath = path.join(tempPath, `output-app.${extension}`); - loader.start('Initializing output APK...'); + loader.start(`Initializing output ${extension.toUpperCase()}...`); try { - const zip = new AdmZip(options.apkPath); + const zip = new AdmZip(options.path); // Remove old signature files zip.deleteFile('META-INF/*'); - zip.writeZip(tempApkPath); + zip.writeZip(tempArchivePath); } catch (error) { throw new RockError( - `Failed to initialize output APK file: ${options.outputPath}`, + `Failed to initialize output file: ${options.outputPath}`, { cause: (error as SubprocessError).stderr }, ); } - loader.stop(`Initialized output APK.`); + loader.stop(`Initialized output file.`); // 3. Replace JS bundle if provided if (options.jsBundlePath) { loader.start('Replacing JS bundle...'); await replaceJsBundle({ - apkPath: tempApkPath, + archivePath: tempArchivePath, jsBundlePath: options.jsBundlePath, }); loader.stop( @@ -91,32 +93,51 @@ export async function signAndroid(options: SignAndroidOptions) { ); } - // 4. Align APK file - loader.start('Aligning output APK file...'); - const outputApkPath = options.outputPath ?? options.apkPath; - await alignApkFile(tempApkPath, outputApkPath); - loader.stop( - `Created output APK file: ${colorLink(relativeToCwd(outputApkPath))}.`, - ); + // 4. Align archive before signing if apk + const outputPath = options.outputPath ?? options.path; + + const alignArchive = async () => { + loader.start('Aligning output file...'); + await alignArchiveFile(tempArchivePath, outputPath); + loader.stop( + `Created output ${extension.toUpperCase()} file: ${colorLink(relativeToCwd(outputPath))}.`, + ); + } - // 5. Sign APK file - loader.start('Signing the APK file...'); + if (!isAab(outputPath)) { + await alignArchive() + } + + // 5. Sign archive file + loader.start(`Signing the ${extension.toUpperCase()} file...`); const keystorePath = options.keystorePath ?? 'android/app/debug.keystore'; - await signApkFile({ - apkPath: outputApkPath, + + const signArgs = { + path: outputPath, keystorePath, keystorePassword: options.keystorePassword ?? 'pass:android', keyAlias: options.keyAlias, keyPassword: options.keyPassword, - }); - loader.stop(`Signed the APK file with keystore: ${colorLink(keystorePath)}.`); + } + + if (isAab(outputPath)) { + await signAab(signArgs); + } else { + await signApk(signArgs); + } + loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); + + // 6. Align archive after signing if aab + if (isAab(outputPath)) { + await alignArchive() + } outro('Success 🎉.'); } function validateOptions(options: SignAndroidOptions) { - if (!fs.existsSync(options.apkPath)) { - throw new RockError(`APK file not found "${options.apkPath}"`); + if (!fs.existsSync(options.path)) { + throw new RockError(`File not found "${options.path}"`); } if (options.buildJsBundle && options.jsBundlePath) { @@ -131,22 +152,24 @@ function validateOptions(options: SignAndroidOptions) { } type ReplaceJsBundleOptions = { - apkPath: string; + archivePath: string; jsBundlePath: string; }; async function replaceJsBundle({ - apkPath, + archivePath, jsBundlePath, }: ReplaceJsBundleOptions) { try { - const zip = new AdmZip(apkPath); - zip.deleteFile('assets/index.android.bundle'); - zip.addLocalFile(jsBundlePath, 'assets', 'index.android.bundle'); - zip.writeZip(apkPath); + const zip = new AdmZip(archivePath); + const assetsPath = isAab(archivePath) ? 'assets/base' : 'assets'; + + zip.deleteFile(path.join(assetsPath, 'index.android.bundle')); + zip.addLocalFile(jsBundlePath, assetsPath, 'index.android.bundle'); + zip.writeZip(archivePath); } catch (error) { throw new RockError( - `Failed to replace JS bundle in destination file: ${apkPath}}`, + `Failed to replace JS bundle in destination file: ${archivePath}}`, { cause: error }, ); } @@ -159,7 +182,7 @@ function isSdkGTE35(versionString: string) { return match[1].localeCompare('35.0.0', undefined, { numeric: true }) >= 0; } -async function alignApkFile(inputApkPath: string, outputApkPath: string) { +async function alignArchiveFile(inputArchivePath: string, outputPath: string) { const zipAlignPath = findAndroidBuildTool('zipalign'); if (!zipAlignPath) { throw new RockError( @@ -177,34 +200,69 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm '-f', // Overwrites existing output file. '-v', // Overwrites existing output file. '4', // alignment in bytes, e.g. '4' provides 32-bit alignment - inputApkPath, - outputApkPath, + inputArchivePath, + outputPath, ]; try { await spawn(zipAlignPath, zipalignArgs); } catch (error) { throw new RockError( - `Failed to align APK file: ${zipAlignPath} ${zipalignArgs.join(' ')}`, + `Failed to align archive file: ${zipAlignPath} ${zipalignArgs.join(' ')}`, { cause: (error as SubprocessError).stderr }, ); } } -type SignApkOptions = { - apkPath: string; +type SignOptions = { + path: string; keystorePath: string; keystorePassword: string; keyAlias?: string; keyPassword?: string; }; -async function signApkFile({ - apkPath, +async function signAab({ + path, + keystorePath, + keystorePassword, + keyAlias, + keyPassword, +}: SignOptions) { + if (!fs.existsSync(keystorePath)) { + throw new RockError( + `Keystore file not found "${keystorePath}". Provide a valid keystore path using the "--keystore" option.`, + ); + } + + // For AAB files, we use jarsigner instead of apksigner + // jarsigner -keystore "" -storepass "" -keypass "" + const jarsignerArgs = [ + "-keystore", + keystorePath, + "-storepass", + keystorePassword, + ...(keyPassword ? ['-keypass', keyPassword] : []), + path, + keyAlias ?? '' + ] + + try { + await spawn('jarsigner', jarsignerArgs); + } catch (error) { + throw new RockError( + `Failed to sign AAB file: jarsigner ${jarsignerArgs.join(' ')}`, + { cause: (error as SubprocessError).stderr }, + ); + } +} + +async function signApk({ + path, keystorePath, keystorePassword, keyAlias, keyPassword, -}: SignApkOptions) { +}: SignOptions) { if (!fs.existsSync(keystorePath)) { throw new RockError( `Keystore file not found "${keystorePath}". Provide a valid keystore path using the "--keystore" option.`, @@ -230,7 +288,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm formatPassword(keystorePassword), ...(keyAlias ? ['--ks-key-alias', keyAlias] : []), ...(keyPassword ? ['--key-pass', formatPassword(keyPassword)] : []), - apkPath, + path, ]; try { @@ -264,3 +322,7 @@ function formatPassword(password: string) { function getSignOutputPath() { return path.join(getDotRockPath(), 'android/sign'); } + +function isAab(filePath: string): boolean { + return path.extname(filePath).toLowerCase() === '.aab'; +} \ No newline at end of file From 3ec1556a2340193e317437e06876acc5ed63c831 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Mon, 29 Sep 2025 19:28:28 +0200 Subject: [PATCH 02/22] feat: android aab signing - docs update --- website/src/docs/cli.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/website/src/docs/cli.md b/website/src/docs/cli.md index bc2cb7c0b..34441aaaa 100644 --- a/website/src/docs/cli.md +++ b/website/src/docs/cli.md @@ -230,17 +230,17 @@ Same as for `build:android` and: ### `rock sign:android` Options -The `sign:android ` command signs your Android app with a keystore, producing a signed APK file ready for distribution. It allows for replacing the JS bundle with a new version. +The `sign:android ` command signs your Android app with a keystore, producing a signed APK or AAB file ready for distribution. It allows for replacing the JS bundle with a new version. | Argument | Description | | :----------- | :------------------- | -| `binaryPath` | Path to the APK file | +| `binaryPath` | Path to the APK or AAB file | | Option | Description | | :----------------------------- | :---------------------------------------- | | `--keystore ` | Path to keystore file | | `--keystore-password ` | Password for keystore file | -| `--output ` | Path to output APK file | +| `--output ` | Path to output APK or AAB file | | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | From fc799c927f9627f5e82e7961d2cb714bf6979f20 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:00:05 +0200 Subject: [PATCH 03/22] feat: android aab signing - fixes assets path --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 95fad4e27..af9361e3b 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -162,7 +162,7 @@ async function replaceJsBundle({ }: ReplaceJsBundleOptions) { try { const zip = new AdmZip(archivePath); - const assetsPath = isAab(archivePath) ? 'assets/base' : 'assets'; + const assetsPath = isAab(archivePath) ? 'base/assets' : 'assets'; zip.deleteFile(path.join(assetsPath, 'index.android.bundle')); zip.addLocalFile(jsBundlePath, assetsPath, 'index.android.bundle'); From 196225032cb2a62fcb9f7eca2be060c2e34543fb Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:05:03 +0200 Subject: [PATCH 04/22] feat: android aab signing - changeset --- .changeset/whole-tools-occur.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changeset/whole-tools-occur.md diff --git a/.changeset/whole-tools-occur.md b/.changeset/whole-tools-occur.md new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/.changeset/whole-tools-occur.md @@ -0,0 +1 @@ + From c0410d4b1375b4056d0c31d84f14b1391f46d443 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:06:26 +0200 Subject: [PATCH 05/22] feat: android aab signing - missing semicolon --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index af9361e3b..9513b3413 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -244,7 +244,7 @@ async function signAab({ ...(keyPassword ? ['-keypass', keyPassword] : []), path, keyAlias ?? '' - ] + ]; try { await spawn('jarsigner', jarsignerArgs); From 18f59fb6dd8aaaccfeaa2336eb1547a4ed0f477a Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:08:07 +0200 Subject: [PATCH 06/22] feat: android aab signing - ensures non-empty alias --- .../src/lib/commands/signAndroid/signAndroid.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 9513b3413..9a2b68268 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -234,6 +234,10 @@ async function signAab({ ); } + if (!keyAlias) { + throw new RockError('Missing or empty alias. A valid alias must be provided as the last argument.') + } + // For AAB files, we use jarsigner instead of apksigner // jarsigner -keystore "" -storepass "" -keypass "" const jarsignerArgs = [ @@ -243,7 +247,7 @@ async function signAab({ keystorePassword, ...(keyPassword ? ['-keypass', keyPassword] : []), path, - keyAlias ?? '' + keyAlias ]; try { From 7a69dd58d465fe0c403cce89cc04fb63e7a8a74b Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:17:04 +0200 Subject: [PATCH 07/22] feat: android aab signing - avoids shadowing node:path --- .../src/lib/commands/signAndroid/command.ts | 6 ++--- .../lib/commands/signAndroid/signAndroid.ts | 24 +++++++++---------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index 296bb8646..ff1aa4501 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -16,7 +16,7 @@ export type SignFlags = { const ARGUMENTS = [ { - name: 'path', + name: 'binaryPath', description: 'Archive (apk or aab) file path', }, ]; @@ -66,9 +66,9 @@ export const registerSignCommand = (api: PluginApi) => { description: 'Sign the Android app with modified JS bundle.', args: ARGUMENTS, options: OPTIONS, - action: async (path, flags: SignFlags) => { + action: async (binaryPath, flags: SignFlags) => { await signAndroid({ - path, + binaryPath, keystorePath: flags.keystore, keystorePassword: flags.keystorePassword, keyAlias: flags.keyAlias, diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 9a2b68268..ee3821e47 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -16,7 +16,7 @@ import { findAndroidBuildTool, getAndroidBuildToolsPath } from '../../paths.js'; import { buildJsBundle } from './bundle.js'; export type SignAndroidOptions = { - path: string; + binaryPath: string; keystorePath?: string; keystorePassword?: string; keyAlias?: string; @@ -30,7 +30,7 @@ export type SignAndroidOptions = { export async function signAndroid(options: SignAndroidOptions) { validateOptions(options); - const extension = path.extname(options.path).slice(1); + const extension = path.extname(options.binaryPath).slice(1); intro(`Modifying ${extension.toUpperCase()} file`); @@ -67,7 +67,7 @@ export async function signAndroid(options: SignAndroidOptions) { loader.start(`Initializing output ${extension.toUpperCase()}...`); try { - const zip = new AdmZip(options.path); + const zip = new AdmZip(options.binaryPath); // Remove old signature files zip.deleteFile('META-INF/*'); zip.writeZip(tempArchivePath); @@ -94,7 +94,7 @@ export async function signAndroid(options: SignAndroidOptions) { } // 4. Align archive before signing if apk - const outputPath = options.outputPath ?? options.path; + const outputPath = options.outputPath ?? options.binaryPath; const alignArchive = async () => { loader.start('Aligning output file...'); @@ -113,7 +113,7 @@ export async function signAndroid(options: SignAndroidOptions) { const keystorePath = options.keystorePath ?? 'android/app/debug.keystore'; const signArgs = { - path: outputPath, + binaryPath: outputPath, keystorePath, keystorePassword: options.keystorePassword ?? 'pass:android', keyAlias: options.keyAlias, @@ -136,8 +136,8 @@ export async function signAndroid(options: SignAndroidOptions) { } function validateOptions(options: SignAndroidOptions) { - if (!fs.existsSync(options.path)) { - throw new RockError(`File not found "${options.path}"`); + if (!fs.existsSync(options.binaryPath)) { + throw new RockError(`File not found "${options.binaryPath}"`); } if (options.buildJsBundle && options.jsBundlePath) { @@ -214,7 +214,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm } type SignOptions = { - path: string; + binaryPath: string; keystorePath: string; keystorePassword: string; keyAlias?: string; @@ -222,7 +222,7 @@ type SignOptions = { }; async function signAab({ - path, + binaryPath, keystorePath, keystorePassword, keyAlias, @@ -246,7 +246,7 @@ async function signAab({ "-storepass", keystorePassword, ...(keyPassword ? ['-keypass', keyPassword] : []), - path, + binaryPath, keyAlias ]; @@ -261,7 +261,7 @@ async function signAab({ } async function signApk({ - path, + binaryPath, keystorePath, keystorePassword, keyAlias, @@ -292,7 +292,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm formatPassword(keystorePassword), ...(keyAlias ? ['--ks-key-alias', keyAlias] : []), ...(keyPassword ? ['--key-pass', formatPassword(keyPassword)] : []), - path, + binaryPath, ]; try { From 6b51b8740cc83eb3e4e1feb25e26ee70d58b463a Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:19:06 +0200 Subject: [PATCH 08/22] feat: android aab signing - strips jarsigner password input --- .../src/lib/commands/signAndroid/signAndroid.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index ee3821e47..fab10d96f 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -244,8 +244,8 @@ async function signAab({ "-keystore", keystorePath, "-storepass", - keystorePassword, - ...(keyPassword ? ['-keypass', keyPassword] : []), + stripPassword(keystorePassword), + ...(keyPassword ? ['-keypass', stripPassword(keyPassword)] : []), binaryPath, keyAlias ]; @@ -323,6 +323,15 @@ function formatPassword(password: string) { return `pass:${password}`; } +/** + * jarsigner expects password info with no prefixes + * + * @see https://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html + */ +function stripPassword(password: string) { + return password.replace(/^(pass:|env:|file:)/, ''); +} + function getSignOutputPath() { return path.join(getDotRockPath(), 'android/sign'); } From 9f6b227cca26aa8ae47c92f4f6a73c6ccac05933 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 12:48:02 +0200 Subject: [PATCH 09/22] feat: android aab signing - corrects key-alias message --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index fab10d96f..aab289660 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -235,7 +235,7 @@ async function signAab({ } if (!keyAlias) { - throw new RockError('Missing or empty alias. A valid alias must be provided as the last argument.') + throw new RockError('Missing or empty alias. A valid alias must be provided using the "--key-alias" option.') } // For AAB files, we use jarsigner instead of apksigner From c3005d7cf19f0b416fd29d7165470aa2fa70bf45 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Tue, 30 Sep 2025 13:00:23 +0200 Subject: [PATCH 10/22] feat: android aab signing - uses input path for isAab checks --- .../src/lib/commands/signAndroid/signAndroid.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index aab289660..a27bcec2a 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -104,7 +104,7 @@ export async function signAndroid(options: SignAndroidOptions) { ); } - if (!isAab(outputPath)) { + if (!isAab(options.binaryPath)) { await alignArchive() } @@ -120,7 +120,7 @@ export async function signAndroid(options: SignAndroidOptions) { keyPassword: options.keyPassword, } - if (isAab(outputPath)) { + if (isAab(options.binaryPath)) { await signAab(signArgs); } else { await signApk(signArgs); @@ -128,7 +128,7 @@ export async function signAndroid(options: SignAndroidOptions) { loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); // 6. Align archive after signing if aab - if (isAab(outputPath)) { + if (isAab(options.binaryPath)) { await alignArchive() } From 4f80eb2b79d6c5a315e0ebbb5238e32a95a46ef8 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Wed, 1 Oct 2025 13:44:30 +0200 Subject: [PATCH 11/22] feat: android aab signing - updates changeset --- .changeset/whole-tools-occur.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.changeset/whole-tools-occur.md b/.changeset/whole-tools-occur.md index 8b1378917..67bbf5296 100644 --- a/.changeset/whole-tools-occur.md +++ b/.changeset/whole-tools-occur.md @@ -1 +1,5 @@ +--- +'@rock-js/platform-android': minor +--- +feat: android aab signing From 321b308bed886b8c8aec5a42e8c9e350bf969b4b Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Wed, 1 Oct 2025 13:47:12 +0200 Subject: [PATCH 12/22] feat: android aab signing - adds note on sign/align sequence --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index a27bcec2a..7b026ff38 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -94,6 +94,7 @@ export async function signAndroid(options: SignAndroidOptions) { } // 4. Align archive before signing if apk + // sign/align sequence is important, see https://developer.android.com/tools/zipalign const outputPath = options.outputPath ?? options.binaryPath; const alignArchive = async () => { @@ -128,6 +129,7 @@ export async function signAndroid(options: SignAndroidOptions) { loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); // 6. Align archive after signing if aab + // sign/align sequence is important, see https://developer.android.com/tools/zipalign if (isAab(options.binaryPath)) { await alignArchive() } From 2dd2769965b0a6ec5bf92434b44ef4bf3ba07c0c Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 16:37:56 +0200 Subject: [PATCH 13/22] feat: android aab signing - removes aab-specific signing flow --- .../lib/commands/signAndroid/signAndroid.ts | 87 +++---------------- 1 file changed, 13 insertions(+), 74 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 7b026ff38..32119f50c 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -93,21 +93,14 @@ export async function signAndroid(options: SignAndroidOptions) { ); } - // 4. Align archive before signing if apk - // sign/align sequence is important, see https://developer.android.com/tools/zipalign const outputPath = options.outputPath ?? options.binaryPath; - const alignArchive = async () => { - loader.start('Aligning output file...'); - await alignArchiveFile(tempArchivePath, outputPath); - loader.stop( - `Created output ${extension.toUpperCase()} file: ${colorLink(relativeToCwd(outputPath))}.`, - ); - } - - if (!isAab(options.binaryPath)) { - await alignArchive() - } + // 4. Align archive + loader.start('Aligning output file...'); + await alignArchiveFile(tempArchivePath, outputPath); + loader.stop( + `Created output ${extension.toUpperCase()} file: ${colorLink(relativeToCwd(outputPath))}.`, + ); // 5. Sign archive file loader.start(`Signing the ${extension.toUpperCase()} file...`); @@ -120,19 +113,10 @@ export async function signAndroid(options: SignAndroidOptions) { keyAlias: options.keyAlias, keyPassword: options.keyPassword, } - - if (isAab(options.binaryPath)) { - await signAab(signArgs); - } else { - await signApk(signArgs); - } - loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); - // 6. Align archive after signing if aab - // sign/align sequence is important, see https://developer.android.com/tools/zipalign - if (isAab(options.binaryPath)) { - await alignArchive() - } + await signArchive(signArgs); + + loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); outro('Success 🎉.'); } @@ -223,46 +207,7 @@ type SignOptions = { keyPassword?: string; }; -async function signAab({ - binaryPath, - keystorePath, - keystorePassword, - keyAlias, - keyPassword, -}: SignOptions) { - if (!fs.existsSync(keystorePath)) { - throw new RockError( - `Keystore file not found "${keystorePath}". Provide a valid keystore path using the "--keystore" option.`, - ); - } - - if (!keyAlias) { - throw new RockError('Missing or empty alias. A valid alias must be provided using the "--key-alias" option.') - } - - // For AAB files, we use jarsigner instead of apksigner - // jarsigner -keystore "" -storepass "" -keypass "" - const jarsignerArgs = [ - "-keystore", - keystorePath, - "-storepass", - stripPassword(keystorePassword), - ...(keyPassword ? ['-keypass', stripPassword(keyPassword)] : []), - binaryPath, - keyAlias - ]; - - try { - await spawn('jarsigner', jarsignerArgs); - } catch (error) { - throw new RockError( - `Failed to sign AAB file: jarsigner ${jarsignerArgs.join(' ')}`, - { cause: (error as SubprocessError).stderr }, - ); - } -} - -async function signApk({ +async function signArchive({ binaryPath, keystorePath, keystorePassword, @@ -285,6 +230,8 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm ); } + const aabArgs = isAab(binaryPath) ? ['--min-sdk-version', '24'] : []; + // apksigner sign --ks-pass "pass:android" --ks "android/app/debug.keystore" --ks-key-alias "androiddebugkey" --key-pass "pass:android" "$OUTPUT2_APK" const apksignerArgs = [ 'sign', @@ -294,6 +241,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm formatPassword(keystorePassword), ...(keyAlias ? ['--ks-key-alias', keyAlias] : []), ...(keyPassword ? ['--key-pass', formatPassword(keyPassword)] : []), + ...aabArgs, binaryPath, ]; @@ -325,15 +273,6 @@ function formatPassword(password: string) { return `pass:${password}`; } -/** - * jarsigner expects password info with no prefixes - * - * @see https://docs.oracle.com/javase/6/docs/technotes/tools/windows/jarsigner.html - */ -function stripPassword(password: string) { - return password.replace(/^(pass:|env:|file:)/, ''); -} - function getSignOutputPath() { return path.join(getDotRockPath(), 'android/sign'); } From c617315a3c4a1d7ec2a00b7ee6f3b99d862dc955 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 16:49:12 +0200 Subject: [PATCH 14/22] feat: android aab signing - minimizes changes --- .../src/lib/commands/signAndroid/signAndroid.ts | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 32119f50c..f9caf0a15 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -93,10 +93,9 @@ export async function signAndroid(options: SignAndroidOptions) { ); } - const outputPath = options.outputPath ?? options.binaryPath; - // 4. Align archive loader.start('Aligning output file...'); + const outputPath = options.outputPath ?? options.binaryPath; await alignArchiveFile(tempArchivePath, outputPath); loader.stop( `Created output ${extension.toUpperCase()} file: ${colorLink(relativeToCwd(outputPath))}.`, @@ -105,17 +104,13 @@ export async function signAndroid(options: SignAndroidOptions) { // 5. Sign archive file loader.start(`Signing the ${extension.toUpperCase()} file...`); const keystorePath = options.keystorePath ?? 'android/app/debug.keystore'; - - const signArgs = { + await signArchive({ binaryPath: outputPath, keystorePath, keystorePassword: options.keystorePassword ?? 'pass:android', keyAlias: options.keyAlias, keyPassword: options.keyPassword, - } - - await signArchive(signArgs); - + }); loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); outro('Success 🎉.'); From 5863312da2922d0743b61d9549217d0b494e26ba Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:04:47 +0200 Subject: [PATCH 15/22] feat: android aab signing - adds --min-sdk-version arg --- .../src/lib/commands/signAndroid/command.ts | 6 ++++++ .../src/lib/commands/signAndroid/signAndroid.ts | 16 +++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index ff1aa4501..ca00d53c1 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -12,6 +12,7 @@ export type SignFlags = { buildJsbundle?: boolean; jsbundle?: string; noHermes?: boolean; + minSdkVersion?: string; }; const ARGUMENTS = [ @@ -58,6 +59,10 @@ const OPTIONS = [ name: '--no-hermes', description: 'Do not use Hermes to build the JS bundle.', }, + { + name: '--min-sdk-version', + description: 'Override for apksigner\'s --min-sdk-version.', + }, ]; export const registerSignCommand = (api: PluginApi) => { @@ -77,6 +82,7 @@ export const registerSignCommand = (api: PluginApi) => { buildJsBundle: flags.buildJsbundle, jsBundlePath: flags.jsbundle, useHermes: !flags.noHermes, + minSdkVersion: flags.minSdkVersion }); }, }); diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index f9caf0a15..0bfd54173 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -25,6 +25,7 @@ export type SignAndroidOptions = { buildJsBundle?: boolean; jsBundlePath?: string; useHermes?: boolean; + minSdkVersion?: string; }; export async function signAndroid(options: SignAndroidOptions) { @@ -110,6 +111,7 @@ export async function signAndroid(options: SignAndroidOptions) { keystorePassword: options.keystorePassword ?? 'pass:android', keyAlias: options.keyAlias, keyPassword: options.keyPassword, + minSdkVersion: options.minSdkVersion, }); loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); @@ -200,6 +202,7 @@ type SignOptions = { keystorePassword: string; keyAlias?: string; keyPassword?: string; + minSdkVersion?: string; }; async function signArchive({ @@ -208,6 +211,7 @@ async function signArchive({ keystorePassword, keyAlias, keyPassword, + minSdkVersion }: SignOptions) { if (!fs.existsSync(keystorePath)) { throw new RockError( @@ -225,8 +229,6 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm ); } - const aabArgs = isAab(binaryPath) ? ['--min-sdk-version', '24'] : []; - // apksigner sign --ks-pass "pass:android" --ks "android/app/debug.keystore" --ks-key-alias "androiddebugkey" --key-pass "pass:android" "$OUTPUT2_APK" const apksignerArgs = [ 'sign', @@ -236,7 +238,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm formatPassword(keystorePassword), ...(keyAlias ? ['--ks-key-alias', keyAlias] : []), ...(keyPassword ? ['--key-pass', formatPassword(keyPassword)] : []), - ...aabArgs, + ...getSdkVersionArgs(isAab(binaryPath), minSdkVersion), binaryPath, ]; @@ -272,6 +274,14 @@ function getSignOutputPath() { return path.join(getDotRockPath(), 'android/sign'); } +function getSdkVersionArgs(aab?: boolean, minSdkVersion?: string) { + if (!aab && !minSdkVersion) { + return []; + } + + return ['--min-sdk-version', minSdkVersion || '36']; +} + function isAab(filePath: string): boolean { return path.extname(filePath).toLowerCase() === '.aab'; } \ No newline at end of file From f7448cb17ff65f28fa64018aa08bfe3daa84a760 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:11:56 +0200 Subject: [PATCH 16/22] feat: android aab signing - adds --min-sdk-version note in the docs --- website/src/docs/cli/introduction.md | 1 + 1 file changed, 1 insertion(+) diff --git a/website/src/docs/cli/introduction.md b/website/src/docs/cli/introduction.md index 9f7151a80..771953f8e 100644 --- a/website/src/docs/cli/introduction.md +++ b/website/src/docs/cli/introduction.md @@ -208,6 +208,7 @@ The `sign:ios` command either signs your iOS app with certificates and provision | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | +| `--min-sdk-version` | API level apksigner will use to determine signing strength. | ## Platform Android From 2f5fcf7d319fd18aad6681b4c45c081018a10d86 Mon Sep 17 00:00:00 2001 From: Marek Lisik <119244317+mlisikbf@users.noreply.github.com> Date: Fri, 17 Oct 2025 17:17:00 +0200 Subject: [PATCH 17/22] Update packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Michał Pierzchała --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 0bfd54173..75f0c2275 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -78,7 +78,7 @@ export async function signAndroid(options: SignAndroidOptions) { { cause: (error as SubprocessError).stderr }, ); } - loader.stop(`Initialized output file.`); + loader.stop(`Initialized output ${extension.toUpperCase()}`); // 3. Replace JS bundle if provided if (options.jsBundlePath) { From d09a08793588026fce13415c8f93881cf9698d35 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:20:37 +0200 Subject: [PATCH 18/22] feat: android aab signing - adds applies prettier --- .../src/lib/commands/signAndroid/command.ts | 6 +++--- .../src/lib/commands/signAndroid/signAndroid.ts | 8 +++++--- website/src/docs/cli/introduction.md | 6 +++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index ca00d53c1..ff3b2ef58 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -60,8 +60,8 @@ const OPTIONS = [ description: 'Do not use Hermes to build the JS bundle.', }, { - name: '--min-sdk-version', - description: 'Override for apksigner\'s --min-sdk-version.', + name: '--min-sdk-version', + description: "Override for apksigner's --min-sdk-version.", }, ]; @@ -82,7 +82,7 @@ export const registerSignCommand = (api: PluginApi) => { buildJsBundle: flags.buildJsbundle, jsBundlePath: flags.jsbundle, useHermes: !flags.noHermes, - minSdkVersion: flags.minSdkVersion + minSdkVersion: flags.minSdkVersion, }); }, }); diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 75f0c2275..596577773 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -113,7 +113,9 @@ export async function signAndroid(options: SignAndroidOptions) { keyPassword: options.keyPassword, minSdkVersion: options.minSdkVersion, }); - loader.stop(`Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`); + loader.stop( + `Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`, + ); outro('Success 🎉.'); } @@ -211,7 +213,7 @@ async function signArchive({ keystorePassword, keyAlias, keyPassword, - minSdkVersion + minSdkVersion, }: SignOptions) { if (!fs.existsSync(keystorePath)) { throw new RockError( @@ -284,4 +286,4 @@ function getSdkVersionArgs(aab?: boolean, minSdkVersion?: string) { function isAab(filePath: string): boolean { return path.extname(filePath).toLowerCase() === '.aab'; -} \ No newline at end of file +} diff --git a/website/src/docs/cli/introduction.md b/website/src/docs/cli/introduction.md index 771953f8e..bc0b8b866 100644 --- a/website/src/docs/cli/introduction.md +++ b/website/src/docs/cli/introduction.md @@ -247,15 +247,15 @@ Same as for `build:android` and: The `sign:android ` command signs your Android app with a keystore, producing a signed APK or AAB file ready for distribution. It allows for replacing the JS bundle with a new version. -| Argument | Description | -| :----------- | :------------------- | +| Argument | Description | +| :----------- | :-------------------------- | | `binaryPath` | Path to the APK or AAB file | | Option | Description | | :----------------------------- | :---------------------------------------- | | `--keystore ` | Path to keystore file | | `--keystore-password ` | Password for keystore file | -| `--output ` | Path to output APK or AAB file | +| `--output ` | Path to output APK or AAB file | | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | From 6fcdad1db0c263b5dee078d1d0c2f07997122838 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:21:51 +0200 Subject: [PATCH 19/22] feat: android aab signing - moves doc comment --- website/src/docs/cli/introduction.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/website/src/docs/cli/introduction.md b/website/src/docs/cli/introduction.md index bc0b8b866..3056c7f82 100644 --- a/website/src/docs/cli/introduction.md +++ b/website/src/docs/cli/introduction.md @@ -208,7 +208,6 @@ The `sign:ios` command either signs your iOS app with certificates and provision | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | -| `--min-sdk-version` | API level apksigner will use to determine signing strength. | ## Platform Android @@ -259,6 +258,7 @@ The `sign:android ` command signs your Android app with a keystore, | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | +| `--min-sdk-version` | API level apksigner will use to determine signing strength. | ## Platform HarmonyOS (experimental) From 7bed7a28bddf84ecac3e4e025ef1008d23b04ff9 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:24:42 +0200 Subject: [PATCH 20/22] feat: android aab signing - removes reduntant brace --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 596577773..6ec082e4b 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -154,7 +154,7 @@ async function replaceJsBundle({ zip.writeZip(archivePath); } catch (error) { throw new RockError( - `Failed to replace JS bundle in destination file: ${archivePath}}`, + `Failed to replace JS bundle in destination file: ${archivePath}`, { cause: error }, ); } From 829f423f6672c014fc49215841bba241b6e5ac60 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 17:26:34 +0200 Subject: [PATCH 21/22] feat: android aab signing - adds comment on default --min-sdk-version --- .../src/lib/commands/signAndroid/signAndroid.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 6ec082e4b..79c89694a 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -281,6 +281,8 @@ function getSdkVersionArgs(aab?: boolean, minSdkVersion?: string) { return []; } + // the default here will only be applied for AABs + // we use a higher value as it is irrelevant for AAB verification, but allows apksigner to use better signing algorithms return ['--min-sdk-version', minSdkVersion || '36']; } From 83d7cd46bead471b52d3549267b4beaa303e1c89 Mon Sep 17 00:00:00 2001 From: mlisikbf Date: Fri, 17 Oct 2025 18:12:54 +0200 Subject: [PATCH 22/22] feat: android aab signing - removes --new-sdk-version from sign command --- .../src/lib/commands/signAndroid/command.ts | 6 ------ .../src/lib/commands/signAndroid/signAndroid.ts | 16 +--------------- website/src/docs/cli/introduction.md | 1 - 3 files changed, 1 insertion(+), 22 deletions(-) diff --git a/packages/platform-android/src/lib/commands/signAndroid/command.ts b/packages/platform-android/src/lib/commands/signAndroid/command.ts index ff3b2ef58..ff1aa4501 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/command.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/command.ts @@ -12,7 +12,6 @@ export type SignFlags = { buildJsbundle?: boolean; jsbundle?: string; noHermes?: boolean; - minSdkVersion?: string; }; const ARGUMENTS = [ @@ -59,10 +58,6 @@ const OPTIONS = [ name: '--no-hermes', description: 'Do not use Hermes to build the JS bundle.', }, - { - name: '--min-sdk-version', - description: "Override for apksigner's --min-sdk-version.", - }, ]; export const registerSignCommand = (api: PluginApi) => { @@ -82,7 +77,6 @@ export const registerSignCommand = (api: PluginApi) => { buildJsBundle: flags.buildJsbundle, jsBundlePath: flags.jsbundle, useHermes: !flags.noHermes, - minSdkVersion: flags.minSdkVersion, }); }, }); diff --git a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts index 79c89694a..87f930749 100644 --- a/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts +++ b/packages/platform-android/src/lib/commands/signAndroid/signAndroid.ts @@ -25,7 +25,6 @@ export type SignAndroidOptions = { buildJsBundle?: boolean; jsBundlePath?: string; useHermes?: boolean; - minSdkVersion?: string; }; export async function signAndroid(options: SignAndroidOptions) { @@ -111,7 +110,6 @@ export async function signAndroid(options: SignAndroidOptions) { keystorePassword: options.keystorePassword ?? 'pass:android', keyAlias: options.keyAlias, keyPassword: options.keyPassword, - minSdkVersion: options.minSdkVersion, }); loader.stop( `Signed the ${extension.toUpperCase()} file with keystore: ${colorLink(keystorePath)}.`, @@ -204,7 +202,6 @@ type SignOptions = { keystorePassword: string; keyAlias?: string; keyPassword?: string; - minSdkVersion?: string; }; async function signArchive({ @@ -213,7 +210,6 @@ async function signArchive({ keystorePassword, keyAlias, keyPassword, - minSdkVersion, }: SignOptions) { if (!fs.existsSync(keystorePath)) { throw new RockError( @@ -240,7 +236,7 @@ Please follow instructions at: https://reactnative.dev/docs/set-up-your-environm formatPassword(keystorePassword), ...(keyAlias ? ['--ks-key-alias', keyAlias] : []), ...(keyPassword ? ['--key-pass', formatPassword(keyPassword)] : []), - ...getSdkVersionArgs(isAab(binaryPath), minSdkVersion), + ...(isAab(binaryPath) ? ['--min-sdk-version', '36'] : []), binaryPath, ]; @@ -276,16 +272,6 @@ function getSignOutputPath() { return path.join(getDotRockPath(), 'android/sign'); } -function getSdkVersionArgs(aab?: boolean, minSdkVersion?: string) { - if (!aab && !minSdkVersion) { - return []; - } - - // the default here will only be applied for AABs - // we use a higher value as it is irrelevant for AAB verification, but allows apksigner to use better signing algorithms - return ['--min-sdk-version', minSdkVersion || '36']; -} - function isAab(filePath: string): boolean { return path.extname(filePath).toLowerCase() === '.aab'; } diff --git a/website/src/docs/cli/introduction.md b/website/src/docs/cli/introduction.md index 3056c7f82..a5f840d68 100644 --- a/website/src/docs/cli/introduction.md +++ b/website/src/docs/cli/introduction.md @@ -258,7 +258,6 @@ The `sign:android ` command signs your Android app with a keystore, | `--build-jsbundle` | Build JS bundle before signing | | `--jsbundle ` | Path to JS bundle to apply before signing | | `--no-hermes` | Don't use Hermes for JS bundle | -| `--min-sdk-version` | API level apksigner will use to determine signing strength. | ## Platform HarmonyOS (experimental)