Skip to content

Commit

Permalink
feat: support signing pkgs
Browse files Browse the repository at this point in the history
  • Loading branch information
mistydemeo committed Sep 11, 2024
1 parent 48c476e commit 70a21a5
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 5 deletions.
46 changes: 44 additions & 2 deletions cargo-dist/src/backend/installer/macpkg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,13 @@ use axoprocess::Cmd;
use camino::Utf8PathBuf;
use serde::Serialize;
use temp_dir::TempDir;
use tracing::info;
use tracing::{info, warn};

use crate::{create_tmp, DistResult};
use crate::{
create_tmp,
sign::macos::{Codesign, Keychain},
DistResult, TargetTriple,
};

use super::ExecutableZipFragment;

Expand All @@ -30,13 +34,36 @@ pub struct PkgInstallerInfo {
pub version: String,
/// Executable aliases
pub bin_aliases: BTreeMap<String, Vec<String>>,
/// Whether to sign package artifacts
pub macos_sign: bool,
/// The target we're building from
pub host_target: TargetTriple,
}

struct SigningEnv {
pub keychain: Keychain,
pub identity: String,
}

impl PkgInstallerInfo {
/// Build the pkg installer
pub fn build(&self) -> DistResult<()> {
info!("building a pkg: {}", self.identifier);

let signing_env = if self.macos_sign {
if let Some(signer) = Codesign::new(&self.host_target)? {
Some(SigningEnv {
keychain: signer.create_keychain()?,
identity: signer.identity().to_owned(),
})
} else {
warn!("Signing was requested, but we weren't able to construct the signing environment - config may be missing");
None
}
} else {
None
};

// We can't build directly from dist_dir because the
// package installer wants the directory we feed it
// to have the final package layout, which in this case
Expand Down Expand Up @@ -81,6 +108,14 @@ impl PkgInstallerInfo {
pkgcmd.arg("--install-location").arg(&self.install_location);
pkgcmd.arg("--version").arg(&self.version);
pkgcmd.arg(&pkg_path);

// If we've been asked to sign, and we have the required
// environment, do that here.
if let Some(signing_env) = &signing_env {
pkgcmd.arg("--sign").arg(&signing_env.identity);
pkgcmd.arg("--keychain").arg(&signing_env.keychain.path);
}

// Ensures stdout from the build process doesn't taint the dist-manifest
pkgcmd.stdout_to_stderr();
pkgcmd.run()?;
Expand All @@ -89,6 +124,13 @@ impl PkgInstallerInfo {
let mut productcmd = Cmd::new("/usr/bin/productbuild", "create final product .pkg");
productcmd.arg("--package").arg(&pkg_path);
productcmd.arg(&product_path);

// We also need to sign the product .pkg.
if let Some(signing_env) = &signing_env {
productcmd.arg("--sign").arg(&signing_env.identity);
productcmd.arg("--keychain").arg(&signing_env.keychain.path);
}

productcmd.stdout_to_stderr();
productcmd.run()?;

Expand Down
21 changes: 19 additions & 2 deletions cargo-dist/src/sign/macos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ use tracing::warn;

use crate::{create_tmp, DistError, DistResult, TargetTriple};

struct Keychain {
/// Represents a temporary macOS keychain database. The database object will be created within `_root`, and deleted once this struct is dropped.
pub struct Keychain {
_root: TempDir,
root_path: Utf8PathBuf,
password: String,
/// The path to the keychain database.
pub path: Utf8PathBuf,
}

Expand Down Expand Up @@ -156,6 +158,7 @@ impl std::fmt::Debug for CodesignEnv {
}

impl Codesign {
/// Creates a new Codesign instance, if the host is darwin and the required information is in the environment
pub fn new(host_target: &TargetTriple) -> DistResult<Option<Self>> {
if !host_target.contains("darwin") {
return Ok(None);
Expand All @@ -182,11 +185,20 @@ impl Codesign {
val
}

pub fn sign(&self, file: &Utf8Path) -> DistResult<()> {
/// Creates a Keychain with this signer's certificate imported,
/// then returns it.
pub fn create_keychain(&self) -> DistResult<Keychain> {
let password = uuid::Uuid::new_v4().as_hyphenated().to_string();
let keychain = Keychain::create(password)?;
keychain.import_certificate(&self.env.certificate, &self.env.password)?;

Ok(keychain)
}

/// Signs a binary using `codesign`.
pub fn sign(&self, file: &Utf8Path) -> DistResult<()> {
let keychain = self.create_keychain()?;

let mut cmd = Cmd::new("/usr/bin/codesign", "sign macOS artifacts");
cmd.arg("--sign").arg(&self.env.identity);
cmd.arg("--keychain").arg(&keychain.path);
Expand All @@ -196,4 +208,9 @@ impl Codesign {

Ok(())
}

/// Returns the signing identity represented by this signer.
pub fn identity(&self) -> &str {
&self.env.identity
}
}
2 changes: 1 addition & 1 deletion cargo-dist/src/sign/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use camino::Utf8Path;

use crate::{config::ProductionMode, DistResult, TargetTriple};

mod macos;
pub mod macos;
mod ssldotcom;

/// Code/artifact signing providers
Expand Down
4 changes: 4 additions & 0 deletions cargo-dist/src/tasks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2274,6 +2274,8 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
};

let bin_aliases = bin_aliases.for_target(&variant.target);
let macos_sign = self.inner.macos_sign;

Check failure on line 2277 in cargo-dist/src/tasks.rs

View workflow job for this annotation

GitHub Actions / clippy

no field `macos_sign` on type `tasks::DistGraph`

error[E0609]: no field `macos_sign` on type `tasks::DistGraph` --> cargo-dist/src/tasks.rs:2277:41 | 2277 | let macos_sign = self.inner.macos_sign; | ^^^^^^^^^^ unknown field | help: one of the expressions' fields has a field of the same name | 2277 | let macos_sign = self.inner.config.builds.macos_sign; | ++++++++++++++

Check failure on line 2277 in cargo-dist/src/tasks.rs

View workflow job for this annotation

GitHub Actions / clippy

no field `macos_sign` on type `tasks::DistGraph`

error[E0609]: no field `macos_sign` on type `tasks::DistGraph` --> cargo-dist/src/tasks.rs:2277:41 | 2277 | let macos_sign = self.inner.macos_sign; | ^^^^^^^^^^ unknown field | help: one of the expressions' fields has a field of the same name | 2277 | let macos_sign = self.inner.config.builds.macos_sign; | ++++++++++++++
let host_target = self.inner.tools.cargo.host_target.clone();

// Gather up the bundles the installer supports
let installer_artifact = Artifact {
Expand All @@ -2297,6 +2299,8 @@ impl<'pkg_graph> DistGraphBuilder<'pkg_graph> {
install_location: config.install_location.clone(),
version: version.to_string(),
bin_aliases,
macos_sign,
host_target,
})),
is_global: false,
};
Expand Down

0 comments on commit 70a21a5

Please sign in to comment.