mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 14:36:16 +01:00
Improve error handling and fix issues
This commit is contained in:
@@ -32,7 +32,7 @@ impl CertificateIdentity {
|
||||
let hash_string = hex::encode(hasher.finalize()).to_lowercase();
|
||||
let key_path = configuration_path.join("keys").join(hash_string);
|
||||
fs::create_dir_all(&key_path)
|
||||
.map_err(|e| Error::Certificate(format!("Failed to create key directory: {}", e)))?;
|
||||
.map_err(|e| Error::Filesystem(format!("Failed to create key directory: {}", e)))?;
|
||||
|
||||
let key_file = key_path.join("key.pem");
|
||||
let cert_file = key_path.join("cert.pem");
|
||||
@@ -54,7 +54,7 @@ impl CertificateIdentity {
|
||||
.private_key_to_pem_pkcs8()
|
||||
.map_err(|e| Error::Certificate(format!("Failed to encode private key: {}", e)))?;
|
||||
fs::write(&key_file, pem_data)
|
||||
.map_err(|e| Error::Certificate(format!("Failed to save key file: {}", e)))?;
|
||||
.map_err(|e| Error::Filesystem(format!("Failed to save key file: {}", e)))?;
|
||||
key
|
||||
};
|
||||
|
||||
@@ -75,7 +75,7 @@ impl CertificateIdentity {
|
||||
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
|
||||
})?;
|
||||
fs::write(&cert_identity.cert_file, cert_pem).map_err(|e| {
|
||||
Error::Certificate(format!("Failed to save certificate file: {}", e))
|
||||
Error::Filesystem(format!("Failed to save certificate file: {}", e))
|
||||
})?;
|
||||
|
||||
return Ok(cert_identity);
|
||||
@@ -200,7 +200,7 @@ impl CertificateIdentity {
|
||||
Error::Certificate(format!("Failed to encode certificate to PEM: {}", e))
|
||||
})?;
|
||||
fs::write(&self.cert_file, cert_pem)
|
||||
.map_err(|e| Error::Certificate(format!("Failed to save certificate file: {}", e)))?;
|
||||
.map_err(|e| Error::Filesystem(format!("Failed to save certificate file: {}", e)))?;
|
||||
|
||||
self.certificate = Some(certificate);
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ impl DeveloperSession {
|
||||
if let ICloudError::AuthSrpWithMessage(code, message) = e {
|
||||
Error::DeveloperSession(code, format!("Developer request failed: {}", message))
|
||||
} else {
|
||||
Error::Generic
|
||||
Error::Generic("Failed to send developer request".to_string())
|
||||
}
|
||||
})?;
|
||||
|
||||
@@ -85,20 +85,22 @@ impl DeveloperSession {
|
||||
let teams = response
|
||||
.get("teams")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("teams".to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for team in teams {
|
||||
let dict = team.as_dictionary().ok_or(Error::Parse)?;
|
||||
let dict = team
|
||||
.as_dictionary()
|
||||
.ok_or(Error::Parse("team".to_string()))?;
|
||||
let name = dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let team_id = dict
|
||||
.get("teamId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("teamId".to_string()))?
|
||||
.to_string();
|
||||
result.push(DeveloperTeam {
|
||||
_name: name,
|
||||
@@ -140,25 +142,27 @@ impl DeveloperSession {
|
||||
let devices = response
|
||||
.get("devices")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("devices".to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for device in devices {
|
||||
let dict = device.as_dictionary().ok_or(Error::Parse)?;
|
||||
let dict = device
|
||||
.as_dictionary()
|
||||
.ok_or(Error::Parse("device".to_string()))?;
|
||||
let device_id = dict
|
||||
.get("deviceId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("deviceId".to_string()))?
|
||||
.to_string();
|
||||
let name = dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let device_number = dict
|
||||
.get("deviceNumber")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("deviceNumber".to_string()))?
|
||||
.to_string();
|
||||
result.push(DeveloperDevice {
|
||||
_device_id: device_id,
|
||||
@@ -187,22 +191,22 @@ impl DeveloperSession {
|
||||
let device_dict = response
|
||||
.get("device")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("device".to_string()))?;
|
||||
|
||||
let device_id = device_dict
|
||||
.get("deviceId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("deviceId".to_string()))?
|
||||
.to_string();
|
||||
let name = device_dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let device_number = device_dict
|
||||
.get("deviceNumber")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("deviceNumber".to_string()))?
|
||||
.to_string();
|
||||
|
||||
Ok(DeveloperDevice {
|
||||
@@ -226,25 +230,27 @@ impl DeveloperSession {
|
||||
let certs = response
|
||||
.get("certificates")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("certificates".to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for cert in certs {
|
||||
let dict = cert.as_dictionary().ok_or(Error::Parse)?;
|
||||
let dict = cert
|
||||
.as_dictionary()
|
||||
.ok_or(Error::Parse("certificate".to_string()))?;
|
||||
let name = dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let certificate_id = dict
|
||||
.get("certificateId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("certificateId".to_string()))?
|
||||
.to_string();
|
||||
let serial_number = dict
|
||||
.get("serialNumber")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("serialNumber".to_string()))?
|
||||
.to_string();
|
||||
let machine_name = dict
|
||||
.get("machineName")
|
||||
@@ -254,7 +260,7 @@ impl DeveloperSession {
|
||||
let cert_content = dict
|
||||
.get("certContent")
|
||||
.and_then(|v| v.as_data())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("certContent".to_string()))?
|
||||
.to_vec();
|
||||
|
||||
result.push(DevelopmentCertificate {
|
||||
@@ -309,11 +315,11 @@ impl DeveloperSession {
|
||||
let cert_dict = response
|
||||
.get("certRequest")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("certRequest".to_string()))?;
|
||||
let id = cert_dict
|
||||
.get("certRequestId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("certRequestId".to_string()))?
|
||||
.to_string();
|
||||
|
||||
Ok(id)
|
||||
@@ -333,35 +339,37 @@ impl DeveloperSession {
|
||||
let app_ids = response
|
||||
.get("appIds")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("appIds".to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for app_id in app_ids {
|
||||
let dict = app_id.as_dictionary().ok_or(Error::Parse)?;
|
||||
let dict = app_id
|
||||
.as_dictionary()
|
||||
.ok_or(Error::Parse("appId".to_string()))?;
|
||||
let name = dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let app_id_id = dict
|
||||
.get("appIdId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("appIdId".to_string()))?
|
||||
.to_string();
|
||||
let identifier = dict
|
||||
.get("identifier")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("identifier".to_string()))?
|
||||
.to_string();
|
||||
let features = dict
|
||||
.get("features")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("features".to_string()))?;
|
||||
let expiration_date = if dict.contains_key("expirationDate") {
|
||||
Some(
|
||||
dict.get("expirationDate")
|
||||
.and_then(|v| v.as_date())
|
||||
.ok_or(Error::Parse)?,
|
||||
.ok_or(Error::Parse("expirationDate".to_string()))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
@@ -379,11 +387,11 @@ impl DeveloperSession {
|
||||
let max_quantity = response
|
||||
.get("maxQuantity")
|
||||
.and_then(|v| v.as_unsigned_integer())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("maxQuantity".to_string()))?;
|
||||
let available_quantity = response
|
||||
.get("availableQuantity")
|
||||
.and_then(|v| v.as_unsigned_integer())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("availableQuantity".to_string()))?;
|
||||
|
||||
Ok(ListAppIdsResponse {
|
||||
app_ids: result,
|
||||
@@ -436,11 +444,11 @@ impl DeveloperSession {
|
||||
let cert_dict = response
|
||||
.get("appId")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("appId".to_string()))?;
|
||||
let feats = cert_dict
|
||||
.get("features")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("features".to_string()))?;
|
||||
|
||||
Ok(feats.clone())
|
||||
}
|
||||
@@ -475,25 +483,27 @@ impl DeveloperSession {
|
||||
let app_groups = response
|
||||
.get("applicationGroupList")
|
||||
.and_then(|v| v.as_array())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("applicationGroupList".to_string()))?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for app_group in app_groups {
|
||||
let dict = app_group.as_dictionary().ok_or(Error::Parse)?;
|
||||
let dict = app_group
|
||||
.as_dictionary()
|
||||
.ok_or(Error::Parse("applicationGroup".to_string()))?;
|
||||
let application_group = dict
|
||||
.get("applicationGroup")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("applicationGroup".to_string()))?
|
||||
.to_string();
|
||||
let name = dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let identifier = dict
|
||||
.get("identifier")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("identifier".to_string()))?
|
||||
.to_string();
|
||||
|
||||
result.push(ApplicationGroup {
|
||||
@@ -526,21 +536,21 @@ impl DeveloperSession {
|
||||
let app_group_dict = response
|
||||
.get("applicationGroup")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("applicationGroup".to_string()))?;
|
||||
let application_group = app_group_dict
|
||||
.get("applicationGroup")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("applicationGroup".to_string()))?
|
||||
.to_string();
|
||||
let name = app_group_dict
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let identifier = app_group_dict
|
||||
.get("identifier")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("identifier".to_string()))?
|
||||
.to_string();
|
||||
|
||||
Ok(ApplicationGroup {
|
||||
@@ -593,21 +603,21 @@ impl DeveloperSession {
|
||||
let profile = response
|
||||
.get("provisioningProfile")
|
||||
.and_then(|v| v.as_dictionary())
|
||||
.ok_or(Error::Parse)?;
|
||||
.ok_or(Error::Parse("provisioningProfile".to_string()))?;
|
||||
let name = profile
|
||||
.get("name")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("name".to_string()))?
|
||||
.to_string();
|
||||
let provisioning_profile_id = profile
|
||||
.get("provisioningProfileId")
|
||||
.and_then(|v| v.as_string())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("provisioningProfileId".to_string()))?
|
||||
.to_string();
|
||||
let encoded_profile = profile
|
||||
.get("encodedProfile")
|
||||
.and_then(|v| v.as_data())
|
||||
.ok_or(Error::Parse)?
|
||||
.ok_or(Error::Parse("encodedProfile".to_string()))?
|
||||
.to_vec();
|
||||
|
||||
Ok(ProvisioningProfile {
|
||||
|
||||
33
src/lib.rs
33
src/lib.rs
@@ -10,12 +10,39 @@ pub use developer_session::{
|
||||
DevelopmentCertificate, ListAppIdsResponse, ProvisioningProfile,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
#[derive(Debug, Clone, ThisError)]
|
||||
pub enum Error {
|
||||
#[error("Authentication error {0}: {1}")]
|
||||
Auth(i64, String),
|
||||
#[error("Developer session error {0}: {1}")]
|
||||
DeveloperSession(i64, String),
|
||||
Generic,
|
||||
Parse,
|
||||
#[error("Error: {0}")]
|
||||
Generic(String),
|
||||
#[error("Failed to parse: {0}")]
|
||||
Parse(String),
|
||||
#[error("Invalid bundle: {0}")]
|
||||
InvalidBundle(String),
|
||||
#[error("Certificate error: {0}")]
|
||||
Certificate(String),
|
||||
#[error("Failed to use files: {0}")]
|
||||
Filesystem(String),
|
||||
}
|
||||
|
||||
pub trait SideloadLogger {
|
||||
async fn log(&self, message: &str);
|
||||
async fn error(&self, error: &Error);
|
||||
}
|
||||
|
||||
pub struct DefaultLogger;
|
||||
|
||||
impl SideloadLogger for DefaultLogger {
|
||||
async fn log(&self, message: &str) {
|
||||
println!("{message}");
|
||||
}
|
||||
|
||||
async fn error(&self, error: &Error) {
|
||||
eprintln!("Error: {}", error);
|
||||
}
|
||||
}
|
||||
|
||||
219
src/sideload.rs
219
src/sideload.rs
@@ -1,85 +1,81 @@
|
||||
// This file was made using https://github.com/Dadoum/Sideloader as a reference.
|
||||
|
||||
use crate::Error;
|
||||
use zsign_rust::ZSignOptions;
|
||||
|
||||
use crate::application::Application;
|
||||
use crate::{Error, SideloadLogger};
|
||||
use crate::{
|
||||
certificate::CertificateIdentity,
|
||||
developer_session::DeveloperDeviceType,
|
||||
developer_session::{DeveloperDeviceType, DeveloperSession},
|
||||
device::{DeviceInfo, install_app},
|
||||
};
|
||||
use std::{io::Write, path::PathBuf};
|
||||
|
||||
fn error_and_return(logger: &impl SideloadLogger, error: Error) -> Result<(), Error> {
|
||||
logger.error(&error);
|
||||
Err(error)
|
||||
}
|
||||
|
||||
pub async fn sideload_app(
|
||||
handle: &tauri::AppHandle,
|
||||
window: &tauri::Window,
|
||||
anisette_server: String,
|
||||
device: DeviceInfo,
|
||||
logger: impl SideloadLogger,
|
||||
dev_session: &DeveloperSession,
|
||||
device: &DeviceInfo,
|
||||
app_path: PathBuf,
|
||||
) -> Result<(), Error> {
|
||||
if device.uuid.is_empty() {
|
||||
return emit_error_and_return(window, "No device selected");
|
||||
return error_and_return(&logger, Error::Generic("No device selected".to_string()));
|
||||
}
|
||||
let dev_session = match crate::sideloader::apple::get_developer_session(
|
||||
&handle,
|
||||
window,
|
||||
anisette_server.clone(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(acc) => acc,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!("Failed to login to Apple account: {:?}", e),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let team = match dev_session.get_team().await {
|
||||
Ok(t) => t,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(window, &format!("Failed to get team: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
window
|
||||
.emit("build-output", "Successfully retrieved team".to_string())
|
||||
.ok();
|
||||
|
||||
logger.log("Successfully retrieved team");
|
||||
|
||||
ensure_device_registered(&dev_session, window, &team, &device).await?;
|
||||
|
||||
let config_dir = handle.path().app_config_dir().map_err(|e| e.to_string())?;
|
||||
let cert = match CertificateIdentity::new(config_dir, &dev_session, get_apple_email()).await {
|
||||
Ok(c) => c,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(window, &format!("Failed to get certificate: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
window
|
||||
.emit(
|
||||
"build-output",
|
||||
"Certificate acquired succesfully".to_string(),
|
||||
)
|
||||
.ok();
|
||||
|
||||
logger.log("Successfully acquired certificate");
|
||||
|
||||
let mut list_app_id_response = match dev_session
|
||||
.list_app_ids(DeveloperDeviceType::Ios, &team)
|
||||
.await
|
||||
{
|
||||
Ok(ids) => ids,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(window, &format!("Failed to list app IDs: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
|
||||
let mut app = crate::sideloader::application::Application::new(app_path);
|
||||
let mut app = Application::new(app_path);
|
||||
let is_sidestore = app.bundle.bundle_identifier().unwrap_or("") == "com.SideStore.SideStore";
|
||||
let main_app_bundle_id = match app.bundle.bundle_identifier() {
|
||||
Some(id) => id.to_string(),
|
||||
None => {
|
||||
return emit_error_and_return(window, "No bundle identifier found in IPA");
|
||||
return error_and_return(
|
||||
&logger,
|
||||
Error::InvalidBundle("No bundle identifier found in IPA".to_string()),
|
||||
);
|
||||
}
|
||||
};
|
||||
let main_app_id_str = format!("{}.{}", main_app_bundle_id, team.team_id);
|
||||
let main_app_name = match app.bundle.bundle_name() {
|
||||
Some(name) => name.to_string(),
|
||||
None => {
|
||||
return emit_error_and_return(window, "No bundle name found in IPA");
|
||||
return error_and_return(
|
||||
&logger,
|
||||
Error::InvalidBundle("No bundle name found in IPA".to_string()),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,13 +84,13 @@ pub async fn sideload_app(
|
||||
for ext in extensions.iter_mut() {
|
||||
if let Some(id) = ext.bundle_identifier() {
|
||||
if !(id.starts_with(&main_app_bundle_id) && id.len() > main_app_bundle_id.len()) {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!(
|
||||
return error_and_return(
|
||||
&logger,
|
||||
Error::InvalidBundle(format!(
|
||||
"Extension {} is not part of the main app bundle identifier: {}",
|
||||
ext.bundle_name().unwrap_or("Unknown"),
|
||||
id
|
||||
),
|
||||
)),
|
||||
);
|
||||
} else {
|
||||
ext.set_bundle_identifier(&format!(
|
||||
@@ -123,13 +119,13 @@ pub async fn sideload_app(
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
if app_ids_to_register.len() > list_app_id_response.available_quantity.try_into().unwrap() {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!(
|
||||
return error_and_return(
|
||||
&logger,
|
||||
Error::InvalidBundle(format!(
|
||||
"This app requires {} app ids, but you only have {} available",
|
||||
app_ids_to_register.len(),
|
||||
list_app_id_response.available_quantity
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -140,7 +136,7 @@ pub async fn sideload_app(
|
||||
.add_app_id(DeveloperDeviceType::Ios, &team, &name, &id)
|
||||
.await
|
||||
{
|
||||
return emit_error_and_return(window, &format!("Failed to register app ID: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
}
|
||||
list_app_id_response = match dev_session
|
||||
@@ -149,7 +145,7 @@ pub async fn sideload_app(
|
||||
{
|
||||
Ok(ids) => ids,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(window, &format!("Failed to list app IDs: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -169,19 +165,17 @@ pub async fn sideload_app(
|
||||
{
|
||||
Some(id) => id,
|
||||
None => {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!(
|
||||
return error_and_return(
|
||||
&logger,
|
||||
Error::Generic(format!(
|
||||
"Main app ID {} not found in registered app IDs",
|
||||
main_app_id_str
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
window
|
||||
.emit("build-output", "Registered app IDs".to_string())
|
||||
.ok();
|
||||
logger.log("Successfully registered app IDs");
|
||||
|
||||
for app_id in app_ids.iter_mut() {
|
||||
let app_group_feature_enabled = app_id
|
||||
@@ -190,7 +184,9 @@ pub async fn sideload_app(
|
||||
"APG3427HIY", /* Gotta love apple and their magic strings! */
|
||||
)
|
||||
.and_then(|v| v.as_boolean())
|
||||
.ok_or("App group feature not found in app id")?;
|
||||
.ok_or(Error::Generic(
|
||||
"App group feature not found in app id".to_string(),
|
||||
))?;
|
||||
if !app_group_feature_enabled {
|
||||
let mut body = plist::Dictionary::new();
|
||||
body.insert("APG3427HIY".to_string(), plist::Value::Boolean(true));
|
||||
@@ -200,10 +196,7 @@ pub async fn sideload_app(
|
||||
{
|
||||
Ok(new_feats) => new_feats,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!("Failed to update app ID features: {:?}", e),
|
||||
);
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
app_id.features = new_features;
|
||||
@@ -225,7 +218,7 @@ pub async fn sideload_app(
|
||||
{
|
||||
Ok(groups) => groups,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(window, &format!("Failed to list app groups: {:?}", e));
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -246,10 +239,7 @@ pub async fn sideload_app(
|
||||
{
|
||||
Ok(group) => group,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!("Failed to register app group: {:?}", e),
|
||||
);
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -267,13 +257,7 @@ pub async fn sideload_app(
|
||||
)
|
||||
.await;
|
||||
if assign_res.is_err() {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!(
|
||||
"Failed to assign app group to app ID: {:?}",
|
||||
assign_res.err()
|
||||
),
|
||||
);
|
||||
return error_and_return(&logger, assign_res.err().unwrap());
|
||||
}
|
||||
// let provisioning_profile = match account
|
||||
// // This doesn't seem right to me, but it's what Sideloader does... Shouldn't it be downloading the provisioning profile for this app ID, not the main?
|
||||
@@ -291,9 +275,7 @@ pub async fn sideload_app(
|
||||
// provisioning_profiles.insert(app_id.identifier.clone(), provisioning_profile);
|
||||
}
|
||||
|
||||
window
|
||||
.emit("build-output", "Registered app groups".to_string())
|
||||
.ok();
|
||||
logger.log("Successfully registered app groups");
|
||||
|
||||
let provisioning_profile = match dev_session
|
||||
.download_team_provisioning_profile(DeveloperDeviceType::Ios, &team, &main_app_id)
|
||||
@@ -301,10 +283,7 @@ pub async fn sideload_app(
|
||||
{
|
||||
Ok(pp /* tee hee */) => pp,
|
||||
Err(e) => {
|
||||
return emit_error_and_return(
|
||||
window,
|
||||
&format!("Failed to download provisioning profile: {:?}", e),
|
||||
);
|
||||
return error_and_return(&logger, e);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -318,9 +297,10 @@ pub async fn sideload_app(
|
||||
std::fs::remove_file(&profile_path).map_err(|e| e.to_string())?;
|
||||
}
|
||||
|
||||
let mut file = std::fs::File::create(&profile_path).map_err(|e| e.to_string())?;
|
||||
let mut file =
|
||||
std::fs::File::create(&profile_path).map_err(|e| Error::Filesystem(e.to_string()))?;
|
||||
file.write_all(&provisioning_profile.encoded_profile)
|
||||
.map_err(|e| e.to_string())?;
|
||||
.map_err(|e| Error::Filesystem(e.to_string()))?;
|
||||
|
||||
// Without this, zsign complains it can't find the provision file
|
||||
#[cfg(target_os = "windows")]
|
||||
@@ -332,67 +312,28 @@ pub async fn sideload_app(
|
||||
// TODO: Recursive for sub-bundles?
|
||||
app.bundle.write_info().map_err(|e| e.to_string())?;
|
||||
|
||||
window
|
||||
.emit("build-output", "Signining app...".to_string())
|
||||
.ok();
|
||||
|
||||
let zsign_command = handle.shell().sidecar("zsign").unwrap().args([
|
||||
"-k",
|
||||
cert.get_private_key_file_path().to_str().unwrap(),
|
||||
"-c",
|
||||
cert.get_certificate_file_path().to_str().unwrap(),
|
||||
"-m",
|
||||
profile_path.to_str().unwrap(),
|
||||
app.bundle.bundle_dir.to_str().unwrap(),
|
||||
]);
|
||||
let (mut rx, mut _child) = zsign_command.spawn().expect("Failed to spawn zsign");
|
||||
|
||||
let mut signing_failed = false;
|
||||
while let Some(event) = rx.recv().await {
|
||||
match event {
|
||||
CommandEvent::Stdout(line_bytes) | CommandEvent::Stderr(line_bytes) => {
|
||||
let line = String::from_utf8_lossy(&line_bytes);
|
||||
window
|
||||
.emit("build-output", Some(line))
|
||||
.expect("failed to emit event");
|
||||
}
|
||||
CommandEvent::Terminated(result) => {
|
||||
if result.code != Some(0) {
|
||||
window
|
||||
.emit("build-output", "App signing failed!".to_string())
|
||||
.ok();
|
||||
signing_failed = true;
|
||||
break;
|
||||
}
|
||||
window.emit("build-output", "App signed!").ok();
|
||||
|
||||
window
|
||||
.emit(
|
||||
"build-output",
|
||||
"Installing app (Transfer)... 0%".to_string(),
|
||||
)
|
||||
.ok();
|
||||
|
||||
let res = install_app(&device, &app.bundle.bundle_dir, |percentage| {
|
||||
window
|
||||
.emit("build-output", format!("Installing app... {}%", percentage))
|
||||
.expect("failed to emit event");
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = res {
|
||||
window
|
||||
.emit("build-output", format!("Failed to install app: {:?}", e))
|
||||
.ok();
|
||||
signing_failed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
match ZSignOptions::new(app.bundle.bundle_dir.to_str().unwrap())
|
||||
.with_cert_file(cert.get_certificate_file_path().to_str().unwrap())
|
||||
.with_pkey_file(cert.get_private_key_file_path().to_str().unwrap())
|
||||
.with_prov_file(profile_path.to_str().unwrap())
|
||||
.sign()
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
return error_and_return(&logger, &format!("Failed to sign app: {:?}", e));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if signing_failed {
|
||||
return Err("Signing or installation failed".to_string());
|
||||
logger.log("App signed!");
|
||||
|
||||
logger.log("Installing app (Transfer)... 0%");
|
||||
|
||||
let res = install_app(&device, &app.bundle.bundle_dir, |percentage| {
|
||||
logger.log(format!("Installing app... {}%", percentage));
|
||||
})
|
||||
.await;
|
||||
if let Err(e) = res {
|
||||
return error_and_return(&logger, &format!("Failed to install app: {:?}", e));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
Reference in New Issue
Block a user