electron-cli 0.3.0-alpha.14 → 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 +18 -1
- package/Cargo.toml +2 -1
- package/README.md +18 -6
- package/package.json +1 -1
- package/src/commands/make.rs +44 -15
- package/src/commands/package.rs +533 -21
- package/src/commands/publish.rs +45 -14
- package/src/forge_config.rs +547 -0
- package/src/main.rs +1 -0
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 {
|
|
@@ -67,6 +99,7 @@ struct PackageJsonConfig {
|
|
|
67
99
|
product_name: Option<String>,
|
|
68
100
|
app_version: Option<String>,
|
|
69
101
|
packager: PackagerConfig,
|
|
102
|
+
warnings: Vec<String>,
|
|
70
103
|
}
|
|
71
104
|
|
|
72
105
|
#[derive(Debug, Default)]
|
|
@@ -81,6 +114,35 @@ struct PackagerConfig {
|
|
|
81
114
|
icon: Vec<String>,
|
|
82
115
|
extra_resource: Vec<String>,
|
|
83
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>,
|
|
84
146
|
}
|
|
85
147
|
|
|
86
148
|
pub fn run(args: PackageArgs) -> Result<()> {
|
|
@@ -132,8 +194,9 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
132
194
|
&app_resources_dir,
|
|
133
195
|
&platform,
|
|
134
196
|
)?;
|
|
197
|
+
let (signing, signing_warnings) = package_signing(root, &package_config, &platform)?;
|
|
135
198
|
|
|
136
|
-
let mut warnings =
|
|
199
|
+
let mut warnings = package_config.warnings.clone();
|
|
137
200
|
if snapshot.package_json.is_none() {
|
|
138
201
|
warnings.push("No package.json found.".to_string());
|
|
139
202
|
}
|
|
@@ -169,6 +232,7 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
169
232
|
|
|
170
233
|
warnings.extend(runtime_dependency_warnings(root, &snapshot));
|
|
171
234
|
warnings.extend(metadata_warnings);
|
|
235
|
+
warnings.extend(signing_warnings);
|
|
172
236
|
|
|
173
237
|
let create_dirs = vec![package_root.clone(), app_resources_dir.clone()];
|
|
174
238
|
let mut copy_steps = vec![
|
|
@@ -199,6 +263,7 @@ pub(crate) fn build_report(snapshot: ProjectSnapshot, args: &PackageArgs) -> Res
|
|
|
199
263
|
app_name,
|
|
200
264
|
executable_name,
|
|
201
265
|
metadata,
|
|
266
|
+
signing,
|
|
202
267
|
platform,
|
|
203
268
|
arch,
|
|
204
269
|
electron_dist: utf8_path(electron_dist)?,
|
|
@@ -324,6 +389,33 @@ fn print_report(report: &PackageReport, json: bool) -> Result<()> {
|
|
|
324
389
|
println!(" target: {} {}", report.platform, report.arch);
|
|
325
390
|
println!(" status: {}", report.status.as_str());
|
|
326
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
|
+
|
|
327
419
|
println!();
|
|
328
420
|
println!("Output");
|
|
329
421
|
println!(" {}", report.bundle_dir);
|
|
@@ -346,44 +438,40 @@ fn print_report(report: &PackageReport, json: bool) -> Result<()> {
|
|
|
346
438
|
}
|
|
347
439
|
|
|
348
440
|
fn read_package_json_config(snapshot: &ProjectSnapshot) -> Result<PackageJsonConfig> {
|
|
349
|
-
let
|
|
350
|
-
return Ok(PackageJsonConfig::default());
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
let package_json_path = Path::new(package_json_path.as_str());
|
|
354
|
-
let raw = fs::read_to_string(package_json_path)
|
|
355
|
-
.with_context(|| format!("Could not read {}", package_json_path.display()))?;
|
|
356
|
-
let package = serde_json::from_str::<JsonValue>(&raw)
|
|
357
|
-
.with_context(|| format!("Could not parse {}", package_json_path.display()))?;
|
|
441
|
+
let project_config = crate::forge_config::read(snapshot)?;
|
|
358
442
|
|
|
359
443
|
let mut packager = PackagerConfig::default();
|
|
360
|
-
if let Some(config) =
|
|
361
|
-
.
|
|
362
|
-
.and_then(|config| config.get("forge"))
|
|
444
|
+
if let Some(config) = project_config
|
|
445
|
+
.forge()
|
|
363
446
|
.and_then(|forge| forge.get("packagerConfig"))
|
|
364
447
|
{
|
|
365
448
|
packager.merge(parse_packager_config(config));
|
|
366
449
|
}
|
|
367
|
-
if let Some(config) =
|
|
450
|
+
if let Some(config) = project_config
|
|
451
|
+
.package()
|
|
452
|
+
.and_then(|package| package.get("electronPackagerConfig"))
|
|
453
|
+
{
|
|
368
454
|
packager.merge(parse_packager_config(config));
|
|
369
455
|
}
|
|
370
|
-
if let Some(config) =
|
|
371
|
-
.
|
|
372
|
-
.or_else(|| package.get("electron-cli"))
|
|
456
|
+
if let Some(config) = project_config
|
|
457
|
+
.electron_cli()
|
|
373
458
|
.and_then(|config| config.get("packagerConfig"))
|
|
374
459
|
{
|
|
375
460
|
packager.merge(parse_packager_config(config));
|
|
376
461
|
}
|
|
377
462
|
|
|
378
463
|
Ok(PackageJsonConfig {
|
|
379
|
-
product_name:
|
|
380
|
-
.
|
|
464
|
+
product_name: project_config
|
|
465
|
+
.package()
|
|
466
|
+
.and_then(|package| package.get("productName"))
|
|
381
467
|
.and_then(JsonValue::as_str)
|
|
382
468
|
.map(ToOwned::to_owned),
|
|
383
|
-
app_version:
|
|
384
|
-
.
|
|
469
|
+
app_version: project_config
|
|
470
|
+
.package()
|
|
471
|
+
.and_then(|package| package.get("version"))
|
|
385
472
|
.and_then(JsonValue::as_str)
|
|
386
473
|
.map(ToOwned::to_owned),
|
|
474
|
+
warnings: project_config.warnings().to_vec(),
|
|
387
475
|
packager,
|
|
388
476
|
})
|
|
389
477
|
}
|
|
@@ -403,6 +491,114 @@ fn parse_packager_config(value: &JsonValue) -> PackagerConfig {
|
|
|
403
491
|
.get("darwinDarkModeSupport")
|
|
404
492
|
.and_then(JsonValue::as_bool)
|
|
405
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
|
+
},
|
|
406
602
|
}
|
|
407
603
|
}
|
|
408
604
|
|
|
@@ -476,6 +672,146 @@ fn package_metadata(
|
|
|
476
672
|
))
|
|
477
673
|
}
|
|
478
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
|
+
|
|
479
815
|
fn resolve_icon_resource(
|
|
480
816
|
root: &Path,
|
|
481
817
|
configured_icons: &[String],
|
|
@@ -1161,6 +1497,12 @@ impl PackagerConfig {
|
|
|
1161
1497
|
}
|
|
1162
1498
|
self.darwin_dark_mode_support =
|
|
1163
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
|
+
}
|
|
1164
1506
|
}
|
|
1165
1507
|
}
|
|
1166
1508
|
|
|
@@ -1380,6 +1722,176 @@ mod tests {
|
|
|
1380
1722
|
let _ = fs::remove_dir_all(root);
|
|
1381
1723
|
}
|
|
1382
1724
|
|
|
1725
|
+
#[test]
|
|
1726
|
+
fn plans_packager_metadata_from_forge_config_js() {
|
|
1727
|
+
let root = unique_temp_dir("forge-config-metadata");
|
|
1728
|
+
write_package_json(&root);
|
|
1729
|
+
fs::write(
|
|
1730
|
+
root.join("forge.config.js"),
|
|
1731
|
+
r#"
|
|
1732
|
+
module.exports = {
|
|
1733
|
+
packagerConfig: {
|
|
1734
|
+
name: 'Forge Config App',
|
|
1735
|
+
executableName: 'ForgeExec',
|
|
1736
|
+
appBundleId: 'com.example.forge-config',
|
|
1737
|
+
},
|
|
1738
|
+
};
|
|
1739
|
+
"#,
|
|
1740
|
+
)
|
|
1741
|
+
.expect("forge config should be written");
|
|
1742
|
+
write_app_file(&root);
|
|
1743
|
+
write_fake_electron_dist(&root);
|
|
1744
|
+
|
|
1745
|
+
let args = PackageArgs {
|
|
1746
|
+
cwd: root.clone(),
|
|
1747
|
+
out_dir: PathBuf::from("out"),
|
|
1748
|
+
name: None,
|
|
1749
|
+
platform: None,
|
|
1750
|
+
arch: None,
|
|
1751
|
+
force: false,
|
|
1752
|
+
dry_run: true,
|
|
1753
|
+
json: true,
|
|
1754
|
+
};
|
|
1755
|
+
let snapshot = crate::project::inspect(&root).expect("project should inspect");
|
|
1756
|
+
let report = build_report(snapshot, &args).expect("report should build");
|
|
1757
|
+
|
|
1758
|
+
assert_eq!(report.app_name, "Forge Config App");
|
|
1759
|
+
assert_eq!(
|
|
1760
|
+
report.executable_name,
|
|
1761
|
+
executable_name("ForgeExec", &report.platform)
|
|
1762
|
+
);
|
|
1763
|
+
assert_eq!(
|
|
1764
|
+
report.metadata.bundle_identifier,
|
|
1765
|
+
"com.example.forge-config"
|
|
1766
|
+
);
|
|
1767
|
+
|
|
1768
|
+
let _ = fs::remove_dir_all(root);
|
|
1769
|
+
}
|
|
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
|
+
|
|
1383
1895
|
#[test]
|
|
1384
1896
|
fn packages_macos_info_plist_metadata() {
|
|
1385
1897
|
if current_platform() != "darwin" {
|