electron-cli 0.3.0-alpha.15 → 0.3.0-alpha.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/README.md +12 -2
- package/package.json +1 -1
- package/src/commands/package.rs +469 -0
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
package/README.md
CHANGED
|
@@ -41,7 +41,9 @@ The Rust-native flow currently owns:
|
|
|
41
41
|
|
|
42
42
|
The GitHub publisher creates or reuses a release, uploads selected make artifacts, and can replace an existing asset with `--force`. It reads `GITHUB_TOKEN` or `GH_TOKEN` and can infer `OWNER/REPO` from package metadata, Forge GitHub publisher config, or `package.json` `repository`. You can also pass `--github-repo`.
|
|
43
43
|
|
|
44
|
-
The
|
|
44
|
+
The package command recognizes macOS `packagerConfig.osxSign` and `packagerConfig.osxNotarize` options and reports the signing/notarization plan without serializing credential values. Actual Rust-native signing and notarization execution is not implemented yet.
|
|
45
|
+
|
|
46
|
+
The DMG maker is currently a pure-Rust FAT32 image with the app bundle and an Applications entry. The MSI maker writes a compressed embedded CAB, Windows Installer database tables, and a Start Menu shortcut when the packaged executable is present. HFS+/APFS DMG layout customization, installer UI customization, Windows/Linux icon embedding, signing execution, and notarization execution are still TODO.
|
|
45
47
|
|
|
46
48
|
Package metadata can be configured in `package.json`:
|
|
47
49
|
|
|
@@ -53,7 +55,15 @@ Package metadata can be configured in `package.json`:
|
|
|
53
55
|
"appBundleId": "com.example.my-app",
|
|
54
56
|
"appCategoryType": "public.app-category.developer-tools",
|
|
55
57
|
"icon": "assets/icon",
|
|
56
|
-
"extraResource": "assets/config.json"
|
|
58
|
+
"extraResource": "assets/config.json",
|
|
59
|
+
"osxSign": {
|
|
60
|
+
"identity": "Developer ID Application: Example, Inc. (TEAMID1234)",
|
|
61
|
+
"entitlements": "assets/entitlements.plist",
|
|
62
|
+
"hardenedRuntime": true
|
|
63
|
+
},
|
|
64
|
+
"osxNotarize": {
|
|
65
|
+
"keychainProfile": "notary-profile"
|
|
66
|
+
}
|
|
57
67
|
},
|
|
58
68
|
"makers": [
|
|
59
69
|
{ "name": "@electron-forge/maker-zip" },
|
package/package.json
CHANGED
package/src/commands/package.rs
CHANGED
|
@@ -18,6 +18,7 @@ pub(crate) struct PackageReport {
|
|
|
18
18
|
app_name: String,
|
|
19
19
|
executable_name: String,
|
|
20
20
|
metadata: PackageMetadata,
|
|
21
|
+
signing: PackageSigningPlan,
|
|
21
22
|
platform: String,
|
|
22
23
|
arch: String,
|
|
23
24
|
electron_dist: Utf8PathBuf,
|
|
@@ -55,6 +56,37 @@ struct IconResource {
|
|
|
55
56
|
to: Utf8PathBuf,
|
|
56
57
|
}
|
|
57
58
|
|
|
59
|
+
#[derive(Clone, Debug, Serialize)]
|
|
60
|
+
struct PackageSigningPlan {
|
|
61
|
+
macos: MacosSigningPlan,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
#[derive(Clone, Debug, Serialize)]
|
|
65
|
+
struct MacosSigningPlan {
|
|
66
|
+
sign: MacosSignPlan,
|
|
67
|
+
notarize: MacosNotarizePlan,
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
#[derive(Clone, Debug, Serialize)]
|
|
71
|
+
struct MacosSignPlan {
|
|
72
|
+
configured: bool,
|
|
73
|
+
enabled: bool,
|
|
74
|
+
identity: Option<String>,
|
|
75
|
+
entitlements: Vec<Utf8PathBuf>,
|
|
76
|
+
entitlements_inherit: Option<Utf8PathBuf>,
|
|
77
|
+
hardened_runtime: Option<bool>,
|
|
78
|
+
gatekeeper_assess: Option<bool>,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#[derive(Clone, Debug, Serialize)]
|
|
82
|
+
struct MacosNotarizePlan {
|
|
83
|
+
configured: bool,
|
|
84
|
+
enabled: bool,
|
|
85
|
+
auth_method: Option<String>,
|
|
86
|
+
keychain_profile: Option<String>,
|
|
87
|
+
keychain: Option<String>,
|
|
88
|
+
}
|
|
89
|
+
|
|
58
90
|
#[derive(Clone, Copy, Debug, Serialize)]
|
|
59
91
|
#[serde(rename_all = "kebab-case")]
|
|
60
92
|
enum PackageStatus {
|
|
@@ -82,6 +114,35 @@ struct PackagerConfig {
|
|
|
82
114
|
icon: Vec<String>,
|
|
83
115
|
extra_resource: Vec<String>,
|
|
84
116
|
darwin_dark_mode_support: bool,
|
|
117
|
+
osx_sign: MacosSignConfig,
|
|
118
|
+
osx_notarize: MacosNotarizeConfig,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
#[derive(Clone, Debug, Default)]
|
|
122
|
+
struct MacosSignConfig {
|
|
123
|
+
configured: bool,
|
|
124
|
+
enabled: bool,
|
|
125
|
+
invalid_type: bool,
|
|
126
|
+
identity: Option<String>,
|
|
127
|
+
entitlements: Vec<String>,
|
|
128
|
+
entitlements_inherit: Option<String>,
|
|
129
|
+
hardened_runtime: Option<bool>,
|
|
130
|
+
gatekeeper_assess: Option<bool>,
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
#[derive(Clone, Debug, Default)]
|
|
134
|
+
struct MacosNotarizeConfig {
|
|
135
|
+
configured: bool,
|
|
136
|
+
enabled: bool,
|
|
137
|
+
invalid_type: bool,
|
|
138
|
+
apple_id_set: bool,
|
|
139
|
+
apple_id_password_set: bool,
|
|
140
|
+
team_id_set: bool,
|
|
141
|
+
apple_api_key: Option<String>,
|
|
142
|
+
apple_api_key_id_set: bool,
|
|
143
|
+
apple_api_issuer_set: bool,
|
|
144
|
+
keychain_profile: Option<String>,
|
|
145
|
+
keychain: Option<String>,
|
|
85
146
|
}
|
|
86
147
|
|
|
87
148
|
pub fn run(args: PackageArgs) -> Result<()> {
|
|
@@ -133,6 +194,7 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
133
194
|
&app_resources_dir,
|
|
134
195
|
&platform,
|
|
135
196
|
)?;
|
|
197
|
+
let (signing, signing_warnings) = package_signing(root, &package_config, &platform)?;
|
|
136
198
|
|
|
137
199
|
let mut warnings = package_config.warnings.clone();
|
|
138
200
|
if snapshot.package_json.is_none() {
|
|
@@ -170,6 +232,7 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
170
232
|
|
|
171
233
|
warnings.extend(runtime_dependency_warnings(root, &snapshot));
|
|
172
234
|
warnings.extend(metadata_warnings);
|
|
235
|
+
warnings.extend(signing_warnings);
|
|
173
236
|
|
|
174
237
|
let create_dirs = vec![package_root.clone(), app_resources_dir.clone()];
|
|
175
238
|
let mut copy_steps = vec![
|
|
@@ -200,6 +263,7 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
200
263
|
app_name,
|
|
201
264
|
executable_name,
|
|
202
265
|
metadata,
|
|
266
|
+
signing,
|
|
203
267
|
platform,
|
|
204
268
|
arch,
|
|
205
269
|
electron_dist: utf8_path(electron_dist)?,
|
|
@@ -325,6 +389,33 @@ fn print_report(report: &PackageReport, json: bool) -> Result<()> {
|
|
|
325
389
|
println!(" target: {} {}", report.platform, report.arch);
|
|
326
390
|
println!(" status: {}", report.status.as_str());
|
|
327
391
|
|
|
392
|
+
if report.signing.macos.sign.configured || report.signing.macos.notarize.configured {
|
|
393
|
+
println!();
|
|
394
|
+
println!("Signing");
|
|
395
|
+
println!(
|
|
396
|
+
" macOS signing: {}",
|
|
397
|
+
if report.signing.macos.sign.enabled {
|
|
398
|
+
"configured"
|
|
399
|
+
} else {
|
|
400
|
+
"disabled"
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
if let Some(identity) = &report.signing.macos.sign.identity {
|
|
404
|
+
println!(" identity: {identity}");
|
|
405
|
+
}
|
|
406
|
+
println!(
|
|
407
|
+
" macOS notarization: {}",
|
|
408
|
+
if report.signing.macos.notarize.enabled {
|
|
409
|
+
"configured"
|
|
410
|
+
} else {
|
|
411
|
+
"disabled"
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
if let Some(method) = &report.signing.macos.notarize.auth_method {
|
|
415
|
+
println!(" notarization auth: {method}");
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
328
419
|
println!();
|
|
329
420
|
println!("Output");
|
|
330
421
|
println!(" {}", report.bundle_dir);
|
|
@@ -400,6 +491,114 @@ fn parse_packager_config(value: &JsonValue) -> PackagerConfig {
|
|
|
400
491
|
.get("darwinDarkModeSupport")
|
|
401
492
|
.and_then(JsonValue::as_bool)
|
|
402
493
|
.unwrap_or(false),
|
|
494
|
+
osx_sign: parse_macos_sign_config(value.get("osxSign")),
|
|
495
|
+
osx_notarize: parse_macos_notarize_config(value.get("osxNotarize")),
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
fn parse_macos_sign_config(value: Option<&JsonValue>) -> MacosSignConfig {
|
|
500
|
+
match value {
|
|
501
|
+
None => MacosSignConfig::default(),
|
|
502
|
+
Some(JsonValue::Bool(false)) => MacosSignConfig {
|
|
503
|
+
configured: true,
|
|
504
|
+
enabled: false,
|
|
505
|
+
..MacosSignConfig::default()
|
|
506
|
+
},
|
|
507
|
+
Some(JsonValue::Bool(true)) => MacosSignConfig {
|
|
508
|
+
configured: true,
|
|
509
|
+
enabled: true,
|
|
510
|
+
..MacosSignConfig::default()
|
|
511
|
+
},
|
|
512
|
+
Some(JsonValue::Object(object)) => {
|
|
513
|
+
let entitlements = [
|
|
514
|
+
"entitlements",
|
|
515
|
+
"entitlementsInherit",
|
|
516
|
+
"entitlementsLoginHelper",
|
|
517
|
+
]
|
|
518
|
+
.iter()
|
|
519
|
+
.filter_map(|key| {
|
|
520
|
+
object
|
|
521
|
+
.get(*key)
|
|
522
|
+
.and_then(JsonValue::as_str)
|
|
523
|
+
.map(ToOwned::to_owned)
|
|
524
|
+
})
|
|
525
|
+
.collect();
|
|
526
|
+
|
|
527
|
+
MacosSignConfig {
|
|
528
|
+
configured: true,
|
|
529
|
+
enabled: true,
|
|
530
|
+
invalid_type: false,
|
|
531
|
+
identity: object
|
|
532
|
+
.get("identity")
|
|
533
|
+
.or_else(|| object.get("identityName"))
|
|
534
|
+
.and_then(JsonValue::as_str)
|
|
535
|
+
.map(ToOwned::to_owned),
|
|
536
|
+
entitlements,
|
|
537
|
+
entitlements_inherit: object
|
|
538
|
+
.get("entitlementsInherit")
|
|
539
|
+
.and_then(JsonValue::as_str)
|
|
540
|
+
.map(ToOwned::to_owned),
|
|
541
|
+
hardened_runtime: object.get("hardenedRuntime").and_then(JsonValue::as_bool),
|
|
542
|
+
gatekeeper_assess: object.get("gatekeeperAssess").and_then(JsonValue::as_bool),
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
Some(_) => MacosSignConfig {
|
|
546
|
+
configured: true,
|
|
547
|
+
invalid_type: true,
|
|
548
|
+
..MacosSignConfig::default()
|
|
549
|
+
},
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
fn parse_macos_notarize_config(value: Option<&JsonValue>) -> MacosNotarizeConfig {
|
|
554
|
+
match value {
|
|
555
|
+
None => MacosNotarizeConfig::default(),
|
|
556
|
+
Some(JsonValue::Bool(false)) => MacosNotarizeConfig {
|
|
557
|
+
configured: true,
|
|
558
|
+
enabled: false,
|
|
559
|
+
..MacosNotarizeConfig::default()
|
|
560
|
+
},
|
|
561
|
+
Some(JsonValue::Bool(true)) => MacosNotarizeConfig {
|
|
562
|
+
configured: true,
|
|
563
|
+
enabled: true,
|
|
564
|
+
..MacosNotarizeConfig::default()
|
|
565
|
+
},
|
|
566
|
+
Some(JsonValue::Object(object)) => MacosNotarizeConfig {
|
|
567
|
+
configured: true,
|
|
568
|
+
enabled: true,
|
|
569
|
+
invalid_type: false,
|
|
570
|
+
apple_id_set: object.get("appleId").and_then(JsonValue::as_str).is_some(),
|
|
571
|
+
apple_id_password_set: object
|
|
572
|
+
.get("appleIdPassword")
|
|
573
|
+
.and_then(JsonValue::as_str)
|
|
574
|
+
.is_some(),
|
|
575
|
+
team_id_set: object.get("teamId").and_then(JsonValue::as_str).is_some(),
|
|
576
|
+
apple_api_key: object
|
|
577
|
+
.get("appleApiKey")
|
|
578
|
+
.and_then(JsonValue::as_str)
|
|
579
|
+
.map(ToOwned::to_owned),
|
|
580
|
+
apple_api_key_id_set: object
|
|
581
|
+
.get("appleApiKeyId")
|
|
582
|
+
.and_then(JsonValue::as_str)
|
|
583
|
+
.is_some(),
|
|
584
|
+
apple_api_issuer_set: object
|
|
585
|
+
.get("appleApiIssuer")
|
|
586
|
+
.and_then(JsonValue::as_str)
|
|
587
|
+
.is_some(),
|
|
588
|
+
keychain_profile: object
|
|
589
|
+
.get("keychainProfile")
|
|
590
|
+
.and_then(JsonValue::as_str)
|
|
591
|
+
.map(ToOwned::to_owned),
|
|
592
|
+
keychain: object
|
|
593
|
+
.get("keychain")
|
|
594
|
+
.and_then(JsonValue::as_str)
|
|
595
|
+
.map(ToOwned::to_owned),
|
|
596
|
+
},
|
|
597
|
+
Some(_) => MacosNotarizeConfig {
|
|
598
|
+
configured: true,
|
|
599
|
+
invalid_type: true,
|
|
600
|
+
..MacosNotarizeConfig::default()
|
|
601
|
+
},
|
|
403
602
|
}
|
|
404
603
|
}
|
|
405
604
|
|
|
@@ -473,6 +672,146 @@ fn package_metadata(
|
|
|
473
672
|
))
|
|
474
673
|
}
|
|
475
674
|
|
|
675
|
+
fn package_signing(
|
|
676
|
+
root: &Path,
|
|
677
|
+
config: &PackageJsonConfig,
|
|
678
|
+
platform: &str,
|
|
679
|
+
) -> Result<(PackageSigningPlan, Vec<String>)> {
|
|
680
|
+
let mut warnings = Vec::new();
|
|
681
|
+
let sign = macos_sign_plan(root, &config.packager.osx_sign, platform, &mut warnings)?;
|
|
682
|
+
let notarize = macos_notarize_plan(root, config, platform, &mut warnings);
|
|
683
|
+
|
|
684
|
+
Ok((
|
|
685
|
+
PackageSigningPlan {
|
|
686
|
+
macos: MacosSigningPlan { sign, notarize },
|
|
687
|
+
},
|
|
688
|
+
warnings,
|
|
689
|
+
))
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
fn macos_sign_plan(
|
|
693
|
+
root: &Path,
|
|
694
|
+
config: &MacosSignConfig,
|
|
695
|
+
platform: &str,
|
|
696
|
+
warnings: &mut Vec<String>,
|
|
697
|
+
) -> Result<MacosSignPlan> {
|
|
698
|
+
if config.invalid_type {
|
|
699
|
+
warnings.push("packagerConfig.osxSign must be false, true, or an object.".to_string());
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
let entitlements = config
|
|
703
|
+
.entitlements
|
|
704
|
+
.iter()
|
|
705
|
+
.filter(|path| !path.trim().is_empty())
|
|
706
|
+
.map(|path| {
|
|
707
|
+
let resolved = resolve_project_path(root, path);
|
|
708
|
+
if !resolved.exists() {
|
|
709
|
+
warnings.push(format!(
|
|
710
|
+
"Configured macOS entitlements file does not exist: {}.",
|
|
711
|
+
resolved.display()
|
|
712
|
+
));
|
|
713
|
+
}
|
|
714
|
+
utf8_path(resolved)
|
|
715
|
+
})
|
|
716
|
+
.collect::<Result<Vec<_>>>()?;
|
|
717
|
+
let entitlements_inherit = config
|
|
718
|
+
.entitlements_inherit
|
|
719
|
+
.as_deref()
|
|
720
|
+
.filter(|path| !path.trim().is_empty())
|
|
721
|
+
.map(|path| utf8_path(resolve_project_path(root, path)))
|
|
722
|
+
.transpose()?;
|
|
723
|
+
|
|
724
|
+
if config.configured && platform != "darwin" {
|
|
725
|
+
warnings.push(format!(
|
|
726
|
+
"macOS signing is configured but ignored for target platform {platform}."
|
|
727
|
+
));
|
|
728
|
+
} else if config.enabled {
|
|
729
|
+
warnings.push(
|
|
730
|
+
"macOS signing is configured, but Rust-native signing is not implemented yet; package output will be unsigned.".to_string(),
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
Ok(MacosSignPlan {
|
|
735
|
+
configured: config.configured,
|
|
736
|
+
enabled: config.enabled,
|
|
737
|
+
identity: config.identity.clone(),
|
|
738
|
+
entitlements,
|
|
739
|
+
entitlements_inherit,
|
|
740
|
+
hardened_runtime: config.hardened_runtime,
|
|
741
|
+
gatekeeper_assess: config.gatekeeper_assess,
|
|
742
|
+
})
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
fn macos_notarize_plan(
|
|
746
|
+
root: &Path,
|
|
747
|
+
package_config: &PackageJsonConfig,
|
|
748
|
+
platform: &str,
|
|
749
|
+
warnings: &mut Vec<String>,
|
|
750
|
+
) -> MacosNotarizePlan {
|
|
751
|
+
let config = &package_config.packager.osx_notarize;
|
|
752
|
+
if config.invalid_type {
|
|
753
|
+
warnings.push("packagerConfig.osxNotarize must be false, true, or an object.".to_string());
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
let auth_method = macos_notarize_auth_method(config);
|
|
757
|
+
if config.configured && platform != "darwin" {
|
|
758
|
+
warnings.push(format!(
|
|
759
|
+
"macOS notarization is configured but ignored for target platform {platform}."
|
|
760
|
+
));
|
|
761
|
+
} else if config.enabled {
|
|
762
|
+
warnings.push(
|
|
763
|
+
"macOS notarization is configured, but Rust-native notarization is not implemented yet.".to_string(),
|
|
764
|
+
);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
if config.enabled && !package_config.packager.osx_sign.enabled {
|
|
768
|
+
warnings.push(
|
|
769
|
+
"macOS notarization requires packagerConfig.osxSign to be enabled first.".to_string(),
|
|
770
|
+
);
|
|
771
|
+
}
|
|
772
|
+
if config.enabled && auth_method.is_none() {
|
|
773
|
+
warnings.push(
|
|
774
|
+
"macOS notarization config is missing a complete notarytool authentication set: appleId/appleIdPassword/teamId, appleApiKey/appleApiKeyId/appleApiIssuer, or keychainProfile.".to_string(),
|
|
775
|
+
);
|
|
776
|
+
}
|
|
777
|
+
if let Some(api_key) = &config.apple_api_key {
|
|
778
|
+
let path = resolve_project_path(root, api_key);
|
|
779
|
+
if !path.exists() {
|
|
780
|
+
warnings.push(format!(
|
|
781
|
+
"Configured Apple API key file does not exist: {}.",
|
|
782
|
+
path.display()
|
|
783
|
+
));
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
MacosNotarizePlan {
|
|
788
|
+
configured: config.configured,
|
|
789
|
+
enabled: config.enabled,
|
|
790
|
+
auth_method,
|
|
791
|
+
keychain_profile: config.keychain_profile.clone(),
|
|
792
|
+
keychain: config.keychain.clone(),
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
fn macos_notarize_auth_method(config: &MacosNotarizeConfig) -> Option<String> {
|
|
797
|
+
if config
|
|
798
|
+
.keychain_profile
|
|
799
|
+
.as_deref()
|
|
800
|
+
.is_some_and(|value| !value.trim().is_empty())
|
|
801
|
+
{
|
|
802
|
+
Some("keychain-profile".to_string())
|
|
803
|
+
} else if config.apple_api_key.is_some()
|
|
804
|
+
&& config.apple_api_key_id_set
|
|
805
|
+
&& config.apple_api_issuer_set
|
|
806
|
+
{
|
|
807
|
+
Some("app-store-connect-api-key".to_string())
|
|
808
|
+
} else if config.apple_id_set && config.apple_id_password_set && config.team_id_set {
|
|
809
|
+
Some("apple-id".to_string())
|
|
810
|
+
} else {
|
|
811
|
+
None
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
476
815
|
fn resolve_icon_resource(
|
|
477
816
|
root: &Path,
|
|
478
817
|
configured_icons: &[String],
|
|
@@ -1158,6 +1497,12 @@ impl PackagerConfig {
|
|
|
1158
1497
|
}
|
|
1159
1498
|
self.darwin_dark_mode_support =
|
|
1160
1499
|
other.darwin_dark_mode_support || self.darwin_dark_mode_support;
|
|
1500
|
+
if other.osx_sign.configured {
|
|
1501
|
+
self.osx_sign = other.osx_sign;
|
|
1502
|
+
}
|
|
1503
|
+
if other.osx_notarize.configured {
|
|
1504
|
+
self.osx_notarize = other.osx_notarize;
|
|
1505
|
+
}
|
|
1161
1506
|
}
|
|
1162
1507
|
}
|
|
1163
1508
|
|
|
@@ -1423,6 +1768,130 @@ mod tests {
|
|
|
1423
1768
|
let _ = fs::remove_dir_all(root);
|
|
1424
1769
|
}
|
|
1425
1770
|
|
|
1771
|
+
#[test]
|
|
1772
|
+
fn plans_macos_signing_and_notarization_without_serializing_secrets() {
|
|
1773
|
+
let root = unique_temp_dir("macos-signing-plan");
|
|
1774
|
+
write_package_json(&root);
|
|
1775
|
+
fs::write(root.join("entitlements.plist"), "<plist></plist>")
|
|
1776
|
+
.expect("entitlements should be written");
|
|
1777
|
+
fs::write(root.join("AuthKey_TEST.p8"), "secret api key")
|
|
1778
|
+
.expect("api key should be written");
|
|
1779
|
+
fs::write(
|
|
1780
|
+
root.join("forge.config.js"),
|
|
1781
|
+
r#"
|
|
1782
|
+
module.exports = {
|
|
1783
|
+
packagerConfig: {
|
|
1784
|
+
osxSign: {
|
|
1785
|
+
identity: 'Developer ID Application: Example, Inc. (TEAMID1234)',
|
|
1786
|
+
entitlements: 'entitlements.plist',
|
|
1787
|
+
entitlementsInherit: 'entitlements.plist',
|
|
1788
|
+
hardenedRuntime: true,
|
|
1789
|
+
gatekeeperAssess: false,
|
|
1790
|
+
},
|
|
1791
|
+
osxNotarize: {
|
|
1792
|
+
appleApiKey: 'AuthKey_TEST.p8',
|
|
1793
|
+
appleApiKeyId: 'SECRET_KEY_ID',
|
|
1794
|
+
appleApiIssuer: 'SECRET_ISSUER_ID',
|
|
1795
|
+
},
|
|
1796
|
+
},
|
|
1797
|
+
};
|
|
1798
|
+
"#,
|
|
1799
|
+
)
|
|
1800
|
+
.expect("forge config should be written");
|
|
1801
|
+
write_app_file(&root);
|
|
1802
|
+
write_fake_electron_dist(&root);
|
|
1803
|
+
|
|
1804
|
+
let args = PackageArgs {
|
|
1805
|
+
cwd: root.clone(),
|
|
1806
|
+
out_dir: PathBuf::from("out"),
|
|
1807
|
+
name: None,
|
|
1808
|
+
platform: Some("darwin".to_string()),
|
|
1809
|
+
arch: Some("arm64".to_string()),
|
|
1810
|
+
force: false,
|
|
1811
|
+
dry_run: true,
|
|
1812
|
+
json: true,
|
|
1813
|
+
};
|
|
1814
|
+
let snapshot = crate::project::inspect(&root).expect("project should inspect");
|
|
1815
|
+
let report = build_report(snapshot, &args).expect("report should build");
|
|
1816
|
+
|
|
1817
|
+
assert!(report.signing.macos.sign.configured);
|
|
1818
|
+
assert!(report.signing.macos.sign.enabled);
|
|
1819
|
+
assert_eq!(
|
|
1820
|
+
report.signing.macos.sign.identity.as_deref(),
|
|
1821
|
+
Some("Developer ID Application: Example, Inc. (TEAMID1234)")
|
|
1822
|
+
);
|
|
1823
|
+
assert_eq!(report.signing.macos.sign.hardened_runtime, Some(true));
|
|
1824
|
+
assert_eq!(report.signing.macos.sign.gatekeeper_assess, Some(false));
|
|
1825
|
+
assert_eq!(report.signing.macos.sign.entitlements.len(), 2);
|
|
1826
|
+
assert!(report.signing.macos.notarize.configured);
|
|
1827
|
+
assert_eq!(
|
|
1828
|
+
report.signing.macos.notarize.auth_method.as_deref(),
|
|
1829
|
+
Some("app-store-connect-api-key")
|
|
1830
|
+
);
|
|
1831
|
+
assert!(report
|
|
1832
|
+
.warnings
|
|
1833
|
+
.iter()
|
|
1834
|
+
.any(|warning| warning.contains("Rust-native signing is not implemented")));
|
|
1835
|
+
assert!(report
|
|
1836
|
+
.warnings
|
|
1837
|
+
.iter()
|
|
1838
|
+
.any(|warning| warning.contains("Rust-native notarization is not implemented")));
|
|
1839
|
+
|
|
1840
|
+
let json = serde_json::to_string(&report).expect("report should serialize");
|
|
1841
|
+
assert!(!json.contains("SECRET_KEY_ID"));
|
|
1842
|
+
assert!(!json.contains("SECRET_ISSUER_ID"));
|
|
1843
|
+
assert!(!json.contains("secret api key"));
|
|
1844
|
+
|
|
1845
|
+
let _ = fs::remove_dir_all(root);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
#[test]
|
|
1849
|
+
fn warns_when_macos_notarization_is_configured_without_signing() {
|
|
1850
|
+
let root = unique_temp_dir("notarize-without-sign");
|
|
1851
|
+
write_package_json(&root);
|
|
1852
|
+
fs::write(
|
|
1853
|
+
root.join("forge.config.js"),
|
|
1854
|
+
r#"
|
|
1855
|
+
module.exports = {
|
|
1856
|
+
packagerConfig: {
|
|
1857
|
+
osxSign: false,
|
|
1858
|
+
osxNotarize: {
|
|
1859
|
+
keychainProfile: 'notary-profile',
|
|
1860
|
+
},
|
|
1861
|
+
},
|
|
1862
|
+
};
|
|
1863
|
+
"#,
|
|
1864
|
+
)
|
|
1865
|
+
.expect("forge config should be written");
|
|
1866
|
+
write_app_file(&root);
|
|
1867
|
+
write_fake_electron_dist(&root);
|
|
1868
|
+
|
|
1869
|
+
let args = PackageArgs {
|
|
1870
|
+
cwd: root.clone(),
|
|
1871
|
+
out_dir: PathBuf::from("out"),
|
|
1872
|
+
name: None,
|
|
1873
|
+
platform: Some("darwin".to_string()),
|
|
1874
|
+
arch: Some("arm64".to_string()),
|
|
1875
|
+
force: false,
|
|
1876
|
+
dry_run: true,
|
|
1877
|
+
json: true,
|
|
1878
|
+
};
|
|
1879
|
+
let snapshot = crate::project::inspect(&root).expect("project should inspect");
|
|
1880
|
+
let report = build_report(snapshot, &args).expect("report should build");
|
|
1881
|
+
|
|
1882
|
+
assert!(report.signing.macos.sign.configured);
|
|
1883
|
+
assert!(!report.signing.macos.sign.enabled);
|
|
1884
|
+
assert_eq!(
|
|
1885
|
+
report.signing.macos.notarize.auth_method.as_deref(),
|
|
1886
|
+
Some("keychain-profile")
|
|
1887
|
+
);
|
|
1888
|
+
assert!(report.warnings.iter().any(|warning| {
|
|
1889
|
+
warning.contains("macOS notarization requires packagerConfig.osxSign")
|
|
1890
|
+
}));
|
|
1891
|
+
|
|
1892
|
+
let _ = fs::remove_dir_all(root);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1426
1895
|
#[test]
|
|
1427
1896
|
fn packages_macos_info_plist_metadata() {
|
|
1428
1897
|
if current_platform() != "darwin" {
|