@lynxship/cli 0.1.7 → 0.1.10
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/README.md +175 -19
- package/dist/android-toolchain.d.ts +23 -0
- package/dist/android-toolchain.d.ts.map +1 -0
- package/dist/android-toolchain.js +319 -0
- package/dist/artifact-build.d.ts +17 -0
- package/dist/artifact-build.d.ts.map +1 -0
- package/dist/artifact-build.js +56 -0
- package/dist/artifact-name.d.ts +1 -1
- package/dist/artifact-name.d.ts.map +1 -1
- package/dist/autolink.d.ts +3 -2
- package/dist/autolink.d.ts.map +1 -1
- package/dist/autolink.js +130 -6
- package/dist/bundle-build.d.ts +2 -0
- package/dist/bundle-build.d.ts.map +1 -1
- package/dist/bundle-build.js +11 -3
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/desktop-build.d.ts +22 -0
- package/dist/desktop-build.d.ts.map +1 -0
- package/dist/desktop-build.js +161 -0
- package/dist/desktop-signing.d.ts +14 -0
- package/dist/desktop-signing.d.ts.map +1 -0
- package/dist/desktop-signing.js +200 -0
- package/dist/guidance.d.ts.map +1 -1
- package/dist/guidance.js +113 -4
- package/dist/harmony-build.d.ts +22 -0
- package/dist/harmony-build.d.ts.map +1 -0
- package/dist/harmony-build.js +194 -0
- package/dist/index.js +333 -73
- package/dist/ios-toolchain.d.ts +19 -0
- package/dist/ios-toolchain.d.ts.map +1 -0
- package/dist/ios-toolchain.js +303 -0
- package/dist/lynx-devtool.d.ts +18 -0
- package/dist/lynx-devtool.d.ts.map +1 -0
- package/dist/lynx-devtool.js +125 -0
- package/dist/process-runner.d.ts +11 -0
- package/dist/process-runner.d.ts.map +1 -1
- package/dist/process-runner.js +29 -0
- package/dist/prompt.d.ts +1 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +6 -0
- package/dist/remote.d.ts +3 -3
- package/dist/remote.d.ts.map +1 -1
- package/dist/target-toolchain.d.ts +17 -0
- package/dist/target-toolchain.d.ts.map +1 -0
- package/dist/target-toolchain.js +122 -0
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +8 -2
- package/dist/web-build.d.ts +16 -0
- package/dist/web-build.d.ts.map +1 -0
- package/dist/web-build.js +116 -0
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { JsonRepository } from "@lynxship/db";
|
|
|
8
8
|
import { assert, createId, sha256, } from "@lynxship/contracts";
|
|
9
9
|
import { createSigningKey, signManifest, } from "@lynxship/signing";
|
|
10
10
|
import { AppStoreConnectApiProvider, GooglePlayApiProvider, SubmissionService, } from "@lynxship/submit";
|
|
11
|
-
import { DEFAULT_CONFIG, loadConfig, platformValue, } from "./config.js";
|
|
11
|
+
import { DEFAULT_CONFIG, loadConfig, platformValue, resolveProfile, } from "./config.js";
|
|
12
12
|
import { hasAndroidHost, isSupportedAndroidPlatform, runRealAndroidBuild, } from "./android-build.js";
|
|
13
13
|
import { initializeAndroidHost, suggestedAndroidApplicationId, } from "./android-host.js";
|
|
14
14
|
import { initializeIosHost, suggestedIosBundleIdentifier } from "./ios-host.js";
|
|
@@ -23,10 +23,17 @@ import { inspectAutolink, requireAutolinkReady } from "./autolink.js";
|
|
|
23
23
|
import { assertCompatibleBinaryBuild, inspectRuntimeFingerprint, } from "./runtime-fingerprint.js";
|
|
24
24
|
import { inspectOtaHost } from "./ota-doctor.js";
|
|
25
25
|
import { otaAssetName, otaAssetPaths } from "./ota-assets.js";
|
|
26
|
-
import { prompt } from "./prompt.js";
|
|
26
|
+
import { confirm, prompt } from "./prompt.js";
|
|
27
27
|
import { commandExists, packageManagerCommand, runProcess, runRspeedy, } from "./process-runner.js";
|
|
28
28
|
import { guidanceForError } from "./guidance.js";
|
|
29
29
|
import { buildLynxBundle } from "./bundle-build.js";
|
|
30
|
+
import { fixAndroidToolchain, formatAndroidToolchainFailure, inspectAndroidToolchain, } from "./android-toolchain.js";
|
|
31
|
+
import { formatIosToolchainFailure, inspectIosToolchain, } from "./ios-toolchain.js";
|
|
32
|
+
import { formatDevToolFailure, inspectLynxDevTool } from "./lynx-devtool.js";
|
|
33
|
+
import { hasHarmonyHost, runRealHarmonyBuild } from "./harmony-build.js";
|
|
34
|
+
import { hasWebConfiguration, runRealWebBuild } from "./web-build.js";
|
|
35
|
+
import { hasDesktopHost, runRealDesktopBuild } from "./desktop-build.js";
|
|
36
|
+
import { inspectDesktopTarget, inspectHarmonyTarget, inspectWebTarget, } from "./target-toolchain.js";
|
|
30
37
|
const rawArgs = process.argv.slice(2);
|
|
31
38
|
const args = [...rawArgs];
|
|
32
39
|
const ui = createCliUi(rawArgs);
|
|
@@ -134,6 +141,11 @@ function configuredProjectId(config) {
|
|
|
134
141
|
assert(typeof config.projectId === "string" && config.projectId.length > 0, "CLI_PROJECT_ID_REQUIRED", "lynxship.json must contain a generated projectId. Run `lynxship init` for a new project configuration.");
|
|
135
142
|
return config.projectId;
|
|
136
143
|
}
|
|
144
|
+
function mobilePlatformValue(value) {
|
|
145
|
+
const platform = platformValue(value);
|
|
146
|
+
assert(platform === "android" || platform === "ios", "PLATFORM_COMMAND_UNSUPPORTED", "This command supports only android or ios. Web, HarmonyOS and Desktop use their own build adapters.");
|
|
147
|
+
return platform;
|
|
148
|
+
}
|
|
137
149
|
async function renderConfigurationFooter() {
|
|
138
150
|
if (!ui.interactive || ui.options.quiet)
|
|
139
151
|
return;
|
|
@@ -191,7 +203,10 @@ async function saveState(state, repository, builds, submissions) {
|
|
|
191
203
|
let stateSaveQueue = Promise.resolve();
|
|
192
204
|
async function executeBuild(options, showResult = true) {
|
|
193
205
|
const { config, profile, platform, skipUpload, wait, local, state, repository, builds, submissions, progress: sharedProgress, progressPrefix, onProgress, onEvent, skipBundleBuild, } = options;
|
|
194
|
-
|
|
206
|
+
if (wait && !local)
|
|
207
|
+
await ensureNativeHostForBuild(platform, profile, config);
|
|
208
|
+
if (platform === "android" || platform === "ios")
|
|
209
|
+
await requireAutolinkReady(root, platform);
|
|
195
210
|
const runtime = await inspectRuntimeFingerprint(root, platform, config);
|
|
196
211
|
const job = await builds.create({
|
|
197
212
|
projectId: configuredProjectId(config),
|
|
@@ -205,7 +220,7 @@ async function executeBuild(options, showResult = true) {
|
|
|
205
220
|
ui.info(`Using profile: ${profile} · platform: ${platform}`);
|
|
206
221
|
const ownsProgress = !sharedProgress;
|
|
207
222
|
const progress = sharedProgress ??
|
|
208
|
-
ui.progress(platform
|
|
223
|
+
ui.progress(`${platform[0].toUpperCase()}${platform.slice(1)} build`);
|
|
209
224
|
const prefix = progressPrefix ? `${progressPrefix} · ` : "";
|
|
210
225
|
const reportEvent = (message) => {
|
|
211
226
|
if (onEvent)
|
|
@@ -227,12 +242,14 @@ async function executeBuild(options, showResult = true) {
|
|
|
227
242
|
const realAndroid = platform === "android" &&
|
|
228
243
|
isSupportedAndroidPlatform() &&
|
|
229
244
|
(await hasAndroidHost(root));
|
|
230
|
-
const realIos = platform === "ios" && hasIosHost(root, config
|
|
245
|
+
const realIos = platform === "ios" && hasIosHost(root, resolveProfile(config, profile));
|
|
231
246
|
if (platform === "ios" && !realIos && !local)
|
|
232
|
-
assert(false, "IOS_HOST_REQUIRED", "A macOS Xcode host is required for a real iOS build.
|
|
247
|
+
assert(false, "IOS_HOST_REQUIRED", "A macOS Xcode host is required for a real iOS build. Run `lynxship ios host init --bundle-identifier <id>` or pass --bundle-identifier <id> to build.");
|
|
233
248
|
if (platform === "android" && !realAndroid && !local)
|
|
234
|
-
assert(false, "ANDROID_HOST_REQUIRED", "This project has no Android Gradle host.
|
|
249
|
+
assert(false, "ANDROID_HOST_REQUIRED", "This project has no Android Gradle host. Run `lynxship android host init --application-id <id>` or pass --application-id <id> to build. `lynxship dev` remains available for Lynx Explorer; `--local` does not create an APK.");
|
|
235
250
|
if (realAndroid) {
|
|
251
|
+
const toolchain = await inspectAndroidToolchain(root);
|
|
252
|
+
assert(toolchain.ok, "ANDROID_TOOLCHAIN_REQUIRED", `Android toolchain is not ready: ${formatAndroidToolchainFailure(toolchain)}`);
|
|
236
253
|
await runRealAndroidBuild(job, {
|
|
237
254
|
root,
|
|
238
255
|
profile: config.build?.[profile] ?? {},
|
|
@@ -244,6 +261,8 @@ async function executeBuild(options, showResult = true) {
|
|
|
244
261
|
});
|
|
245
262
|
}
|
|
246
263
|
else if (realIos) {
|
|
264
|
+
const toolchain = await inspectIosToolchain(root, resolveProfile(config, profile));
|
|
265
|
+
assert(toolchain.ok, "IOS_TOOLCHAIN_REQUIRED", `iOS toolchain is not ready: ${formatIosToolchainFailure(toolchain)}`);
|
|
247
266
|
await runRealIosBuild(job, {
|
|
248
267
|
root,
|
|
249
268
|
profile: config.build?.[profile] ?? {},
|
|
@@ -254,6 +273,40 @@ async function executeBuild(options, showResult = true) {
|
|
|
254
273
|
onProgress: reportProgress,
|
|
255
274
|
});
|
|
256
275
|
}
|
|
276
|
+
else if (!local && platform === "web") {
|
|
277
|
+
await runRealWebBuild(job, {
|
|
278
|
+
root,
|
|
279
|
+
profile: resolveProfile(config, profile),
|
|
280
|
+
uploadArtifacts: !skipUpload,
|
|
281
|
+
skipBundleBuild,
|
|
282
|
+
quiet: json,
|
|
283
|
+
onEvent: reportEvent,
|
|
284
|
+
onProgress: reportProgress,
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
else if (!local && platform === "harmony") {
|
|
288
|
+
await runRealHarmonyBuild(job, {
|
|
289
|
+
root,
|
|
290
|
+
profile: resolveProfile(config, profile),
|
|
291
|
+
uploadArtifacts: !skipUpload,
|
|
292
|
+
skipBundleBuild,
|
|
293
|
+
quiet: json,
|
|
294
|
+
onEvent: reportEvent,
|
|
295
|
+
onProgress: reportProgress,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
else if (!local && platform === "desktop") {
|
|
299
|
+
await runRealDesktopBuild(job, {
|
|
300
|
+
root,
|
|
301
|
+
profile: resolveProfile(config, profile),
|
|
302
|
+
uploadArtifacts: !skipUpload,
|
|
303
|
+
allowUnsigned: options.allowUnsigned,
|
|
304
|
+
skipBundleBuild,
|
|
305
|
+
quiet: json,
|
|
306
|
+
onEvent: reportEvent,
|
|
307
|
+
onProgress: reportProgress,
|
|
308
|
+
});
|
|
309
|
+
}
|
|
257
310
|
else {
|
|
258
311
|
await builds.run(job.id);
|
|
259
312
|
}
|
|
@@ -275,7 +328,7 @@ async function executeBuild(options, showResult = true) {
|
|
|
275
328
|
const result = builds.get(job.id);
|
|
276
329
|
if (showResult) {
|
|
277
330
|
printValue(result, {
|
|
278
|
-
title: `${platform
|
|
331
|
+
title: `${platform[0].toUpperCase()}${platform.slice(1)} build result`,
|
|
279
332
|
rows: [
|
|
280
333
|
{ label: "Build ID", value: result.id, valueColor: "purple" },
|
|
281
334
|
{ label: "Platform", value: result.platform, valueColor: "blue" },
|
|
@@ -298,12 +351,12 @@ async function executeBuild(options, showResult = true) {
|
|
|
298
351
|
async function buildSharedLynxBundle() {
|
|
299
352
|
const progress = ui.progress("Shared Lynx bundle");
|
|
300
353
|
try {
|
|
301
|
-
progress.update(undefined, "Building Lynx bundle once for
|
|
354
|
+
progress.update(undefined, "Building the shared Lynx bundle once for native targets…");
|
|
302
355
|
await buildLynxBundle(root, {
|
|
303
356
|
quiet: json,
|
|
304
357
|
onOutput: (message) => progress.event(message),
|
|
305
358
|
});
|
|
306
|
-
progress.update(100, "Shared Lynx bundle ready");
|
|
359
|
+
progress.update(100, "Shared native Lynx bundle ready");
|
|
307
360
|
}
|
|
308
361
|
finally {
|
|
309
362
|
progress.stop();
|
|
@@ -344,6 +397,9 @@ function commandTitle(command) {
|
|
|
344
397
|
storage: "Cloudflare R2 setup",
|
|
345
398
|
android: "Android signing setup",
|
|
346
399
|
store: "App store submission setup",
|
|
400
|
+
devtool: "Lynx DevTool diagnostics",
|
|
401
|
+
trace: "Lynx Trace diagnostics",
|
|
402
|
+
recorder: "Lynx Recorder diagnostics",
|
|
347
403
|
}[command] ?? command);
|
|
348
404
|
}
|
|
349
405
|
function helpText() {
|
|
@@ -356,17 +412,20 @@ Commands:
|
|
|
356
412
|
preview Preview the production Lynx bundle locally
|
|
357
413
|
inspect Inspect Rspeedy/Rspack configuration
|
|
358
414
|
profile Build with Rspack profiling enabled
|
|
415
|
+
devtool doctor Check Lynx DevTool integration and dev runtime
|
|
416
|
+
trace doctor Check Lynx Trace prerequisites
|
|
417
|
+
recorder doctor Check Lynx Recorder prerequisites
|
|
359
418
|
autolink check Check Lynx native-library Autolink wiring
|
|
360
419
|
autolink codegen Run the project's Native Module codegen script
|
|
361
420
|
ota doctor Check native OTA host integration
|
|
362
|
-
run Install an artifact on an Android/iOS target
|
|
363
|
-
logs Stream Android/iOS native logs
|
|
421
|
+
run Install an artifact on an Android/iOS/HarmonyOS target
|
|
422
|
+
logs Stream Android/iOS/HarmonyOS native logs
|
|
364
423
|
build create Create a local/cloud build job
|
|
365
424
|
build list List build jobs
|
|
366
425
|
build status <id> Show one build job
|
|
367
426
|
build cancel <id> Cancel a build job
|
|
368
427
|
build retry <id> Retry a failed build job
|
|
369
|
-
build all Build Android
|
|
428
|
+
build all Build Android, iOS, HarmonyOS, Web and Desktop
|
|
370
429
|
submit Submit the latest successful build
|
|
371
430
|
update Upload and publish a signed OTA update
|
|
372
431
|
update rollback Roll back an OTA channel to a previous release
|
|
@@ -379,10 +438,11 @@ Commands:
|
|
|
379
438
|
store configure Configure Google Play or App Store Connect submission
|
|
380
439
|
|
|
381
440
|
Build options:
|
|
382
|
-
--platform <p> Target android, ios or all
|
|
441
|
+
--platform <p> Target android, ios, harmony, web, desktop or all (default: android)
|
|
383
442
|
--profile <name> Build profile (default: production)
|
|
384
443
|
--no-wait Queue the build without executing it locally
|
|
385
|
-
--no-upload Keep the
|
|
444
|
+
--no-upload Keep the artifact local and skip R2 (CI verification)
|
|
445
|
+
--allow-unsigned Allow an unsigned Desktop artifact only with --no-upload (local tests)
|
|
386
446
|
--local Use the contract-only build path for tests
|
|
387
447
|
|
|
388
448
|
Submit options:
|
|
@@ -405,12 +465,13 @@ Rollback options:
|
|
|
405
465
|
|
|
406
466
|
Device and diagnostics options:
|
|
407
467
|
doctor --platform <p> Check the local toolchain (default: android)
|
|
468
|
+
doctor --fix Install missing Android SDK packages after confirmation
|
|
408
469
|
autolink check --platform <p>
|
|
409
470
|
Check native-library wiring (default: android)
|
|
410
471
|
ota doctor --platform <p>
|
|
411
472
|
Check OTA host integration (default: android)
|
|
412
|
-
run --artifact <path> Install a specific APK, IPA or
|
|
413
|
-
run --device <id> Select
|
|
473
|
+
run --artifact <path> Install a specific APK, IPA, app or signed HAP
|
|
474
|
+
run --device <id> Select an Android, iOS or HarmonyOS device
|
|
414
475
|
run --simulator Install an iOS .app with simctl
|
|
415
476
|
logs --device <id> Select the device or simulator for native logs
|
|
416
477
|
|
|
@@ -469,6 +530,56 @@ async function initializeBuildProject() {
|
|
|
469
530
|
await initializeProject();
|
|
470
531
|
ui.success("Created lynxship.json");
|
|
471
532
|
}
|
|
533
|
+
async function ensureNativeHostForBuild(platform, profile, config) {
|
|
534
|
+
if (platform === "android") {
|
|
535
|
+
assert(isSupportedAndroidPlatform(), "ANDROID_PLATFORM_UNSUPPORTED", "Android builds are supported on Windows, macOS, and Linux.");
|
|
536
|
+
if (await hasAndroidHost(root))
|
|
537
|
+
return;
|
|
538
|
+
assert(!(await exists(join(root, "android"))), "ANDROID_HOST_EXISTS", "An android/ directory exists but does not contain a usable Gradle host. LynxShip will not overwrite it; repair it or remove it deliberately, then rerun the build.");
|
|
539
|
+
const suggestedId = suggestedAndroidApplicationId(root);
|
|
540
|
+
const applicationId = flag("--application-id") ??
|
|
541
|
+
(ui.interactive
|
|
542
|
+
? await prompt("Android application ID", suggestedId)
|
|
543
|
+
: "");
|
|
544
|
+
assert(applicationId, "ANDROID_HOST_REQUIRED", `No Android Gradle host exists. Run \`lynxship android host init --application-id ${suggestedId}\` or pass --application-id <id> to build in non-interactive mode.`);
|
|
545
|
+
ui.info("No Android host found. Creating the official LynxShip host before the build…");
|
|
546
|
+
const result = await initializeAndroidHost(root, {
|
|
547
|
+
applicationId,
|
|
548
|
+
appName: basename(root),
|
|
549
|
+
});
|
|
550
|
+
ui.success(`Android host created: ${result.directory}`);
|
|
551
|
+
return;
|
|
552
|
+
}
|
|
553
|
+
if (platform === "web") {
|
|
554
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
555
|
+
assert(hasWebConfiguration(root) || Boolean(resolvedProfile.web?.script), "WEB_CONFIGURATION_REQUIRED", "No Lynx Web configuration was detected. Add environments.web to lynx.config.* or configure build.<profile>.web.script.");
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (platform === "harmony") {
|
|
559
|
+
assert(hasHarmonyHost(root), "HARMONY_HOST_REQUIRED", "No complete HarmonyOS host was found. Add an official Lynx Harmony host under harmony/ before building.");
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
if (platform === "desktop") {
|
|
563
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
564
|
+
assert(await hasDesktopHost(root, resolvedProfile), "DESKTOP_HOST_REQUIRED", "No Lynxtron desktop host was found. Use the official Lynxtron template or configure a pack script before building.");
|
|
565
|
+
return;
|
|
566
|
+
}
|
|
567
|
+
assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS hosts and IPA builds require macOS with Xcode. Run this build on macOS or a macOS CI worker.");
|
|
568
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
569
|
+
if (hasIosHost(root, resolvedProfile))
|
|
570
|
+
return;
|
|
571
|
+
assert(!(await exists(join(root, "ios"))), "IOS_HOST_EXISTS", "An ios/ directory exists but does not contain a usable Xcode host. LynxShip will not overwrite it; repair it deliberately, then rerun the build.");
|
|
572
|
+
const suggestedId = suggestedIosBundleIdentifier(root);
|
|
573
|
+
const bundleIdentifier = flag("--bundle-identifier") ??
|
|
574
|
+
(ui.interactive ? await prompt("iOS bundle identifier", suggestedId) : "");
|
|
575
|
+
assert(bundleIdentifier, "IOS_HOST_REQUIRED", `No iOS host exists. Run \`lynxship ios host init --bundle-identifier ${suggestedId}\` or pass --bundle-identifier <id> to build in non-interactive mode.`);
|
|
576
|
+
ui.info("No iOS host found. Creating the official LynxShip host before the build…");
|
|
577
|
+
const result = await initializeIosHost(root, {
|
|
578
|
+
bundleIdentifier,
|
|
579
|
+
appName: basename(root),
|
|
580
|
+
});
|
|
581
|
+
ui.success(`iOS host created: ${result.directory}`);
|
|
582
|
+
}
|
|
472
583
|
function forwardedToolArgs(values) {
|
|
473
584
|
const result = [];
|
|
474
585
|
const valueFlags = new Set([
|
|
@@ -541,6 +652,33 @@ async function runAutolinkCodegen() {
|
|
|
541
652
|
done: "Native Module specifications were generated successfully.",
|
|
542
653
|
});
|
|
543
654
|
}
|
|
655
|
+
async function runDevToolDoctor(kind) {
|
|
656
|
+
await initializeBuildProject();
|
|
657
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
658
|
+
const status = await inspectLynxDevTool(root, platform);
|
|
659
|
+
const ready = kind === "devtool"
|
|
660
|
+
? status.ok
|
|
661
|
+
: kind === "trace"
|
|
662
|
+
? status.traceReady
|
|
663
|
+
: status.recorderReady;
|
|
664
|
+
if (!ready)
|
|
665
|
+
process.exitCode = 1;
|
|
666
|
+
printValue({ ...status, requested: kind, ready }, {
|
|
667
|
+
title: `Lynx ${kind === "devtool" ? "DevTool" : kind[0].toUpperCase() + kind.slice(1)} · ${platform}`,
|
|
668
|
+
rows: status.checks.map((item) => ({
|
|
669
|
+
label: item.name,
|
|
670
|
+
value: `${item.status} · ${item.value}${item.fix && item.status !== "pass" ? ` · fix: ${item.fix}` : ""}`,
|
|
671
|
+
valueColor: item.status === "pass"
|
|
672
|
+
? "green"
|
|
673
|
+
: item.status === "warn"
|
|
674
|
+
? "yellow"
|
|
675
|
+
: "red",
|
|
676
|
+
})),
|
|
677
|
+
done: ready
|
|
678
|
+
? `${kind} prerequisites are ready. Open Lynx DevTool Desktop and connect the target device by USB.`
|
|
679
|
+
: `Lynx ${kind} prerequisites are incomplete: ${formatDevToolFailure(status) || "review the failed checks"}`,
|
|
680
|
+
});
|
|
681
|
+
}
|
|
544
682
|
async function runDevice() {
|
|
545
683
|
const platform = platformValue(flag("--platform", "android"));
|
|
546
684
|
const artifact = flag("--artifact");
|
|
@@ -563,7 +701,7 @@ async function runDevice() {
|
|
|
563
701
|
onOutput: (line) => ui.info(`│ ${line}`),
|
|
564
702
|
});
|
|
565
703
|
}
|
|
566
|
-
else {
|
|
704
|
+
else if (platform === "ios") {
|
|
567
705
|
assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS device/simulator commands require macOS");
|
|
568
706
|
assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found in PATH");
|
|
569
707
|
const device = flag("--device", "booted");
|
|
@@ -592,6 +730,23 @@ async function runDevice() {
|
|
|
592
730
|
});
|
|
593
731
|
}
|
|
594
732
|
}
|
|
733
|
+
else if (platform === "harmony") {
|
|
734
|
+
assert(commandExists("hdc"), "HARMONY_HDC_REQUIRED", "hdc was not found in PATH. Install the OpenHarmony SDK platform tools before installing a HAP.");
|
|
735
|
+
assert(artifactPath.endsWith(".hap"), "DEVICE_ARTIFACT_INVALID", "HarmonyOS run requires a signed .hap artifact.");
|
|
736
|
+
const device = flag("--device");
|
|
737
|
+
await runProcess("hdc", device
|
|
738
|
+
? ["-t", device, "install", "-r", artifactPath]
|
|
739
|
+
: ["install", "-r", artifactPath], {
|
|
740
|
+
cwd: root,
|
|
741
|
+
quiet: json,
|
|
742
|
+
onOutput: (line) => ui.info(`│ ${line}`),
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
746
|
+
assert(false, "TARGET_RUN_UNSUPPORTED", platform === "web"
|
|
747
|
+
? "Web artifacts are previewed with `lynxship preview` or served by the project; they are not installed on a device."
|
|
748
|
+
: "Desktop installers are launched by the operating system after packaging; LynxShip does not claim a cross-platform install command.");
|
|
749
|
+
}
|
|
595
750
|
printValue({ status: "installed", platform, artifact: artifactPath }, {
|
|
596
751
|
title: "Device install",
|
|
597
752
|
rows: [{ label: "Artifact", value: artifactPath, valueColor: "green" }],
|
|
@@ -600,7 +755,7 @@ async function runDevice() {
|
|
|
600
755
|
}
|
|
601
756
|
async function streamNativeLogs() {
|
|
602
757
|
const platform = platformValue(flag("--platform", "android"));
|
|
603
|
-
const device = flag("--device", platform === "
|
|
758
|
+
const device = flag("--device", platform === "ios" ? "booted" : undefined);
|
|
604
759
|
if (platform === "android") {
|
|
605
760
|
assert(commandExists("adb"), "ANDROID_ADB_REQUIRED", "adb was not found in PATH");
|
|
606
761
|
await runProcess("adb", device ? ["-s", device, "logcat"] : ["logcat"], {
|
|
@@ -609,7 +764,7 @@ async function streamNativeLogs() {
|
|
|
609
764
|
onOutput: (line) => ui.info(`│ ${line}`),
|
|
610
765
|
});
|
|
611
766
|
}
|
|
612
|
-
else {
|
|
767
|
+
else if (platform === "ios") {
|
|
613
768
|
assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS logs require macOS");
|
|
614
769
|
assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found in PATH");
|
|
615
770
|
assert(device !== "booted", "IOS_DEVICE_LOGS_UNSUPPORTED", "Use --device <simulator-identifier> for iOS simulator logs.");
|
|
@@ -625,6 +780,22 @@ async function streamNativeLogs() {
|
|
|
625
780
|
"debug",
|
|
626
781
|
], { cwd: root, quiet: json, onOutput: (line) => ui.info(`│ ${line}`) });
|
|
627
782
|
}
|
|
783
|
+
else if (platform === "harmony") {
|
|
784
|
+
assert(commandExists("hdc"), "HARMONY_HDC_REQUIRED", "hdc was not found in PATH. Install the OpenHarmony SDK platform tools before streaming logs.");
|
|
785
|
+
const argsForHdc = device
|
|
786
|
+
? ["-t", device, "shell", "hilog"]
|
|
787
|
+
: ["shell", "hilog"];
|
|
788
|
+
await runProcess("hdc", argsForHdc, {
|
|
789
|
+
cwd: root,
|
|
790
|
+
quiet: json,
|
|
791
|
+
onOutput: (line) => ui.info(`│ ${line}`),
|
|
792
|
+
});
|
|
793
|
+
}
|
|
794
|
+
else {
|
|
795
|
+
assert(false, "TARGET_LOGS_UNSUPPORTED", platform === "web"
|
|
796
|
+
? "Web logs are emitted by the browser/runtime console; use lynxship dev or the project's Web DevTools."
|
|
797
|
+
: "Desktop logs are emitted by the packaged runtime; use the target OS logging tools.");
|
|
798
|
+
}
|
|
628
799
|
}
|
|
629
800
|
async function main() {
|
|
630
801
|
const command = args.shift() ?? "help";
|
|
@@ -678,12 +849,42 @@ async function main() {
|
|
|
678
849
|
const config = await loadConfig(root);
|
|
679
850
|
const configuration = await readConfigurationStatus();
|
|
680
851
|
const doctorPlatform = platformValue(flag("--platform", "android"));
|
|
681
|
-
const autolink =
|
|
682
|
-
|
|
852
|
+
const autolink = doctorPlatform === "android" || doctorPlatform === "ios"
|
|
853
|
+
? await inspectAutolink(root)
|
|
854
|
+
: undefined;
|
|
855
|
+
const autolinkForPlatform = autolink && (doctorPlatform === "android" || doctorPlatform === "ios")
|
|
856
|
+
? autolink[doctorPlatform]
|
|
857
|
+
: undefined;
|
|
683
858
|
const lockfile = await findLockfile(root);
|
|
684
859
|
const credentialStore = credentialStorageDescription();
|
|
685
860
|
const nativeCredentialStore = !credentialStore.includes("owner-only");
|
|
686
861
|
const androidHost = doctorPlatform === "android" ? await hasAndroidHost(root) : false;
|
|
862
|
+
const doctorProfileName = flag("--profile", "production");
|
|
863
|
+
const doctorProfile = config.build?.[doctorProfileName]
|
|
864
|
+
? resolveProfile(config, doctorProfileName)
|
|
865
|
+
: {
|
|
866
|
+
name: doctorProfileName,
|
|
867
|
+
...(DEFAULT_CONFIG.build?.production ?? {}),
|
|
868
|
+
};
|
|
869
|
+
const targetToolchain = doctorPlatform === "web"
|
|
870
|
+
? await inspectWebTarget(root, doctorProfile)
|
|
871
|
+
: doctorPlatform === "harmony"
|
|
872
|
+
? await inspectHarmonyTarget(root, doctorProfile)
|
|
873
|
+
: doctorPlatform === "desktop"
|
|
874
|
+
? await inspectDesktopTarget(root, doctorProfile)
|
|
875
|
+
: undefined;
|
|
876
|
+
assert(!args.includes("--fix") || doctorPlatform === "android", "CLI_DOCTOR_FIX_PLATFORM", "`doctor --fix` currently repairs Android SDK packages; run it with `--platform android`.");
|
|
877
|
+
assert(!args.includes("--fix") || ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run `lynxship doctor --platform android --fix` in an interactive terminal.");
|
|
878
|
+
let androidToolchain = doctorPlatform === "android"
|
|
879
|
+
? await inspectAndroidToolchain(root)
|
|
880
|
+
: undefined;
|
|
881
|
+
const iosToolchain = doctorPlatform === "ios"
|
|
882
|
+
? await inspectIosToolchain(root, doctorProfile)
|
|
883
|
+
: undefined;
|
|
884
|
+
if (androidToolchain && args.includes("--fix")) {
|
|
885
|
+
await fixAndroidToolchain(root, androidToolchain, (message) => confirm(message), (line) => ui.info(line));
|
|
886
|
+
androidToolchain = await inspectAndroidToolchain(root);
|
|
887
|
+
}
|
|
687
888
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
688
889
|
const nodeSupported = nodeMajor >= 22;
|
|
689
890
|
const nodeRecommended = nodeMajor % 2 === 0;
|
|
@@ -732,8 +933,34 @@ async function main() {
|
|
|
732
933
|
? "Gradle host found"
|
|
733
934
|
: "missing · fix: lynxship android host init --application-id com.example.myapp",
|
|
734
935
|
},
|
|
936
|
+
...(androidToolchain?.checks ?? []).map((toolchainCheck) => ({
|
|
937
|
+
name: toolchainCheck.name,
|
|
938
|
+
ok: toolchainCheck.ok,
|
|
939
|
+
status: toolchainCheck.status,
|
|
940
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
941
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
942
|
+
: toolchainCheck.value,
|
|
943
|
+
})),
|
|
735
944
|
]
|
|
736
|
-
:
|
|
945
|
+
: doctorPlatform === "ios"
|
|
946
|
+
? (iosToolchain?.checks ?? [])
|
|
947
|
+
.filter((toolchainCheck) => toolchainCheck.name !== "ios-host")
|
|
948
|
+
.map((toolchainCheck) => ({
|
|
949
|
+
name: toolchainCheck.name,
|
|
950
|
+
ok: toolchainCheck.ok,
|
|
951
|
+
status: toolchainCheck.status,
|
|
952
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
953
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
954
|
+
: toolchainCheck.value,
|
|
955
|
+
}))
|
|
956
|
+
: (targetToolchain?.checks ?? []).map((toolchainCheck) => ({
|
|
957
|
+
name: toolchainCheck.name,
|
|
958
|
+
ok: toolchainCheck.ok,
|
|
959
|
+
status: toolchainCheck.status,
|
|
960
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
961
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
962
|
+
: toolchainCheck.value,
|
|
963
|
+
}))),
|
|
737
964
|
{
|
|
738
965
|
name: "cloudflare-r2",
|
|
739
966
|
ok: configuration.r2,
|
|
@@ -742,32 +969,47 @@ async function main() {
|
|
|
742
969
|
? "configured"
|
|
743
970
|
: "missing · fix: lynxship storage configure",
|
|
744
971
|
},
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
972
|
+
...(doctorPlatform === "android"
|
|
973
|
+
? [
|
|
974
|
+
{
|
|
975
|
+
name: "android-signing",
|
|
976
|
+
ok: configuration.android,
|
|
977
|
+
status: configuration.android
|
|
978
|
+
? "pass"
|
|
979
|
+
: "fail",
|
|
980
|
+
value: configuration.android
|
|
981
|
+
? "configured"
|
|
982
|
+
: "missing · fix: lynxship android configure",
|
|
983
|
+
},
|
|
984
|
+
]
|
|
985
|
+
: doctorPlatform === "ios"
|
|
986
|
+
? [
|
|
987
|
+
{
|
|
988
|
+
name: "ios-host",
|
|
989
|
+
ok: process.platform === "darwin" && hasIosHost(root),
|
|
990
|
+
status: process.platform === "darwin" && hasIosHost(root)
|
|
991
|
+
? "pass"
|
|
992
|
+
: "fail",
|
|
993
|
+
value: process.platform === "darwin" && hasIosHost(root)
|
|
994
|
+
? "Xcode host found"
|
|
995
|
+
: "missing · fix: use macOS, then lynxship ios host init --bundle-identifier com.example.myapp",
|
|
996
|
+
},
|
|
997
|
+
]
|
|
998
|
+
: []),
|
|
999
|
+
...(autolinkForPlatform
|
|
1000
|
+
? [
|
|
1001
|
+
{
|
|
1002
|
+
name: `lynx-autolink-${doctorPlatform}`,
|
|
1003
|
+
ok: autolinkForPlatform.ready,
|
|
1004
|
+
status: autolinkForPlatform.ready
|
|
1005
|
+
? "pass"
|
|
1006
|
+
: "fail",
|
|
1007
|
+
value: autolinkForPlatform.ready
|
|
1008
|
+
? autolinkForPlatform.reason
|
|
1009
|
+
: `${autolinkForPlatform.reason} · fix: install the native plugin, then run autolink codegen`,
|
|
1010
|
+
},
|
|
1011
|
+
]
|
|
1012
|
+
: []),
|
|
771
1013
|
];
|
|
772
1014
|
const result = {
|
|
773
1015
|
ok: checks.every((check) => check.status !== "fail"),
|
|
@@ -778,6 +1020,8 @@ async function main() {
|
|
|
778
1020
|
ui.warn("One or more environment checks failed");
|
|
779
1021
|
else if (hasWarnings)
|
|
780
1022
|
ui.warn("Environment is usable, but one recommendation needs attention");
|
|
1023
|
+
if (!result.ok)
|
|
1024
|
+
process.exitCode = 1;
|
|
781
1025
|
printValue(result, {
|
|
782
1026
|
title: "Doctor result",
|
|
783
1027
|
rows: checks.map((check) => ({
|
|
@@ -808,6 +1052,12 @@ async function main() {
|
|
|
808
1052
|
});
|
|
809
1053
|
return;
|
|
810
1054
|
}
|
|
1055
|
+
if (["devtool", "trace", "recorder"].includes(command)) {
|
|
1056
|
+
const subcommand = args.shift() ?? "doctor";
|
|
1057
|
+
assert(subcommand === "doctor", "CLI_DEVTOOL_COMMAND", "Use `lynxship devtool doctor`, `lynxship trace doctor` or `lynxship recorder doctor`.");
|
|
1058
|
+
await runDevToolDoctor(command);
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
811
1061
|
if (command === "autolink") {
|
|
812
1062
|
const subcommand = args.shift() ?? "check";
|
|
813
1063
|
assert(["check", "codegen"].includes(subcommand), "CLI_AUTOLINK_COMMAND", "Use `lynxship autolink check` or `lynxship autolink codegen`");
|
|
@@ -815,7 +1065,7 @@ async function main() {
|
|
|
815
1065
|
await runAutolinkCodegen();
|
|
816
1066
|
return;
|
|
817
1067
|
}
|
|
818
|
-
const platform =
|
|
1068
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
819
1069
|
const status = (await inspectAutolink(root))[platform];
|
|
820
1070
|
printValue(status, {
|
|
821
1071
|
title: `Lynx Autolink · ${platform}`,
|
|
@@ -844,7 +1094,7 @@ async function main() {
|
|
|
844
1094
|
}
|
|
845
1095
|
if (command === "ota") {
|
|
846
1096
|
assert((args.shift() ?? "doctor") === "doctor", "CLI_OTA_COMMAND", "Only `lynxship ota doctor` is available");
|
|
847
|
-
const platform =
|
|
1097
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
848
1098
|
const status = await inspectOtaHost(root, platform);
|
|
849
1099
|
printValue(status, {
|
|
850
1100
|
title: `OTA host · ${platform}`,
|
|
@@ -1026,7 +1276,7 @@ async function main() {
|
|
|
1026
1276
|
if (command === "store") {
|
|
1027
1277
|
assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run store configure in an interactive terminal");
|
|
1028
1278
|
assert((args.shift() ?? "configure") === "configure", "CLI_STORE_COMMAND", "Only store configure is available");
|
|
1029
|
-
const platform =
|
|
1279
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1030
1280
|
ui.info(platform === "android"
|
|
1031
1281
|
? "Configuring Google Play submission. Secret fields will stay invisible…"
|
|
1032
1282
|
: "Configuring App Store Connect submission. Private key contents stay protected…");
|
|
@@ -1066,7 +1316,7 @@ async function main() {
|
|
|
1066
1316
|
const { state, repository, builds, submissions } = await loadState();
|
|
1067
1317
|
if (command === "submit") {
|
|
1068
1318
|
const config = await loadConfig(root);
|
|
1069
|
-
const platform =
|
|
1319
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1070
1320
|
await requireOperationalConfiguration(platform);
|
|
1071
1321
|
const credentials = await loadCredentials(root);
|
|
1072
1322
|
const localMode = args.includes("--local") || process.env.LYNXSHIP_SUBMIT_MODE === "mock";
|
|
@@ -1151,7 +1401,7 @@ async function main() {
|
|
|
1151
1401
|
}
|
|
1152
1402
|
if (command === "update" && args[0] !== "rollback") {
|
|
1153
1403
|
const config = await loadConfig(root);
|
|
1154
|
-
const platform =
|
|
1404
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1155
1405
|
await requireOperationalConfiguration(platform);
|
|
1156
1406
|
const projectId = configuredProjectId(config);
|
|
1157
1407
|
const localMode = args.includes("--local") || process.env.LYNXSHIP_SUBMIT_MODE === "mock";
|
|
@@ -1273,7 +1523,7 @@ async function main() {
|
|
|
1273
1523
|
if (command === "update")
|
|
1274
1524
|
args.shift();
|
|
1275
1525
|
const config = await loadConfig(root);
|
|
1276
|
-
const platform =
|
|
1526
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1277
1527
|
const releaseId = flag("--release-id");
|
|
1278
1528
|
const reason = flag("--reason");
|
|
1279
1529
|
const channel = config.update?.channel ?? "production";
|
|
@@ -1345,6 +1595,8 @@ async function main() {
|
|
|
1345
1595
|
const buildAll = subcommand === "all" || platformArgument === "all";
|
|
1346
1596
|
const platform = buildAll ? "android" : platformValue(platformArgument);
|
|
1347
1597
|
const skipUpload = args.includes("--no-upload");
|
|
1598
|
+
const allowUnsigned = args.includes("--allow-unsigned");
|
|
1599
|
+
assert(!allowUnsigned || skipUpload, "CLI_UNSIGNED_UPLOAD_BLOCKED", "--allow-unsigned is only available with --no-upload and can never upload an unsigned Desktop artifact.");
|
|
1348
1600
|
await requireOperationalConfiguration(platform, { requireR2: !skipUpload });
|
|
1349
1601
|
if (subcommand === "list") {
|
|
1350
1602
|
printValue(builds.list());
|
|
@@ -1372,11 +1624,13 @@ async function main() {
|
|
|
1372
1624
|
const profile = flag("--profile", "production");
|
|
1373
1625
|
const wait = !args.includes("--no-wait");
|
|
1374
1626
|
const local = args.includes("--local");
|
|
1375
|
-
const platforms = buildAll
|
|
1627
|
+
const platforms = buildAll
|
|
1628
|
+
? ["android", "ios", "harmony", "web", "desktop"]
|
|
1629
|
+
: [platform];
|
|
1376
1630
|
if (buildAll && wait && !local) {
|
|
1377
|
-
assert(process.platform === "darwin", "BUILD_ALL_MACOS_REQUIRED", "A real build
|
|
1378
|
-
|
|
1379
|
-
|
|
1631
|
+
assert(process.platform === "darwin", "BUILD_ALL_MACOS_REQUIRED", "A real all-target build includes iOS and therefore requires macOS locally. Run supported targets individually on Windows/Linux, or use a macOS CI worker for the complete matrix.");
|
|
1632
|
+
for (const target of platforms)
|
|
1633
|
+
await ensureNativeHostForBuild(target, profile, config);
|
|
1380
1634
|
}
|
|
1381
1635
|
if (!buildAll) {
|
|
1382
1636
|
await executeBuild({
|
|
@@ -1384,6 +1638,7 @@ async function main() {
|
|
|
1384
1638
|
profile,
|
|
1385
1639
|
platform,
|
|
1386
1640
|
skipUpload,
|
|
1641
|
+
allowUnsigned,
|
|
1387
1642
|
wait,
|
|
1388
1643
|
local,
|
|
1389
1644
|
state,
|
|
@@ -1395,21 +1650,24 @@ async function main() {
|
|
|
1395
1650
|
}
|
|
1396
1651
|
if (buildAll && wait && !local)
|
|
1397
1652
|
await buildSharedLynxBundle();
|
|
1398
|
-
const progress = ui.progress("
|
|
1399
|
-
const progressValues = {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1653
|
+
const progress = ui.progress("All Lynx targets build");
|
|
1654
|
+
const progressValues = {};
|
|
1655
|
+
const progressLabels = {};
|
|
1656
|
+
const platformName = (target) => target === "android"
|
|
1657
|
+
? "Android"
|
|
1658
|
+
: target === "ios"
|
|
1659
|
+
? "iOS"
|
|
1660
|
+
: target === "harmony"
|
|
1661
|
+
? "HarmonyOS"
|
|
1662
|
+
: target === "web"
|
|
1663
|
+
? "Web"
|
|
1664
|
+
: "Desktop";
|
|
1408
1665
|
const outcomes = await Promise.allSettled(platforms.map((target) => executeBuild({
|
|
1409
1666
|
config,
|
|
1410
1667
|
profile,
|
|
1411
1668
|
platform: target,
|
|
1412
1669
|
skipUpload,
|
|
1670
|
+
allowUnsigned,
|
|
1413
1671
|
wait,
|
|
1414
1672
|
local,
|
|
1415
1673
|
state,
|
|
@@ -1418,18 +1676,18 @@ async function main() {
|
|
|
1418
1676
|
submissions,
|
|
1419
1677
|
progress,
|
|
1420
1678
|
progressPrefix: platformName(target),
|
|
1421
|
-
skipBundleBuild: wait && !local,
|
|
1679
|
+
skipBundleBuild: wait && !local && target !== "web",
|
|
1422
1680
|
onEvent: (message) => progress.event(`${platformName(target)} · ${message}`),
|
|
1423
1681
|
onProgress: (value, label) => {
|
|
1424
1682
|
if (value !== undefined)
|
|
1425
1683
|
progressValues[target] = value;
|
|
1426
1684
|
if (label)
|
|
1427
1685
|
progressLabels[target] = label;
|
|
1428
|
-
const average = (
|
|
1686
|
+
const average = platforms.reduce((total, current) => total + (progressValues[current] ?? 0), 0) / platforms.length;
|
|
1429
1687
|
progress.update(average, `${platformName(target)} · ${progressLabels[target]}`);
|
|
1430
1688
|
},
|
|
1431
1689
|
}, false)));
|
|
1432
|
-
progress.update(100, "
|
|
1690
|
+
progress.update(100, "All Lynx target builds finished");
|
|
1433
1691
|
progress.stop();
|
|
1434
1692
|
const summaryBuilds = outcomes.map((outcome, index) => {
|
|
1435
1693
|
if (outcome.status === "fulfilled")
|
|
@@ -1466,7 +1724,7 @@ async function main() {
|
|
|
1466
1724
|
})),
|
|
1467
1725
|
done: failed
|
|
1468
1726
|
? "At least one platform build failed. Review its events and retry that platform."
|
|
1469
|
-
: "
|
|
1727
|
+
: "All Lynx target build jobs completed. Submit supported store artifacts separately.",
|
|
1470
1728
|
});
|
|
1471
1729
|
for (const result of summaryBuilds)
|
|
1472
1730
|
if (result.state === "success" &&
|
|
@@ -1482,6 +1740,8 @@ function exitCode(error) {
|
|
|
1482
1740
|
return 2;
|
|
1483
1741
|
if (code === "BUILD_SIGNING_REQUIRED")
|
|
1484
1742
|
return 2;
|
|
1743
|
+
if (code === "DESKTOP_SIGNING_REQUIRED")
|
|
1744
|
+
return 2;
|
|
1485
1745
|
if (code?.startsWith("AUTH_"))
|
|
1486
1746
|
return 4;
|
|
1487
1747
|
if (code?.startsWith("BUILD_"))
|