Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for embedded provisioning profiles and embedded apps in macOS DMGs #289

Open
wants to merge 32 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3d61858
added embedded.provisionprofile support for macos apps (#1)
mabeledo Dec 5, 2024
e790235
add more info to errors
mabeledo Dec 5, 2024
78be10e
fixed path mistake
mabeledo Dec 5, 2024
943a36b
fmt fixes
mabeledo Dec 5, 2024
738f143
clippy fixes
mabeledo Dec 5, 2024
b440f10
clippy fixes
mabeledo Dec 5, 2024
6ab1d87
fix arch conditional compilation
mabeledo Dec 6, 2024
f65cef8
fix arch conditional compilation
mabeledo Dec 6, 2024
acab004
fix arch conditional compilation
mabeledo Dec 6, 2024
d1be4bc
fix arch conditional compilation
mabeledo Dec 7, 2024
10317bb
fix arch conditional compilation
mabeledo Dec 7, 2024
0aa478a
fix arch conditional compilation
mabeledo Dec 7, 2024
5252320
fix arch conditional compilation
mabeledo Dec 7, 2024
ec3c130
fix arch conditional compilation
mabeledo Dec 7, 2024
91d9727
added embedded app support
mabeledo Dec 10, 2024
4da7ae3
fmt
mabeledo Dec 10, 2024
fc8d38a
exclude apps from re-signing
mabeledo Dec 10, 2024
4736e1d
exclude apps from re-signing
mabeledo Dec 10, 2024
21bb62d
exclude apps from re-signing
mabeledo Dec 10, 2024
20e0edc
fixed missing var
mabeledo Dec 11, 2024
36b188b
fixed missing var
mabeledo Dec 11, 2024
7c78beb
comments addressed
mabeledo Jan 15, 2025
2f4f004
update cargo lock
mabeledo Jan 15, 2025
c967f10
Merge branch 'main' into main
mabeledo Jan 15, 2025
7ee3b2c
fix rpath typo
mabeledo Jan 15, 2025
7958269
Merge branch 'main' of personal:mabeledo/cargo-packager
mabeledo Jan 15, 2025
c674455
change files
lucasfernog-crabnebula Jan 16, 2025
8c66e1d
make function available to windows, linux
mabeledo Jan 16, 2025
247f442
Merge branch 'main' of personal:mabeledo/cargo-packager
mabeledo Jan 16, 2025
c5df200
removed conditional
mabeledo Jan 16, 2025
3505ab4
update cargo-deny action to v2
mabeledo Jan 16, 2025
a7eef43
downgrade cargo-deny action to v1; patch Cargo.lock file
mabeledo Jan 16, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/embedded-apps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"cargo-packager": patch
"@crabnebula/packager": patch
---

Added support to embedding additional apps in the macOS app bundle.
6 changes: 6 additions & 0 deletions .changes/embedded-provisionprofile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"cargo-packager": patch
"@crabnebula/packager": patch
---

Added support to adding an `embedded.provisionprofile` file to the macOS bundle.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ Cargo.lock
*.node.bak

yarn.lock
package-lock.json
package-lock.json

.idea
17 changes: 17 additions & 0 deletions bindings/packager/nodejs/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,23 @@
"string",
"null"
]
},
"embeddedProvisionprofilePath": {
"description": "Path to the embedded.provisionprofile file for the package.",
"type": [
"string",
"null"
]
},
"embeddedApps": {
"description": "Apps that need to be packaged within the app.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false
Expand Down
10 changes: 10 additions & 0 deletions bindings/packager/nodejs/src-ts/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,16 @@ export interface MacOsConfig {
* Path to the Info.plist file for the package.
*/
infoPlistPath?: string | null;

/**
* Path to the embedded.provisionprofile file for the package.
*/
embeddedProvisionprofilePath?: string | null;

/**
* Apps that need to be packaged within the app.
*/
embeddedApps?: string[] | null;
}
/**
* The Linux Debian configuration.
Expand Down
17 changes: 17 additions & 0 deletions crates/packager/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -766,6 +766,23 @@
"string",
"null"
]
},
"embeddedProvisionprofilePath": {
"description": "Path to the embedded.provisionprofile file for the package.",
"type": [
"string",
"null"
]
},
"embeddedApps": {
"description": "Apps that need to be packaged within the app.",
"type": [
"array",
"null"
],
"items": {
"type": "string"
}
}
},
"additionalProperties": false
Expand Down
2 changes: 1 addition & 1 deletion crates/packager/src/config/category.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ struct AppCategoryVisitor {
did_you_mean: Option<&'static str>,
}

impl<'d> serde::de::Visitor<'d> for AppCategoryVisitor {
impl serde::de::Visitor<'_> for AppCategoryVisitor {
type Value = AppCategory;

fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
Expand Down
32 changes: 31 additions & 1 deletion crates/packager/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ impl DeepLinkProtocol {
}
}

/// Set he name. Maps to `CFBundleTypeName` on macOS. Defaults to the first item in `ext`
/// Set the name. Maps to `CFBundleTypeName` on macOS. Defaults to the first item in `ext`
pub fn name<S: Into<String>>(mut self, name: S) -> Self {
self.name.replace(name.into());
self
Expand Down Expand Up @@ -736,6 +736,15 @@ pub struct MacOsConfig {
/// Path to the Info.plist file for the package.
#[serde(alias = "info-plist-path", alias = "info_plist_path")]
pub info_plist_path: Option<PathBuf>,
/// Path to the embedded.provisionprofile file for the package.
#[serde(
alias = "embedded-provisionprofile-path",
alias = "embedded_provisionprofile_path"
)]
pub embedded_provisionprofile_path: Option<PathBuf>,
/// Apps that need to be packaged within the app.
#[serde(alias = "embedded-apps", alias = "embedded_apps")]
pub embedded_apps: Option<Vec<String>>,
}

impl MacOsConfig {
Expand Down Expand Up @@ -803,6 +812,27 @@ impl MacOsConfig {
self.info_plist_path.replace(info_plist_path.into());
self
}

/// Path to the embedded.provisionprofile file for the package.
pub fn embedded_provisionprofile_path<S: Into<PathBuf>>(
mut self,
embedded_provisionprofile_path: S,
) -> Self {
self.embedded_provisionprofile_path
.replace(embedded_provisionprofile_path.into());
self
}

/// Apps that need to be packaged within the app.
pub fn embedded_apps<I, S>(mut self, embedded_apps: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.embedded_apps
.replace(embedded_apps.into_iter().map(Into::into).collect());
self
}
}

/// A wix language.
Expand Down
10 changes: 9 additions & 1 deletion crates/packager/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ pub enum Error {
AppImageScriptFailed(std::io::Error),
/// Failed to get parent directory of a path
#[error("Failed to get parent directory of {0}")]
ParentDirNotFound(std::path::PathBuf),
ParentDirNotFound(PathBuf),
/// A hook, for example `beforePackagaingCommand`, has failed.
#[error("{0} `{1}` failed: {2}")]
HookCommandFailure(String, String, std::io::Error),
Expand Down Expand Up @@ -235,6 +235,14 @@ pub enum Error {
#[error("Failed to remove extended attributes from app bundle: {0}")]
#[cfg(target_os = "macos")]
FailedToRemoveExtendedAttributes(std::io::Error),
/// Could not find the embedded.provisionprofile file.
#[error("Embedded provision profile file {0} not found")]
#[cfg(target_os = "macos")]
EmbeddedProvisionprofileFileNotFound(PathBuf),
/// Could not copy the embedded.provisionprofile file to the Contents directory.
#[error("Could not copy embedded provision profile file {0}: {1}")]
#[cfg(target_os = "macos")]
FailedToCopyEmbeddedProvisionprofile(PathBuf, std::io::Error),
}

/// Convenient type alias of Result type for cargo-packager.
Expand Down
84 changes: 72 additions & 12 deletions crates/packager/src/package/app/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use crate::{
#[tracing::instrument(level = "trace", skip(ctx))]
pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
let Context { config, .. } = ctx;
// we should use the bundle name (App name) as a MacOS standard.
// we should use the bundle name (App name) as a macOS standard.
// version or platform shouldn't be included in the App name.
let app_product_name = format!("{}.app", config.product_name);
let app_bundle_path = config.out_dir().join(&app_product_name);
Expand Down Expand Up @@ -72,6 +72,12 @@ pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
tracing::debug!("Copying resources");
config.copy_resources(&resources_dir)?;

tracing::debug!("Copying embedded.provisionprofile");
copy_embedded_provisionprofile_file(&contents_directory, config)?;

tracing::debug!("Copying embedded apps");
let embedded_apps = copy_embedded_apps(&contents_directory, config)?;

tracing::debug!("Copying external binaries");
config.copy_external_binaries(&bin_dir)?;
tracing::debug!("Copying binaries");
Expand All @@ -90,7 +96,8 @@ pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
let files = walkdir::WalkDir::new(&app_bundle_path)
.into_iter()
.flatten()
.map(|dir| dir.into_path());
.map(|dir| dir.into_path())
.filter(|path| !embedded_apps.iter().any(|x| path.starts_with(x)));

// Filter all files for Mach-O headers. This will target all .dylib and native executable files
for file in files {
Expand Down Expand Up @@ -203,7 +210,7 @@ fn create_info_plist(
plist.insert(
"CFBundleIconFile".into(),
path.file_name()
.ok_or_else(|| crate::Error::FailedToExtractFilename(path.clone()))?
.ok_or_else(|| Error::FailedToExtractFilename(path.clone()))?
.to_string_lossy()
.into_owned()
.into(),
Expand Down Expand Up @@ -349,18 +356,18 @@ fn create_info_plist(
#[tracing::instrument(level = "trace")]
fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
if !from.exists() {
return Err(crate::Error::DoesNotExist(from.to_path_buf()));
return Err(Error::DoesNotExist(from.to_path_buf()));
}
if !from.is_dir() {
return Err(crate::Error::IsNotDirectory(from.to_path_buf()));
return Err(Error::IsNotDirectory(from.to_path_buf()));
}
if to.exists() {
return Err(crate::Error::AlreadyExists(to.to_path_buf()));
return Err(Error::AlreadyExists(to.to_path_buf()));
}

let parent = to
.parent()
.ok_or_else(|| crate::Error::ParentDirNotFound(to.to_path_buf()))?;
.ok_or_else(|| Error::ParentDirNotFound(to.to_path_buf()))?;
fs::create_dir_all(parent).map_err(|e| Error::IoWithPath(parent.to_path_buf(), e))?;
for entry in walkdir::WalkDir::new(from) {
let entry = entry?;
Expand Down Expand Up @@ -425,27 +432,27 @@ fn copy_frameworks_to_bundle(
let src_path = PathBuf::from(framework);
let src_name = src_path
.file_name()
.ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?;
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
let dest_path = dest_dir.join(src_name);
copy_dir(&src_path, &dest_path)?;
paths.push(dest_path);
continue;
} else if framework.ends_with(".dylib") {
let src_path = PathBuf::from(&framework);
if !src_path.exists() {
return Err(crate::Error::FrameworkNotFound(framework.to_string()));
return Err(Error::FrameworkNotFound(framework.to_string()));
}
let src_name = src_path
.file_name()
.ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?;
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
fs::create_dir_all(&dest_dir)?;
let dest_path = dest_dir.join(src_name);
fs::copy(&src_path, &dest_path)
.map_err(|e| Error::CopyFile(src_path.clone(), dest_path.clone(), e))?;
paths.push(dest_path);
continue;
} else if framework.contains('/') {
return Err(crate::Error::InvalidFramework {
return Err(Error::InvalidFramework {
framework: framework.to_string(),
reason: "framework extension should be either .framework, .dylib or .app",
});
Expand All @@ -466,7 +473,7 @@ fn copy_frameworks_to_bundle(
continue;
}

return Err(crate::Error::FrameworkNotFound(framework.to_string()));
return Err(Error::FrameworkNotFound(framework.to_string()));
}
}

Expand All @@ -482,3 +489,56 @@ fn remove_extra_attr(app_bundle_path: &Path) -> crate::Result<()> {
.map(|_| ())
.map_err(crate::Error::FailedToRemoveExtendedAttributes)
}

// Copies the embedded.provisionprofile file to the Contents directory, if needed.
#[cfg(target_os = "macos")]
fn copy_embedded_provisionprofile_file(
contents_directory: &Path,
config: &Config,
) -> crate::Result<()> {
if let Some(embedded_provisionprofile_file) = config
.macos()
.and_then(|m| m.embedded_provisionprofile_path.as_ref())
{
if !embedded_provisionprofile_file.exists() {
return Err(crate::Error::EmbeddedProvisionprofileFileNotFound(
embedded_provisionprofile_file.to_path_buf(),
));
}

fs::copy(
embedded_provisionprofile_file,
contents_directory.join("embedded.provisionprofile"),
)
.map_err(|e| {
crate::Error::FailedToCopyEmbeddedProvisionprofile(
embedded_provisionprofile_file.to_path_buf(),
e,
)
})?;
}
Ok(())
}

// Copies app structures that may need to be embedded inside this app.
#[tracing::instrument(level = "trace", skip(config))]
fn copy_embedded_apps(contents_directory: &Path, config: &Config) -> crate::Result<Vec<PathBuf>> {
let mut paths = Vec::new();

if let Some(embedded_apps) = config.macos().and_then(|m| m.embedded_apps.as_ref()) {
let dest_dir = contents_directory.join("MacOS");

for embedded_app in embedded_apps {
let src_path = PathBuf::from(embedded_app);
let src_name = src_path
.file_name()
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
let dest_path = dest_dir.join(src_name);
copy_dir(&src_path, &dest_path)?;

tracing::debug!("Copied embedded app: {:?}", dest_path);
paths.push(dest_path);
}
}
Ok(paths)
}
Loading