electron-cli 0.3.0-alpha.12 → 0.3.0-alpha.14
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 +33 -5
- package/package.json +1 -1
- package/src/cli.rs +16 -16
- package/src/commands/make.rs +473 -33
- package/src/commands/package.rs +5 -5
- package/src/commands/publish.rs +758 -83
- package/src/project.rs +1 -1
package/src/commands/publish.rs
CHANGED
|
@@ -8,20 +8,28 @@ use std::{
|
|
|
8
8
|
use anyhow::{bail, Context, Result};
|
|
9
9
|
use camino::Utf8PathBuf;
|
|
10
10
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
|
11
|
+
use serde_json::{Map as JsonMap, Value as JsonValue};
|
|
11
12
|
|
|
12
13
|
use crate::{
|
|
13
14
|
cli::{MakeArgs, PublishArgs, PublishTarget},
|
|
14
15
|
commands::make::{self, MakeReport},
|
|
15
16
|
output,
|
|
17
|
+
project::ProjectSnapshot,
|
|
16
18
|
};
|
|
17
19
|
|
|
18
20
|
#[derive(Debug, Serialize)]
|
|
19
21
|
struct PublishReport {
|
|
20
22
|
make: MakeReport,
|
|
21
23
|
publisher: String,
|
|
24
|
+
#[serde(skip)]
|
|
25
|
+
publisher_kind: PublishTarget,
|
|
22
26
|
channel: String,
|
|
23
27
|
local: Option<LocalPublishPlan>,
|
|
24
28
|
github: Option<GithubPublishPlan>,
|
|
29
|
+
#[serde(skip)]
|
|
30
|
+
force_publish: bool,
|
|
31
|
+
#[serde(skip)]
|
|
32
|
+
github_auth_token: Option<String>,
|
|
25
33
|
skip_make: bool,
|
|
26
34
|
dry_run: bool,
|
|
27
35
|
status: PublishStatus,
|
|
@@ -49,13 +57,21 @@ struct GithubPublishPlan {
|
|
|
49
57
|
asset_url: Option<String>,
|
|
50
58
|
}
|
|
51
59
|
|
|
52
|
-
#[derive(Debug, Serialize)]
|
|
60
|
+
#[derive(Clone, Copy, Debug, Serialize)]
|
|
53
61
|
#[serde(rename_all = "kebab-case")]
|
|
54
62
|
enum PublishStatus {
|
|
55
63
|
Planned,
|
|
56
64
|
Published,
|
|
57
65
|
}
|
|
58
66
|
|
|
67
|
+
#[derive(Debug, Serialize)]
|
|
68
|
+
struct PublishRunReport<'a> {
|
|
69
|
+
publishes: &'a [PublishReport],
|
|
70
|
+
dry_run: bool,
|
|
71
|
+
status: PublishStatus,
|
|
72
|
+
warnings: Vec<String>,
|
|
73
|
+
}
|
|
74
|
+
|
|
59
75
|
#[derive(Debug, Serialize)]
|
|
60
76
|
struct PublishManifest {
|
|
61
77
|
schema_version: u8,
|
|
@@ -78,33 +94,112 @@ struct PublishedArtifact {
|
|
|
78
94
|
size: u64,
|
|
79
95
|
}
|
|
80
96
|
|
|
97
|
+
#[derive(Debug)]
|
|
98
|
+
struct ResolvedPublishers {
|
|
99
|
+
publishers: Vec<ResolvedPublisher>,
|
|
100
|
+
warnings: Vec<String>,
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
#[derive(Clone, Debug)]
|
|
104
|
+
struct ResolvedPublisher {
|
|
105
|
+
target: PublishTarget,
|
|
106
|
+
to: PathBuf,
|
|
107
|
+
channel: String,
|
|
108
|
+
github_repo: Option<String>,
|
|
109
|
+
github_tag: Option<String>,
|
|
110
|
+
github_tag_prefix: Option<String>,
|
|
111
|
+
github_release_name: Option<String>,
|
|
112
|
+
github_draft: bool,
|
|
113
|
+
github_prerelease: bool,
|
|
114
|
+
github_api_url: String,
|
|
115
|
+
github_auth_token: Option<String>,
|
|
116
|
+
force_publish: bool,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
#[derive(Debug)]
|
|
120
|
+
struct ConfiguredPublisher {
|
|
121
|
+
label: String,
|
|
122
|
+
target: Option<PublishTarget>,
|
|
123
|
+
platforms: Vec<String>,
|
|
124
|
+
to: Option<PathBuf>,
|
|
125
|
+
channel: Option<String>,
|
|
126
|
+
github_repo: Option<String>,
|
|
127
|
+
github_tag: Option<String>,
|
|
128
|
+
github_tag_prefix: Option<String>,
|
|
129
|
+
github_release_name: Option<String>,
|
|
130
|
+
github_draft: Option<bool>,
|
|
131
|
+
github_prerelease: Option<bool>,
|
|
132
|
+
github_api_url: Option<String>,
|
|
133
|
+
github_auth_token: Option<String>,
|
|
134
|
+
force_publish: Option<bool>,
|
|
135
|
+
}
|
|
136
|
+
|
|
81
137
|
pub fn run(args: PublishArgs) -> Result<()> {
|
|
82
|
-
let mut
|
|
138
|
+
let mut make_reports = make::build_reports(&make_args(&args))?;
|
|
139
|
+
let mut reports = build_reports_from_make_reports(&args, &make_reports)?;
|
|
83
140
|
|
|
84
141
|
if args.dry_run {
|
|
85
|
-
return
|
|
142
|
+
return print_reports(&reports, args.json, PublishStatus::Planned);
|
|
86
143
|
}
|
|
87
144
|
|
|
88
|
-
|
|
89
|
-
report.status = PublishStatus::Published;
|
|
145
|
+
execute_publish_reports(&mut reports, &mut make_reports, &args)?;
|
|
90
146
|
|
|
91
|
-
|
|
147
|
+
print_reports(&reports, args.json, PublishStatus::Published)
|
|
92
148
|
}
|
|
93
149
|
|
|
150
|
+
#[cfg(test)]
|
|
94
151
|
fn build_report(args: &PublishArgs) -> Result<PublishReport> {
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
152
|
+
let reports = build_reports(args)?;
|
|
153
|
+
if reports.len() != 1 {
|
|
154
|
+
bail!(
|
|
155
|
+
"Expected one publish target, but resolved {}. Pass --target and --publisher to select one target.",
|
|
156
|
+
reports.len()
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
Ok(reports
|
|
160
|
+
.into_iter()
|
|
161
|
+
.next()
|
|
162
|
+
.expect("length was checked above"))
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
#[cfg(test)]
|
|
166
|
+
fn build_reports(args: &PublishArgs) -> Result<Vec<PublishReport>> {
|
|
167
|
+
let make_reports = make::build_reports(&make_args(args))?;
|
|
168
|
+
build_reports_from_make_reports(args, &make_reports)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
fn build_reports_from_make_reports(
|
|
172
|
+
args: &PublishArgs,
|
|
173
|
+
make_reports: &[MakeReport],
|
|
174
|
+
) -> Result<Vec<PublishReport>> {
|
|
175
|
+
let first_make = make_reports
|
|
176
|
+
.first()
|
|
177
|
+
.context("No make targets were resolved for publish.")?;
|
|
178
|
+
let project = first_make.package().project();
|
|
179
|
+
let platform = first_make.package().platform();
|
|
180
|
+
let resolved = resolve_publishers(project, args, platform)?;
|
|
181
|
+
let mut reports = Vec::new();
|
|
182
|
+
|
|
183
|
+
for publisher in &resolved.publishers {
|
|
184
|
+
for make in make_reports {
|
|
185
|
+
reports.push(build_report_for_publisher(
|
|
186
|
+
args,
|
|
187
|
+
make.clone(),
|
|
188
|
+
publisher,
|
|
189
|
+
&resolved.warnings,
|
|
190
|
+
)?);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
Ok(reports)
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn build_report_for_publisher(
|
|
198
|
+
args: &PublishArgs,
|
|
199
|
+
make: MakeReport,
|
|
200
|
+
publisher: &ResolvedPublisher,
|
|
201
|
+
config_warnings: &[String],
|
|
202
|
+
) -> Result<PublishReport> {
|
|
108
203
|
let root = Path::new(make.package().project().root.as_str());
|
|
109
204
|
let artifact_name = make
|
|
110
205
|
.artifact()
|
|
@@ -113,22 +208,23 @@ fn build_report(args: &PublishArgs) -> Result<PublishReport> {
|
|
|
113
208
|
.to_string();
|
|
114
209
|
|
|
115
210
|
let mut warnings = make.warnings().to_vec();
|
|
211
|
+
warnings.extend(config_warnings.iter().cloned());
|
|
116
212
|
if args.skip_make && !Path::new(make.artifact().as_str()).exists() {
|
|
117
213
|
warnings.push(format!(
|
|
118
214
|
"Make artifact does not exist: {}.",
|
|
119
215
|
make.artifact()
|
|
120
216
|
));
|
|
121
217
|
}
|
|
122
|
-
let (local, github) = match
|
|
218
|
+
let (local, github) = match publisher.target {
|
|
123
219
|
PublishTarget::Local => {
|
|
124
|
-
let local = build_local_plan(root,
|
|
125
|
-
if Path::new(local.destination_artifact.as_str()).exists() && !
|
|
220
|
+
let local = build_local_plan(root, publisher, &make, &artifact_name)?;
|
|
221
|
+
if Path::new(local.destination_artifact.as_str()).exists() && !publisher.force_publish {
|
|
126
222
|
warnings.push(format!(
|
|
127
223
|
"Publish artifact already exists: {}. Use --force to overwrite it.",
|
|
128
224
|
local.destination_artifact
|
|
129
225
|
));
|
|
130
226
|
}
|
|
131
|
-
if Path::new(local.manifest.as_str()).exists() && !
|
|
227
|
+
if Path::new(local.manifest.as_str()).exists() && !publisher.force_publish {
|
|
132
228
|
warnings.push(format!(
|
|
133
229
|
"Publish manifest already exists: {}. Use --force to overwrite it.",
|
|
134
230
|
local.manifest
|
|
@@ -137,17 +233,20 @@ fn build_report(args: &PublishArgs) -> Result<PublishReport> {
|
|
|
137
233
|
(Some(local), None)
|
|
138
234
|
}
|
|
139
235
|
PublishTarget::Github => {
|
|
140
|
-
let github = build_github_plan(
|
|
236
|
+
let github = build_github_plan(publisher, &make, &artifact_name, &mut warnings)?;
|
|
141
237
|
(None, Some(github))
|
|
142
238
|
}
|
|
143
239
|
};
|
|
144
240
|
|
|
145
241
|
Ok(PublishReport {
|
|
146
242
|
make,
|
|
147
|
-
publisher:
|
|
148
|
-
|
|
243
|
+
publisher: publisher.target.as_str().to_string(),
|
|
244
|
+
publisher_kind: publisher.target,
|
|
245
|
+
channel: publisher.channel.clone(),
|
|
149
246
|
local,
|
|
150
247
|
github,
|
|
248
|
+
force_publish: publisher.force_publish,
|
|
249
|
+
github_auth_token: publisher.github_auth_token.clone(),
|
|
151
250
|
skip_make: args.skip_make,
|
|
152
251
|
dry_run: args.dry_run,
|
|
153
252
|
status: PublishStatus::Planned,
|
|
@@ -158,13 +257,13 @@ fn build_report(args: &PublishArgs) -> Result<PublishReport> {
|
|
|
158
257
|
|
|
159
258
|
fn build_local_plan(
|
|
160
259
|
root: &Path,
|
|
161
|
-
|
|
260
|
+
publisher: &ResolvedPublisher,
|
|
162
261
|
make: &MakeReport,
|
|
163
262
|
artifact_name: &str,
|
|
164
263
|
) -> Result<LocalPublishPlan> {
|
|
165
|
-
let publish_root = resolve_destination(root, &
|
|
264
|
+
let publish_root = resolve_destination(root, &publisher.to);
|
|
166
265
|
let destination_dir = publish_root
|
|
167
|
-
.join(&
|
|
266
|
+
.join(&publisher.channel)
|
|
168
267
|
.join(make.package().platform())
|
|
169
268
|
.join(make.package().arch());
|
|
170
269
|
let destination_artifact = destination_dir.join(artifact_name);
|
|
@@ -178,12 +277,12 @@ fn build_local_plan(
|
|
|
178
277
|
}
|
|
179
278
|
|
|
180
279
|
fn build_github_plan(
|
|
181
|
-
|
|
280
|
+
publisher: &ResolvedPublisher,
|
|
182
281
|
make: &MakeReport,
|
|
183
282
|
artifact_name: &str,
|
|
184
283
|
warnings: &mut Vec<String>,
|
|
185
284
|
) -> Result<GithubPublishPlan> {
|
|
186
|
-
let repo =
|
|
285
|
+
let repo = publisher
|
|
187
286
|
.github_repo
|
|
188
287
|
.clone()
|
|
189
288
|
.or_else(|| {
|
|
@@ -204,44 +303,359 @@ fn build_github_plan(
|
|
|
204
303
|
));
|
|
205
304
|
}
|
|
206
305
|
|
|
207
|
-
let tag =
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
306
|
+
let tag = publisher.github_tag.clone().unwrap_or_else(|| {
|
|
307
|
+
default_github_tag(
|
|
308
|
+
make,
|
|
309
|
+
&publisher.channel,
|
|
310
|
+
publisher.github_tag_prefix.as_deref(),
|
|
311
|
+
)
|
|
312
|
+
});
|
|
313
|
+
let release_name = publisher
|
|
212
314
|
.github_release_name
|
|
213
315
|
.clone()
|
|
214
316
|
.unwrap_or_else(|| tag.clone());
|
|
215
|
-
let prerelease =
|
|
317
|
+
let prerelease = publisher.github_prerelease || tag.contains('-');
|
|
216
318
|
|
|
217
319
|
Ok(GithubPublishPlan {
|
|
218
320
|
repo,
|
|
219
321
|
tag,
|
|
220
322
|
release_name,
|
|
221
|
-
draft:
|
|
323
|
+
draft: publisher.github_draft,
|
|
222
324
|
prerelease,
|
|
223
|
-
api_url:
|
|
325
|
+
api_url: publisher.github_api_url.trim_end_matches('/').to_string(),
|
|
224
326
|
artifact_name: artifact_name.to_string(),
|
|
225
327
|
release_url: None,
|
|
226
328
|
asset_url: None,
|
|
227
329
|
})
|
|
228
330
|
}
|
|
229
331
|
|
|
332
|
+
fn make_args(args: &PublishArgs) -> MakeArgs {
|
|
333
|
+
MakeArgs {
|
|
334
|
+
cwd: args.cwd.clone(),
|
|
335
|
+
out_dir: args.out_dir.clone(),
|
|
336
|
+
name: args.name.clone(),
|
|
337
|
+
platform: args.platform.clone(),
|
|
338
|
+
arch: args.arch.clone(),
|
|
339
|
+
target: args.target,
|
|
340
|
+
skip_package: false,
|
|
341
|
+
force: args.force,
|
|
342
|
+
dry_run: false,
|
|
343
|
+
json: false,
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
fn resolve_publishers(
|
|
348
|
+
project: &ProjectSnapshot,
|
|
349
|
+
args: &PublishArgs,
|
|
350
|
+
platform: &str,
|
|
351
|
+
) -> Result<ResolvedPublishers> {
|
|
352
|
+
if let Some(target) = args.publisher {
|
|
353
|
+
return Ok(ResolvedPublishers {
|
|
354
|
+
publishers: vec![resolved_publisher_from_args(args, target, None)],
|
|
355
|
+
warnings: Vec::new(),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
let configured = configured_publishers(project)?;
|
|
360
|
+
let mut warnings = Vec::new();
|
|
361
|
+
let mut publishers = Vec::new();
|
|
362
|
+
|
|
363
|
+
for publisher in &configured {
|
|
364
|
+
let Some(target) = publisher.target else {
|
|
365
|
+
warnings.push(format!(
|
|
366
|
+
"Configured publisher is not implemented yet and will be skipped: {}.",
|
|
367
|
+
publisher.label
|
|
368
|
+
));
|
|
369
|
+
continue;
|
|
370
|
+
};
|
|
371
|
+
if !publisher_applies_to_platform(publisher, platform) {
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
374
|
+
publishers.push(resolved_publisher_from_args(args, target, Some(publisher)));
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if publishers.is_empty() {
|
|
378
|
+
if !configured.is_empty() {
|
|
379
|
+
warnings.push(format!(
|
|
380
|
+
"No supported configured publishers apply to {platform}; defaulting to local. Pass --publisher to override."
|
|
381
|
+
));
|
|
382
|
+
}
|
|
383
|
+
publishers.push(resolved_publisher_from_args(
|
|
384
|
+
args,
|
|
385
|
+
PublishTarget::Local,
|
|
386
|
+
None,
|
|
387
|
+
));
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
Ok(ResolvedPublishers {
|
|
391
|
+
publishers,
|
|
392
|
+
warnings,
|
|
393
|
+
})
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
fn resolved_publisher_from_args(
|
|
397
|
+
args: &PublishArgs,
|
|
398
|
+
target: PublishTarget,
|
|
399
|
+
configured: Option<&ConfiguredPublisher>,
|
|
400
|
+
) -> ResolvedPublisher {
|
|
401
|
+
let default_to = PathBuf::from("out/publish/local");
|
|
402
|
+
let default_channel = "default".to_string();
|
|
403
|
+
let default_github_api_url = "https://api.github.com".to_string();
|
|
404
|
+
|
|
405
|
+
ResolvedPublisher {
|
|
406
|
+
target,
|
|
407
|
+
to: args
|
|
408
|
+
.to
|
|
409
|
+
.clone()
|
|
410
|
+
.or_else(|| configured.and_then(|publisher| publisher.to.clone()))
|
|
411
|
+
.unwrap_or(default_to),
|
|
412
|
+
channel: args
|
|
413
|
+
.channel
|
|
414
|
+
.clone()
|
|
415
|
+
.or_else(|| configured.and_then(|publisher| publisher.channel.clone()))
|
|
416
|
+
.unwrap_or(default_channel),
|
|
417
|
+
github_repo: args
|
|
418
|
+
.github_repo
|
|
419
|
+
.clone()
|
|
420
|
+
.or_else(|| configured.and_then(|publisher| publisher.github_repo.clone())),
|
|
421
|
+
github_tag: args
|
|
422
|
+
.github_tag
|
|
423
|
+
.clone()
|
|
424
|
+
.or_else(|| configured.and_then(|publisher| publisher.github_tag.clone())),
|
|
425
|
+
github_tag_prefix: configured.and_then(|publisher| publisher.github_tag_prefix.clone()),
|
|
426
|
+
github_release_name: args
|
|
427
|
+
.github_release_name
|
|
428
|
+
.clone()
|
|
429
|
+
.or_else(|| configured.and_then(|publisher| publisher.github_release_name.clone())),
|
|
430
|
+
github_draft: args.github_draft
|
|
431
|
+
|| configured
|
|
432
|
+
.and_then(|publisher| publisher.github_draft)
|
|
433
|
+
.unwrap_or(false),
|
|
434
|
+
github_prerelease: args.github_prerelease
|
|
435
|
+
|| configured
|
|
436
|
+
.and_then(|publisher| publisher.github_prerelease)
|
|
437
|
+
.unwrap_or(false),
|
|
438
|
+
github_api_url: args
|
|
439
|
+
.github_api_url
|
|
440
|
+
.clone()
|
|
441
|
+
.or_else(|| configured.and_then(|publisher| publisher.github_api_url.clone()))
|
|
442
|
+
.unwrap_or(default_github_api_url),
|
|
443
|
+
github_auth_token: configured.and_then(|publisher| publisher.github_auth_token.clone()),
|
|
444
|
+
force_publish: args.force
|
|
445
|
+
|| configured
|
|
446
|
+
.and_then(|publisher| publisher.force_publish)
|
|
447
|
+
.unwrap_or(false),
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
fn configured_publishers(project: &ProjectSnapshot) -> Result<Vec<ConfiguredPublisher>> {
|
|
452
|
+
let Some(package_json_path) = &project.package_json else {
|
|
453
|
+
return Ok(Vec::new());
|
|
454
|
+
};
|
|
455
|
+
let package_json_path = Path::new(package_json_path.as_str());
|
|
456
|
+
let raw = fs::read_to_string(package_json_path)
|
|
457
|
+
.with_context(|| format!("Could not read {}", package_json_path.display()))?;
|
|
458
|
+
let package = serde_json::from_str::<JsonValue>(&raw)
|
|
459
|
+
.with_context(|| format!("Could not parse {}", package_json_path.display()))?;
|
|
460
|
+
|
|
461
|
+
let mut publishers = Vec::new();
|
|
462
|
+
for value in [
|
|
463
|
+
package
|
|
464
|
+
.get("config")
|
|
465
|
+
.and_then(|config| config.get("forge"))
|
|
466
|
+
.and_then(|forge| forge.get("publishers")),
|
|
467
|
+
package
|
|
468
|
+
.get("electronCli")
|
|
469
|
+
.or_else(|| package.get("electron-cli"))
|
|
470
|
+
.and_then(|config| config.get("publishers")),
|
|
471
|
+
]
|
|
472
|
+
.into_iter()
|
|
473
|
+
.flatten()
|
|
474
|
+
{
|
|
475
|
+
publishers.extend(parse_publisher_list(value));
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
Ok(publishers)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
fn parse_publisher_list(value: &JsonValue) -> Vec<ConfiguredPublisher> {
|
|
482
|
+
match value {
|
|
483
|
+
JsonValue::Array(values) => values.iter().filter_map(parse_publisher).collect(),
|
|
484
|
+
_ => Vec::new(),
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
fn parse_publisher(value: &JsonValue) -> Option<ConfiguredPublisher> {
|
|
489
|
+
match value {
|
|
490
|
+
JsonValue::String(label) => Some(ConfiguredPublisher {
|
|
491
|
+
label: label.clone(),
|
|
492
|
+
target: publisher_target(label),
|
|
493
|
+
platforms: Vec::new(),
|
|
494
|
+
to: None,
|
|
495
|
+
channel: None,
|
|
496
|
+
github_repo: None,
|
|
497
|
+
github_tag: None,
|
|
498
|
+
github_tag_prefix: None,
|
|
499
|
+
github_release_name: None,
|
|
500
|
+
github_draft: None,
|
|
501
|
+
github_prerelease: None,
|
|
502
|
+
github_api_url: None,
|
|
503
|
+
github_auth_token: None,
|
|
504
|
+
force_publish: None,
|
|
505
|
+
}),
|
|
506
|
+
JsonValue::Object(object) => {
|
|
507
|
+
let label = object
|
|
508
|
+
.get("name")
|
|
509
|
+
.or_else(|| object.get("publisher"))
|
|
510
|
+
.or_else(|| object.get("target"))
|
|
511
|
+
.and_then(JsonValue::as_str)?
|
|
512
|
+
.to_string();
|
|
513
|
+
Some(ConfiguredPublisher {
|
|
514
|
+
target: publisher_target(&label),
|
|
515
|
+
platforms: string_values(object.get("platforms")),
|
|
516
|
+
to: publisher_config_string(object, &["to", "path", "dir", "directory"])
|
|
517
|
+
.map(PathBuf::from),
|
|
518
|
+
channel: publisher_config_string(object, &["channel"]),
|
|
519
|
+
github_repo: publisher_config_github_repo(object),
|
|
520
|
+
github_tag: publisher_config_string(object, &["tag", "tagName", "tag_name"]),
|
|
521
|
+
github_tag_prefix: publisher_config_string(object, &["tagPrefix", "tag_prefix"]),
|
|
522
|
+
github_release_name: publisher_config_string(
|
|
523
|
+
object,
|
|
524
|
+
&["releaseName", "release_name"],
|
|
525
|
+
),
|
|
526
|
+
github_draft: publisher_config_bool(object, &["draft"]),
|
|
527
|
+
github_prerelease: publisher_config_bool(object, &["prerelease", "preRelease"]),
|
|
528
|
+
github_api_url: publisher_config_api_url(object),
|
|
529
|
+
github_auth_token: publisher_config_string(object, &["authToken", "auth_token"]),
|
|
530
|
+
force_publish: publisher_config_bool(object, &["force"]),
|
|
531
|
+
label,
|
|
532
|
+
})
|
|
533
|
+
}
|
|
534
|
+
_ => None,
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
fn publisher_target(label: &str) -> Option<PublishTarget> {
|
|
539
|
+
let label = label.trim().to_ascii_lowercase();
|
|
540
|
+
let compact = label
|
|
541
|
+
.trim_start_matches("@electron-forge/")
|
|
542
|
+
.trim_start_matches("electron-forge-")
|
|
543
|
+
.trim_start_matches("publisher-");
|
|
544
|
+
|
|
545
|
+
if compact == "github"
|
|
546
|
+
|| label.ends_with("/publisher-github")
|
|
547
|
+
|| label.ends_with("publisher-github")
|
|
548
|
+
{
|
|
549
|
+
Some(PublishTarget::Github)
|
|
550
|
+
} else if compact == "local"
|
|
551
|
+
|| label.ends_with("/publisher-local")
|
|
552
|
+
|| label.ends_with("publisher-local")
|
|
553
|
+
{
|
|
554
|
+
Some(PublishTarget::Local)
|
|
555
|
+
} else {
|
|
556
|
+
None
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
fn publisher_applies_to_platform(publisher: &ConfiguredPublisher, platform: &str) -> bool {
|
|
561
|
+
publisher.platforms.is_empty()
|
|
562
|
+
|| publisher
|
|
563
|
+
.platforms
|
|
564
|
+
.iter()
|
|
565
|
+
.any(|configured| configured == platform || configured == "*")
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
fn string_values(value: Option<&JsonValue>) -> Vec<String> {
|
|
569
|
+
match value {
|
|
570
|
+
Some(JsonValue::String(value)) => vec![value.clone()],
|
|
571
|
+
Some(JsonValue::Array(values)) => values
|
|
572
|
+
.iter()
|
|
573
|
+
.filter_map(JsonValue::as_str)
|
|
574
|
+
.map(ToOwned::to_owned)
|
|
575
|
+
.collect(),
|
|
576
|
+
_ => Vec::new(),
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
fn publisher_config_string(object: &JsonMap<String, JsonValue>, keys: &[&str]) -> Option<String> {
|
|
581
|
+
keys.iter().find_map(|key| {
|
|
582
|
+
publisher_config_value(object, key)
|
|
583
|
+
.and_then(JsonValue::as_str)
|
|
584
|
+
.map(str::trim)
|
|
585
|
+
.filter(|value| !value.is_empty())
|
|
586
|
+
.map(ToOwned::to_owned)
|
|
587
|
+
})
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
fn publisher_config_bool(object: &JsonMap<String, JsonValue>, keys: &[&str]) -> Option<bool> {
|
|
591
|
+
keys.iter()
|
|
592
|
+
.find_map(|key| publisher_config_value(object, key).and_then(JsonValue::as_bool))
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
fn publisher_config_value<'a>(
|
|
596
|
+
object: &'a JsonMap<String, JsonValue>,
|
|
597
|
+
key: &str,
|
|
598
|
+
) -> Option<&'a JsonValue> {
|
|
599
|
+
object
|
|
600
|
+
.get("config")
|
|
601
|
+
.and_then(JsonValue::as_object)
|
|
602
|
+
.and_then(|config| config.get(key))
|
|
603
|
+
.or_else(|| object.get(key))
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
fn publisher_config_github_repo(object: &JsonMap<String, JsonValue>) -> Option<String> {
|
|
607
|
+
["repository", "repo", "githubRepo", "github_repo"]
|
|
608
|
+
.iter()
|
|
609
|
+
.find_map(|key| publisher_config_value(object, key).and_then(github_repo_from_config_value))
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
fn github_repo_from_config_value(value: &JsonValue) -> Option<String> {
|
|
613
|
+
match value {
|
|
614
|
+
JsonValue::String(value) => {
|
|
615
|
+
let value = value.trim();
|
|
616
|
+
(!value.is_empty()).then(|| value.to_string())
|
|
617
|
+
}
|
|
618
|
+
JsonValue::Object(object) => {
|
|
619
|
+
let owner = object
|
|
620
|
+
.get("owner")
|
|
621
|
+
.or_else(|| object.get("user"))
|
|
622
|
+
.and_then(JsonValue::as_str)?
|
|
623
|
+
.trim();
|
|
624
|
+
let name = object
|
|
625
|
+
.get("name")
|
|
626
|
+
.or_else(|| object.get("repo"))
|
|
627
|
+
.and_then(JsonValue::as_str)?
|
|
628
|
+
.trim();
|
|
629
|
+
if owner.is_empty() || name.is_empty() {
|
|
630
|
+
None
|
|
631
|
+
} else {
|
|
632
|
+
Some(format!("{owner}/{name}"))
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
_ => None,
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
fn publisher_config_api_url(object: &JsonMap<String, JsonValue>) -> Option<String> {
|
|
640
|
+
publisher_config_string(object, &["apiUrl", "api_url", "baseUrl", "base_url"]).or_else(|| {
|
|
641
|
+
publisher_config_value(object, "octokitOptions")
|
|
642
|
+
.and_then(JsonValue::as_object)
|
|
643
|
+
.and_then(|octokit| {
|
|
644
|
+
octokit
|
|
645
|
+
.get("baseUrl")
|
|
646
|
+
.or_else(|| octokit.get("base_url"))
|
|
647
|
+
.and_then(JsonValue::as_str)
|
|
648
|
+
})
|
|
649
|
+
.map(str::trim)
|
|
650
|
+
.filter(|value| !value.is_empty())
|
|
651
|
+
.map(ToOwned::to_owned)
|
|
652
|
+
})
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
#[cfg(test)]
|
|
230
656
|
fn execute_publish(report: &mut PublishReport, args: &PublishArgs) -> Result<()> {
|
|
231
657
|
if !args.skip_make {
|
|
232
|
-
|
|
233
|
-
cwd: args.cwd.clone(),
|
|
234
|
-
out_dir: args.out_dir.clone(),
|
|
235
|
-
name: args.name.clone(),
|
|
236
|
-
platform: args.platform.clone(),
|
|
237
|
-
arch: args.arch.clone(),
|
|
238
|
-
target: args.target,
|
|
239
|
-
skip_package: false,
|
|
240
|
-
force: args.force,
|
|
241
|
-
dry_run: false,
|
|
242
|
-
json: false,
|
|
243
|
-
};
|
|
244
|
-
make::execute_make(&mut report.make, &make_args)?;
|
|
658
|
+
make::execute_make(&mut report.make, &make_args(args))?;
|
|
245
659
|
report.make.mark_made()?;
|
|
246
660
|
} else if !Path::new(report.make.artifact().as_str()).exists() {
|
|
247
661
|
bail!(
|
|
@@ -252,18 +666,68 @@ fn execute_publish(report: &mut PublishReport, args: &PublishArgs) -> Result<()>
|
|
|
252
666
|
|
|
253
667
|
let published_at_unix_seconds = now_unix_seconds()?;
|
|
254
668
|
report.published_at_unix_seconds = Some(published_at_unix_seconds);
|
|
669
|
+
execute_publish_destination(report, published_at_unix_seconds)?;
|
|
670
|
+
report.status = PublishStatus::Published;
|
|
255
671
|
|
|
256
|
-
|
|
257
|
-
PublishTarget::Local => execute_local_publish(report, args, published_at_unix_seconds),
|
|
258
|
-
PublishTarget::Github => execute_github_publish(report, args),
|
|
259
|
-
}
|
|
672
|
+
Ok(())
|
|
260
673
|
}
|
|
261
674
|
|
|
262
|
-
fn
|
|
263
|
-
|
|
675
|
+
fn execute_publish_reports(
|
|
676
|
+
reports: &mut [PublishReport],
|
|
677
|
+
make_reports: &mut [MakeReport],
|
|
264
678
|
args: &PublishArgs,
|
|
679
|
+
) -> Result<()> {
|
|
680
|
+
if !args.skip_make {
|
|
681
|
+
make::execute_make_reports(make_reports, &make_args(args))?;
|
|
682
|
+
sync_make_reports(reports, make_reports);
|
|
683
|
+
} else {
|
|
684
|
+
ensure_make_artifacts_exist(make_reports)?;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
let published_at_unix_seconds = now_unix_seconds()?;
|
|
688
|
+
for report in reports {
|
|
689
|
+
report.published_at_unix_seconds = Some(published_at_unix_seconds);
|
|
690
|
+
execute_publish_destination(report, published_at_unix_seconds)?;
|
|
691
|
+
report.status = PublishStatus::Published;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
Ok(())
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
fn sync_make_reports(reports: &mut [PublishReport], make_reports: &[MakeReport]) {
|
|
698
|
+
for report in reports {
|
|
699
|
+
if let Some(make) = make_reports
|
|
700
|
+
.iter()
|
|
701
|
+
.find(|make| make.artifact().as_str() == report.make.artifact().as_str())
|
|
702
|
+
{
|
|
703
|
+
report.make = make.clone();
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
fn ensure_make_artifacts_exist(make_reports: &[MakeReport]) -> Result<()> {
|
|
709
|
+
for make in make_reports {
|
|
710
|
+
if !Path::new(make.artifact().as_str()).exists() {
|
|
711
|
+
bail!(
|
|
712
|
+
"Make artifact does not exist: {}. Run without --skip-make or run electron-cli make first.",
|
|
713
|
+
make.artifact()
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
Ok(())
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
fn execute_publish_destination(
|
|
721
|
+
report: &mut PublishReport,
|
|
265
722
|
published_at_unix_seconds: u64,
|
|
266
723
|
) -> Result<()> {
|
|
724
|
+
match report.publisher_kind {
|
|
725
|
+
PublishTarget::Local => execute_local_publish(report, published_at_unix_seconds),
|
|
726
|
+
PublishTarget::Github => execute_github_publish(report),
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
fn execute_local_publish(report: &PublishReport, published_at_unix_seconds: u64) -> Result<()> {
|
|
267
731
|
let local = report
|
|
268
732
|
.local
|
|
269
733
|
.as_ref()
|
|
@@ -273,7 +737,7 @@ fn execute_local_publish(
|
|
|
273
737
|
|
|
274
738
|
for path in [destination_artifact, manifest] {
|
|
275
739
|
if path.exists() {
|
|
276
|
-
if
|
|
740
|
+
if report.force_publish {
|
|
277
741
|
fs::remove_file(path)
|
|
278
742
|
.with_context(|| format!("Could not remove {}", path.display()))?;
|
|
279
743
|
} else {
|
|
@@ -303,10 +767,10 @@ fn execute_local_publish(
|
|
|
303
767
|
Ok(())
|
|
304
768
|
}
|
|
305
769
|
|
|
306
|
-
fn execute_github_publish(report: &mut PublishReport
|
|
307
|
-
let token = github_token()?;
|
|
770
|
+
fn execute_github_publish(report: &mut PublishReport) -> Result<()> {
|
|
771
|
+
let token = github_token(report.github_auth_token.as_deref())?;
|
|
308
772
|
let agent = github_agent();
|
|
309
|
-
publish_to_github(report,
|
|
773
|
+
publish_to_github(report, &token, &agent)
|
|
310
774
|
}
|
|
311
775
|
|
|
312
776
|
#[derive(Debug, Serialize)]
|
|
@@ -338,12 +802,7 @@ struct GithubErrorBody {
|
|
|
338
802
|
message: Option<String>,
|
|
339
803
|
}
|
|
340
804
|
|
|
341
|
-
fn publish_to_github(
|
|
342
|
-
report: &mut PublishReport,
|
|
343
|
-
args: &PublishArgs,
|
|
344
|
-
token: &str,
|
|
345
|
-
agent: &ureq::Agent,
|
|
346
|
-
) -> Result<()> {
|
|
805
|
+
fn publish_to_github(report: &mut PublishReport, token: &str, agent: &ureq::Agent) -> Result<()> {
|
|
347
806
|
let artifact_path = Path::new(report.make.artifact().as_str());
|
|
348
807
|
let github = report
|
|
349
808
|
.github
|
|
@@ -359,7 +818,7 @@ fn publish_to_github(
|
|
|
359
818
|
.iter()
|
|
360
819
|
.find(|asset| asset.name == github.artifact_name)
|
|
361
820
|
{
|
|
362
|
-
if
|
|
821
|
+
if report.force_publish {
|
|
363
822
|
delete_github_asset(agent, token, github, asset.id)?;
|
|
364
823
|
} else {
|
|
365
824
|
bail!(
|
|
@@ -511,9 +970,13 @@ fn github_agent() -> ureq::Agent {
|
|
|
511
970
|
.into()
|
|
512
971
|
}
|
|
513
972
|
|
|
514
|
-
fn github_token() -> Result<String> {
|
|
515
|
-
|
|
516
|
-
.
|
|
973
|
+
fn github_token(configured_token: Option<&str>) -> Result<String> {
|
|
974
|
+
configured_token
|
|
975
|
+
.map(str::trim)
|
|
976
|
+
.filter(|token| !token.is_empty())
|
|
977
|
+
.map(ToOwned::to_owned)
|
|
978
|
+
.or_else(|| std::env::var("GITHUB_TOKEN").ok())
|
|
979
|
+
.or_else(|| std::env::var("GH_TOKEN").ok())
|
|
517
980
|
.context("GitHub publisher requires GITHUB_TOKEN or GH_TOKEN")
|
|
518
981
|
}
|
|
519
982
|
|
|
@@ -608,6 +1071,85 @@ fn print_report(report: &PublishReport, json: bool) -> Result<()> {
|
|
|
608
1071
|
Ok(())
|
|
609
1072
|
}
|
|
610
1073
|
|
|
1074
|
+
fn print_reports(reports: &[PublishReport], json: bool, status: PublishStatus) -> Result<()> {
|
|
1075
|
+
if reports.len() == 1 {
|
|
1076
|
+
return print_report(&reports[0], json);
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
let warnings = combined_warnings(reports);
|
|
1080
|
+
if json {
|
|
1081
|
+
return output::json(&PublishRunReport {
|
|
1082
|
+
publishes: reports,
|
|
1083
|
+
dry_run: reports.iter().any(|report| report.dry_run),
|
|
1084
|
+
status,
|
|
1085
|
+
warnings,
|
|
1086
|
+
});
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
println!("electron-cli publish");
|
|
1090
|
+
println!();
|
|
1091
|
+
if let Some(first) = reports.first() {
|
|
1092
|
+
println!("Project");
|
|
1093
|
+
println!(" root: {}", first.make.package().project().root);
|
|
1094
|
+
match first.make.package().project().package_label() {
|
|
1095
|
+
Some(label) => println!(" package: {label}"),
|
|
1096
|
+
None => println!(" package: not found"),
|
|
1097
|
+
}
|
|
1098
|
+
println!(" app name: {}", first.make.package().app_name());
|
|
1099
|
+
println!(
|
|
1100
|
+
" target platform: {} {}",
|
|
1101
|
+
first.make.package().platform(),
|
|
1102
|
+
first.make.package().arch()
|
|
1103
|
+
);
|
|
1104
|
+
println!(" status: {}", status.as_str());
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
println!();
|
|
1108
|
+
println!("Publishes");
|
|
1109
|
+
for report in reports {
|
|
1110
|
+
println!(
|
|
1111
|
+
" {} {}: {}",
|
|
1112
|
+
report.publisher,
|
|
1113
|
+
report.make.target(),
|
|
1114
|
+
report.make.artifact()
|
|
1115
|
+
);
|
|
1116
|
+
if let Some(local) = &report.local {
|
|
1117
|
+
println!(" artifact: {}", local.destination_artifact);
|
|
1118
|
+
println!(" manifest: {}", local.manifest);
|
|
1119
|
+
}
|
|
1120
|
+
if let Some(github) = &report.github {
|
|
1121
|
+
println!(" repository: {}", github.repo);
|
|
1122
|
+
println!(" tag: {}", github.tag);
|
|
1123
|
+
if let Some(url) = &github.release_url {
|
|
1124
|
+
println!(" release url: {url}");
|
|
1125
|
+
}
|
|
1126
|
+
if let Some(url) = &github.asset_url {
|
|
1127
|
+
println!(" asset url: {url}");
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
|
|
1132
|
+
if !warnings.is_empty() {
|
|
1133
|
+
println!();
|
|
1134
|
+
println!("Warnings");
|
|
1135
|
+
for warning in warnings {
|
|
1136
|
+
println!(" {warning}");
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
Ok(())
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
fn combined_warnings(reports: &[PublishReport]) -> Vec<String> {
|
|
1144
|
+
let mut warnings = Vec::new();
|
|
1145
|
+
for warning in reports.iter().flat_map(|report| report.warnings.iter()) {
|
|
1146
|
+
if !warnings.contains(warning) {
|
|
1147
|
+
warnings.push(warning.clone());
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
warnings
|
|
1151
|
+
}
|
|
1152
|
+
|
|
611
1153
|
fn resolve_destination(root: &Path, destination: &Path) -> PathBuf {
|
|
612
1154
|
if destination.is_absolute() {
|
|
613
1155
|
destination.to_path_buf()
|
|
@@ -616,16 +1158,17 @@ fn resolve_destination(root: &Path, destination: &Path) -> PathBuf {
|
|
|
616
1158
|
}
|
|
617
1159
|
}
|
|
618
1160
|
|
|
619
|
-
fn default_github_tag(make: &MakeReport, channel: &str) -> String {
|
|
1161
|
+
fn default_github_tag(make: &MakeReport, channel: &str, tag_prefix: Option<&str>) -> String {
|
|
620
1162
|
make.package()
|
|
621
1163
|
.project()
|
|
622
1164
|
.version
|
|
623
1165
|
.as_deref()
|
|
624
1166
|
.map(|version| {
|
|
625
|
-
|
|
1167
|
+
let prefix = tag_prefix.unwrap_or("v");
|
|
1168
|
+
if prefix.is_empty() || version.starts_with(prefix) {
|
|
626
1169
|
version.to_string()
|
|
627
1170
|
} else {
|
|
628
|
-
format!("
|
|
1171
|
+
format!("{prefix}{version}")
|
|
629
1172
|
}
|
|
630
1173
|
})
|
|
631
1174
|
.unwrap_or_else(|| channel.to_string())
|
|
@@ -807,6 +1350,106 @@ mod tests {
|
|
|
807
1350
|
let _ = fs::remove_dir_all(root);
|
|
808
1351
|
}
|
|
809
1352
|
|
|
1353
|
+
#[test]
|
|
1354
|
+
fn builds_github_publish_report_from_configured_forge_publisher() {
|
|
1355
|
+
let root = unique_temp_dir("configured-github-plan");
|
|
1356
|
+
write_package_json_with_publishers(
|
|
1357
|
+
&root,
|
|
1358
|
+
r#"[
|
|
1359
|
+
{
|
|
1360
|
+
"name":"@electron-forge/publisher-github",
|
|
1361
|
+
"platforms":["*"],
|
|
1362
|
+
"config":{
|
|
1363
|
+
"repository":{"owner":"Ikana","name":"electron-cli"},
|
|
1364
|
+
"draft":true,
|
|
1365
|
+
"prerelease":true,
|
|
1366
|
+
"tagPrefix":"release-",
|
|
1367
|
+
"releaseName":"Configured Release",
|
|
1368
|
+
"baseUrl":"http://127.0.0.1:9"
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
]"#,
|
|
1372
|
+
);
|
|
1373
|
+
write_app_file(&root);
|
|
1374
|
+
write_fake_electron_dist(&root);
|
|
1375
|
+
|
|
1376
|
+
let mut args = publish_args(root.clone(), true);
|
|
1377
|
+
args.target = None;
|
|
1378
|
+
args.publisher = None;
|
|
1379
|
+
args.github_api_url = None;
|
|
1380
|
+
args.channel = None;
|
|
1381
|
+
let report = build_report(&args).expect("report should build");
|
|
1382
|
+
|
|
1383
|
+
assert_eq!(report.publisher, "github");
|
|
1384
|
+
assert!(report.local.is_none());
|
|
1385
|
+
assert_eq!(report.channel, "default");
|
|
1386
|
+
let github = report.github.as_ref().expect("github plan should exist");
|
|
1387
|
+
assert_eq!(github.repo, "Ikana/electron-cli");
|
|
1388
|
+
assert_eq!(github.tag, "release-0.1.0");
|
|
1389
|
+
assert_eq!(github.release_name, "Configured Release");
|
|
1390
|
+
assert!(github.draft);
|
|
1391
|
+
assert!(github.prerelease);
|
|
1392
|
+
assert_eq!(github.api_url, "http://127.0.0.1:9");
|
|
1393
|
+
|
|
1394
|
+
let _ = fs::remove_dir_all(root);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
#[test]
|
|
1398
|
+
fn builds_publish_reports_from_configured_makers_and_publishers() {
|
|
1399
|
+
let root = unique_temp_dir("configured-publishers");
|
|
1400
|
+
write_package_json_with_makers_and_publishers(
|
|
1401
|
+
&root,
|
|
1402
|
+
r#"[
|
|
1403
|
+
{"name":"@electron-forge/maker-zip"},
|
|
1404
|
+
{"name":"@electron-forge/maker-deb","platforms":["linux"]}
|
|
1405
|
+
]"#,
|
|
1406
|
+
r#"[
|
|
1407
|
+
{"name":"@electron-forge/publisher-github","config":{"repository":"Ikana/electron-cli"}},
|
|
1408
|
+
{"name":"local","config":{"to":"dist/publish","channel":"beta"}},
|
|
1409
|
+
{"name":"@electron-forge/publisher-s3"}
|
|
1410
|
+
]"#,
|
|
1411
|
+
);
|
|
1412
|
+
write_app_file(&root);
|
|
1413
|
+
write_fake_electron_dist(&root);
|
|
1414
|
+
|
|
1415
|
+
let mut args = publish_args(root.clone(), true);
|
|
1416
|
+
args.platform = Some("linux".to_string());
|
|
1417
|
+
args.arch = Some("x64".to_string());
|
|
1418
|
+
args.target = None;
|
|
1419
|
+
args.publisher = None;
|
|
1420
|
+
args.to = None;
|
|
1421
|
+
args.channel = None;
|
|
1422
|
+
args.github_api_url = None;
|
|
1423
|
+
let reports = build_reports(&args).expect("reports should build");
|
|
1424
|
+
|
|
1425
|
+
assert_eq!(reports.len(), 4);
|
|
1426
|
+
assert_eq!(reports[0].publisher, "github");
|
|
1427
|
+
assert_eq!(reports[0].make.target(), "zip");
|
|
1428
|
+
assert_eq!(reports[1].publisher, "github");
|
|
1429
|
+
assert_eq!(reports[1].make.target(), "deb");
|
|
1430
|
+
assert_eq!(reports[2].publisher, "local");
|
|
1431
|
+
assert_eq!(reports[2].make.target(), "zip");
|
|
1432
|
+
assert_eq!(reports[3].publisher, "local");
|
|
1433
|
+
assert_eq!(reports[3].make.target(), "deb");
|
|
1434
|
+
let local = reports[2].local.as_ref().expect("local plan should exist");
|
|
1435
|
+
let local_parent = Path::new(local.destination_artifact.as_str())
|
|
1436
|
+
.parent()
|
|
1437
|
+
.expect("local artifact should have parent");
|
|
1438
|
+
assert!(local_parent.ends_with(
|
|
1439
|
+
PathBuf::from("dist")
|
|
1440
|
+
.join("publish")
|
|
1441
|
+
.join("beta")
|
|
1442
|
+
.join("linux")
|
|
1443
|
+
.join("x64")
|
|
1444
|
+
));
|
|
1445
|
+
assert!(reports[0]
|
|
1446
|
+
.warnings
|
|
1447
|
+
.iter()
|
|
1448
|
+
.any(|warning| warning.contains("@electron-forge/publisher-s3")));
|
|
1449
|
+
|
|
1450
|
+
let _ = fs::remove_dir_all(root);
|
|
1451
|
+
}
|
|
1452
|
+
|
|
810
1453
|
#[test]
|
|
811
1454
|
fn publishes_make_artifact_to_github_release() {
|
|
812
1455
|
let server = MockGithubServer::new(3);
|
|
@@ -824,7 +1467,7 @@ mod tests {
|
|
|
824
1467
|
fs::write(artifact, b"artifact bytes").expect("artifact should be written");
|
|
825
1468
|
|
|
826
1469
|
let agent = github_agent();
|
|
827
|
-
publish_to_github(&mut report,
|
|
1470
|
+
publish_to_github(&mut report, "test-token", &agent)
|
|
828
1471
|
.expect("github publish should succeed");
|
|
829
1472
|
|
|
830
1473
|
let github = report.github.as_ref().expect("github plan should exist");
|
|
@@ -903,16 +1546,16 @@ mod tests {
|
|
|
903
1546
|
name: None,
|
|
904
1547
|
platform: None,
|
|
905
1548
|
arch: None,
|
|
906
|
-
target: crate::cli::MakeTarget::Zip,
|
|
907
|
-
publisher: crate::cli::PublishTarget::Local,
|
|
908
|
-
to: PathBuf::from("out/publish/local"),
|
|
1549
|
+
target: Some(crate::cli::MakeTarget::Zip),
|
|
1550
|
+
publisher: Some(crate::cli::PublishTarget::Local),
|
|
1551
|
+
to: Some(PathBuf::from("out/publish/local")),
|
|
909
1552
|
github_repo: None,
|
|
910
1553
|
github_tag: None,
|
|
911
1554
|
github_release_name: None,
|
|
912
1555
|
github_draft: false,
|
|
913
1556
|
github_prerelease: false,
|
|
914
|
-
github_api_url: "https://api.github.com".to_string(),
|
|
915
|
-
channel: "default".to_string(),
|
|
1557
|
+
github_api_url: Some("https://api.github.com".to_string()),
|
|
1558
|
+
channel: Some("default".to_string()),
|
|
916
1559
|
skip_make: false,
|
|
917
1560
|
force: false,
|
|
918
1561
|
dry_run,
|
|
@@ -922,8 +1565,8 @@ mod tests {
|
|
|
922
1565
|
|
|
923
1566
|
fn github_publish_args(root: PathBuf, dry_run: bool, api_url: &str) -> PublishArgs {
|
|
924
1567
|
let mut args = publish_args(root, dry_run);
|
|
925
|
-
args.publisher = crate::cli::PublishTarget::Github;
|
|
926
|
-
args.github_api_url = api_url.to_string();
|
|
1568
|
+
args.publisher = Some(crate::cli::PublishTarget::Github);
|
|
1569
|
+
args.github_api_url = Some(api_url.to_string());
|
|
927
1570
|
args
|
|
928
1571
|
}
|
|
929
1572
|
|
|
@@ -943,6 +1586,38 @@ mod tests {
|
|
|
943
1586
|
.expect("package.json should be written");
|
|
944
1587
|
}
|
|
945
1588
|
|
|
1589
|
+
fn write_package_json_with_publishers(root: &Path, publishers: &str) {
|
|
1590
|
+
fs::write(
|
|
1591
|
+
root.join("package.json"),
|
|
1592
|
+
format!(
|
|
1593
|
+
r#"{{
|
|
1594
|
+
"name":"starter-app",
|
|
1595
|
+
"version":"0.1.0",
|
|
1596
|
+
"main":"src/main.js",
|
|
1597
|
+
"devDependencies":{{"electron":"30.0.0"}},
|
|
1598
|
+
"config":{{"forge":{{"publishers":{publishers}}}}}
|
|
1599
|
+
}}"#
|
|
1600
|
+
),
|
|
1601
|
+
)
|
|
1602
|
+
.expect("package.json with publishers should be written");
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
fn write_package_json_with_makers_and_publishers(root: &Path, makers: &str, publishers: &str) {
|
|
1606
|
+
fs::write(
|
|
1607
|
+
root.join("package.json"),
|
|
1608
|
+
format!(
|
|
1609
|
+
r#"{{
|
|
1610
|
+
"name":"starter-app",
|
|
1611
|
+
"version":"0.1.0",
|
|
1612
|
+
"main":"src/main.js",
|
|
1613
|
+
"devDependencies":{{"electron":"30.0.0"}},
|
|
1614
|
+
"config":{{"forge":{{"makers":{makers},"publishers":{publishers}}}}}
|
|
1615
|
+
}}"#
|
|
1616
|
+
),
|
|
1617
|
+
)
|
|
1618
|
+
.expect("package.json with makers and publishers should be written");
|
|
1619
|
+
}
|
|
1620
|
+
|
|
946
1621
|
fn write_app_file(root: &Path) {
|
|
947
1622
|
fs::create_dir_all(root.join("src")).expect("src should be created");
|
|
948
1623
|
fs::write(root.join("src/main.js"), "console.log('hello');")
|