electron-cli 0.3.0-alpha.13 → 0.3.0-alpha.15
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 +22 -6
- package/package.json +1 -1
- package/src/cli.rs +13 -13
- package/src/commands/make.rs +47 -16
- package/src/commands/package.rs +69 -26
- package/src/commands/publish.rs +789 -83
- package/src/forge_config.rs +547 -0
- package/src/main.rs +1 -0
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,350 @@ 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 project_config = crate::forge_config::read(project)?;
|
|
453
|
+
|
|
454
|
+
let mut publishers = Vec::new();
|
|
455
|
+
for value in [
|
|
456
|
+
project_config
|
|
457
|
+
.forge()
|
|
458
|
+
.and_then(|forge| forge.get("publishers")),
|
|
459
|
+
project_config
|
|
460
|
+
.electron_cli()
|
|
461
|
+
.and_then(|config| config.get("publishers")),
|
|
462
|
+
]
|
|
463
|
+
.into_iter()
|
|
464
|
+
.flatten()
|
|
465
|
+
{
|
|
466
|
+
publishers.extend(parse_publisher_list(value));
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
Ok(publishers)
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
fn parse_publisher_list(value: &JsonValue) -> Vec<ConfiguredPublisher> {
|
|
473
|
+
match value {
|
|
474
|
+
JsonValue::Array(values) => values.iter().filter_map(parse_publisher).collect(),
|
|
475
|
+
_ => Vec::new(),
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
fn parse_publisher(value: &JsonValue) -> Option<ConfiguredPublisher> {
|
|
480
|
+
match value {
|
|
481
|
+
JsonValue::String(label) => Some(ConfiguredPublisher {
|
|
482
|
+
label: label.clone(),
|
|
483
|
+
target: publisher_target(label),
|
|
484
|
+
platforms: Vec::new(),
|
|
485
|
+
to: None,
|
|
486
|
+
channel: None,
|
|
487
|
+
github_repo: None,
|
|
488
|
+
github_tag: None,
|
|
489
|
+
github_tag_prefix: None,
|
|
490
|
+
github_release_name: None,
|
|
491
|
+
github_draft: None,
|
|
492
|
+
github_prerelease: None,
|
|
493
|
+
github_api_url: None,
|
|
494
|
+
github_auth_token: None,
|
|
495
|
+
force_publish: None,
|
|
496
|
+
}),
|
|
497
|
+
JsonValue::Object(object) => {
|
|
498
|
+
let label = object
|
|
499
|
+
.get("name")
|
|
500
|
+
.or_else(|| object.get("publisher"))
|
|
501
|
+
.or_else(|| object.get("target"))
|
|
502
|
+
.and_then(JsonValue::as_str)?
|
|
503
|
+
.to_string();
|
|
504
|
+
Some(ConfiguredPublisher {
|
|
505
|
+
target: publisher_target(&label),
|
|
506
|
+
platforms: string_values(object.get("platforms")),
|
|
507
|
+
to: publisher_config_string(object, &["to", "path", "dir", "directory"])
|
|
508
|
+
.map(PathBuf::from),
|
|
509
|
+
channel: publisher_config_string(object, &["channel"]),
|
|
510
|
+
github_repo: publisher_config_github_repo(object),
|
|
511
|
+
github_tag: publisher_config_string(object, &["tag", "tagName", "tag_name"]),
|
|
512
|
+
github_tag_prefix: publisher_config_string(object, &["tagPrefix", "tag_prefix"]),
|
|
513
|
+
github_release_name: publisher_config_string(
|
|
514
|
+
object,
|
|
515
|
+
&["releaseName", "release_name"],
|
|
516
|
+
),
|
|
517
|
+
github_draft: publisher_config_bool(object, &["draft"]),
|
|
518
|
+
github_prerelease: publisher_config_bool(object, &["prerelease", "preRelease"]),
|
|
519
|
+
github_api_url: publisher_config_api_url(object),
|
|
520
|
+
github_auth_token: publisher_config_string(object, &["authToken", "auth_token"]),
|
|
521
|
+
force_publish: publisher_config_bool(object, &["force"]),
|
|
522
|
+
label,
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
_ => None,
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
fn publisher_target(label: &str) -> Option<PublishTarget> {
|
|
530
|
+
let label = label.trim().to_ascii_lowercase();
|
|
531
|
+
let compact = label
|
|
532
|
+
.trim_start_matches("@electron-forge/")
|
|
533
|
+
.trim_start_matches("electron-forge-")
|
|
534
|
+
.trim_start_matches("publisher-");
|
|
535
|
+
|
|
536
|
+
if compact == "github"
|
|
537
|
+
|| label.ends_with("/publisher-github")
|
|
538
|
+
|| label.ends_with("publisher-github")
|
|
539
|
+
{
|
|
540
|
+
Some(PublishTarget::Github)
|
|
541
|
+
} else if compact == "local"
|
|
542
|
+
|| label.ends_with("/publisher-local")
|
|
543
|
+
|| label.ends_with("publisher-local")
|
|
544
|
+
{
|
|
545
|
+
Some(PublishTarget::Local)
|
|
546
|
+
} else {
|
|
547
|
+
None
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
fn publisher_applies_to_platform(publisher: &ConfiguredPublisher, platform: &str) -> bool {
|
|
552
|
+
publisher.platforms.is_empty()
|
|
553
|
+
|| publisher
|
|
554
|
+
.platforms
|
|
555
|
+
.iter()
|
|
556
|
+
.any(|configured| configured == platform || configured == "*")
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
fn string_values(value: Option<&JsonValue>) -> Vec<String> {
|
|
560
|
+
match value {
|
|
561
|
+
Some(JsonValue::String(value)) => vec![value.clone()],
|
|
562
|
+
Some(JsonValue::Array(values)) => values
|
|
563
|
+
.iter()
|
|
564
|
+
.filter_map(JsonValue::as_str)
|
|
565
|
+
.map(ToOwned::to_owned)
|
|
566
|
+
.collect(),
|
|
567
|
+
_ => Vec::new(),
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
fn publisher_config_string(object: &JsonMap<String, JsonValue>, keys: &[&str]) -> Option<String> {
|
|
572
|
+
keys.iter().find_map(|key| {
|
|
573
|
+
publisher_config_value(object, key)
|
|
574
|
+
.and_then(JsonValue::as_str)
|
|
575
|
+
.map(str::trim)
|
|
576
|
+
.filter(|value| !value.is_empty())
|
|
577
|
+
.map(ToOwned::to_owned)
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
fn publisher_config_bool(object: &JsonMap<String, JsonValue>, keys: &[&str]) -> Option<bool> {
|
|
582
|
+
keys.iter()
|
|
583
|
+
.find_map(|key| publisher_config_value(object, key).and_then(JsonValue::as_bool))
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
fn publisher_config_value<'a>(
|
|
587
|
+
object: &'a JsonMap<String, JsonValue>,
|
|
588
|
+
key: &str,
|
|
589
|
+
) -> Option<&'a JsonValue> {
|
|
590
|
+
object
|
|
591
|
+
.get("config")
|
|
592
|
+
.and_then(JsonValue::as_object)
|
|
593
|
+
.and_then(|config| config.get(key))
|
|
594
|
+
.or_else(|| object.get(key))
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
fn publisher_config_github_repo(object: &JsonMap<String, JsonValue>) -> Option<String> {
|
|
598
|
+
["repository", "repo", "githubRepo", "github_repo"]
|
|
599
|
+
.iter()
|
|
600
|
+
.find_map(|key| publisher_config_value(object, key).and_then(github_repo_from_config_value))
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
fn github_repo_from_config_value(value: &JsonValue) -> Option<String> {
|
|
604
|
+
match value {
|
|
605
|
+
JsonValue::String(value) => {
|
|
606
|
+
let value = value.trim();
|
|
607
|
+
(!value.is_empty()).then(|| value.to_string())
|
|
608
|
+
}
|
|
609
|
+
JsonValue::Object(object) => {
|
|
610
|
+
let owner = object
|
|
611
|
+
.get("owner")
|
|
612
|
+
.or_else(|| object.get("user"))
|
|
613
|
+
.and_then(JsonValue::as_str)?
|
|
614
|
+
.trim();
|
|
615
|
+
let name = object
|
|
616
|
+
.get("name")
|
|
617
|
+
.or_else(|| object.get("repo"))
|
|
618
|
+
.and_then(JsonValue::as_str)?
|
|
619
|
+
.trim();
|
|
620
|
+
if owner.is_empty() || name.is_empty() {
|
|
621
|
+
None
|
|
622
|
+
} else {
|
|
623
|
+
Some(format!("{owner}/{name}"))
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
_ => None,
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
fn publisher_config_api_url(object: &JsonMap<String, JsonValue>) -> Option<String> {
|
|
631
|
+
publisher_config_string(object, &["apiUrl", "api_url", "baseUrl", "base_url"]).or_else(|| {
|
|
632
|
+
publisher_config_value(object, "octokitOptions")
|
|
633
|
+
.and_then(JsonValue::as_object)
|
|
634
|
+
.and_then(|octokit| {
|
|
635
|
+
octokit
|
|
636
|
+
.get("baseUrl")
|
|
637
|
+
.or_else(|| octokit.get("base_url"))
|
|
638
|
+
.and_then(JsonValue::as_str)
|
|
639
|
+
})
|
|
640
|
+
.map(str::trim)
|
|
641
|
+
.filter(|value| !value.is_empty())
|
|
642
|
+
.map(ToOwned::to_owned)
|
|
643
|
+
})
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
#[cfg(test)]
|
|
230
647
|
fn execute_publish(report: &mut PublishReport, args: &PublishArgs) -> Result<()> {
|
|
231
648
|
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: Some(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)?;
|
|
649
|
+
make::execute_make(&mut report.make, &make_args(args))?;
|
|
245
650
|
report.make.mark_made()?;
|
|
246
651
|
} else if !Path::new(report.make.artifact().as_str()).exists() {
|
|
247
652
|
bail!(
|
|
@@ -252,18 +657,68 @@ fn execute_publish(report: &mut PublishReport, args: &PublishArgs) -> Result<()>
|
|
|
252
657
|
|
|
253
658
|
let published_at_unix_seconds = now_unix_seconds()?;
|
|
254
659
|
report.published_at_unix_seconds = Some(published_at_unix_seconds);
|
|
660
|
+
execute_publish_destination(report, published_at_unix_seconds)?;
|
|
661
|
+
report.status = PublishStatus::Published;
|
|
255
662
|
|
|
256
|
-
|
|
257
|
-
PublishTarget::Local => execute_local_publish(report, args, published_at_unix_seconds),
|
|
258
|
-
PublishTarget::Github => execute_github_publish(report, args),
|
|
259
|
-
}
|
|
663
|
+
Ok(())
|
|
260
664
|
}
|
|
261
665
|
|
|
262
|
-
fn
|
|
263
|
-
|
|
666
|
+
fn execute_publish_reports(
|
|
667
|
+
reports: &mut [PublishReport],
|
|
668
|
+
make_reports: &mut [MakeReport],
|
|
264
669
|
args: &PublishArgs,
|
|
670
|
+
) -> Result<()> {
|
|
671
|
+
if !args.skip_make {
|
|
672
|
+
make::execute_make_reports(make_reports, &make_args(args))?;
|
|
673
|
+
sync_make_reports(reports, make_reports);
|
|
674
|
+
} else {
|
|
675
|
+
ensure_make_artifacts_exist(make_reports)?;
|
|
676
|
+
}
|
|
677
|
+
|
|
678
|
+
let published_at_unix_seconds = now_unix_seconds()?;
|
|
679
|
+
for report in reports {
|
|
680
|
+
report.published_at_unix_seconds = Some(published_at_unix_seconds);
|
|
681
|
+
execute_publish_destination(report, published_at_unix_seconds)?;
|
|
682
|
+
report.status = PublishStatus::Published;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
Ok(())
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
fn sync_make_reports(reports: &mut [PublishReport], make_reports: &[MakeReport]) {
|
|
689
|
+
for report in reports {
|
|
690
|
+
if let Some(make) = make_reports
|
|
691
|
+
.iter()
|
|
692
|
+
.find(|make| make.artifact().as_str() == report.make.artifact().as_str())
|
|
693
|
+
{
|
|
694
|
+
report.make = make.clone();
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
fn ensure_make_artifacts_exist(make_reports: &[MakeReport]) -> Result<()> {
|
|
700
|
+
for make in make_reports {
|
|
701
|
+
if !Path::new(make.artifact().as_str()).exists() {
|
|
702
|
+
bail!(
|
|
703
|
+
"Make artifact does not exist: {}. Run without --skip-make or run electron-cli make first.",
|
|
704
|
+
make.artifact()
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
Ok(())
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
fn execute_publish_destination(
|
|
712
|
+
report: &mut PublishReport,
|
|
265
713
|
published_at_unix_seconds: u64,
|
|
266
714
|
) -> Result<()> {
|
|
715
|
+
match report.publisher_kind {
|
|
716
|
+
PublishTarget::Local => execute_local_publish(report, published_at_unix_seconds),
|
|
717
|
+
PublishTarget::Github => execute_github_publish(report),
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
fn execute_local_publish(report: &PublishReport, published_at_unix_seconds: u64) -> Result<()> {
|
|
267
722
|
let local = report
|
|
268
723
|
.local
|
|
269
724
|
.as_ref()
|
|
@@ -273,7 +728,7 @@ fn execute_local_publish(
|
|
|
273
728
|
|
|
274
729
|
for path in [destination_artifact, manifest] {
|
|
275
730
|
if path.exists() {
|
|
276
|
-
if
|
|
731
|
+
if report.force_publish {
|
|
277
732
|
fs::remove_file(path)
|
|
278
733
|
.with_context(|| format!("Could not remove {}", path.display()))?;
|
|
279
734
|
} else {
|
|
@@ -303,10 +758,10 @@ fn execute_local_publish(
|
|
|
303
758
|
Ok(())
|
|
304
759
|
}
|
|
305
760
|
|
|
306
|
-
fn execute_github_publish(report: &mut PublishReport
|
|
307
|
-
let token = github_token()?;
|
|
761
|
+
fn execute_github_publish(report: &mut PublishReport) -> Result<()> {
|
|
762
|
+
let token = github_token(report.github_auth_token.as_deref())?;
|
|
308
763
|
let agent = github_agent();
|
|
309
|
-
publish_to_github(report,
|
|
764
|
+
publish_to_github(report, &token, &agent)
|
|
310
765
|
}
|
|
311
766
|
|
|
312
767
|
#[derive(Debug, Serialize)]
|
|
@@ -338,12 +793,7 @@ struct GithubErrorBody {
|
|
|
338
793
|
message: Option<String>,
|
|
339
794
|
}
|
|
340
795
|
|
|
341
|
-
fn publish_to_github(
|
|
342
|
-
report: &mut PublishReport,
|
|
343
|
-
args: &PublishArgs,
|
|
344
|
-
token: &str,
|
|
345
|
-
agent: &ureq::Agent,
|
|
346
|
-
) -> Result<()> {
|
|
796
|
+
fn publish_to_github(report: &mut PublishReport, token: &str, agent: &ureq::Agent) -> Result<()> {
|
|
347
797
|
let artifact_path = Path::new(report.make.artifact().as_str());
|
|
348
798
|
let github = report
|
|
349
799
|
.github
|
|
@@ -359,7 +809,7 @@ fn publish_to_github(
|
|
|
359
809
|
.iter()
|
|
360
810
|
.find(|asset| asset.name == github.artifact_name)
|
|
361
811
|
{
|
|
362
|
-
if
|
|
812
|
+
if report.force_publish {
|
|
363
813
|
delete_github_asset(agent, token, github, asset.id)?;
|
|
364
814
|
} else {
|
|
365
815
|
bail!(
|
|
@@ -511,9 +961,13 @@ fn github_agent() -> ureq::Agent {
|
|
|
511
961
|
.into()
|
|
512
962
|
}
|
|
513
963
|
|
|
514
|
-
fn github_token() -> Result<String> {
|
|
515
|
-
|
|
516
|
-
.
|
|
964
|
+
fn github_token(configured_token: Option<&str>) -> Result<String> {
|
|
965
|
+
configured_token
|
|
966
|
+
.map(str::trim)
|
|
967
|
+
.filter(|token| !token.is_empty())
|
|
968
|
+
.map(ToOwned::to_owned)
|
|
969
|
+
.or_else(|| std::env::var("GITHUB_TOKEN").ok())
|
|
970
|
+
.or_else(|| std::env::var("GH_TOKEN").ok())
|
|
517
971
|
.context("GitHub publisher requires GITHUB_TOKEN or GH_TOKEN")
|
|
518
972
|
}
|
|
519
973
|
|
|
@@ -608,6 +1062,85 @@ fn print_report(report: &PublishReport, json: bool) -> Result<()> {
|
|
|
608
1062
|
Ok(())
|
|
609
1063
|
}
|
|
610
1064
|
|
|
1065
|
+
fn print_reports(reports: &[PublishReport], json: bool, status: PublishStatus) -> Result<()> {
|
|
1066
|
+
if reports.len() == 1 {
|
|
1067
|
+
return print_report(&reports[0], json);
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
let warnings = combined_warnings(reports);
|
|
1071
|
+
if json {
|
|
1072
|
+
return output::json(&PublishRunReport {
|
|
1073
|
+
publishes: reports,
|
|
1074
|
+
dry_run: reports.iter().any(|report| report.dry_run),
|
|
1075
|
+
status,
|
|
1076
|
+
warnings,
|
|
1077
|
+
});
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
println!("electron-cli publish");
|
|
1081
|
+
println!();
|
|
1082
|
+
if let Some(first) = reports.first() {
|
|
1083
|
+
println!("Project");
|
|
1084
|
+
println!(" root: {}", first.make.package().project().root);
|
|
1085
|
+
match first.make.package().project().package_label() {
|
|
1086
|
+
Some(label) => println!(" package: {label}"),
|
|
1087
|
+
None => println!(" package: not found"),
|
|
1088
|
+
}
|
|
1089
|
+
println!(" app name: {}", first.make.package().app_name());
|
|
1090
|
+
println!(
|
|
1091
|
+
" target platform: {} {}",
|
|
1092
|
+
first.make.package().platform(),
|
|
1093
|
+
first.make.package().arch()
|
|
1094
|
+
);
|
|
1095
|
+
println!(" status: {}", status.as_str());
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
println!();
|
|
1099
|
+
println!("Publishes");
|
|
1100
|
+
for report in reports {
|
|
1101
|
+
println!(
|
|
1102
|
+
" {} {}: {}",
|
|
1103
|
+
report.publisher,
|
|
1104
|
+
report.make.target(),
|
|
1105
|
+
report.make.artifact()
|
|
1106
|
+
);
|
|
1107
|
+
if let Some(local) = &report.local {
|
|
1108
|
+
println!(" artifact: {}", local.destination_artifact);
|
|
1109
|
+
println!(" manifest: {}", local.manifest);
|
|
1110
|
+
}
|
|
1111
|
+
if let Some(github) = &report.github {
|
|
1112
|
+
println!(" repository: {}", github.repo);
|
|
1113
|
+
println!(" tag: {}", github.tag);
|
|
1114
|
+
if let Some(url) = &github.release_url {
|
|
1115
|
+
println!(" release url: {url}");
|
|
1116
|
+
}
|
|
1117
|
+
if let Some(url) = &github.asset_url {
|
|
1118
|
+
println!(" asset url: {url}");
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
|
|
1123
|
+
if !warnings.is_empty() {
|
|
1124
|
+
println!();
|
|
1125
|
+
println!("Warnings");
|
|
1126
|
+
for warning in warnings {
|
|
1127
|
+
println!(" {warning}");
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
Ok(())
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
fn combined_warnings(reports: &[PublishReport]) -> Vec<String> {
|
|
1135
|
+
let mut warnings = Vec::new();
|
|
1136
|
+
for warning in reports.iter().flat_map(|report| report.warnings.iter()) {
|
|
1137
|
+
if !warnings.contains(warning) {
|
|
1138
|
+
warnings.push(warning.clone());
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
warnings
|
|
1142
|
+
}
|
|
1143
|
+
|
|
611
1144
|
fn resolve_destination(root: &Path, destination: &Path) -> PathBuf {
|
|
612
1145
|
if destination.is_absolute() {
|
|
613
1146
|
destination.to_path_buf()
|
|
@@ -616,16 +1149,17 @@ fn resolve_destination(root: &Path, destination: &Path) -> PathBuf {
|
|
|
616
1149
|
}
|
|
617
1150
|
}
|
|
618
1151
|
|
|
619
|
-
fn default_github_tag(make: &MakeReport, channel: &str) -> String {
|
|
1152
|
+
fn default_github_tag(make: &MakeReport, channel: &str, tag_prefix: Option<&str>) -> String {
|
|
620
1153
|
make.package()
|
|
621
1154
|
.project()
|
|
622
1155
|
.version
|
|
623
1156
|
.as_deref()
|
|
624
1157
|
.map(|version| {
|
|
625
|
-
|
|
1158
|
+
let prefix = tag_prefix.unwrap_or("v");
|
|
1159
|
+
if prefix.is_empty() || version.starts_with(prefix) {
|
|
626
1160
|
version.to_string()
|
|
627
1161
|
} else {
|
|
628
|
-
format!("
|
|
1162
|
+
format!("{prefix}{version}")
|
|
629
1163
|
}
|
|
630
1164
|
})
|
|
631
1165
|
.unwrap_or_else(|| channel.to_string())
|
|
@@ -807,6 +1341,146 @@ mod tests {
|
|
|
807
1341
|
let _ = fs::remove_dir_all(root);
|
|
808
1342
|
}
|
|
809
1343
|
|
|
1344
|
+
#[test]
|
|
1345
|
+
fn builds_github_publish_report_from_configured_forge_publisher() {
|
|
1346
|
+
let root = unique_temp_dir("configured-github-plan");
|
|
1347
|
+
write_package_json_with_publishers(
|
|
1348
|
+
&root,
|
|
1349
|
+
r#"[
|
|
1350
|
+
{
|
|
1351
|
+
"name":"@electron-forge/publisher-github",
|
|
1352
|
+
"platforms":["*"],
|
|
1353
|
+
"config":{
|
|
1354
|
+
"repository":{"owner":"Ikana","name":"electron-cli"},
|
|
1355
|
+
"draft":true,
|
|
1356
|
+
"prerelease":true,
|
|
1357
|
+
"tagPrefix":"release-",
|
|
1358
|
+
"releaseName":"Configured Release",
|
|
1359
|
+
"baseUrl":"http://127.0.0.1:9"
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
]"#,
|
|
1363
|
+
);
|
|
1364
|
+
write_app_file(&root);
|
|
1365
|
+
write_fake_electron_dist(&root);
|
|
1366
|
+
|
|
1367
|
+
let mut args = publish_args(root.clone(), true);
|
|
1368
|
+
args.target = None;
|
|
1369
|
+
args.publisher = None;
|
|
1370
|
+
args.github_api_url = None;
|
|
1371
|
+
args.channel = None;
|
|
1372
|
+
let report = build_report(&args).expect("report should build");
|
|
1373
|
+
|
|
1374
|
+
assert_eq!(report.publisher, "github");
|
|
1375
|
+
assert!(report.local.is_none());
|
|
1376
|
+
assert_eq!(report.channel, "default");
|
|
1377
|
+
let github = report.github.as_ref().expect("github plan should exist");
|
|
1378
|
+
assert_eq!(github.repo, "Ikana/electron-cli");
|
|
1379
|
+
assert_eq!(github.tag, "release-0.1.0");
|
|
1380
|
+
assert_eq!(github.release_name, "Configured Release");
|
|
1381
|
+
assert!(github.draft);
|
|
1382
|
+
assert!(github.prerelease);
|
|
1383
|
+
assert_eq!(github.api_url, "http://127.0.0.1:9");
|
|
1384
|
+
|
|
1385
|
+
let _ = fs::remove_dir_all(root);
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
#[test]
|
|
1389
|
+
fn builds_github_publish_report_from_static_forge_config_js() {
|
|
1390
|
+
let root = unique_temp_dir("configured-github-js-plan");
|
|
1391
|
+
write_package_json(&root);
|
|
1392
|
+
fs::write(
|
|
1393
|
+
root.join("forge.config.js"),
|
|
1394
|
+
r#"
|
|
1395
|
+
module.exports = {
|
|
1396
|
+
publishers: [
|
|
1397
|
+
{
|
|
1398
|
+
name: '@electron-forge/publisher-github',
|
|
1399
|
+
config: {
|
|
1400
|
+
repository: { owner: 'Ikana', name: 'electron-cli' },
|
|
1401
|
+
tagPrefix: 'release-',
|
|
1402
|
+
prerelease: true,
|
|
1403
|
+
},
|
|
1404
|
+
},
|
|
1405
|
+
],
|
|
1406
|
+
};
|
|
1407
|
+
"#,
|
|
1408
|
+
)
|
|
1409
|
+
.expect("forge config should be written");
|
|
1410
|
+
write_app_file(&root);
|
|
1411
|
+
write_fake_electron_dist(&root);
|
|
1412
|
+
|
|
1413
|
+
let mut args = publish_args(root.clone(), true);
|
|
1414
|
+
args.publisher = None;
|
|
1415
|
+
args.github_api_url = None;
|
|
1416
|
+
args.channel = None;
|
|
1417
|
+
let report = build_report(&args).expect("report should build");
|
|
1418
|
+
|
|
1419
|
+
assert_eq!(report.publisher, "github");
|
|
1420
|
+
let github = report.github.as_ref().expect("github plan should exist");
|
|
1421
|
+
assert_eq!(github.repo, "Ikana/electron-cli");
|
|
1422
|
+
assert_eq!(github.tag, "release-0.1.0");
|
|
1423
|
+
assert!(github.prerelease);
|
|
1424
|
+
|
|
1425
|
+
let _ = fs::remove_dir_all(root);
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
#[test]
|
|
1429
|
+
fn builds_publish_reports_from_configured_makers_and_publishers() {
|
|
1430
|
+
let root = unique_temp_dir("configured-publishers");
|
|
1431
|
+
write_package_json_with_makers_and_publishers(
|
|
1432
|
+
&root,
|
|
1433
|
+
r#"[
|
|
1434
|
+
{"name":"@electron-forge/maker-zip"},
|
|
1435
|
+
{"name":"@electron-forge/maker-deb","platforms":["linux"]}
|
|
1436
|
+
]"#,
|
|
1437
|
+
r#"[
|
|
1438
|
+
{"name":"@electron-forge/publisher-github","config":{"repository":"Ikana/electron-cli"}},
|
|
1439
|
+
{"name":"local","config":{"to":"dist/publish","channel":"beta"}},
|
|
1440
|
+
{"name":"@electron-forge/publisher-s3"}
|
|
1441
|
+
]"#,
|
|
1442
|
+
);
|
|
1443
|
+
write_app_file(&root);
|
|
1444
|
+
write_fake_electron_dist(&root);
|
|
1445
|
+
|
|
1446
|
+
let mut args = publish_args(root.clone(), true);
|
|
1447
|
+
args.platform = Some("linux".to_string());
|
|
1448
|
+
args.arch = Some("x64".to_string());
|
|
1449
|
+
args.target = None;
|
|
1450
|
+
args.publisher = None;
|
|
1451
|
+
args.to = None;
|
|
1452
|
+
args.channel = None;
|
|
1453
|
+
args.github_api_url = None;
|
|
1454
|
+
let reports = build_reports(&args).expect("reports should build");
|
|
1455
|
+
|
|
1456
|
+
assert_eq!(reports.len(), 4);
|
|
1457
|
+
assert_eq!(reports[0].publisher, "github");
|
|
1458
|
+
assert_eq!(reports[0].make.target(), "zip");
|
|
1459
|
+
assert_eq!(reports[1].publisher, "github");
|
|
1460
|
+
assert_eq!(reports[1].make.target(), "deb");
|
|
1461
|
+
assert_eq!(reports[2].publisher, "local");
|
|
1462
|
+
assert_eq!(reports[2].make.target(), "zip");
|
|
1463
|
+
assert_eq!(reports[3].publisher, "local");
|
|
1464
|
+
assert_eq!(reports[3].make.target(), "deb");
|
|
1465
|
+
let local = reports[2].local.as_ref().expect("local plan should exist");
|
|
1466
|
+
let local_parent = Path::new(local.destination_artifact.as_str())
|
|
1467
|
+
.parent()
|
|
1468
|
+
.expect("local artifact should have parent");
|
|
1469
|
+
assert!(local_parent.ends_with(
|
|
1470
|
+
PathBuf::from("dist")
|
|
1471
|
+
.join("publish")
|
|
1472
|
+
.join("beta")
|
|
1473
|
+
.join("linux")
|
|
1474
|
+
.join("x64")
|
|
1475
|
+
));
|
|
1476
|
+
assert!(reports[0]
|
|
1477
|
+
.warnings
|
|
1478
|
+
.iter()
|
|
1479
|
+
.any(|warning| warning.contains("@electron-forge/publisher-s3")));
|
|
1480
|
+
|
|
1481
|
+
let _ = fs::remove_dir_all(root);
|
|
1482
|
+
}
|
|
1483
|
+
|
|
810
1484
|
#[test]
|
|
811
1485
|
fn publishes_make_artifact_to_github_release() {
|
|
812
1486
|
let server = MockGithubServer::new(3);
|
|
@@ -824,7 +1498,7 @@ mod tests {
|
|
|
824
1498
|
fs::write(artifact, b"artifact bytes").expect("artifact should be written");
|
|
825
1499
|
|
|
826
1500
|
let agent = github_agent();
|
|
827
|
-
publish_to_github(&mut report,
|
|
1501
|
+
publish_to_github(&mut report, "test-token", &agent)
|
|
828
1502
|
.expect("github publish should succeed");
|
|
829
1503
|
|
|
830
1504
|
let github = report.github.as_ref().expect("github plan should exist");
|
|
@@ -903,16 +1577,16 @@ mod tests {
|
|
|
903
1577
|
name: None,
|
|
904
1578
|
platform: None,
|
|
905
1579
|
arch: None,
|
|
906
|
-
target: crate::cli::MakeTarget::Zip,
|
|
907
|
-
publisher: crate::cli::PublishTarget::Local,
|
|
908
|
-
to: PathBuf::from("out/publish/local"),
|
|
1580
|
+
target: Some(crate::cli::MakeTarget::Zip),
|
|
1581
|
+
publisher: Some(crate::cli::PublishTarget::Local),
|
|
1582
|
+
to: Some(PathBuf::from("out/publish/local")),
|
|
909
1583
|
github_repo: None,
|
|
910
1584
|
github_tag: None,
|
|
911
1585
|
github_release_name: None,
|
|
912
1586
|
github_draft: false,
|
|
913
1587
|
github_prerelease: false,
|
|
914
|
-
github_api_url: "https://api.github.com".to_string(),
|
|
915
|
-
channel: "default".to_string(),
|
|
1588
|
+
github_api_url: Some("https://api.github.com".to_string()),
|
|
1589
|
+
channel: Some("default".to_string()),
|
|
916
1590
|
skip_make: false,
|
|
917
1591
|
force: false,
|
|
918
1592
|
dry_run,
|
|
@@ -922,8 +1596,8 @@ mod tests {
|
|
|
922
1596
|
|
|
923
1597
|
fn github_publish_args(root: PathBuf, dry_run: bool, api_url: &str) -> PublishArgs {
|
|
924
1598
|
let mut args = publish_args(root, dry_run);
|
|
925
|
-
args.publisher = crate::cli::PublishTarget::Github;
|
|
926
|
-
args.github_api_url = api_url.to_string();
|
|
1599
|
+
args.publisher = Some(crate::cli::PublishTarget::Github);
|
|
1600
|
+
args.github_api_url = Some(api_url.to_string());
|
|
927
1601
|
args
|
|
928
1602
|
}
|
|
929
1603
|
|
|
@@ -943,6 +1617,38 @@ mod tests {
|
|
|
943
1617
|
.expect("package.json should be written");
|
|
944
1618
|
}
|
|
945
1619
|
|
|
1620
|
+
fn write_package_json_with_publishers(root: &Path, publishers: &str) {
|
|
1621
|
+
fs::write(
|
|
1622
|
+
root.join("package.json"),
|
|
1623
|
+
format!(
|
|
1624
|
+
r#"{{
|
|
1625
|
+
"name":"starter-app",
|
|
1626
|
+
"version":"0.1.0",
|
|
1627
|
+
"main":"src/main.js",
|
|
1628
|
+
"devDependencies":{{"electron":"30.0.0"}},
|
|
1629
|
+
"config":{{"forge":{{"publishers":{publishers}}}}}
|
|
1630
|
+
}}"#
|
|
1631
|
+
),
|
|
1632
|
+
)
|
|
1633
|
+
.expect("package.json with publishers should be written");
|
|
1634
|
+
}
|
|
1635
|
+
|
|
1636
|
+
fn write_package_json_with_makers_and_publishers(root: &Path, makers: &str, publishers: &str) {
|
|
1637
|
+
fs::write(
|
|
1638
|
+
root.join("package.json"),
|
|
1639
|
+
format!(
|
|
1640
|
+
r#"{{
|
|
1641
|
+
"name":"starter-app",
|
|
1642
|
+
"version":"0.1.0",
|
|
1643
|
+
"main":"src/main.js",
|
|
1644
|
+
"devDependencies":{{"electron":"30.0.0"}},
|
|
1645
|
+
"config":{{"forge":{{"makers":{makers},"publishers":{publishers}}}}}
|
|
1646
|
+
}}"#
|
|
1647
|
+
),
|
|
1648
|
+
)
|
|
1649
|
+
.expect("package.json with makers and publishers should be written");
|
|
1650
|
+
}
|
|
1651
|
+
|
|
946
1652
|
fn write_app_file(root: &Path) {
|
|
947
1653
|
fs::create_dir_all(root.join("src")).expect("src should be created");
|
|
948
1654
|
fs::write(root.join("src/main.js"), "console.log('hello');")
|