|
@@ -2,15 +2,12 @@
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
-use std::{
|
|
|
- ffi::OsString,
|
|
|
- fs::File,
|
|
|
- io::prelude::*,
|
|
|
- path::PathBuf,
|
|
|
- process::{Command, Stdio},
|
|
|
-};
|
|
|
-
|
|
|
-use crate::{bundle::common, Settings};
|
|
|
+use std::ffi::OsString;
|
|
|
+use std::{fs::File, io::prelude::*, path::PathBuf, process::Command};
|
|
|
+
|
|
|
+use crate::{bundle::common::CommandExt, Settings};
|
|
|
+use anyhow::Context;
|
|
|
+use log::info;
|
|
|
use regex::Regex;
|
|
|
|
|
|
const KEYCHAIN_ID: &str = "tauri-build.keychain";
|
|
@@ -28,7 +25,7 @@ pub fn setup_keychain(
|
|
|
) -> crate::Result<()> {
|
|
|
// we delete any previous version of our keychain if present
|
|
|
delete_keychain();
|
|
|
- common::print_info("setup keychain from environment variables...")?;
|
|
|
+ info!("setup keychain from environment variables...");
|
|
|
|
|
|
let keychain_list_output = Command::new("security")
|
|
|
.args(["list-keychain", "-d", "user"])
|
|
@@ -61,36 +58,22 @@ pub fn setup_keychain(
|
|
|
let mut tmp_cert = File::create(cert_path_tmp.clone())?;
|
|
|
tmp_cert.write_all(certificate_encoded)?;
|
|
|
|
|
|
- let decode_certificate = Command::new("base64")
|
|
|
+ Command::new("base64")
|
|
|
.args(["--decode", "-i", &cert_path_tmp, "-o", &cert_path])
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !decode_certificate.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to decode certificate",).into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to decode certificate")?;
|
|
|
|
|
|
- let create_key_chain = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args(["create-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID])
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to create keychain")?;
|
|
|
|
|
|
- if !create_key_chain.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to create keychain",).into());
|
|
|
- }
|
|
|
-
|
|
|
- let unlock_keychain = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args(["unlock-keychain", "-p", KEYCHAIN_PWD, KEYCHAIN_ID])
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to set unlock keychain")?;
|
|
|
|
|
|
- if !unlock_keychain.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to set unlock keychain",).into());
|
|
|
- }
|
|
|
-
|
|
|
- let import_certificate = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args([
|
|
|
"import",
|
|
|
&cert_path,
|
|
@@ -105,30 +88,15 @@ pub fn setup_keychain(
|
|
|
"-T",
|
|
|
"/usr/bin/productbuild",
|
|
|
])
|
|
|
- .stderr(Stdio::inherit())
|
|
|
- .output()?;
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to import keychain certificate")?;
|
|
|
|
|
|
- if !import_certificate.status.success() {
|
|
|
- return Err(
|
|
|
- anyhow::anyhow!(format!(
|
|
|
- "failed to import keychain certificate {:?}",
|
|
|
- std::str::from_utf8(&import_certificate.stdout)
|
|
|
- ))
|
|
|
- .into(),
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- let settings_keychain = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args(["set-keychain-settings", "-t", "3600", "-u", KEYCHAIN_ID])
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !settings_keychain.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to set keychain settings",).into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to set keychain settings")?;
|
|
|
|
|
|
- let partition_list = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args([
|
|
|
"set-key-partition-list",
|
|
|
"-S",
|
|
@@ -138,13 +106,8 @@ pub fn setup_keychain(
|
|
|
KEYCHAIN_PWD,
|
|
|
KEYCHAIN_ID,
|
|
|
])
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !partition_list.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to set keychain settings",).into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to set keychain settings")?;
|
|
|
|
|
|
let current_keychains = String::from_utf8_lossy(&keychain_list_output.stdout)
|
|
|
.split('\n')
|
|
@@ -156,17 +119,12 @@ pub fn setup_keychain(
|
|
|
.filter(|l| !l.is_empty())
|
|
|
.collect::<Vec<String>>();
|
|
|
|
|
|
- let set_keychain_list_entry = Command::new("security")
|
|
|
+ Command::new("security")
|
|
|
.args(["list-keychain", "-d", "user", "-s"])
|
|
|
.args(current_keychains)
|
|
|
.arg(KEYCHAIN_ID)
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !set_keychain_list_entry.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to list keychain",).into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to list keychain")?;
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
@@ -176,9 +134,7 @@ pub fn delete_keychain() {
|
|
|
let _ = Command::new("security")
|
|
|
.arg("delete-keychain")
|
|
|
.arg(KEYCHAIN_ID)
|
|
|
- .stdout(Stdio::piped())
|
|
|
- .stderr(Stdio::piped())
|
|
|
- .status();
|
|
|
+ .output_ok();
|
|
|
}
|
|
|
|
|
|
pub fn sign(
|
|
@@ -187,6 +143,8 @@ pub fn sign(
|
|
|
settings: &Settings,
|
|
|
is_an_executable: bool,
|
|
|
) -> crate::Result<()> {
|
|
|
+ info!(action = "Signing"; "{} with identity \"{}\"", path_to_sign.display(), identity);
|
|
|
+
|
|
|
let setup_keychain = if let (Some(certificate_encoded), Some(certificate_password)) = (
|
|
|
std::env::var_os("APPLE_CERTIFICATE"),
|
|
|
std::env::var_os("APPLE_CERTIFICATE_PASSWORD"),
|
|
@@ -222,7 +180,6 @@ fn try_sign(
|
|
|
is_an_executable: bool,
|
|
|
tauri_keychain: bool,
|
|
|
) -> crate::Result<()> {
|
|
|
- common::print_info(format!(r#"signing with identity "{}""#, identity).as_str())?;
|
|
|
let mut args = vec!["--force", "-s", identity];
|
|
|
|
|
|
if tauri_keychain {
|
|
@@ -231,7 +188,7 @@ fn try_sign(
|
|
|
}
|
|
|
|
|
|
if let Some(entitlements_path) = &settings.macos().entitlements {
|
|
|
- common::print_info(format!("using entitlements file at {}", entitlements_path).as_str())?;
|
|
|
+ info!("using entitlements file at {}", entitlements_path);
|
|
|
args.push("--entitlements");
|
|
|
args.push(entitlements_path);
|
|
|
}
|
|
@@ -245,14 +202,11 @@ fn try_sign(
|
|
|
args.push("--deep");
|
|
|
}
|
|
|
|
|
|
- let status = Command::new("codesign")
|
|
|
+ Command::new("codesign")
|
|
|
.args(args)
|
|
|
.arg(path_to_sign.to_string_lossy().to_string())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !status.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to sign app").into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to sign app")?;
|
|
|
|
|
|
Ok(())
|
|
|
}
|
|
@@ -287,18 +241,14 @@ pub fn notarize(
|
|
|
|
|
|
// use ditto to create a PKZip almost identical to Finder
|
|
|
// this remove almost 99% of false alarm in notarization
|
|
|
- let zip_app = Command::new("ditto")
|
|
|
+ Command::new("ditto")
|
|
|
.args(zip_args)
|
|
|
- .stderr(Stdio::inherit())
|
|
|
- .status()?;
|
|
|
-
|
|
|
- if !zip_app.success() {
|
|
|
- return Err(anyhow::anyhow!("failed to zip app with ditto").into());
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to zip app with ditto")?;
|
|
|
|
|
|
// sign the zip file
|
|
|
if let Some(identity) = &settings.macos().signing_identity {
|
|
|
- sign(zip_path.clone(), identity, &settings, false)?;
|
|
|
+ sign(zip_path.clone(), identity, settings, false)?;
|
|
|
};
|
|
|
|
|
|
let mut notarize_args = vec![
|
|
@@ -317,39 +267,27 @@ pub fn notarize(
|
|
|
notarize_args.push(provider_short_name);
|
|
|
}
|
|
|
|
|
|
- common::print_info("notarizing app")?;
|
|
|
+ info!(action = "Notarizing"; "{}", app_bundle_path.display());
|
|
|
+
|
|
|
let output = Command::new("xcrun")
|
|
|
.args(notarize_args)
|
|
|
.args(auth_args.clone())
|
|
|
- .stderr(Stdio::inherit())
|
|
|
- .output()?;
|
|
|
-
|
|
|
- if !output.status.success() {
|
|
|
- return Err(
|
|
|
- anyhow::anyhow!(format!(
|
|
|
- "failed to upload app to Apple's notarization servers. {}",
|
|
|
- std::str::from_utf8(&output.stdout)?
|
|
|
- ))
|
|
|
- .into(),
|
|
|
- );
|
|
|
- }
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to upload app to Apple's notarization servers.")?;
|
|
|
|
|
|
let stdout = std::str::from_utf8(&output.stdout)?;
|
|
|
if let Some(uuid) = Regex::new(r"\nRequestUUID = (.+?)\n")?
|
|
|
.captures_iter(stdout)
|
|
|
.next()
|
|
|
{
|
|
|
- common::print_info("notarization started; waiting for Apple response...")?;
|
|
|
+ info!("notarization started; waiting for Apple response...");
|
|
|
+
|
|
|
let uuid = uuid[1].to_string();
|
|
|
- get_notarization_status(uuid, auth_args)?;
|
|
|
+ get_notarization_status(uuid, auth_args, settings)?;
|
|
|
staple_app(app_bundle_path.clone())?;
|
|
|
} else {
|
|
|
return Err(
|
|
|
- anyhow::anyhow!(format!(
|
|
|
- "failed to parse RequestUUID from upload output. {}",
|
|
|
- stdout
|
|
|
- ))
|
|
|
- .into(),
|
|
|
+ anyhow::anyhow!("failed to parse RequestUUID from upload output. {}", stdout).into(),
|
|
|
);
|
|
|
}
|
|
|
|
|
@@ -366,36 +304,27 @@ fn staple_app(mut app_bundle_path: PathBuf) -> crate::Result<()> {
|
|
|
|
|
|
app_bundle_path.pop();
|
|
|
|
|
|
- let output = Command::new("xcrun")
|
|
|
+ Command::new("xcrun")
|
|
|
.args(vec!["stapler", "staple", "-v", filename])
|
|
|
.current_dir(app_bundle_path)
|
|
|
- .stderr(Stdio::inherit())
|
|
|
- .output()?;
|
|
|
+ .output_ok()
|
|
|
+ .context("failed to staple app.")?;
|
|
|
|
|
|
- if !output.status.success() {
|
|
|
- Err(
|
|
|
- anyhow::anyhow!(format!(
|
|
|
- "failed to staple app. {}",
|
|
|
- std::str::from_utf8(&output.stdout)?
|
|
|
- ))
|
|
|
- .into(),
|
|
|
- )
|
|
|
- } else {
|
|
|
- Ok(())
|
|
|
- }
|
|
|
+ Ok(())
|
|
|
}
|
|
|
|
|
|
-fn get_notarization_status(uuid: String, auth_args: Vec<String>) -> crate::Result<()> {
|
|
|
+fn get_notarization_status(
|
|
|
+ uuid: String,
|
|
|
+ auth_args: Vec<String>,
|
|
|
+ settings: &Settings,
|
|
|
+) -> crate::Result<()> {
|
|
|
std::thread::sleep(std::time::Duration::from_secs(10));
|
|
|
- let output = Command::new("xcrun")
|
|
|
+ let result = Command::new("xcrun")
|
|
|
.args(vec!["altool", "--notarization-info", &uuid])
|
|
|
.args(auth_args.clone())
|
|
|
- .stderr(Stdio::inherit())
|
|
|
- .output()?;
|
|
|
+ .output_ok();
|
|
|
|
|
|
- if !output.status.success() {
|
|
|
- get_notarization_status(uuid, auth_args)
|
|
|
- } else {
|
|
|
+ if let Ok(output) = result {
|
|
|
let stdout = std::str::from_utf8(&output.stdout)?;
|
|
|
if let Some(status) = Regex::new(r"\n *Status: (.+?)\n")?
|
|
|
.captures_iter(stdout)
|
|
@@ -403,7 +332,7 @@ fn get_notarization_status(uuid: String, auth_args: Vec<String>) -> crate::Resul
|
|
|
{
|
|
|
let status = status[1].to_string();
|
|
|
if status == "in progress" {
|
|
|
- get_notarization_status(uuid, auth_args)
|
|
|
+ get_notarization_status(uuid, auth_args, settings)
|
|
|
} else if status == "invalid" {
|
|
|
Err(
|
|
|
anyhow::anyhow!(format!(
|
|
@@ -425,8 +354,10 @@ fn get_notarization_status(uuid: String, auth_args: Vec<String>) -> crate::Resul
|
|
|
Ok(())
|
|
|
}
|
|
|
} else {
|
|
|
- get_notarization_status(uuid, auth_args)
|
|
|
+ get_notarization_status(uuid, auth_args, settings)
|
|
|
}
|
|
|
+ } else {
|
|
|
+ get_notarization_status(uuid, auth_args, settings)
|
|
|
}
|
|
|
}
|
|
|
|