Skip to content

Commit 17c52f0

Browse files
Support for embedded provisioning profiles and embedded apps in macOS DMGs (#289)
* added embedded.provisionprofile support for macos apps (#1) * add more info to errors * fixed path mistake * fmt fixes * clippy fixes * clippy fixes * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * fix arch conditional compilation * added embedded app support * fmt * exclude apps from re-signing * exclude apps from re-signing * exclude apps from re-signing * fixed missing var * fixed missing var * comments addressed * update cargo lock * fix rpath typo * change files * make function available to windows, linux * removed conditional * update cargo-deny action to v2 * downgrade cargo-deny action to v1; patch Cargo.lock file --------- Co-authored-by: Lucas Nogueira <lucas@crabnebula.dev>
1 parent fbbbf18 commit 17c52f0

10 files changed

Lines changed: 169 additions & 16 deletions

File tree

.changes/embedded-apps.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cargo-packager": patch
3+
"@crabnebula/packager": patch
4+
---
5+
6+
Added support to embedding additional apps in the macOS app bundle.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"cargo-packager": patch
3+
"@crabnebula/packager": patch
4+
---
5+
6+
Added support to adding an `embedded.provisionprofile` file to the macOS bundle.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,4 +34,6 @@ Cargo.lock
3434
*.node.bak
3535

3636
yarn.lock
37-
package-lock.json
37+
package-lock.json
38+
39+
.idea

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bindings/packager/nodejs/schema.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,23 @@
766766
"string",
767767
"null"
768768
]
769+
},
770+
"embeddedProvisionprofilePath": {
771+
"description": "Path to the embedded.provisionprofile file for the package.",
772+
"type": [
773+
"string",
774+
"null"
775+
]
776+
},
777+
"embeddedApps": {
778+
"description": "Apps that need to be packaged within the app.",
779+
"type": [
780+
"array",
781+
"null"
782+
],
783+
"items": {
784+
"type": "string"
785+
}
769786
}
770787
},
771788
"additionalProperties": false

