From e8cb7856e64c4c40b3bba53cd186f81daf23f510 Mon Sep 17 00:00:00 2001 From: "Woojae, Park" <124398+nikescar@users.noreply.github.com> Date: Thu, 31 Jul 2025 14:17:25 +0900 Subject: [PATCH 1/4] fix. x build --arch x64 --platform android error. --- xbuild/src/cargo/mod.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/xbuild/src/cargo/mod.rs b/xbuild/src/cargo/mod.rs index 23646d5..164c53a 100644 --- a/xbuild/src/cargo/mod.rs +++ b/xbuild/src/cargo/mod.rs @@ -330,7 +330,9 @@ impl CargoBuild { self.set_sysroot(&path); self.add_cxxflag("-stdlib=libc++"); self.add_cflag(&format!("-mmacosx-version-min={minimum_version}")); - self.add_link_arg("--target=x86_64-apple-darwin"); + if let Some(triple) = self.triple { + self.add_link_arg(&format!("--target={triple}")); + } self.add_link_arg(&format!("-mmacosx-version-min={minimum_version}")); self.add_link_arg("-rpath"); self.add_link_arg("@executable_path/../Frameworks"); @@ -360,7 +362,9 @@ impl CargoBuild { self.set_sysroot(&path); self.add_cxxflag("-stdlib=libc++"); self.add_cflag(&format!("-miphoneos-version-min={minimum_version}")); - self.add_link_arg("--target=arm64-apple-ios"); + if let Some(triple) = self.triple { + self.add_link_arg(&format!("--target={triple}")); + } self.add_link_arg(&format!("-miphoneos-version-min={minimum_version}")); self.add_link_arg("-rpath"); self.add_link_arg("@executable_path/Frameworks"); From 6ac8ed9da25d88182fc465d1d98dd81e2a2563cb Mon Sep 17 00:00:00 2001 From: "Woojae, Park" <124398+nikescar@users.noreply.github.com> Date: Thu, 31 Jul 2025 18:57:20 +0900 Subject: [PATCH 2/4] fix. play store aab BundleConfig.pb not found error. --- xbuild/src/command/build.rs | 20 +- xbuild/src/gradle/mod.rs | 374 +++++++++++++++++++++++++++++++++++- xbuild/src/lib.rs | 45 +++++ 3 files changed, 437 insertions(+), 2 deletions(-) diff --git a/xbuild/src/command/build.rs b/xbuild/src/command/build.rs index 363b054..a125c58 100644 --- a/xbuild/src/command/build.rs +++ b/xbuild/src/command/build.rs @@ -75,7 +75,19 @@ pub fn build(env: &BuildEnv) -> Result<()> { } } Platform::Android => { - let out = platform_dir.join(format!("{}.{}", env.name(), env.target().format())); + // Include architecture in filename for Android builds + let arch_suffix = if env.target().archs().len() == 1 { + format!("-{}", env.target().archs()[0]) + } else { + // Multi-arch build, use "universal" or concatenate all archs + if env.target().archs().len() > 1 { + let archs: Vec = env.target().archs().iter().map(|a| a.to_string()).collect(); + format!("-{}", archs.join("-")) + } else { + String::new() + } + }; + let out = platform_dir.join(format!("{}{}.{}", env.name(), arch_suffix, env.target().format())); ensure!(has_lib, "Android APKs/AABs require a library"); let mut libraries = vec![]; @@ -200,6 +212,7 @@ pub fn build(env: &BuildEnv) -> Result<()> { runner.end_verbose_task(); return Ok(()); } else { + let out_clone = out.clone(); let mut apk = Apk::new( out, env.config().android().manifest.clone(), @@ -220,6 +233,11 @@ pub fn build(env: &BuildEnv) -> Result<()> { } apk.finish(env.target().signer().cloned())?; + + // Handle additional Android signing if release build and signing parameters are provided + if env.target().opt() == Opt::Release { + crate::gradle::handle_android_signing_for_apk(env, &out_clone)?; + } } } Platform::Macos => { diff --git a/xbuild/src/gradle/mod.rs b/xbuild/src/gradle/mod.rs index 3595533..8821e2a 100644 --- a/xbuild/src/gradle/mod.rs +++ b/xbuild/src/gradle/mod.rs @@ -1,4 +1,4 @@ -use crate::{task, BuildEnv, Format, Opt}; +use crate::{task, BuildEnv, Format, Opt, Store}; use anyhow::{Context, Result}; use apk::Target; use std::path::{Path, PathBuf}; @@ -9,6 +9,244 @@ static GRADLE_PROPERTIES: &[u8] = include_bytes!("./gradle.properties"); static SETTINGS_GRADLE: &[u8] = include_bytes!("./settings.gradle"); static IC_LAUNCHER: &[u8] = include_bytes!("./ic_launcher.xml"); +/// Generate a default Android keystore for signing if none provided +fn generate_default_keystore(env: &BuildEnv, keystore_path: &Path, password: &str, domain: &str) -> Result<()> { + std::fs::create_dir_all(keystore_path.parent().unwrap())?; + + let dname = format!("CN={}, OU=NA, O=Company, L=City, S=State, C=US", domain); + let pkg_name = &env.name(); + let alias_name = format!("{}-release-key", pkg_name); + + task::run( + Command::new("keytool") + .arg("-genkeypair") + .arg("-v") + .arg("-noprompt") + .arg("-storetype") + .arg("PKCS12") + .arg("-alias") + .arg(&alias_name) + .arg("-keystore") + .arg(keystore_path) + .arg("-keyalg") + .arg("RSA") + .arg("-keysize") + .arg("2048") + .arg("-validity") + .arg("10000") + .arg("-storepass") + .arg(password) + .arg("-keypass") + .arg(password) + .arg("-dname") + .arg(&dname), + )?; + + // Export the certificate for upload to Google Play + let pem_path = keystore_path.parent().unwrap().join(format!("{}-release-upload-certificate.pem", pkg_name)); + task::run( + Command::new("keytool") + .arg("-export") + .arg("-rfc") + .arg("-v") + .arg("-noprompt") + .arg("-storepass") + .arg(password) + .arg("-keypass") + .arg(password) + .arg("-keystore") + .arg(keystore_path) + .arg("-alias") + .arg(&alias_name) + .arg("-file") + .arg(&pem_path), + )?; + + Ok(()) +} + +/// Sign AAB with jarsigner +fn sign_aab_with_jarsigner( + aab_path: &Path, + keystore_path: &Path, + storepass: &str, + keyname: &str, + keypass: &str, +) -> Result<()> { + task::run( + Command::new("jarsigner") + .arg("-storepass") + .arg(storepass) + .arg("-keypass") + .arg(keypass) + .arg("-keystore") + .arg(keystore_path) + .arg(aab_path) + .arg(keyname), + )?; + Ok(()) +} + +/// Sign APK with apksigner +fn sign_apk_with_apksigner( + apk_path: &Path, + keystore_path: &Path, + storepass: &str, + keyname: &str, + keypass: &str, +) -> Result<()> { + println!("Starting APK signing process..."); + println!("Input APK: {}", apk_path.display()); + println!("Keystore: {}", keystore_path.display()); + + // First align the APK + let aligned_path = apk_path.with_extension("aligned.apk"); + println!("Aligned APK path: {}", aligned_path.display()); + + // Find zipalign in Android SDK + let android_home = std::env::var("ANDROID_HOME") + .or_else(|_| std::env::var("ANDROID_SDK_ROOT")) + .context("ANDROID_HOME or ANDROID_SDK_ROOT environment variable not set")?; + + let build_tools_dir = Path::new(&android_home).join("build-tools"); + let build_tools_version = std::fs::read_dir(&build_tools_dir)? + .filter_map(|entry| entry.ok()) + .filter(|entry| entry.path().is_dir()) + .map(|entry| entry.file_name().to_string_lossy().to_string()) + .max() + .context("No build-tools found in Android SDK")?; + + let build_tools_path = build_tools_dir.join(&build_tools_version); + let zipalign = build_tools_path.join("zipalign"); + let apksigner = build_tools_path.join("apksigner"); + + println!("Using build tools: {}", build_tools_path.display()); + println!("zipalign: {}", zipalign.display()); + println!("apksigner: {}", apksigner.display()); + + // Align the APK + println!("Aligning APK..."); + task::run( + Command::new(&zipalign) + .arg("-v") + .arg("4") + .arg(apk_path) + .arg(&aligned_path), + )?; + println!("APK alignment completed"); + + // Sign the aligned APK + let apk_name = apk_path.file_stem().unwrap().to_string_lossy(); + let apk_dir = apk_path.parent().unwrap(); + let signed_path = apk_dir.join(format!("{}-signed.apk", apk_name)); + + println!("Signing aligned APK..."); + println!("Signed APK will be created at: {}", signed_path.display()); + + task::run( + Command::new(&apksigner) + .arg("sign") + .arg("--ks") + .arg(keystore_path) + .arg("--ks-key-alias") + .arg(keyname) + .arg("--ks-pass") + .arg(&format!("pass:{}", storepass)) + .arg("--key-pass") + .arg(&format!("pass:{}", keypass)) + .arg("--out") + .arg(&signed_path) + .arg(&aligned_path), + )?; + + // Verify the signed APK was created + if !signed_path.exists() { + return Err(anyhow::anyhow!("Signed APK was not created at: {}", signed_path.display())); + } + + // Verify the APK signature + println!("Verifying APK signature..."); + let verify_result = Command::new(&apksigner) + .arg("verify") + .arg("--verbose") + .arg("--print-certs") + .arg(&signed_path) + .output(); + + match verify_result { + Ok(output) => { + if output.status.success() { + println!("✓ APK signature verification passed"); + println!("Signature details:\n{}", String::from_utf8_lossy(&output.stdout)); + } else { + println!("⚠ APK signature verification failed: {}", String::from_utf8_lossy(&output.stderr)); + } + } + Err(e) => { + println!("⚠ Could not verify APK signature: {}", e); + } + } + + println!("✓ Signed APK created successfully"); + println!("✓ Unsigned APK: {}", apk_path.display()); + println!("✓ Signed APK: {}", signed_path.display()); + println!("APK signing process completed successfully"); + + // Clean up the aligned APK + let _ = std::fs::remove_file(&aligned_path); + + Ok(()) +} + +/// Create encrypted keystore for Google Play using pepk.jar +fn create_encrypted_keystore_for_play( + keystore_path: &Path, + keyname: &str, + _storepass: &str, + _keypass: &str, + pubkey_path: &Path, + output_path: &Path, +) -> Result<()> { + // Download pepk.jar if it doesn't exist + let pepk_jar_path = std::env::temp_dir().join("pepk.jar"); + if !pepk_jar_path.exists() { + let response = reqwest::blocking::get("https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk.jar") + .context("Failed to download pepk.jar")?; + + let bytes = response.bytes().context("Failed to read pepk.jar response")?; + std::fs::write(&pepk_jar_path, &bytes)?; + } + + task::run( + Command::new("java") + .arg("-jar") + .arg(&pepk_jar_path) + .arg("--keystore") + .arg(keystore_path) + .arg("--alias") + .arg(keyname) + .arg("--output") + .arg(output_path) + .arg("--include-cert") + .arg("--rsa-aes-encryption") + .arg("--encryption-key-path") + .arg(pubkey_path), + )?; + + Ok(()) +} + +/// Get domain from manifest for keystore generation +fn get_domain_from_manifest(env: &BuildEnv) -> String { + env.config() + .android() + .manifest + .package + .as_ref() + .unwrap_or(&"com.example.app".to_string()) + .clone() +} + pub fn prepare(env: &BuildEnv) -> Result<()> { let config = env.config().android(); if config.wry { @@ -88,6 +326,11 @@ pub fn build(env: &BuildEnv, libraries: Vec<(Target, PathBuf)>, out: &Path) -> R versionCode {version_code} versionName '{version_name}' }} + packagingOptions {{ + jniLibs {{ + useLegacyPackaging = true + }} + }} {asset_packs} }} dependencies {{ @@ -226,6 +469,135 @@ pub fn build(env: &BuildEnv, libraries: Vec<(Target, PathBuf)>, out: &Path) -> R (Format::Aab, Opt::Release) => "app-release.aab", _ => unreachable!(), }); + + // Handle signing if release build and signing parameters are provided + if opt == Opt::Release { + handle_android_signing(env, &output, format)?; + } + std::fs::copy(output, out)?; Ok(()) } + +/// Handle Android signing for AAB and APK files +fn handle_android_signing(env: &BuildEnv, file_path: &Path, format: Format) -> Result<()> { + let keystore_path = env.target().android_sign_keystore(); + let storepass = env.target().android_sign_storepass(); + let keyname = env.target().android_sign_keyname(); + let keypass = env.target().android_sign_keypass(); + + // Determine if we need to generate a default keystore + let (final_keystore_path, final_storepass, final_keyname, final_keypass) = + if let (Some(keystore), Some(storepass), Some(keyname), Some(keypass)) = + (keystore_path, storepass, keyname, keypass) { + (keystore.to_path_buf(), storepass.to_string(), keyname.to_string(), keypass.to_string()) + } else { + // Generate default keystore + let pkg_name = &env.name(); + let default_keystore = env.platform_dir().join("keys").join(format!("{}-release-key.keystore", pkg_name)); + let default_password = "Test123".to_string(); + let default_keyname = format!("{}-release-key", pkg_name); + let domain = get_domain_from_manifest(env); + + if !default_keystore.exists() { + println!("Generating default Android keystore..."); + generate_default_keystore(env, &default_keystore, &default_password, &domain)?; + } + + (default_keystore, default_password.clone(), default_keyname, default_password) + }; + + // Sign the file based on format + match format { + Format::Aab => { + println!("Signing AAB with jarsigner..."); + sign_aab_with_jarsigner( + file_path, + &final_keystore_path, + &final_storepass, + &final_keyname, + &final_keypass, + )?; + + // Validate the AAB after signing + println!("Validating AAB with bundletool..."); + let is_valid = validate_aab_with_bundletool(file_path)?; + if !is_valid { + println!("Warning: AAB validation failed. The BundleConfig.pb may be missing."); + } + } + Format::Apk => { + println!("Signing APK with apksigner..."); + println!("APK path: {}", file_path.display()); + sign_apk_with_apksigner( + file_path, + &final_keystore_path, + &final_storepass, + &final_keyname, + &final_keypass, + )?; + println!("APK signing completed successfully"); + } + _ => {} + } + + // Handle Google Play encryption if needed + if env.target().store() == Some(Store::Play) { + if let Some(pubkey_path) = env.target().play_app_sign_enc_pubkey() { + println!("Creating encrypted keystore for Google Play..."); + let output_zip = env.platform_dir().join("app-signing-key-encrypted.zip"); + create_encrypted_keystore_for_play( + &final_keystore_path, + &final_keyname, + &final_storepass, + &final_keypass, + pubkey_path, + &output_zip, + )?; + println!("Encrypted keystore created at: {}", output_zip.display()); + } + } + + Ok(()) +} + +/// Handle Android APK signing for non-gradle builds +pub fn handle_android_signing_for_apk(env: &BuildEnv, apk_path: &Path) -> Result<()> { + handle_android_signing(env, apk_path, Format::Apk) +} + +/// Validate AAB file using bundletool +pub fn validate_aab_with_bundletool(aab_path: &Path) -> Result { + // Download bundletool if it doesn't exist + let bundletool_jar_path = std::env::temp_dir().join("bundletool-all-1.18.1.jar"); + if !bundletool_jar_path.exists() { + println!("Downloading bundletool..."); + let response = reqwest::blocking::get("https://github.com/google/bundletool/releases/latest/download/bundletool-all-1.18.1.jar") + .context("Failed to download bundletool")?; + + let bytes = response.bytes().context("Failed to read bundletool response")?; + std::fs::write(&bundletool_jar_path, &bytes)?; + } + + // Validate the AAB and check for BundleConfig.pb + let output = Command::new("java") + .arg("-jar") + .arg(&bundletool_jar_path) + .arg("validate") + .arg("--bundle") + .arg(aab_path) + .output() + .context("Failed to run bundletool validate")?; + + if !output.status.success() { + println!("bundletool validation failed: {}", String::from_utf8_lossy(&output.stderr)); + return Ok(false); + } + + // Check for BundleConfig.pb by grepping the output + let validation_output = String::from_utf8_lossy(&output.stdout); + let bundle_config_count = validation_output.matches("BundleConfig.pb").count(); + + println!("AAB validation passed. BundleConfig.pb found: {} times", bundle_config_count); + Ok(bundle_config_count > 0) +} diff --git a/xbuild/src/lib.rs b/xbuild/src/lib.rs index 122d6b6..44fc50b 100644 --- a/xbuild/src/lib.rs +++ b/xbuild/src/lib.rs @@ -327,6 +327,21 @@ pub struct BuildTargetArgs { /// Path to an api key. #[clap(long)] api_key: Option, + /// Path to Android AAB/APK signing keystore + #[clap(long)] + android_sign_keystore: Option, + /// Android AAB/APK signing keystore password + #[clap(long)] + android_sign_storepass: Option, + /// Android AAB/APK signing key name/alias + #[clap(long)] + android_sign_keyname: Option, + /// Android AAB/APK signing key password + #[clap(long)] + android_sign_keypass: Option, + /// Path to Google Play app signing encryption public key (for --store play) + #[clap(long)] + play_app_sign_enc_pubkey: Option, } impl BuildTargetArgs { @@ -423,6 +438,11 @@ impl BuildTargetArgs { provisioning_profile, api_key, android_gradle, + android_sign_keystore: self.android_sign_keystore, + android_sign_storepass: self.android_sign_storepass, + android_sign_keyname: self.android_sign_keyname, + android_sign_keypass: self.android_sign_keypass, + play_app_sign_enc_pubkey: self.play_app_sign_enc_pubkey, }) } } @@ -439,6 +459,11 @@ pub struct BuildTarget { provisioning_profile: Option>, api_key: Option, android_gradle: bool, + android_sign_keystore: Option, + android_sign_storepass: Option, + android_sign_keyname: Option, + android_sign_keypass: Option, + play_app_sign_enc_pubkey: Option, } impl BuildTarget { @@ -490,6 +515,26 @@ impl BuildTarget { pub fn api_key(&self) -> Option<&Path> { self.api_key.as_deref() } + + pub fn android_sign_keystore(&self) -> Option<&Path> { + self.android_sign_keystore.as_deref() + } + + pub fn android_sign_storepass(&self) -> Option<&str> { + self.android_sign_storepass.as_deref() + } + + pub fn android_sign_keyname(&self) -> Option<&str> { + self.android_sign_keyname.as_deref() + } + + pub fn android_sign_keypass(&self) -> Option<&str> { + self.android_sign_keypass.as_deref() + } + + pub fn play_app_sign_enc_pubkey(&self) -> Option<&Path> { + self.play_app_sign_enc_pubkey.as_deref() + } } pub struct BuildEnv { From 1abb19d12e33afa82691c23333fd6a2f3cda4513 Mon Sep 17 00:00:00 2001 From: "Woojae, Park" <124398+nikescar@users.noreply.github.com> Date: Tue, 5 Aug 2025 22:01:47 +0900 Subject: [PATCH 3/4] fix. lint error. --- xbuild/src/command/build.rs | 12 +++- xbuild/src/gradle/mod.rs | 129 ++++++++++++++++++++++++------------ 2 files changed, 95 insertions(+), 46 deletions(-) diff --git a/xbuild/src/command/build.rs b/xbuild/src/command/build.rs index a125c58..33ca054 100644 --- a/xbuild/src/command/build.rs +++ b/xbuild/src/command/build.rs @@ -81,13 +81,19 @@ pub fn build(env: &BuildEnv) -> Result<()> { } else { // Multi-arch build, use "universal" or concatenate all archs if env.target().archs().len() > 1 { - let archs: Vec = env.target().archs().iter().map(|a| a.to_string()).collect(); + let archs: Vec = + env.target().archs().iter().map(|a| a.to_string()).collect(); format!("-{}", archs.join("-")) } else { String::new() } }; - let out = platform_dir.join(format!("{}{}.{}", env.name(), arch_suffix, env.target().format())); + let out = platform_dir.join(format!( + "{}{}.{}", + env.name(), + arch_suffix, + env.target().format() + )); ensure!(has_lib, "Android APKs/AABs require a library"); let mut libraries = vec![]; @@ -233,7 +239,7 @@ pub fn build(env: &BuildEnv) -> Result<()> { } apk.finish(env.target().signer().cloned())?; - + // Handle additional Android signing if release build and signing parameters are provided if env.target().opt() == Opt::Release { crate::gradle::handle_android_signing_for_apk(env, &out_clone)?; diff --git a/xbuild/src/gradle/mod.rs b/xbuild/src/gradle/mod.rs index 8821e2a..1cbc52a 100644 --- a/xbuild/src/gradle/mod.rs +++ b/xbuild/src/gradle/mod.rs @@ -10,13 +10,18 @@ static SETTINGS_GRADLE: &[u8] = include_bytes!("./settings.gradle"); static IC_LAUNCHER: &[u8] = include_bytes!("./ic_launcher.xml"); /// Generate a default Android keystore for signing if none provided -fn generate_default_keystore(env: &BuildEnv, keystore_path: &Path, password: &str, domain: &str) -> Result<()> { +fn generate_default_keystore( + env: &BuildEnv, + keystore_path: &Path, + password: &str, + domain: &str, +) -> Result<()> { std::fs::create_dir_all(keystore_path.parent().unwrap())?; - + let dname = format!("CN={}, OU=NA, O=Company, L=City, S=State, C=US", domain); let pkg_name = &env.name(); let alias_name = format!("{}-release-key", pkg_name); - + task::run( Command::new("keytool") .arg("-genkeypair") @@ -41,9 +46,12 @@ fn generate_default_keystore(env: &BuildEnv, keystore_path: &Path, password: &st .arg("-dname") .arg(&dname), )?; - + // Export the certificate for upload to Google Play - let pem_path = keystore_path.parent().unwrap().join(format!("{}-release-upload-certificate.pem", pkg_name)); + let pem_path = keystore_path + .parent() + .unwrap() + .join(format!("{}-release-upload-certificate.pem", pkg_name)); task::run( Command::new("keytool") .arg("-export") @@ -61,13 +69,13 @@ fn generate_default_keystore(env: &BuildEnv, keystore_path: &Path, password: &st .arg("-file") .arg(&pem_path), )?; - + Ok(()) } /// Sign AAB with jarsigner fn sign_aab_with_jarsigner( - aab_path: &Path, + aab_path: &Path, keystore_path: &Path, storepass: &str, keyname: &str, @@ -98,16 +106,16 @@ fn sign_apk_with_apksigner( println!("Starting APK signing process..."); println!("Input APK: {}", apk_path.display()); println!("Keystore: {}", keystore_path.display()); - + // First align the APK let aligned_path = apk_path.with_extension("aligned.apk"); println!("Aligned APK path: {}", aligned_path.display()); - + // Find zipalign in Android SDK let android_home = std::env::var("ANDROID_HOME") .or_else(|_| std::env::var("ANDROID_SDK_ROOT")) .context("ANDROID_HOME or ANDROID_SDK_ROOT environment variable not set")?; - + let build_tools_dir = Path::new(&android_home).join("build-tools"); let build_tools_version = std::fs::read_dir(&build_tools_dir)? .filter_map(|entry| entry.ok()) @@ -115,15 +123,15 @@ fn sign_apk_with_apksigner( .map(|entry| entry.file_name().to_string_lossy().to_string()) .max() .context("No build-tools found in Android SDK")?; - + let build_tools_path = build_tools_dir.join(&build_tools_version); let zipalign = build_tools_path.join("zipalign"); let apksigner = build_tools_path.join("apksigner"); - + println!("Using build tools: {}", build_tools_path.display()); println!("zipalign: {}", zipalign.display()); println!("apksigner: {}", apksigner.display()); - + // Align the APK println!("Aligning APK..."); task::run( @@ -134,15 +142,15 @@ fn sign_apk_with_apksigner( .arg(&aligned_path), )?; println!("APK alignment completed"); - + // Sign the aligned APK let apk_name = apk_path.file_stem().unwrap().to_string_lossy(); let apk_dir = apk_path.parent().unwrap(); let signed_path = apk_dir.join(format!("{}-signed.apk", apk_name)); - + println!("Signing aligned APK..."); println!("Signed APK will be created at: {}", signed_path.display()); - + task::run( Command::new(&apksigner) .arg("sign") @@ -158,12 +166,15 @@ fn sign_apk_with_apksigner( .arg(&signed_path) .arg(&aligned_path), )?; - + // Verify the signed APK was created if !signed_path.exists() { - return Err(anyhow::anyhow!("Signed APK was not created at: {}", signed_path.display())); + return Err(anyhow::anyhow!( + "Signed APK was not created at: {}", + signed_path.display() + )); } - + // Verify the APK signature println!("Verifying APK signature..."); let verify_result = Command::new(&apksigner) @@ -172,29 +183,35 @@ fn sign_apk_with_apksigner( .arg("--print-certs") .arg(&signed_path) .output(); - + match verify_result { Ok(output) => { if output.status.success() { println!("✓ APK signature verification passed"); - println!("Signature details:\n{}", String::from_utf8_lossy(&output.stdout)); + println!( + "Signature details:\n{}", + String::from_utf8_lossy(&output.stdout) + ); } else { - println!("⚠ APK signature verification failed: {}", String::from_utf8_lossy(&output.stderr)); + println!( + "⚠ APK signature verification failed: {}", + String::from_utf8_lossy(&output.stderr) + ); } } Err(e) => { println!("⚠ Could not verify APK signature: {}", e); } } - + println!("✓ Signed APK created successfully"); println!("✓ Unsigned APK: {}", apk_path.display()); println!("✓ Signed APK: {}", signed_path.display()); println!("APK signing process completed successfully"); - + // Clean up the aligned APK let _ = std::fs::remove_file(&aligned_path); - + Ok(()) } @@ -210,13 +227,17 @@ fn create_encrypted_keystore_for_play( // Download pepk.jar if it doesn't exist let pepk_jar_path = std::env::temp_dir().join("pepk.jar"); if !pepk_jar_path.exists() { - let response = reqwest::blocking::get("https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk.jar") - .context("Failed to download pepk.jar")?; - - let bytes = response.bytes().context("Failed to read pepk.jar response")?; + let response = reqwest::blocking::get( + "https://www.gstatic.com/play-apps-publisher-rapid/signing-tool/prod/pepk.jar", + ) + .context("Failed to download pepk.jar")?; + + let bytes = response + .bytes() + .context("Failed to read pepk.jar response")?; std::fs::write(&pepk_jar_path, &bytes)?; } - + task::run( Command::new("java") .arg("-jar") @@ -232,7 +253,7 @@ fn create_encrypted_keystore_for_play( .arg("--encryption-key-path") .arg(pubkey_path), )?; - + Ok(()) } @@ -487,14 +508,23 @@ fn handle_android_signing(env: &BuildEnv, file_path: &Path, format: Format) -> R let keypass = env.target().android_sign_keypass(); // Determine if we need to generate a default keystore - let (final_keystore_path, final_storepass, final_keyname, final_keypass) = - if let (Some(keystore), Some(storepass), Some(keyname), Some(keypass)) = - (keystore_path, storepass, keyname, keypass) { - (keystore.to_path_buf(), storepass.to_string(), keyname.to_string(), keypass.to_string()) + let (final_keystore_path, final_storepass, final_keyname, final_keypass) = + if let (Some(keystore), Some(storepass), Some(keyname), Some(keypass)) = + (keystore_path, storepass, keyname, keypass) + { + ( + keystore.to_path_buf(), + storepass.to_string(), + keyname.to_string(), + keypass.to_string(), + ) } else { // Generate default keystore let pkg_name = &env.name(); - let default_keystore = env.platform_dir().join("keys").join(format!("{}-release-key.keystore", pkg_name)); + let default_keystore = env + .platform_dir() + .join("keys") + .join(format!("{}-release-key.keystore", pkg_name)); let default_password = "Test123".to_string(); let default_keyname = format!("{}-release-key", pkg_name); let domain = get_domain_from_manifest(env); @@ -504,7 +534,12 @@ fn handle_android_signing(env: &BuildEnv, file_path: &Path, format: Format) -> R generate_default_keystore(env, &default_keystore, &default_password, &domain)?; } - (default_keystore, default_password.clone(), default_keyname, default_password) + ( + default_keystore, + default_password.clone(), + default_keyname, + default_password, + ) }; // Sign the file based on format @@ -518,7 +553,7 @@ fn handle_android_signing(env: &BuildEnv, file_path: &Path, format: Format) -> R &final_keyname, &final_keypass, )?; - + // Validate the AAB after signing println!("Validating AAB with bundletool..."); let is_valid = validate_aab_with_bundletool(file_path)?; @@ -574,8 +609,10 @@ pub fn validate_aab_with_bundletool(aab_path: &Path) -> Result { println!("Downloading bundletool..."); let response = reqwest::blocking::get("https://github.com/google/bundletool/releases/latest/download/bundletool-all-1.18.1.jar") .context("Failed to download bundletool")?; - - let bytes = response.bytes().context("Failed to read bundletool response")?; + + let bytes = response + .bytes() + .context("Failed to read bundletool response")?; std::fs::write(&bundletool_jar_path, &bytes)?; } @@ -590,14 +627,20 @@ pub fn validate_aab_with_bundletool(aab_path: &Path) -> Result { .context("Failed to run bundletool validate")?; if !output.status.success() { - println!("bundletool validation failed: {}", String::from_utf8_lossy(&output.stderr)); + println!( + "bundletool validation failed: {}", + String::from_utf8_lossy(&output.stderr) + ); return Ok(false); } // Check for BundleConfig.pb by grepping the output let validation_output = String::from_utf8_lossy(&output.stdout); let bundle_config_count = validation_output.matches("BundleConfig.pb").count(); - - println!("AAB validation passed. BundleConfig.pb found: {} times", bundle_config_count); + + println!( + "AAB validation passed. BundleConfig.pb found: {} times", + bundle_config_count + ); Ok(bundle_config_count > 0) } From d7e26c0b54053a4266de3d1f39d49116539d3ddb Mon Sep 17 00:00:00 2001 From: "Woojae, Park" <124398+nikescar@users.noreply.github.com> Date: Tue, 5 Aug 2025 22:11:06 +0900 Subject: [PATCH 4/4] fix. lint error. --- xbuild/src/gradle/mod.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/xbuild/src/gradle/mod.rs b/xbuild/src/gradle/mod.rs index 1cbc52a..6dd4110 100644 --- a/xbuild/src/gradle/mod.rs +++ b/xbuild/src/gradle/mod.rs @@ -18,9 +18,9 @@ fn generate_default_keystore( ) -> Result<()> { std::fs::create_dir_all(keystore_path.parent().unwrap())?; - let dname = format!("CN={}, OU=NA, O=Company, L=City, S=State, C=US", domain); + let dname = format!("CN={domain}, OU=NA, O=Company, L=City, S=State, C=US"); let pkg_name = &env.name(); - let alias_name = format!("{}-release-key", pkg_name); + let alias_name = format!("{pkg_name}-release-key"); task::run( Command::new("keytool") @@ -51,7 +51,7 @@ fn generate_default_keystore( let pem_path = keystore_path .parent() .unwrap() - .join(format!("{}-release-upload-certificate.pem", pkg_name)); + .join(format!("{pkg_name}-release-upload-certificate.pem")); task::run( Command::new("keytool") .arg("-export") @@ -146,7 +146,7 @@ fn sign_apk_with_apksigner( // Sign the aligned APK let apk_name = apk_path.file_stem().unwrap().to_string_lossy(); let apk_dir = apk_path.parent().unwrap(); - let signed_path = apk_dir.join(format!("{}-signed.apk", apk_name)); + let signed_path = apk_dir.join(format!("{apk_name}-signed.apk")); println!("Signing aligned APK..."); println!("Signed APK will be created at: {}", signed_path.display()); @@ -159,9 +159,9 @@ fn sign_apk_with_apksigner( .arg("--ks-key-alias") .arg(keyname) .arg("--ks-pass") - .arg(&format!("pass:{}", storepass)) + .arg(format!("pass:{storepass}")) .arg("--key-pass") - .arg(&format!("pass:{}", keypass)) + .arg(format!("pass:{keypass}")) .arg("--out") .arg(&signed_path) .arg(&aligned_path), @@ -200,7 +200,7 @@ fn sign_apk_with_apksigner( } } Err(e) => { - println!("⚠ Could not verify APK signature: {}", e); + println!("⚠ Could not verify APK signature: {e}"); } } @@ -524,9 +524,9 @@ fn handle_android_signing(env: &BuildEnv, file_path: &Path, format: Format) -> R let default_keystore = env .platform_dir() .join("keys") - .join(format!("{}-release-key.keystore", pkg_name)); + .join(format!("{pkg_name}-release-key.keystore")); let default_password = "Test123".to_string(); - let default_keyname = format!("{}-release-key", pkg_name); + let default_keyname = format!("{pkg_name}-release-key"); let domain = get_domain_from_manifest(env); if !default_keystore.exists() { @@ -639,8 +639,7 @@ pub fn validate_aab_with_bundletool(aab_path: &Path) -> Result { let bundle_config_count = validation_output.matches("BundleConfig.pb").count(); println!( - "AAB validation passed. BundleConfig.pb found: {} times", - bundle_config_count + "AAB validation passed. BundleConfig.pb found: {bundle_config_count} times" ); Ok(bundle_config_count > 0) }