fix ios codesign on device (#4339)

* fix ios codesign on device

* switch to tmpfile
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2025-06-30 19:14:17 -07:00
1 parent ac9b1fc519
commit 7d24a47c22
1 file changed
+38 -48
+38 -48
View File
@@ -513,7 +513,14 @@ impl AppBuilder {
} }
} }
Platform::Ios => self.open_ios_sim(envs).await?, Platform::Ios => {
if self.build.device {
self.codesign_ios().await?;
self.open_ios_device().await?
} else {
self.open_ios_sim(envs).await?
}
}
Platform::Android => { Platform::Android => {
self.open_android_sim(false, devserver_ip, envs).await?; self.open_android_sim(false, devserver_ip, envs).await?;
@@ -853,54 +860,35 @@ impl AppBuilder {
/// ///
/// Converting these commands shouldn't be too hard, but device support would imply we need /// Converting these commands shouldn't be too hard, but device support would imply we need
/// better support for codesigning and entitlements. /// better support for codesigning and entitlements.
#[allow(unused)]
async fn open_ios_device(&self) -> Result<()> { async fn open_ios_device(&self) -> Result<()> {
use serde_json::Value; use serde_json::Value;
let app_path = self.build.root_dir();
install_app(&app_path).await?; // 1. Find an active device
// 2. Determine which device the app was installed to
let device_uuid = get_device_uuid().await?; let device_uuid = get_device_uuid().await?;
// 3. Get the installation URL of the app // 2. Get the installation URL of the app
let installation_url = get_installation_url(&device_uuid, &app_path).await?; let installation_url = get_installation_url(&device_uuid, &self.build.root_dir()).await?;
// 4. Launch the app into the background, paused // 3. Launch the app into the background, paused
launch_app_paused(&device_uuid, &installation_url).await?; launch_app_paused(&device_uuid, &installation_url).await?;
// 5. Pick up the paused app and resume it
resume_app(&device_uuid).await?;
async fn install_app(app_path: &PathBuf) -> Result<()> {
let output = Command::new("xcrun")
.args(["simctl", "install", "booted"])
.arg(app_path)
.output()
.await?;
if !output.status.success() {
return Err(format!("Failed to install app: {:?}", output).into());
}
Ok(())
}
async fn get_device_uuid() -> Result<String> { async fn get_device_uuid() -> Result<String> {
let output = Command::new("xcrun") let tmpfile = tempfile::NamedTempFile::new()
.context("Failed to create temporary file for device list")?;
Command::new("xcrun")
.args([ .args([
"devicectl", "devicectl".to_string(),
"list", "list".to_string(),
"devices", "devices".to_string(),
"--json-output", "--json-output".to_string(),
"target/deviceid.json", tmpfile.path().to_str().unwrap().to_string(),
]) ])
.output() .output()
.await?; .await?;
let json: Value = let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
serde_json::from_str(&std::fs::read_to_string("target/deviceid.json")?) .context("Failed to parse xcrun output")?;
.context("Failed to parse xcrun output")?;
let device_uuid = json["result"]["devices"][0]["identifier"] let device_uuid = json["result"]["devices"][0]["identifier"]
.as_str() .as_str()
.ok_or("Failed to extract device UUID")? .ok_or("Failed to extract device UUID")?
@@ -910,6 +898,9 @@ impl AppBuilder {
} }
async fn get_installation_url(device_uuid: &str, app_path: &Path) -> Result<String> { async fn get_installation_url(device_uuid: &str, app_path: &Path) -> Result<String> {
let tmpfile = tempfile::NamedTempFile::new()
.context("Failed to create temporary file for device list")?;
// xcrun devicectl device install app --device <uuid> --path <path> --json-output // xcrun devicectl device install app --device <uuid> --path <path> --json-output
let output = Command::new("xcrun") let output = Command::new("xcrun")
.args([ .args([
@@ -921,8 +912,8 @@ impl AppBuilder {
device_uuid, device_uuid,
&app_path.display().to_string(), &app_path.display().to_string(),
"--json-output", "--json-output",
"target/xcrun.json",
]) ])
.arg(tmpfile.path())
.output() .output()
.await?; .await?;
@@ -930,7 +921,7 @@ impl AppBuilder {
return Err(format!("Failed to install app: {:?}", output).into()); return Err(format!("Failed to install app: {:?}", output).into());
} }
let json: Value = serde_json::from_str(&std::fs::read_to_string("target/xcrun.json")?) let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
.context("Failed to parse xcrun output")?; .context("Failed to parse xcrun output")?;
let installation_url = json["result"]["installedApplications"][0]["installationURL"] let installation_url = json["result"]["installedApplications"][0]["installationURL"]
.as_str() .as_str()
@@ -941,6 +932,9 @@ impl AppBuilder {
} }
async fn launch_app_paused(device_uuid: &str, installation_url: &str) -> Result<()> { async fn launch_app_paused(device_uuid: &str, installation_url: &str) -> Result<()> {
let tmpfile = tempfile::NamedTempFile::new()
.context("Failed to create temporary file for device list")?;
let output = Command::new("xcrun") let output = Command::new("xcrun")
.args([ .args([
"devicectl", "devicectl",
@@ -953,8 +947,8 @@ impl AppBuilder {
device_uuid, device_uuid,
installation_url, installation_url,
"--json-output", "--json-output",
"target/launch.json",
]) ])
.arg(tmpfile.path())
.output() .output()
.await?; .await?;
@@ -962,11 +956,7 @@ impl AppBuilder {
return Err(format!("Failed to launch app: {:?}", output).into()); return Err(format!("Failed to launch app: {:?}", output).into());
} }
Ok(()) let json: Value = serde_json::from_str(&std::fs::read_to_string(tmpfile.path())?)
}
async fn resume_app(device_uuid: &str) -> Result<()> {
let json: Value = serde_json::from_str(&std::fs::read_to_string("target/launch.json")?)
.context("Failed to parse xcrun output")?; .context("Failed to parse xcrun output")?;
let status_pid = json["result"]["process"]["processIdentifier"] let status_pid = json["result"]["process"]["processIdentifier"]
@@ -994,10 +984,9 @@ impl AppBuilder {
Ok(()) Ok(())
} }
unimplemented!("dioxus-cli doesn't support ios devices yet.") Ok(())
} }
#[allow(unused)]
async fn codesign_ios(&self) -> Result<()> { async fn codesign_ios(&self) -> Result<()> {
const CODESIGN_ERROR: &str = r#"This is likely because you haven't const CODESIGN_ERROR: &str = r#"This is likely because you haven't
- Created a provisioning profile before - Created a provisioning profile before
@@ -1011,7 +1000,7 @@ https://developer.apple.com/documentation/xcode/sharing-your-teams-signing-certi
let profiles_folder = dirs::home_dir() let profiles_folder = dirs::home_dir()
.context("Your machine has no home-dir")? .context("Your machine has no home-dir")?
.join("Library/MobileDevice/Provisioning Profiles"); .join("Library/Developer/Xcode/UserData/Provisioning Profiles");
if !profiles_folder.exists() || profiles_folder.read_dir()?.next().is_none() { if !profiles_folder.exists() || profiles_folder.read_dir()?.next().is_none() {
tracing::error!( tracing::error!(
@@ -1080,10 +1069,11 @@ We checked the folder: {}
struct ProvisioningProfile { struct ProvisioningProfile {
#[serde(rename = "TeamIdentifier")] #[serde(rename = "TeamIdentifier")]
team_identifier: Vec<String>, team_identifier: Vec<String>,
#[serde(rename = "ApplicationIdentifierPrefix")]
application_identifier_prefix: Vec<String>,
#[serde(rename = "Entitlements")] #[serde(rename = "Entitlements")]
entitlements: Entitlements, entitlements: Entitlements,
#[allow(dead_code)]
#[serde(rename = "ApplicationIdentifierPrefix")]
application_identifier_prefix: Vec<String>,
} }
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]