bindings/packager/nodejs/src-ts/config.d.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,16 @@ export interface MacOsConfig {
429429
* Path to the Info.plist file for the package.
430430
*/
431431
infoPlistPath?: string | null;
432+
433+
/**
434+
* Path to the embedded.provisionprofile file for the package.
435+
*/
436+
embeddedProvisionprofilePath?: string | null;
437+
438+
/**
439+
* Apps that need to be packaged within the app.
440+
*/
441+
embeddedApps?: string[] | null;
432442
}
433443
/**
434444
* The Linux Debian configuration.

crates/packager/schema.json

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,23 @@
766766
"string",
767767
"null"
768768
]
769+
},
770+
"embeddedProvisionprofilePath": {
771+
"description": "Path to the embedded.provisionprofile file for the package.",
772+
"type": [
773+
"string",
774+
"null"
775+
]
776+
},
777+
"embeddedApps": {
778+
"description": "Apps that need to be packaged within the app.",
779+
"type": [
780+
"array",
781+
"null"
782+
],
783+
"items": {
784+
"type": "string"
785+
}
769786
}
770787
},
771788
"additionalProperties": false

crates/packager/src/config/mod.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl DeepLinkProtocol {
162162
}
163163
}
164164

165-
/// Set he name. Maps to `CFBundleTypeName` on macOS. Defaults to the first item in `ext`
165+
/// Set the name. Maps to `CFBundleTypeName` on macOS. Defaults to the first item in `ext`
166166
pub fn name<S: Into<String>>(mut self, name: S) -> Self {
167167
self.name.replace(name.into());
168168
self
@@ -736,6 +736,15 @@ pub struct MacOsConfig {
736736
/// Path to the Info.plist file for the package.
737737
#[serde(alias = "info-plist-path", alias = "info_plist_path")]
738738
pub info_plist_path: Option<PathBuf>,
739+
/// Path to the embedded.provisionprofile file for the package.
740+
#[serde(
741+
alias = "embedded-provisionprofile-path",
742+
alias = "embedded_provisionprofile_path"
743+
)]
744+
pub embedded_provisionprofile_path: Option<PathBuf>,
745+
/// Apps that need to be packaged within the app.
746+
#[serde(alias = "embedded-apps", alias = "embedded_apps")]
747+
pub embedded_apps: Option<Vec<String>>,
739748
}
740749

741750
impl MacOsConfig {
@@ -803,6 +812,27 @@ impl MacOsConfig {
803812
self.info_plist_path.replace(info_plist_path.into());
804813
self
805814
}
815+
816+
/// Path to the embedded.provisionprofile file for the package.
817+
pub fn embedded_provisionprofile_path<S: Into<PathBuf>>(
818+
mut self,
819+
embedded_provisionprofile_path: S,
820+
) -> Self {
821+
self.embedded_provisionprofile_path
822+
.replace(embedded_provisionprofile_path.into());
823+
self
824+
}
825+
826+
/// Apps that need to be packaged within the app.
827+
pub fn embedded_apps<I, S>(mut self, embedded_apps: I) -> Self
828+
where
829+
I: IntoIterator<Item = S>,
830+
S: Into<String>,
831+
{
832+
self.embedded_apps
833+
.replace(embedded_apps.into_iter().map(Into::into).collect());
834+
self
835+
}
806836
}
807837

808838
/// A wix language.

crates/packager/src/error.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ pub enum Error {
100100
AppImageScriptFailed(std::io::Error),
101101
/// Failed to get parent directory of a path
102102
#[error("Failed to get parent directory of {0}")]
103-
ParentDirNotFound(std::path::PathBuf),
103+
ParentDirNotFound(PathBuf),
104104
/// A hook, for example `beforePackagaingCommand`, has failed.
105105
#[error("{0} `{1}` failed: {2}")]
106106
HookCommandFailure(String, String, std::io::Error),
@@ -235,6 +235,12 @@ pub enum Error {
235235
#[error("Failed to remove extended attributes from app bundle: {0}")]
236236
#[cfg(target_os = "macos")]
237237
FailedToRemoveExtendedAttributes(std::io::Error),
238+
/// Could not find the embedded.provisionprofile file.
239+
#[error("Embedded provision profile file {0} not found")]
240+
EmbeddedProvisionprofileFileNotFound(PathBuf),
241+
/// Could not copy the embedded.provisionprofile file to the Contents directory.
242+
#[error("Could not copy embedded provision profile file {0}: {1}")]
243+
FailedToCopyEmbeddedProvisionprofile(PathBuf, std::io::Error),
238244
}
239245

240246
/// Convenient type alias of Result type for cargo-packager.

crates/packager/src/package/app/mod.rs

Lines changed: 71 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
#[tracing::instrument(level = "trace", skip(ctx))]
2121
pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
2222
let Context { config, .. } = ctx;
23-
// we should use the bundle name (App name) as a MacOS standard.
23+
// we should use the bundle name (App name) as a macOS standard.
2424
// version or platform shouldn't be included in the App name.
2525
let app_product_name = format!("{}.app", config.product_name);
2626
let app_bundle_path = config.out_dir().join(&app_product_name);
@@ -72,6 +72,12 @@ pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
7272
tracing::debug!("Copying resources");
7373
config.copy_resources(&resources_dir)?;
7474

75+
tracing::debug!("Copying embedded.provisionprofile");
76+
copy_embedded_provisionprofile_file(&contents_directory, config)?;
77+
78+
tracing::debug!("Copying embedded apps");
79+
let embedded_apps = copy_embedded_apps(&contents_directory, config)?;
80+
7581
tracing::debug!("Copying external binaries");
7682
config.copy_external_binaries(&bin_dir)?;
7783
tracing::debug!("Copying binaries");
@@ -90,7 +96,8 @@ pub(crate) fn package(ctx: &Context) -> crate::Result<Vec<PathBuf>> {
9096
let files = walkdir::WalkDir::new(&app_bundle_path)
9197
.into_iter()
9298
.flatten()
93-
.map(|dir| dir.into_path());
99+
.map(|dir| dir.into_path())
100+
.filter(|path| !embedded_apps.iter().any(|x| path.starts_with(x)));
94101

95102
// Filter all files for Mach-O headers. This will target all .dylib and native executable files
96103
for file in files {
@@ -203,7 +210,7 @@ fn create_info_plist(
203210
plist.insert(
204211
"CFBundleIconFile".into(),
205212
path.file_name()
206-
.ok_or_else(|| crate::Error::FailedToExtractFilename(path.clone()))?
213+
.ok_or_else(|| Error::FailedToExtractFilename(path.clone()))?
207214
.to_string_lossy()
208215
.into_owned()
209216
.into(),
@@ -349,18 +356,18 @@ fn create_info_plist(
349356
#[tracing::instrument(level = "trace")]
350357
fn copy_dir(from: &Path, to: &Path) -> crate::Result<()> {
351358
if !from.exists() {
352-
return Err(crate::Error::DoesNotExist(from.to_path_buf()));
359+
return Err(Error::DoesNotExist(from.to_path_buf()));
353360
}
354361
if !from.is_dir() {
355-
return Err(crate::Error::IsNotDirectory(from.to_path_buf()));
362+
return Err(Error::IsNotDirectory(from.to_path_buf()));
356363
}
357364
if to.exists() {
358-
return Err(crate::Error::AlreadyExists(to.to_path_buf()));
365+
return Err(Error::AlreadyExists(to.to_path_buf()));
359366
}
360367

361368
let parent = to
362369
.parent()
363-
.ok_or_else(|| crate::Error::ParentDirNotFound(to.to_path_buf()))?;
370+
.ok_or_else(|| Error::ParentDirNotFound(to.to_path_buf()))?;
364371
fs::create_dir_all(parent).map_err(|e| Error::IoWithPath(parent.to_path_buf(), e))?;
365372
for entry in walkdir::WalkDir::new(from) {
366373
let entry = entry?;
@@ -425,27 +432,27 @@ fn copy_frameworks_to_bundle(
425432
let src_path = PathBuf::from(framework);
426433
let src_name = src_path
427434
.file_name()
428-
.ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?;
435+
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
429436
let dest_path = dest_dir.join(src_name);
430437
copy_dir(&src_path, &dest_path)?;
431438
paths.push(dest_path);
432439
continue;
433440
} else if framework.ends_with(".dylib") {
434441
let src_path = PathBuf::from(&framework);
435442
if !src_path.exists() {
436-
return Err(crate::Error::FrameworkNotFound(framework.to_string()));
443+
return Err(Error::FrameworkNotFound(framework.to_string()));
437444
}
438445
let src_name = src_path
439446
.file_name()
440-
.ok_or_else(|| crate::Error::FailedToExtractFilename(src_path.clone()))?;
447+
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
441448
fs::create_dir_all(&dest_dir)?;
442449
let dest_path = dest_dir.join(src_name);
443450
fs::copy(&src_path, &dest_path)
444451
.map_err(|e| Error::CopyFile(src_path.clone(), dest_path.clone(), e))?;
445452
paths.push(dest_path);
446453
continue;
447454
} else if framework.contains('/') {
448-
return Err(crate::Error::InvalidFramework {
455+
return Err(Error::InvalidFramework {
449456
framework: framework.to_string(),
450457
reason: "framework extension should be either .framework, .dylib or .app",
451458
});
@@ -466,7 +473,7 @@ fn copy_frameworks_to_bundle(
466473
continue;
467474
}
468475

469-
return Err(crate::Error::FrameworkNotFound(framework.to_string()));
476+
return Err(Error::FrameworkNotFound(framework.to_string()));
470477
}
471478
}
472479

@@ -482,3 +489,55 @@ fn remove_extra_attr(app_bundle_path: &Path) -> crate::Result<()> {
482489
.map(|_| ())
483490
.map_err(crate::Error::FailedToRemoveExtendedAttributes)
484491
}
492+
493+
// Copies the embedded.provisionprofile file to the Contents directory, if needed.
494+
fn copy_embedded_provisionprofile_file(
495+
contents_directory: &Path,
496+
config: &Config,
497+
) -> crate::Result<()> {
498+
if let Some(embedded_provisionprofile_file) = config
499+
.macos()
500+
.and_then(|m| m.embedded_provisionprofile_path.as_ref())
501+
{
502+
if !embedded_provisionprofile_file.exists() {
503+
return Err(crate::Error::EmbeddedProvisionprofileFileNotFound(
504+
embedded_provisionprofile_file.to_path_buf(),
505+
));
506+
}
507+
508+
fs::copy(
509+
embedded_provisionprofile_file,
510+
contents_directory.join("embedded.provisionprofile"),
511+
)
512+
.map_err(|e| {
513+
crate::Error::FailedToCopyEmbeddedProvisionprofile(
514+
embedded_provisionprofile_file.to_path_buf(),
515+
e,
516+
)
517+
})?;
518+
}
519+
Ok(())
520+
}
521+
522+
// Copies app structures that may need to be embedded inside this app.
523+
#[tracing::instrument(level = "trace", skip(config))]
524+
fn copy_embedded_apps(contents_directory: &Path, config: &Config) -> crate::Result<Vec<PathBuf>> {
525+
let mut paths = Vec::new();
526+
527+
if let Some(embedded_apps) = config.macos().and_then(|m| m.embedded_apps.as_ref()) {
528+
let dest_dir = contents_directory.join("MacOS");
529+
530+
for embedded_app in embedded_apps {
531+
let src_path = PathBuf::from(embedded_app);
532+
let src_name = src_path
533+
.file_name()
534+
.ok_or_else(|| Error::FailedToExtractFilename(src_path.clone()))?;
535+
let dest_path = dest_dir.join(src_name);
536+
copy_dir(&src_path, &dest_path)?;
537+
538+
tracing::debug!("Copied embedded app: {:?}", dest_path);
539+
paths.push(dest_path);
540+
}
541+
}
542+
Ok(paths)
543+
}

0 commit comments

Comments
 (0)