@lynxship/cli 0.1.8 → 0.1.11
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/dev-qr.d.ts +3 -0
- package/dist/dev-qr.d.ts.map +1 -0
- package/dist/dev-qr.js +12 -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 +359 -74
- 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 +1 -0
- package/dist/ui/components.d.ts.map +1 -1
- package/dist/ui/components.js +7 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +4 -1
- 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 +3 -3
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,18 @@ 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 { extractDevServerUrl, shouldPrintDevServerQr } from "./dev-qr.js";
|
|
37
|
+
import { inspectDesktopTarget, inspectHarmonyTarget, inspectWebTarget, } from "./target-toolchain.js";
|
|
30
38
|
const rawArgs = process.argv.slice(2);
|
|
31
39
|
const args = [...rawArgs];
|
|
32
40
|
const ui = createCliUi(rawArgs);
|
|
@@ -134,6 +142,11 @@ function configuredProjectId(config) {
|
|
|
134
142
|
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
143
|
return config.projectId;
|
|
136
144
|
}
|
|
145
|
+
function mobilePlatformValue(value) {
|
|
146
|
+
const platform = platformValue(value);
|
|
147
|
+
assert(platform === "android" || platform === "ios", "PLATFORM_COMMAND_UNSUPPORTED", "This command supports only android or ios. Web, HarmonyOS and Desktop use their own build adapters.");
|
|
148
|
+
return platform;
|
|
149
|
+
}
|
|
137
150
|
async function renderConfigurationFooter() {
|
|
138
151
|
if (!ui.interactive || ui.options.quiet)
|
|
139
152
|
return;
|
|
@@ -191,7 +204,10 @@ async function saveState(state, repository, builds, submissions) {
|
|
|
191
204
|
let stateSaveQueue = Promise.resolve();
|
|
192
205
|
async function executeBuild(options, showResult = true) {
|
|
193
206
|
const { config, profile, platform, skipUpload, wait, local, state, repository, builds, submissions, progress: sharedProgress, progressPrefix, onProgress, onEvent, skipBundleBuild, } = options;
|
|
194
|
-
|
|
207
|
+
if (wait && !local)
|
|
208
|
+
await ensureNativeHostForBuild(platform, profile, config);
|
|
209
|
+
if (platform === "android" || platform === "ios")
|
|
210
|
+
await requireAutolinkReady(root, platform);
|
|
195
211
|
const runtime = await inspectRuntimeFingerprint(root, platform, config);
|
|
196
212
|
const job = await builds.create({
|
|
197
213
|
projectId: configuredProjectId(config),
|
|
@@ -205,7 +221,7 @@ async function executeBuild(options, showResult = true) {
|
|
|
205
221
|
ui.info(`Using profile: ${profile} · platform: ${platform}`);
|
|
206
222
|
const ownsProgress = !sharedProgress;
|
|
207
223
|
const progress = sharedProgress ??
|
|
208
|
-
ui.progress(platform
|
|
224
|
+
ui.progress(`${platform[0].toUpperCase()}${platform.slice(1)} build`);
|
|
209
225
|
const prefix = progressPrefix ? `${progressPrefix} · ` : "";
|
|
210
226
|
const reportEvent = (message) => {
|
|
211
227
|
if (onEvent)
|
|
@@ -227,12 +243,14 @@ async function executeBuild(options, showResult = true) {
|
|
|
227
243
|
const realAndroid = platform === "android" &&
|
|
228
244
|
isSupportedAndroidPlatform() &&
|
|
229
245
|
(await hasAndroidHost(root));
|
|
230
|
-
const realIos = platform === "ios" && hasIosHost(root, config
|
|
246
|
+
const realIos = platform === "ios" && hasIosHost(root, resolveProfile(config, profile));
|
|
231
247
|
if (platform === "ios" && !realIos && !local)
|
|
232
|
-
assert(false, "IOS_HOST_REQUIRED", "A macOS Xcode host is required for a real iOS build.
|
|
248
|
+
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
249
|
if (platform === "android" && !realAndroid && !local)
|
|
234
|
-
assert(false, "ANDROID_HOST_REQUIRED", "This project has no Android Gradle host.
|
|
250
|
+
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
251
|
if (realAndroid) {
|
|
252
|
+
const toolchain = await inspectAndroidToolchain(root);
|
|
253
|
+
assert(toolchain.ok, "ANDROID_TOOLCHAIN_REQUIRED", `Android toolchain is not ready: ${formatAndroidToolchainFailure(toolchain)}`);
|
|
236
254
|
await runRealAndroidBuild(job, {
|
|
237
255
|
root,
|
|
238
256
|
profile: config.build?.[profile] ?? {},
|
|
@@ -244,6 +262,8 @@ async function executeBuild(options, showResult = true) {
|
|
|
244
262
|
});
|
|
245
263
|
}
|
|
246
264
|
else if (realIos) {
|
|
265
|
+
const toolchain = await inspectIosToolchain(root, resolveProfile(config, profile));
|
|
266
|
+
assert(toolchain.ok, "IOS_TOOLCHAIN_REQUIRED", `iOS toolchain is not ready: ${formatIosToolchainFailure(toolchain)}`);
|
|
247
267
|
await runRealIosBuild(job, {
|
|
248
268
|
root,
|
|
249
269
|
profile: config.build?.[profile] ?? {},
|
|
@@ -254,6 +274,40 @@ async function executeBuild(options, showResult = true) {
|
|
|
254
274
|
onProgress: reportProgress,
|
|
255
275
|
});
|
|
256
276
|
}
|
|
277
|
+
else if (!local && platform === "web") {
|
|
278
|
+
await runRealWebBuild(job, {
|
|
279
|
+
root,
|
|
280
|
+
profile: resolveProfile(config, profile),
|
|
281
|
+
uploadArtifacts: !skipUpload,
|
|
282
|
+
skipBundleBuild,
|
|
283
|
+
quiet: json,
|
|
284
|
+
onEvent: reportEvent,
|
|
285
|
+
onProgress: reportProgress,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
else if (!local && platform === "harmony") {
|
|
289
|
+
await runRealHarmonyBuild(job, {
|
|
290
|
+
root,
|
|
291
|
+
profile: resolveProfile(config, profile),
|
|
292
|
+
uploadArtifacts: !skipUpload,
|
|
293
|
+
skipBundleBuild,
|
|
294
|
+
quiet: json,
|
|
295
|
+
onEvent: reportEvent,
|
|
296
|
+
onProgress: reportProgress,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
else if (!local && platform === "desktop") {
|
|
300
|
+
await runRealDesktopBuild(job, {
|
|
301
|
+
root,
|
|
302
|
+
profile: resolveProfile(config, profile),
|
|
303
|
+
uploadArtifacts: !skipUpload,
|
|
304
|
+
allowUnsigned: options.allowUnsigned,
|
|
305
|
+
skipBundleBuild,
|
|
306
|
+
quiet: json,
|
|
307
|
+
onEvent: reportEvent,
|
|
308
|
+
onProgress: reportProgress,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
257
311
|
else {
|
|
258
312
|
await builds.run(job.id);
|
|
259
313
|
}
|
|
@@ -275,7 +329,7 @@ async function executeBuild(options, showResult = true) {
|
|
|
275
329
|
const result = builds.get(job.id);
|
|
276
330
|
if (showResult) {
|
|
277
331
|
printValue(result, {
|
|
278
|
-
title: `${platform
|
|
332
|
+
title: `${platform[0].toUpperCase()}${platform.slice(1)} build result`,
|
|
279
333
|
rows: [
|
|
280
334
|
{ label: "Build ID", value: result.id, valueColor: "purple" },
|
|
281
335
|
{ label: "Platform", value: result.platform, valueColor: "blue" },
|
|
@@ -298,12 +352,12 @@ async function executeBuild(options, showResult = true) {
|
|
|
298
352
|
async function buildSharedLynxBundle() {
|
|
299
353
|
const progress = ui.progress("Shared Lynx bundle");
|
|
300
354
|
try {
|
|
301
|
-
progress.update(undefined, "Building Lynx bundle once for
|
|
355
|
+
progress.update(undefined, "Building the shared Lynx bundle once for native targets…");
|
|
302
356
|
await buildLynxBundle(root, {
|
|
303
357
|
quiet: json,
|
|
304
358
|
onOutput: (message) => progress.event(message),
|
|
305
359
|
});
|
|
306
|
-
progress.update(100, "Shared Lynx bundle ready");
|
|
360
|
+
progress.update(100, "Shared native Lynx bundle ready");
|
|
307
361
|
}
|
|
308
362
|
finally {
|
|
309
363
|
progress.stop();
|
|
@@ -344,6 +398,9 @@ function commandTitle(command) {
|
|
|
344
398
|
storage: "Cloudflare R2 setup",
|
|
345
399
|
android: "Android signing setup",
|
|
346
400
|
store: "App store submission setup",
|
|
401
|
+
devtool: "Lynx DevTool diagnostics",
|
|
402
|
+
trace: "Lynx Trace diagnostics",
|
|
403
|
+
recorder: "Lynx Recorder diagnostics",
|
|
347
404
|
}[command] ?? command);
|
|
348
405
|
}
|
|
349
406
|
function helpText() {
|
|
@@ -356,17 +413,20 @@ Commands:
|
|
|
356
413
|
preview Preview the production Lynx bundle locally
|
|
357
414
|
inspect Inspect Rspeedy/Rspack configuration
|
|
358
415
|
profile Build with Rspack profiling enabled
|
|
416
|
+
devtool doctor Check Lynx DevTool integration and dev runtime
|
|
417
|
+
trace doctor Check Lynx Trace prerequisites
|
|
418
|
+
recorder doctor Check Lynx Recorder prerequisites
|
|
359
419
|
autolink check Check Lynx native-library Autolink wiring
|
|
360
420
|
autolink codegen Run the project's Native Module codegen script
|
|
361
421
|
ota doctor Check native OTA host integration
|
|
362
|
-
run Install an artifact on an Android/iOS target
|
|
363
|
-
logs Stream Android/iOS native logs
|
|
422
|
+
run Install an artifact on an Android/iOS/HarmonyOS target
|
|
423
|
+
logs Stream Android/iOS/HarmonyOS native logs
|
|
364
424
|
build create Create a local/cloud build job
|
|
365
425
|
build list List build jobs
|
|
366
426
|
build status <id> Show one build job
|
|
367
427
|
build cancel <id> Cancel a build job
|
|
368
428
|
build retry <id> Retry a failed build job
|
|
369
|
-
build all Build Android
|
|
429
|
+
build all Build Android, iOS, HarmonyOS, Web and Desktop
|
|
370
430
|
submit Submit the latest successful build
|
|
371
431
|
update Upload and publish a signed OTA update
|
|
372
432
|
update rollback Roll back an OTA channel to a previous release
|
|
@@ -379,10 +439,11 @@ Commands:
|
|
|
379
439
|
store configure Configure Google Play or App Store Connect submission
|
|
380
440
|
|
|
381
441
|
Build options:
|
|
382
|
-
--platform <p> Target android, ios or all
|
|
442
|
+
--platform <p> Target android, ios, harmony, web, desktop or all (default: android)
|
|
383
443
|
--profile <name> Build profile (default: production)
|
|
384
444
|
--no-wait Queue the build without executing it locally
|
|
385
|
-
--no-upload Keep the
|
|
445
|
+
--no-upload Keep the artifact local and skip R2 (CI verification)
|
|
446
|
+
--allow-unsigned Allow an unsigned Desktop artifact only with --no-upload (local tests)
|
|
386
447
|
--local Use the contract-only build path for tests
|
|
387
448
|
|
|
388
449
|
Submit options:
|
|
@@ -405,12 +466,13 @@ Rollback options:
|
|
|
405
466
|
|
|
406
467
|
Device and diagnostics options:
|
|
407
468
|
doctor --platform <p> Check the local toolchain (default: android)
|
|
469
|
+
doctor --fix Install missing Android SDK packages after confirmation
|
|
408
470
|
autolink check --platform <p>
|
|
409
471
|
Check native-library wiring (default: android)
|
|
410
472
|
ota doctor --platform <p>
|
|
411
473
|
Check OTA host integration (default: android)
|
|
412
|
-
run --artifact <path> Install a specific APK, IPA or
|
|
413
|
-
run --device <id> Select
|
|
474
|
+
run --artifact <path> Install a specific APK, IPA, app or signed HAP
|
|
475
|
+
run --device <id> Select an Android, iOS or HarmonyOS device
|
|
414
476
|
run --simulator Install an iOS .app with simctl
|
|
415
477
|
logs --device <id> Select the device or simulator for native logs
|
|
416
478
|
|
|
@@ -469,6 +531,56 @@ async function initializeBuildProject() {
|
|
|
469
531
|
await initializeProject();
|
|
470
532
|
ui.success("Created lynxship.json");
|
|
471
533
|
}
|
|
534
|
+
async function ensureNativeHostForBuild(platform, profile, config) {
|
|
535
|
+
if (platform === "android") {
|
|
536
|
+
assert(isSupportedAndroidPlatform(), "ANDROID_PLATFORM_UNSUPPORTED", "Android builds are supported on Windows, macOS, and Linux.");
|
|
537
|
+
if (await hasAndroidHost(root))
|
|
538
|
+
return;
|
|
539
|
+
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.");
|
|
540
|
+
const suggestedId = suggestedAndroidApplicationId(root);
|
|
541
|
+
const applicationId = flag("--application-id") ??
|
|
542
|
+
(ui.interactive
|
|
543
|
+
? await prompt("Android application ID", suggestedId)
|
|
544
|
+
: "");
|
|
545
|
+
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.`);
|
|
546
|
+
ui.info("No Android host found. Creating the official LynxShip host before the build…");
|
|
547
|
+
const result = await initializeAndroidHost(root, {
|
|
548
|
+
applicationId,
|
|
549
|
+
appName: basename(root),
|
|
550
|
+
});
|
|
551
|
+
ui.success(`Android host created: ${result.directory}`);
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (platform === "web") {
|
|
555
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
556
|
+
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.");
|
|
557
|
+
return;
|
|
558
|
+
}
|
|
559
|
+
if (platform === "harmony") {
|
|
560
|
+
assert(hasHarmonyHost(root), "HARMONY_HOST_REQUIRED", "No complete HarmonyOS host was found. Add an official Lynx Harmony host under harmony/ before building.");
|
|
561
|
+
return;
|
|
562
|
+
}
|
|
563
|
+
if (platform === "desktop") {
|
|
564
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
565
|
+
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.");
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
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.");
|
|
569
|
+
const resolvedProfile = resolveProfile(config, profile);
|
|
570
|
+
if (hasIosHost(root, resolvedProfile))
|
|
571
|
+
return;
|
|
572
|
+
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.");
|
|
573
|
+
const suggestedId = suggestedIosBundleIdentifier(root);
|
|
574
|
+
const bundleIdentifier = flag("--bundle-identifier") ??
|
|
575
|
+
(ui.interactive ? await prompt("iOS bundle identifier", suggestedId) : "");
|
|
576
|
+
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.`);
|
|
577
|
+
ui.info("No iOS host found. Creating the official LynxShip host before the build…");
|
|
578
|
+
const result = await initializeIosHost(root, {
|
|
579
|
+
bundleIdentifier,
|
|
580
|
+
appName: basename(root),
|
|
581
|
+
});
|
|
582
|
+
ui.success(`iOS host created: ${result.directory}`);
|
|
583
|
+
}
|
|
472
584
|
function forwardedToolArgs(values) {
|
|
473
585
|
const result = [];
|
|
474
586
|
const valueFlags = new Set([
|
|
@@ -507,10 +619,34 @@ async function runRspeedyCommand(subcommand, environment) {
|
|
|
507
619
|
if (subcommand === "dev") {
|
|
508
620
|
ui.info("Lynx Explorer mode: no Android or iOS native host is required. Scan the QR code printed by Rspeedy; source changes reload automatically.");
|
|
509
621
|
}
|
|
622
|
+
let devUrl;
|
|
623
|
+
let devQrPrinted = false;
|
|
624
|
+
let devQrTimer;
|
|
625
|
+
const printDevQr = () => {
|
|
626
|
+
if (!devUrl || devQrPrinted || json || ui.options.quiet)
|
|
627
|
+
return;
|
|
628
|
+
devQrPrinted = true;
|
|
629
|
+
if (devQrTimer)
|
|
630
|
+
clearTimeout(devQrTimer);
|
|
631
|
+
ui.devServerQr(devUrl);
|
|
632
|
+
};
|
|
510
633
|
await runRspeedy(root, subcommand, forwarded, {
|
|
511
634
|
env: environment,
|
|
512
635
|
quiet: json,
|
|
513
|
-
onOutput: (line) =>
|
|
636
|
+
onOutput: (line) => {
|
|
637
|
+
ui.info(`│ ${line}`);
|
|
638
|
+
if (subcommand !== "dev")
|
|
639
|
+
return;
|
|
640
|
+
const url = extractDevServerUrl(line);
|
|
641
|
+
if (url && (!devUrl || url.includes("fullscreen=true"))) {
|
|
642
|
+
devUrl = url;
|
|
643
|
+
if (devQrTimer)
|
|
644
|
+
clearTimeout(devQrTimer);
|
|
645
|
+
devQrTimer = setTimeout(printDevQr, 500);
|
|
646
|
+
}
|
|
647
|
+
if (devUrl && shouldPrintDevServerQr(line))
|
|
648
|
+
printDevQr();
|
|
649
|
+
},
|
|
514
650
|
});
|
|
515
651
|
printValue({ status: "success", command: `rspeedy ${subcommand}` }, {
|
|
516
652
|
title: `Rspeedy ${subcommand}`,
|
|
@@ -541,6 +677,33 @@ async function runAutolinkCodegen() {
|
|
|
541
677
|
done: "Native Module specifications were generated successfully.",
|
|
542
678
|
});
|
|
543
679
|
}
|
|
680
|
+
async function runDevToolDoctor(kind) {
|
|
681
|
+
await initializeBuildProject();
|
|
682
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
683
|
+
const status = await inspectLynxDevTool(root, platform);
|
|
684
|
+
const ready = kind === "devtool"
|
|
685
|
+
? status.ok
|
|
686
|
+
: kind === "trace"
|
|
687
|
+
? status.traceReady
|
|
688
|
+
: status.recorderReady;
|
|
689
|
+
if (!ready)
|
|
690
|
+
process.exitCode = 1;
|
|
691
|
+
printValue({ ...status, requested: kind, ready }, {
|
|
692
|
+
title: `Lynx ${kind === "devtool" ? "DevTool" : kind[0].toUpperCase() + kind.slice(1)} · ${platform}`,
|
|
693
|
+
rows: status.checks.map((item) => ({
|
|
694
|
+
label: item.name,
|
|
695
|
+
value: `${item.status} · ${item.value}${item.fix && item.status !== "pass" ? ` · fix: ${item.fix}` : ""}`,
|
|
696
|
+
valueColor: item.status === "pass"
|
|
697
|
+
? "green"
|
|
698
|
+
: item.status === "warn"
|
|
699
|
+
? "yellow"
|
|
700
|
+
: "red",
|
|
701
|
+
})),
|
|
702
|
+
done: ready
|
|
703
|
+
? `${kind} prerequisites are ready. Open Lynx DevTool Desktop and connect the target device by USB.`
|
|
704
|
+
: `Lynx ${kind} prerequisites are incomplete: ${formatDevToolFailure(status) || "review the failed checks"}`,
|
|
705
|
+
});
|
|
706
|
+
}
|
|
544
707
|
async function runDevice() {
|
|
545
708
|
const platform = platformValue(flag("--platform", "android"));
|
|
546
709
|
const artifact = flag("--artifact");
|
|
@@ -563,7 +726,7 @@ async function runDevice() {
|
|
|
563
726
|
onOutput: (line) => ui.info(`│ ${line}`),
|
|
564
727
|
});
|
|
565
728
|
}
|
|
566
|
-
else {
|
|
729
|
+
else if (platform === "ios") {
|
|
567
730
|
assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS device/simulator commands require macOS");
|
|
568
731
|
assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found in PATH");
|
|
569
732
|
const device = flag("--device", "booted");
|
|
@@ -592,6 +755,23 @@ async function runDevice() {
|
|
|
592
755
|
});
|
|
593
756
|
}
|
|
594
757
|
}
|
|
758
|
+
else if (platform === "harmony") {
|
|
759
|
+
assert(commandExists("hdc"), "HARMONY_HDC_REQUIRED", "hdc was not found in PATH. Install the OpenHarmony SDK platform tools before installing a HAP.");
|
|
760
|
+
assert(artifactPath.endsWith(".hap"), "DEVICE_ARTIFACT_INVALID", "HarmonyOS run requires a signed .hap artifact.");
|
|
761
|
+
const device = flag("--device");
|
|
762
|
+
await runProcess("hdc", device
|
|
763
|
+
? ["-t", device, "install", "-r", artifactPath]
|
|
764
|
+
: ["install", "-r", artifactPath], {
|
|
765
|
+
cwd: root,
|
|
766
|
+
quiet: json,
|
|
767
|
+
onOutput: (line) => ui.info(`│ ${line}`),
|
|
768
|
+
});
|
|
769
|
+
}
|
|
770
|
+
else {
|
|
771
|
+
assert(false, "TARGET_RUN_UNSUPPORTED", platform === "web"
|
|
772
|
+
? "Web artifacts are previewed with `lynxship preview` or served by the project; they are not installed on a device."
|
|
773
|
+
: "Desktop installers are launched by the operating system after packaging; LynxShip does not claim a cross-platform install command.");
|
|
774
|
+
}
|
|
595
775
|
printValue({ status: "installed", platform, artifact: artifactPath }, {
|
|
596
776
|
title: "Device install",
|
|
597
777
|
rows: [{ label: "Artifact", value: artifactPath, valueColor: "green" }],
|
|
@@ -600,7 +780,7 @@ async function runDevice() {
|
|
|
600
780
|
}
|
|
601
781
|
async function streamNativeLogs() {
|
|
602
782
|
const platform = platformValue(flag("--platform", "android"));
|
|
603
|
-
const device = flag("--device", platform === "
|
|
783
|
+
const device = flag("--device", platform === "ios" ? "booted" : undefined);
|
|
604
784
|
if (platform === "android") {
|
|
605
785
|
assert(commandExists("adb"), "ANDROID_ADB_REQUIRED", "adb was not found in PATH");
|
|
606
786
|
await runProcess("adb", device ? ["-s", device, "logcat"] : ["logcat"], {
|
|
@@ -609,7 +789,7 @@ async function streamNativeLogs() {
|
|
|
609
789
|
onOutput: (line) => ui.info(`│ ${line}`),
|
|
610
790
|
});
|
|
611
791
|
}
|
|
612
|
-
else {
|
|
792
|
+
else if (platform === "ios") {
|
|
613
793
|
assert(process.platform === "darwin", "IOS_MACOS_REQUIRED", "iOS logs require macOS");
|
|
614
794
|
assert(commandExists("xcrun"), "IOS_XCRUN_REQUIRED", "xcrun was not found in PATH");
|
|
615
795
|
assert(device !== "booted", "IOS_DEVICE_LOGS_UNSUPPORTED", "Use --device <simulator-identifier> for iOS simulator logs.");
|
|
@@ -625,6 +805,22 @@ async function streamNativeLogs() {
|
|
|
625
805
|
"debug",
|
|
626
806
|
], { cwd: root, quiet: json, onOutput: (line) => ui.info(`│ ${line}`) });
|
|
627
807
|
}
|
|
808
|
+
else if (platform === "harmony") {
|
|
809
|
+
assert(commandExists("hdc"), "HARMONY_HDC_REQUIRED", "hdc was not found in PATH. Install the OpenHarmony SDK platform tools before streaming logs.");
|
|
810
|
+
const argsForHdc = device
|
|
811
|
+
? ["-t", device, "shell", "hilog"]
|
|
812
|
+
: ["shell", "hilog"];
|
|
813
|
+
await runProcess("hdc", argsForHdc, {
|
|
814
|
+
cwd: root,
|
|
815
|
+
quiet: json,
|
|
816
|
+
onOutput: (line) => ui.info(`│ ${line}`),
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
else {
|
|
820
|
+
assert(false, "TARGET_LOGS_UNSUPPORTED", platform === "web"
|
|
821
|
+
? "Web logs are emitted by the browser/runtime console; use lynxship dev or the project's Web DevTools."
|
|
822
|
+
: "Desktop logs are emitted by the packaged runtime; use the target OS logging tools.");
|
|
823
|
+
}
|
|
628
824
|
}
|
|
629
825
|
async function main() {
|
|
630
826
|
const command = args.shift() ?? "help";
|
|
@@ -678,12 +874,42 @@ async function main() {
|
|
|
678
874
|
const config = await loadConfig(root);
|
|
679
875
|
const configuration = await readConfigurationStatus();
|
|
680
876
|
const doctorPlatform = platformValue(flag("--platform", "android"));
|
|
681
|
-
const autolink =
|
|
682
|
-
|
|
877
|
+
const autolink = doctorPlatform === "android" || doctorPlatform === "ios"
|
|
878
|
+
? await inspectAutolink(root)
|
|
879
|
+
: undefined;
|
|
880
|
+
const autolinkForPlatform = autolink && (doctorPlatform === "android" || doctorPlatform === "ios")
|
|
881
|
+
? autolink[doctorPlatform]
|
|
882
|
+
: undefined;
|
|
683
883
|
const lockfile = await findLockfile(root);
|
|
684
884
|
const credentialStore = credentialStorageDescription();
|
|
685
885
|
const nativeCredentialStore = !credentialStore.includes("owner-only");
|
|
686
886
|
const androidHost = doctorPlatform === "android" ? await hasAndroidHost(root) : false;
|
|
887
|
+
const doctorProfileName = flag("--profile", "production");
|
|
888
|
+
const doctorProfile = config.build?.[doctorProfileName]
|
|
889
|
+
? resolveProfile(config, doctorProfileName)
|
|
890
|
+
: {
|
|
891
|
+
name: doctorProfileName,
|
|
892
|
+
...(DEFAULT_CONFIG.build?.production ?? {}),
|
|
893
|
+
};
|
|
894
|
+
const targetToolchain = doctorPlatform === "web"
|
|
895
|
+
? await inspectWebTarget(root, doctorProfile)
|
|
896
|
+
: doctorPlatform === "harmony"
|
|
897
|
+
? await inspectHarmonyTarget(root, doctorProfile)
|
|
898
|
+
: doctorPlatform === "desktop"
|
|
899
|
+
? await inspectDesktopTarget(root, doctorProfile)
|
|
900
|
+
: undefined;
|
|
901
|
+
assert(!args.includes("--fix") || doctorPlatform === "android", "CLI_DOCTOR_FIX_PLATFORM", "`doctor --fix` currently repairs Android SDK packages; run it with `--platform android`.");
|
|
902
|
+
assert(!args.includes("--fix") || ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run `lynxship doctor --platform android --fix` in an interactive terminal.");
|
|
903
|
+
let androidToolchain = doctorPlatform === "android"
|
|
904
|
+
? await inspectAndroidToolchain(root)
|
|
905
|
+
: undefined;
|
|
906
|
+
const iosToolchain = doctorPlatform === "ios"
|
|
907
|
+
? await inspectIosToolchain(root, doctorProfile)
|
|
908
|
+
: undefined;
|
|
909
|
+
if (androidToolchain && args.includes("--fix")) {
|
|
910
|
+
await fixAndroidToolchain(root, androidToolchain, (message) => confirm(message), (line) => ui.info(line));
|
|
911
|
+
androidToolchain = await inspectAndroidToolchain(root);
|
|
912
|
+
}
|
|
687
913
|
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
688
914
|
const nodeSupported = nodeMajor >= 22;
|
|
689
915
|
const nodeRecommended = nodeMajor % 2 === 0;
|
|
@@ -732,8 +958,34 @@ async function main() {
|
|
|
732
958
|
? "Gradle host found"
|
|
733
959
|
: "missing · fix: lynxship android host init --application-id com.example.myapp",
|
|
734
960
|
},
|
|
961
|
+
...(androidToolchain?.checks ?? []).map((toolchainCheck) => ({
|
|
962
|
+
name: toolchainCheck.name,
|
|
963
|
+
ok: toolchainCheck.ok,
|
|
964
|
+
status: toolchainCheck.status,
|
|
965
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
966
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
967
|
+
: toolchainCheck.value,
|
|
968
|
+
})),
|
|
735
969
|
]
|
|
736
|
-
:
|
|
970
|
+
: doctorPlatform === "ios"
|
|
971
|
+
? (iosToolchain?.checks ?? [])
|
|
972
|
+
.filter((toolchainCheck) => toolchainCheck.name !== "ios-host")
|
|
973
|
+
.map((toolchainCheck) => ({
|
|
974
|
+
name: toolchainCheck.name,
|
|
975
|
+
ok: toolchainCheck.ok,
|
|
976
|
+
status: toolchainCheck.status,
|
|
977
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
978
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
979
|
+
: toolchainCheck.value,
|
|
980
|
+
}))
|
|
981
|
+
: (targetToolchain?.checks ?? []).map((toolchainCheck) => ({
|
|
982
|
+
name: toolchainCheck.name,
|
|
983
|
+
ok: toolchainCheck.ok,
|
|
984
|
+
status: toolchainCheck.status,
|
|
985
|
+
value: toolchainCheck.fix && toolchainCheck.status !== "pass"
|
|
986
|
+
? `${toolchainCheck.value} · fix: ${toolchainCheck.fix}`
|
|
987
|
+
: toolchainCheck.value,
|
|
988
|
+
}))),
|
|
737
989
|
{
|
|
738
990
|
name: "cloudflare-r2",
|
|
739
991
|
ok: configuration.r2,
|
|
@@ -742,32 +994,47 @@ async function main() {
|
|
|
742
994
|
? "configured"
|
|
743
995
|
: "missing · fix: lynxship storage configure",
|
|
744
996
|
},
|
|
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
|
-
|
|
997
|
+
...(doctorPlatform === "android"
|
|
998
|
+
? [
|
|
999
|
+
{
|
|
1000
|
+
name: "android-signing",
|
|
1001
|
+
ok: configuration.android,
|
|
1002
|
+
status: configuration.android
|
|
1003
|
+
? "pass"
|
|
1004
|
+
: "fail",
|
|
1005
|
+
value: configuration.android
|
|
1006
|
+
? "configured"
|
|
1007
|
+
: "missing · fix: lynxship android configure",
|
|
1008
|
+
},
|
|
1009
|
+
]
|
|
1010
|
+
: doctorPlatform === "ios"
|
|
1011
|
+
? [
|
|
1012
|
+
{
|
|
1013
|
+
name: "ios-host",
|
|
1014
|
+
ok: process.platform === "darwin" && hasIosHost(root),
|
|
1015
|
+
status: process.platform === "darwin" && hasIosHost(root)
|
|
1016
|
+
? "pass"
|
|
1017
|
+
: "fail",
|
|
1018
|
+
value: process.platform === "darwin" && hasIosHost(root)
|
|
1019
|
+
? "Xcode host found"
|
|
1020
|
+
: "missing · fix: use macOS, then lynxship ios host init --bundle-identifier com.example.myapp",
|
|
1021
|
+
},
|
|
1022
|
+
]
|
|
1023
|
+
: []),
|
|
1024
|
+
...(autolinkForPlatform
|
|
1025
|
+
? [
|
|
1026
|
+
{
|
|
1027
|
+
name: `lynx-autolink-${doctorPlatform}`,
|
|
1028
|
+
ok: autolinkForPlatform.ready,
|
|
1029
|
+
status: autolinkForPlatform.ready
|
|
1030
|
+
? "pass"
|
|
1031
|
+
: "fail",
|
|
1032
|
+
value: autolinkForPlatform.ready
|
|
1033
|
+
? autolinkForPlatform.reason
|
|
1034
|
+
: `${autolinkForPlatform.reason} · fix: install the native plugin, then run autolink codegen`,
|
|
1035
|
+
},
|
|
1036
|
+
]
|
|
1037
|
+
: []),
|
|
771
1038
|
];
|
|
772
1039
|
const result = {
|
|
773
1040
|
ok: checks.every((check) => check.status !== "fail"),
|
|
@@ -778,6 +1045,8 @@ async function main() {
|
|
|
778
1045
|
ui.warn("One or more environment checks failed");
|
|
779
1046
|
else if (hasWarnings)
|
|
780
1047
|
ui.warn("Environment is usable, but one recommendation needs attention");
|
|
1048
|
+
if (!result.ok)
|
|
1049
|
+
process.exitCode = 1;
|
|
781
1050
|
printValue(result, {
|
|
782
1051
|
title: "Doctor result",
|
|
783
1052
|
rows: checks.map((check) => ({
|
|
@@ -808,6 +1077,12 @@ async function main() {
|
|
|
808
1077
|
});
|
|
809
1078
|
return;
|
|
810
1079
|
}
|
|
1080
|
+
if (["devtool", "trace", "recorder"].includes(command)) {
|
|
1081
|
+
const subcommand = args.shift() ?? "doctor";
|
|
1082
|
+
assert(subcommand === "doctor", "CLI_DEVTOOL_COMMAND", "Use `lynxship devtool doctor`, `lynxship trace doctor` or `lynxship recorder doctor`.");
|
|
1083
|
+
await runDevToolDoctor(command);
|
|
1084
|
+
return;
|
|
1085
|
+
}
|
|
811
1086
|
if (command === "autolink") {
|
|
812
1087
|
const subcommand = args.shift() ?? "check";
|
|
813
1088
|
assert(["check", "codegen"].includes(subcommand), "CLI_AUTOLINK_COMMAND", "Use `lynxship autolink check` or `lynxship autolink codegen`");
|
|
@@ -815,7 +1090,7 @@ async function main() {
|
|
|
815
1090
|
await runAutolinkCodegen();
|
|
816
1091
|
return;
|
|
817
1092
|
}
|
|
818
|
-
const platform =
|
|
1093
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
819
1094
|
const status = (await inspectAutolink(root))[platform];
|
|
820
1095
|
printValue(status, {
|
|
821
1096
|
title: `Lynx Autolink · ${platform}`,
|
|
@@ -844,7 +1119,7 @@ async function main() {
|
|
|
844
1119
|
}
|
|
845
1120
|
if (command === "ota") {
|
|
846
1121
|
assert((args.shift() ?? "doctor") === "doctor", "CLI_OTA_COMMAND", "Only `lynxship ota doctor` is available");
|
|
847
|
-
const platform =
|
|
1122
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
848
1123
|
const status = await inspectOtaHost(root, platform);
|
|
849
1124
|
printValue(status, {
|
|
850
1125
|
title: `OTA host · ${platform}`,
|
|
@@ -1026,7 +1301,7 @@ async function main() {
|
|
|
1026
1301
|
if (command === "store") {
|
|
1027
1302
|
assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run store configure in an interactive terminal");
|
|
1028
1303
|
assert((args.shift() ?? "configure") === "configure", "CLI_STORE_COMMAND", "Only store configure is available");
|
|
1029
|
-
const platform =
|
|
1304
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1030
1305
|
ui.info(platform === "android"
|
|
1031
1306
|
? "Configuring Google Play submission. Secret fields will stay invisible…"
|
|
1032
1307
|
: "Configuring App Store Connect submission. Private key contents stay protected…");
|
|
@@ -1066,7 +1341,7 @@ async function main() {
|
|
|
1066
1341
|
const { state, repository, builds, submissions } = await loadState();
|
|
1067
1342
|
if (command === "submit") {
|
|
1068
1343
|
const config = await loadConfig(root);
|
|
1069
|
-
const platform =
|
|
1344
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1070
1345
|
await requireOperationalConfiguration(platform);
|
|
1071
1346
|
const credentials = await loadCredentials(root);
|
|
1072
1347
|
const localMode = args.includes("--local") || process.env.LYNXSHIP_SUBMIT_MODE === "mock";
|
|
@@ -1151,7 +1426,7 @@ async function main() {
|
|
|
1151
1426
|
}
|
|
1152
1427
|
if (command === "update" && args[0] !== "rollback") {
|
|
1153
1428
|
const config = await loadConfig(root);
|
|
1154
|
-
const platform =
|
|
1429
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1155
1430
|
await requireOperationalConfiguration(platform);
|
|
1156
1431
|
const projectId = configuredProjectId(config);
|
|
1157
1432
|
const localMode = args.includes("--local") || process.env.LYNXSHIP_SUBMIT_MODE === "mock";
|
|
@@ -1273,7 +1548,7 @@ async function main() {
|
|
|
1273
1548
|
if (command === "update")
|
|
1274
1549
|
args.shift();
|
|
1275
1550
|
const config = await loadConfig(root);
|
|
1276
|
-
const platform =
|
|
1551
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
1277
1552
|
const releaseId = flag("--release-id");
|
|
1278
1553
|
const reason = flag("--reason");
|
|
1279
1554
|
const channel = config.update?.channel ?? "production";
|
|
@@ -1345,6 +1620,8 @@ async function main() {
|
|
|
1345
1620
|
const buildAll = subcommand === "all" || platformArgument === "all";
|
|
1346
1621
|
const platform = buildAll ? "android" : platformValue(platformArgument);
|
|
1347
1622
|
const skipUpload = args.includes("--no-upload");
|
|
1623
|
+
const allowUnsigned = args.includes("--allow-unsigned");
|
|
1624
|
+
assert(!allowUnsigned || skipUpload, "CLI_UNSIGNED_UPLOAD_BLOCKED", "--allow-unsigned is only available with --no-upload and can never upload an unsigned Desktop artifact.");
|
|
1348
1625
|
await requireOperationalConfiguration(platform, { requireR2: !skipUpload });
|
|
1349
1626
|
if (subcommand === "list") {
|
|
1350
1627
|
printValue(builds.list());
|
|
@@ -1372,11 +1649,13 @@ async function main() {
|
|
|
1372
1649
|
const profile = flag("--profile", "production");
|
|
1373
1650
|
const wait = !args.includes("--no-wait");
|
|
1374
1651
|
const local = args.includes("--local");
|
|
1375
|
-
const platforms = buildAll
|
|
1652
|
+
const platforms = buildAll
|
|
1653
|
+
? ["android", "ios", "harmony", "web", "desktop"]
|
|
1654
|
+
: [platform];
|
|
1376
1655
|
if (buildAll && wait && !local) {
|
|
1377
|
-
assert(process.platform === "darwin", "BUILD_ALL_MACOS_REQUIRED", "A real build
|
|
1378
|
-
|
|
1379
|
-
|
|
1656
|
+
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.");
|
|
1657
|
+
for (const target of platforms)
|
|
1658
|
+
await ensureNativeHostForBuild(target, profile, config);
|
|
1380
1659
|
}
|
|
1381
1660
|
if (!buildAll) {
|
|
1382
1661
|
await executeBuild({
|
|
@@ -1384,6 +1663,7 @@ async function main() {
|
|
|
1384
1663
|
profile,
|
|
1385
1664
|
platform,
|
|
1386
1665
|
skipUpload,
|
|
1666
|
+
allowUnsigned,
|
|
1387
1667
|
wait,
|
|
1388
1668
|
local,
|
|
1389
1669
|
state,
|
|
@@ -1395,21 +1675,24 @@ async function main() {
|
|
|
1395
1675
|
}
|
|
1396
1676
|
if (buildAll && wait && !local)
|
|
1397
1677
|
await buildSharedLynxBundle();
|
|
1398
|
-
const progress = ui.progress("
|
|
1399
|
-
const progressValues = {
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1678
|
+
const progress = ui.progress("All Lynx targets build");
|
|
1679
|
+
const progressValues = {};
|
|
1680
|
+
const progressLabels = {};
|
|
1681
|
+
const platformName = (target) => target === "android"
|
|
1682
|
+
? "Android"
|
|
1683
|
+
: target === "ios"
|
|
1684
|
+
? "iOS"
|
|
1685
|
+
: target === "harmony"
|
|
1686
|
+
? "HarmonyOS"
|
|
1687
|
+
: target === "web"
|
|
1688
|
+
? "Web"
|
|
1689
|
+
: "Desktop";
|
|
1408
1690
|
const outcomes = await Promise.allSettled(platforms.map((target) => executeBuild({
|
|
1409
1691
|
config,
|
|
1410
1692
|
profile,
|
|
1411
1693
|
platform: target,
|
|
1412
1694
|
skipUpload,
|
|
1695
|
+
allowUnsigned,
|
|
1413
1696
|
wait,
|
|
1414
1697
|
local,
|
|
1415
1698
|
state,
|
|
@@ -1418,18 +1701,18 @@ async function main() {
|
|
|
1418
1701
|
submissions,
|
|
1419
1702
|
progress,
|
|
1420
1703
|
progressPrefix: platformName(target),
|
|
1421
|
-
skipBundleBuild: wait && !local,
|
|
1704
|
+
skipBundleBuild: wait && !local && target !== "web",
|
|
1422
1705
|
onEvent: (message) => progress.event(`${platformName(target)} · ${message}`),
|
|
1423
1706
|
onProgress: (value, label) => {
|
|
1424
1707
|
if (value !== undefined)
|
|
1425
1708
|
progressValues[target] = value;
|
|
1426
1709
|
if (label)
|
|
1427
1710
|
progressLabels[target] = label;
|
|
1428
|
-
const average = (
|
|
1711
|
+
const average = platforms.reduce((total, current) => total + (progressValues[current] ?? 0), 0) / platforms.length;
|
|
1429
1712
|
progress.update(average, `${platformName(target)} · ${progressLabels[target]}`);
|
|
1430
1713
|
},
|
|
1431
1714
|
}, false)));
|
|
1432
|
-
progress.update(100, "
|
|
1715
|
+
progress.update(100, "All Lynx target builds finished");
|
|
1433
1716
|
progress.stop();
|
|
1434
1717
|
const summaryBuilds = outcomes.map((outcome, index) => {
|
|
1435
1718
|
if (outcome.status === "fulfilled")
|
|
@@ -1466,7 +1749,7 @@ async function main() {
|
|
|
1466
1749
|
})),
|
|
1467
1750
|
done: failed
|
|
1468
1751
|
? "At least one platform build failed. Review its events and retry that platform."
|
|
1469
|
-
: "
|
|
1752
|
+
: "All Lynx target build jobs completed. Submit supported store artifacts separately.",
|
|
1470
1753
|
});
|
|
1471
1754
|
for (const result of summaryBuilds)
|
|
1472
1755
|
if (result.state === "success" &&
|
|
@@ -1482,6 +1765,8 @@ function exitCode(error) {
|
|
|
1482
1765
|
return 2;
|
|
1483
1766
|
if (code === "BUILD_SIGNING_REQUIRED")
|
|
1484
1767
|
return 2;
|
|
1768
|
+
if (code === "DESKTOP_SIGNING_REQUIRED")
|
|
1769
|
+
return 2;
|
|
1485
1770
|
if (code?.startsWith("AUTH_"))
|
|
1486
1771
|
return 4;
|
|
1487
1772
|
if (code?.startsWith("BUILD_"))
|