@lynxship/cli 0.1.20 → 0.1.21
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 +66 -5
- package/dist/android/signing.d.ts +16 -0
- package/dist/android/signing.d.ts.map +1 -0
- package/dist/android/signing.js +131 -0
- package/dist/android/types.d.ts +12 -0
- package/dist/android/types.d.ts.map +1 -0
- package/dist/android/types.js +1 -0
- package/dist/android-build.d.ts +4 -13
- package/dist/android-build.d.ts.map +1 -1
- package/dist/android-build.js +14 -133
- package/dist/android-toolchain/inspection.d.ts +3 -0
- package/dist/android-toolchain/inspection.d.ts.map +1 -0
- package/dist/android-toolchain/inspection.js +122 -0
- package/dist/android-toolchain/probes.d.ts +23 -0
- package/dist/android-toolchain/probes.d.ts.map +1 -0
- package/dist/android-toolchain/probes.js +183 -0
- package/dist/android-toolchain/types.d.ts +20 -0
- package/dist/android-toolchain/types.d.ts.map +1 -0
- package/dist/android-toolchain/types.js +1 -0
- package/dist/android-toolchain.d.ts +4 -20
- package/dist/android-toolchain.d.ts.map +1 -1
- package/dist/android-toolchain.js +4 -300
- package/dist/app-config.d.ts +25 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +106 -0
- package/dist/bundle-build.d.ts.map +1 -1
- package/dist/bundle-build.js +11 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +66 -0
- package/dist/commands/build-execution.d.ts +41 -0
- package/dist/commands/build-execution.d.ts.map +1 -0
- package/dist/commands/build-execution.js +182 -0
- package/dist/commands/configuration.d.ts +19 -0
- package/dist/commands/configuration.d.ts.map +1 -0
- package/dist/commands/configuration.js +174 -0
- package/dist/commands/development.d.ts +22 -0
- package/dist/commands/development.d.ts.map +1 -0
- package/dist/commands/development.js +132 -0
- package/dist/commands/device.d.ts +3 -0
- package/dist/commands/device.d.ts.map +1 -0
- package/dist/commands/device.js +86 -0
- package/dist/commands/doctor.d.ts +18 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +320 -0
- package/dist/commands/help.d.ts +2 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +118 -0
- package/dist/commands/i18n.d.ts +14 -0
- package/dist/commands/i18n.d.ts.map +1 -0
- package/dist/commands/i18n.js +88 -0
- package/dist/commands/logs.d.ts +3 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +51 -0
- package/dist/commands/metadata.d.ts +3 -0
- package/dist/commands/metadata.d.ts.map +1 -0
- package/dist/commands/metadata.js +45 -0
- package/dist/commands/ota.d.ts +28 -0
- package/dist/commands/ota.d.ts.map +1 -0
- package/dist/commands/ota.js +203 -0
- package/dist/commands/project.d.ts +14 -0
- package/dist/commands/project.d.ts.map +1 -0
- package/dist/commands/project.js +122 -0
- package/dist/commands/remote-build.d.ts +35 -0
- package/dist/commands/remote-build.d.ts.map +1 -0
- package/dist/commands/remote-build.js +73 -0
- package/dist/commands/submit.d.ts +27 -0
- package/dist/commands/submit.d.ts.map +1 -0
- package/dist/commands/submit.js +92 -0
- package/dist/ecosystem.d.ts +9 -0
- package/dist/ecosystem.d.ts.map +1 -0
- package/dist/ecosystem.js +95 -0
- package/dist/frameworks.d.ts.map +1 -1
- package/dist/frameworks.js +5 -1
- package/dist/guidance/catalog.d.ts +3 -0
- package/dist/guidance/catalog.d.ts.map +1 -0
- package/dist/guidance/catalog.js +504 -0
- package/dist/guidance/types.d.ts +10 -0
- package/dist/guidance/types.d.ts.map +1 -0
- package/dist/guidance/types.js +1 -0
- package/dist/guidance.d.ts +2 -9
- package/dist/guidance.d.ts.map +1 -1
- package/dist/guidance.js +1 -502
- package/dist/i18n/discovery.d.ts +14 -0
- package/dist/i18n/discovery.d.ts.map +1 -0
- package/dist/i18n/discovery.js +106 -0
- package/dist/i18n/plan.d.ts +12 -0
- package/dist/i18n/plan.d.ts.map +1 -0
- package/dist/i18n/plan.js +95 -0
- package/dist/i18n/setup.d.ts +11 -0
- package/dist/i18n/setup.d.ts.map +1 -0
- package/dist/i18n/setup.js +47 -0
- package/dist/i18n/types.d.ts +17 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2069
- package/dist/ios/assets.d.ts +19 -0
- package/dist/ios/assets.d.ts.map +1 -0
- package/dist/ios/assets.js +128 -0
- package/dist/ios/production-build.d.ts +6 -0
- package/dist/ios/production-build.d.ts.map +1 -0
- package/dist/ios/production-build.js +200 -0
- package/dist/ios/project.d.ts +4 -0
- package/dist/ios/project.d.ts.map +1 -0
- package/dist/ios/project.js +43 -0
- package/dist/ios/simulator-build.d.ts +8 -0
- package/dist/ios/simulator-build.d.ts.map +1 -0
- package/dist/ios/simulator-build.js +188 -0
- package/dist/ios/simulator.d.ts +5 -0
- package/dist/ios/simulator.d.ts.map +1 -0
- package/dist/ios/simulator.js +60 -0
- package/dist/ios/types.d.ts +14 -0
- package/dist/ios/types.d.ts.map +1 -0
- package/dist/ios/types.js +1 -0
- package/dist/ios-build.d.ts +4 -37
- package/dist/ios-build.d.ts.map +1 -1
- package/dist/ios-build.js +3 -575
- package/dist/ios-host.d.ts.map +1 -1
- package/dist/ios-toolchain/inspection.d.ts +4 -0
- package/dist/ios-toolchain/inspection.d.ts.map +1 -0
- package/dist/ios-toolchain/inspection.js +211 -0
- package/dist/ios-toolchain/probes.d.ts +24 -0
- package/dist/ios-toolchain/probes.d.ts.map +1 -0
- package/dist/ios-toolchain/probes.js +127 -0
- package/dist/ios-toolchain/types.d.ts +17 -0
- package/dist/ios-toolchain/types.d.ts.map +1 -0
- package/dist/ios-toolchain/types.js +1 -0
- package/dist/ios-toolchain.d.ts +3 -18
- package/dist/ios-toolchain.d.ts.map +1 -1
- package/dist/ios-toolchain.js +1 -333
- package/dist/plugins/contracts.d.ts +28 -0
- package/dist/plugins/contracts.d.ts.map +1 -0
- package/dist/plugins/contracts.js +1 -0
- package/dist/plugins/discovery.d.ts +19 -0
- package/dist/plugins/discovery.d.ts.map +1 -0
- package/dist/plugins/discovery.js +250 -0
- package/dist/plugins/native-operations.d.ts +28 -0
- package/dist/plugins/native-operations.d.ts.map +1 -0
- package/dist/plugins/native-operations.js +217 -0
- package/dist/plugins/operations.d.ts +9 -0
- package/dist/plugins/operations.d.ts.map +1 -0
- package/dist/plugins/operations.js +76 -0
- package/dist/plugins/validation.d.ts +9 -0
- package/dist/plugins/validation.d.ts.map +1 -0
- package/dist/plugins/validation.js +52 -0
- package/dist/plugins.d.ts +3 -34
- package/dist/plugins.d.ts.map +1 -1
- package/dist/plugins.js +2 -595
- package/dist/process-runner.d.ts +4 -0
- package/dist/process-runner.d.ts.map +1 -1
- package/dist/process-runner.js +14 -0
- package/dist/remote.d.ts +22 -1
- package/dist/remote.d.ts.map +1 -1
- package/dist/remote.js +141 -2
- package/dist/rspeedy-app-config.d.ts +4 -0
- package/dist/rspeedy-app-config.d.ts.map +1 -0
- package/dist/rspeedy-app-config.js +32 -0
- package/dist/runtime/args.d.ts +3 -0
- package/dist/runtime/args.d.ts.map +1 -0
- package/dist/runtime/args.js +11 -0
- package/dist/runtime/build-commands.d.ts +3 -0
- package/dist/runtime/build-commands.d.ts.map +1 -0
- package/dist/runtime/build-commands.js +282 -0
- package/dist/runtime/context.d.ts +63 -0
- package/dist/runtime/context.d.ts.map +1 -0
- package/dist/runtime/context.js +179 -0
- package/dist/runtime/early-commands.d.ts +4 -0
- package/dist/runtime/early-commands.d.ts.map +1 -0
- package/dist/runtime/early-commands.js +284 -0
- package/dist/runtime/project.d.ts +8 -0
- package/dist/runtime/project.d.ts.map +1 -0
- package/dist/runtime/project.js +38 -0
- package/dist/runtime/state.d.ts +47 -0
- package/dist/runtime/state.d.ts.map +1 -0
- package/dist/runtime/state.js +33 -0
- package/dist/secure-store/backends.d.ts +20 -0
- package/dist/secure-store/backends.d.ts.map +1 -0
- package/dist/secure-store/backends.js +248 -0
- package/dist/secure-store/types.d.ts +27 -0
- package/dist/secure-store/types.d.ts.map +1 -0
- package/dist/secure-store/types.js +1 -0
- package/dist/secure-store.d.ts +2 -26
- package/dist/secure-store.d.ts.map +1 -1
- package/dist/secure-store.js +3 -245
- package/package.json +7 -4
- package/templates/android-host/app/build.gradle +1 -0
- package/templates/android-host/app/src/main/java/template/MainActivity.java +98 -3
- package/templates/ios-host/__IOS_TARGET_NAME__/DemoLynxProvider.swift +26 -16
- package/templates/ios-host/__IOS_TARGET_NAME__/ViewController.swift +107 -19
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
import { join, relative, resolve } from "node:path";
|
|
5
|
+
import { LynxShipError } from "@lynxship/contracts";
|
|
6
|
+
import { FrameworkError, normalizeLynxShipAppConfig, } from "@lynxship/framework";
|
|
7
|
+
const APP_CONFIG_FILES = [
|
|
8
|
+
"app.config.ts",
|
|
9
|
+
"app.config.js",
|
|
10
|
+
"app.config.mts",
|
|
11
|
+
"app.config.mjs",
|
|
12
|
+
"app.config.cts",
|
|
13
|
+
"app.config.cjs",
|
|
14
|
+
];
|
|
15
|
+
const MAX_APP_CONFIG_BYTES = 512 * 1024;
|
|
16
|
+
function appConfigError(code, message, details = {}) {
|
|
17
|
+
return new LynxShipError(code, message, details);
|
|
18
|
+
}
|
|
19
|
+
function isInsideRoot(root, candidate) {
|
|
20
|
+
const rootPath = resolve(root);
|
|
21
|
+
const candidatePath = resolve(candidate);
|
|
22
|
+
const relativePath = relative(rootPath, candidatePath);
|
|
23
|
+
return relativePath === "" || relativePath.split(/[\\/]/u)[0] !== "..";
|
|
24
|
+
}
|
|
25
|
+
/** Find the single Sparkling-compatible app config without executing it. */
|
|
26
|
+
export function findAppConfig(root) {
|
|
27
|
+
const matches = APP_CONFIG_FILES.map((file) => join(root, file)).filter((file) => existsSync(file));
|
|
28
|
+
if (matches.length > 1) {
|
|
29
|
+
throw appConfigError("CLI_APP_CONFIG_AMBIGUOUS", `Multiple app.config files were found. Keep exactly one: ${matches.join(", ")}`, { files: matches });
|
|
30
|
+
}
|
|
31
|
+
return matches[0];
|
|
32
|
+
}
|
|
33
|
+
async function readConfigSource(path) {
|
|
34
|
+
const info = await stat(path).catch(() => undefined);
|
|
35
|
+
if (!info?.isFile())
|
|
36
|
+
throw appConfigError("CLI_APP_CONFIG_INVALID", `The app config is not a regular file: ${path}`, { path });
|
|
37
|
+
if (info.size > MAX_APP_CONFIG_BYTES)
|
|
38
|
+
throw appConfigError("CLI_APP_CONFIG_TOO_LARGE", `The app config exceeds the ${MAX_APP_CONFIG_BYTES} byte limit: ${path}`, { path, maxBytes: MAX_APP_CONFIG_BYTES });
|
|
39
|
+
return readFile(path, "utf8");
|
|
40
|
+
}
|
|
41
|
+
function isLynxAppConfigSource(source) {
|
|
42
|
+
return /\blynxConfig\b/.test(source);
|
|
43
|
+
}
|
|
44
|
+
async function resolveExport(value, context) {
|
|
45
|
+
if (typeof value === "function")
|
|
46
|
+
return value(context);
|
|
47
|
+
return value;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Load and validate a Sparkling-style app.config file only on an explicit
|
|
51
|
+
* command path. Discovery remains source-only so `doctor` and project scans
|
|
52
|
+
* never execute arbitrary project modules accidentally.
|
|
53
|
+
*/
|
|
54
|
+
export async function loadAppConfig(root, options = {}) {
|
|
55
|
+
const path = findAppConfig(root);
|
|
56
|
+
if (!path)
|
|
57
|
+
return undefined;
|
|
58
|
+
const source = await readConfigSource(path);
|
|
59
|
+
if (!isLynxAppConfigSource(source)) {
|
|
60
|
+
if (options.required)
|
|
61
|
+
throw appConfigError("CLI_APP_CONFIG_LYNX_REQUIRED", `The app config does not expose a lynxConfig property: ${path}`, { path });
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
const jiti = createJiti(path, {
|
|
66
|
+
fsCache: false,
|
|
67
|
+
moduleCache: false,
|
|
68
|
+
sourceMaps: false,
|
|
69
|
+
tsconfigPaths: true,
|
|
70
|
+
});
|
|
71
|
+
const exported = await jiti.import(path, { default: true });
|
|
72
|
+
const value = await resolveExport(exported, {
|
|
73
|
+
command: options.command ?? "build",
|
|
74
|
+
env: process.env.NODE_ENV ?? "production",
|
|
75
|
+
});
|
|
76
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
77
|
+
throw appConfigError("CLI_APP_CONFIG_INVALID", "app.config must export an object or a function returning an object.", { path });
|
|
78
|
+
return {
|
|
79
|
+
path,
|
|
80
|
+
config: normalizeLynxShipAppConfig(value),
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
if (error instanceof LynxShipError)
|
|
85
|
+
throw error;
|
|
86
|
+
if (error instanceof FrameworkError)
|
|
87
|
+
throw appConfigError("CLI_APP_CONFIG_INVALID", `Invalid Lynx app config ${path}: ${error.message}`, { path, frameworkCode: error.code });
|
|
88
|
+
throw appConfigError("CLI_APP_CONFIG_LOAD_FAILED", `Could not load ${path}: ${error instanceof Error ? error.message : String(error)}`, { path });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** Resolve an app.config asset destination while keeping it inside the project. */
|
|
92
|
+
export function resolveAppConfigAssetPath(root, config, platform, fallback) {
|
|
93
|
+
const configured = config?.config.paths?.[platform];
|
|
94
|
+
const destination = resolve(root, configured ?? fallback);
|
|
95
|
+
if (!isInsideRoot(root, destination))
|
|
96
|
+
throw appConfigError("CLI_APP_CONFIG_PATH_OUTSIDE_PROJECT", `app.config paths.${platform} must remain inside the project: ${configured}`, { platform, path: configured });
|
|
97
|
+
return destination;
|
|
98
|
+
}
|
|
99
|
+
export function resolveAppConfigProjectPath(root, value, field) {
|
|
100
|
+
if (!value)
|
|
101
|
+
return undefined;
|
|
102
|
+
const path = resolve(root, value);
|
|
103
|
+
if (!isInsideRoot(root, path))
|
|
104
|
+
throw appConfigError("CLI_APP_CONFIG_PATH_OUTSIDE_PROJECT", `app.config ${field} must remain inside the project: ${value}`, { field, path: value });
|
|
105
|
+
return path;
|
|
106
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bundle-build.d.ts","sourceRoot":"","sources":["../src/bundle-build.ts"],"names":[],"mappings":"AAkBA,MAAM,WAAW,kBAAkB;IACjC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;QAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,MAAM,CAAC,EAAE,MAAM,CAAC;YAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,OAAO,CAAC,EAAE;gBAAE,OAAO,EAAE,MAAM,CAAC;gBAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;aAAE,CAAC;SAChD,CAAC;KACH,CAAC;CACH;AAED,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,MAAM,EACb,mBAAmB,CAAC,EAAE,MAAM,GAC3B,MAAM,GAAG,SAAS,CAWpB;AAuGD,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CAwEf"}
|
package/dist/bundle-build.js
CHANGED
|
@@ -4,6 +4,8 @@ import { assert, LynxShipError } from "@lynxship/contracts";
|
|
|
4
4
|
import { acquireMicroHs } from "@lynxship/microhs";
|
|
5
5
|
import { commandExists, packageManagerScriptCommand, runProcess, runRspeedy, } from "./process-runner.js";
|
|
6
6
|
import { detectLynxFramework } from "./frameworks.js";
|
|
7
|
+
import { loadAppConfig } from "./app-config.js";
|
|
8
|
+
import { buildRspeedyAppConfig } from "./rspeedy-app-config.js";
|
|
7
9
|
export function resolveMisoBuildTarget(flake, configuredAttribute) {
|
|
8
10
|
const explicitAttribute = configuredAttribute?.trim();
|
|
9
11
|
if (explicitAttribute)
|
|
@@ -111,6 +113,15 @@ export async function buildLynxBundle(root, options = {}) {
|
|
|
111
113
|
await copyMisoBundle(root, options.miso?.artifact ?? "result/main.lynx.bundle", options.onOutput);
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
116
|
+
if (!options.script && !options.rspeedyArgs) {
|
|
117
|
+
const appConfig = await loadAppConfig(root, { required: false });
|
|
118
|
+
if (appConfig) {
|
|
119
|
+
options.onOutput?.(`Building Rspeedy from ${appConfig.path}`);
|
|
120
|
+
await buildRspeedyAppConfig(root, appConfig);
|
|
121
|
+
options.onOutput?.("Rspeedy app.config bundle ready");
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
114
125
|
if (!options.script && options.rspeedyArgs) {
|
|
115
126
|
await runRspeedy(root, "build", options.rspeedyArgs, {
|
|
116
127
|
env: options.env,
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runEarlyCommand, commandHeader } from "./runtime/early-commands.js";
|
|
3
|
+
import { createCliRuntime } from "./runtime/context.js";
|
|
4
|
+
import { runBuildOrStateCommand } from "./runtime/build-commands.js";
|
|
5
|
+
import { guidanceForError } from "./guidance.js";
|
|
6
|
+
import { exitCode } from "./commands/metadata.js";
|
|
7
|
+
const rawArgs = process.argv.slice(2);
|
|
8
|
+
const context = createCliRuntime(rawArgs);
|
|
9
|
+
async function runCli() {
|
|
10
|
+
const command = context.args.shift() ?? "help";
|
|
11
|
+
const wantsHelp = command === "help" ||
|
|
12
|
+
command === "--help" ||
|
|
13
|
+
command === "-h" ||
|
|
14
|
+
rawArgs.includes("--help") ||
|
|
15
|
+
rawArgs.includes("-h");
|
|
16
|
+
const shouldShowBanner = !context.json &&
|
|
17
|
+
(wantsHelp || rawArgs.length === 0 || rawArgs.includes("--banner"));
|
|
18
|
+
if (shouldShowBanner)
|
|
19
|
+
context.ui.banner();
|
|
20
|
+
if (wantsHelp) {
|
|
21
|
+
const { helpText } = await import("./commands/help.js");
|
|
22
|
+
if (context.ui.interactive)
|
|
23
|
+
context.ui.header("Help");
|
|
24
|
+
context.printValue(helpText());
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
context.ui.header(commandHeader(command, context.args));
|
|
28
|
+
context.ui.debug(`cwd=${context.root}`);
|
|
29
|
+
if (await runEarlyCommand(context, command))
|
|
30
|
+
return;
|
|
31
|
+
await runBuildOrStateCommand(context, command);
|
|
32
|
+
}
|
|
33
|
+
void runCli()
|
|
34
|
+
.catch((error) => {
|
|
35
|
+
const message = error instanceof Error ? error.message : "Unknown error";
|
|
36
|
+
const code = error.code ?? "CLI_ERROR";
|
|
37
|
+
const nextSteps = guidanceForError(error, { args: rawArgs });
|
|
38
|
+
if (context.json) {
|
|
39
|
+
console.log(JSON.stringify({
|
|
40
|
+
error: message,
|
|
41
|
+
code,
|
|
42
|
+
...(nextSteps.commands.length > 0
|
|
43
|
+
? {
|
|
44
|
+
nextSteps: nextSteps.commands,
|
|
45
|
+
...(nextSteps.note ? { note: nextSteps.note } : {}),
|
|
46
|
+
...(nextSteps.environment
|
|
47
|
+
? { environment: nextSteps.environment }
|
|
48
|
+
: {}),
|
|
49
|
+
}
|
|
50
|
+
: {}),
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
context.ui.error(message);
|
|
55
|
+
context.ui.nextSteps(nextSteps);
|
|
56
|
+
}
|
|
57
|
+
process.exitCode = exitCode(error);
|
|
58
|
+
})
|
|
59
|
+
.finally(async () => {
|
|
60
|
+
try {
|
|
61
|
+
await context.renderConfigurationFooter();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Configuration status must never hide the original command result.
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BuildJob, MobilePlatform, Platform } from "@lynxship/contracts";
|
|
2
|
+
import { BuildOrchestrator } from "@lynxship/build-orchestrator";
|
|
3
|
+
import type { JsonRepository } from "@lynxship/db";
|
|
4
|
+
import { SubmissionService } from "@lynxship/submit";
|
|
5
|
+
import { type LynxShipConfig } from "../config.js";
|
|
6
|
+
import { type CliState } from "../runtime/state.js";
|
|
7
|
+
import { type BoxRow, CliUi, type ProgressHandle } from "../ui/index.js";
|
|
8
|
+
export interface BuildExecutionOptions {
|
|
9
|
+
root: string;
|
|
10
|
+
ui: CliUi;
|
|
11
|
+
json: boolean;
|
|
12
|
+
ensureNativeHostForBuild: (platform: Platform, profile: string, config: LynxShipConfig) => Promise<void>;
|
|
13
|
+
requireAutolinkReady: (root: string, platform: MobilePlatform) => Promise<unknown>;
|
|
14
|
+
configuredProjectId: (config: LynxShipConfig) => string;
|
|
15
|
+
printValue: (value: unknown, view?: {
|
|
16
|
+
title: string;
|
|
17
|
+
rows: BoxRow[];
|
|
18
|
+
done: string;
|
|
19
|
+
}) => void;
|
|
20
|
+
config: LynxShipConfig;
|
|
21
|
+
profile: string;
|
|
22
|
+
platform: Platform;
|
|
23
|
+
skipUpload: boolean;
|
|
24
|
+
wait: boolean;
|
|
25
|
+
local: boolean;
|
|
26
|
+
state: CliState;
|
|
27
|
+
repository: JsonRepository<CliState>;
|
|
28
|
+
builds: BuildOrchestrator;
|
|
29
|
+
submissions: SubmissionService;
|
|
30
|
+
allowUnsigned: boolean;
|
|
31
|
+
progress?: ProgressHandle;
|
|
32
|
+
progressPrefix?: string;
|
|
33
|
+
onProgress?: (value?: number, label?: string) => void;
|
|
34
|
+
onEvent?: (message: string) => void;
|
|
35
|
+
skipBundleBuild?: boolean;
|
|
36
|
+
simulator?: boolean;
|
|
37
|
+
simulatorDevice?: string;
|
|
38
|
+
simulatorAutostart?: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function executeBuild(options: BuildExecutionOptions, showResult?: boolean): Promise<BuildJob>;
|
|
41
|
+
//# sourceMappingURL=build-execution.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-execution.d.ts","sourceRoot":"","sources":["../../src/commands/build-execution.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAE9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAA8B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAoB/E,OAAO,EAAa,KAAK,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC/D,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEzE,MAAM,WAAW,qBAAqB;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,OAAO,CAAC;IACd,wBAAwB,EAAE,CACxB,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,cAAc,KACnB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,oBAAoB,EAAE,CACpB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,cAAc,KACrB,OAAO,CAAC,OAAO,CAAC,CAAC;IACtB,mBAAmB,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,MAAM,CAAC;IACxD,UAAU,EAAE,CACV,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KACnD,IAAI,CAAC;IACV,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,QAAQ,CAAC;IAChB,UAAU,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,WAAW,EAAE,iBAAiB,CAAC;IAC/B,aAAa,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,EAAE,cAAc,CAAC;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,UAAU,UAAO,GAChB,OAAO,CAAC,QAAQ,CAAC,CAsNnB"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { assert } from "@lynxship/contracts";
|
|
3
|
+
import { loadConfig, resolveProfile } from "../config.js";
|
|
4
|
+
import { applyProjectPlugins } from "../plugins.js";
|
|
5
|
+
import { hasAndroidHost, isSupportedAndroidPlatform, runRealAndroidBuild, } from "../android-build.js";
|
|
6
|
+
import { hasIosHost, runRealIosBuild } from "../ios-build.js";
|
|
7
|
+
import { inspectAndroidToolchain, formatAndroidToolchainFailure, } from "../android-toolchain.js";
|
|
8
|
+
import { inspectIosToolchain, formatIosToolchainFailure, } from "../ios-toolchain.js";
|
|
9
|
+
import { runRealHarmonyBuild } from "../harmony-build.js";
|
|
10
|
+
import { runRealWebBuild } from "../web-build.js";
|
|
11
|
+
import { runRealDesktopBuild } from "../desktop-build.js";
|
|
12
|
+
import { inspectRuntimeFingerprint } from "../runtime-fingerprint.js";
|
|
13
|
+
import { saveState } from "../runtime/state.js";
|
|
14
|
+
export async function executeBuild(options, showResult = true) {
|
|
15
|
+
const { root, ui, json, ensureNativeHostForBuild, requireAutolinkReady, configuredProjectId, printValue, config, profile, platform, skipUpload, wait, local, state, repository, builds, submissions, progress: sharedProgress, progressPrefix, onProgress, onEvent, skipBundleBuild, simulator = false, simulatorDevice, simulatorAutostart = false, } = options;
|
|
16
|
+
if (wait && !local)
|
|
17
|
+
await ensureNativeHostForBuild(platform, profile, config);
|
|
18
|
+
// Host initialization can write lynxship.json (for example, the generated
|
|
19
|
+
// iOS scheme). Always read it again before resolving the build profile so a
|
|
20
|
+
// first build uses the host that was just created in the same invocation.
|
|
21
|
+
const loadedConfig = wait && !local ? await loadConfig(root) : config;
|
|
22
|
+
const pluginApplication = wait && !local
|
|
23
|
+
? await applyProjectPlugins(root, loadedConfig, { platform, profile })
|
|
24
|
+
: {
|
|
25
|
+
config: loadedConfig,
|
|
26
|
+
report: { configured: 0, plugins: [] },
|
|
27
|
+
applied: [],
|
|
28
|
+
templates: [],
|
|
29
|
+
cloud: [],
|
|
30
|
+
build: [],
|
|
31
|
+
changes: [],
|
|
32
|
+
autolink: [],
|
|
33
|
+
};
|
|
34
|
+
const effectiveConfig = pluginApplication.config;
|
|
35
|
+
if (platform === "android" || platform === "ios")
|
|
36
|
+
await requireAutolinkReady(root, platform);
|
|
37
|
+
const runtime = await inspectRuntimeFingerprint(root, platform, effectiveConfig);
|
|
38
|
+
const resolvedBuildProfile = resolveProfile(effectiveConfig, profile);
|
|
39
|
+
const job = await builds.create({
|
|
40
|
+
projectId: configuredProjectId(effectiveConfig),
|
|
41
|
+
organizationId: "local_org",
|
|
42
|
+
platform,
|
|
43
|
+
profile,
|
|
44
|
+
sourceHash: createHash("sha256").update(root).digest("hex"),
|
|
45
|
+
runtimeVersion: runtime.value,
|
|
46
|
+
runtimeInputs: runtime.inputs,
|
|
47
|
+
});
|
|
48
|
+
ui.info(`Using profile: ${profile} · platform: ${platform}`);
|
|
49
|
+
const ownsProgress = !sharedProgress;
|
|
50
|
+
const progress = sharedProgress ??
|
|
51
|
+
ui.progress(`${platform[0].toUpperCase()}${platform.slice(1)} build`);
|
|
52
|
+
const prefix = progressPrefix ? `${progressPrefix} · ` : "";
|
|
53
|
+
const reportEvent = (message) => {
|
|
54
|
+
if (onEvent)
|
|
55
|
+
onEvent(message);
|
|
56
|
+
else
|
|
57
|
+
progress.event(`${prefix}${message}`);
|
|
58
|
+
};
|
|
59
|
+
const reportProgress = (value, label) => {
|
|
60
|
+
if (onProgress)
|
|
61
|
+
onProgress(value, label);
|
|
62
|
+
else
|
|
63
|
+
progress.update(value, label ? `${prefix}${label}` : undefined);
|
|
64
|
+
};
|
|
65
|
+
try {
|
|
66
|
+
reportProgress(undefined, "Preparing build pipeline…");
|
|
67
|
+
if (wait) {
|
|
68
|
+
if (platform === "android" && !isSupportedAndroidPlatform() && !local)
|
|
69
|
+
assert(false, "ANDROID_PLATFORM_UNSUPPORTED", "Android builds are supported only on Linux, macOS and Windows.");
|
|
70
|
+
const realAndroid = platform === "android" &&
|
|
71
|
+
isSupportedAndroidPlatform() &&
|
|
72
|
+
(await hasAndroidHost(root));
|
|
73
|
+
const realIos = platform === "ios" &&
|
|
74
|
+
hasIosHost(root, resolveProfile(effectiveConfig, profile));
|
|
75
|
+
if (platform === "ios" && !realIos && !local)
|
|
76
|
+
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.");
|
|
77
|
+
if (platform === "android" && !realAndroid && !local)
|
|
78
|
+
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.");
|
|
79
|
+
if (realAndroid) {
|
|
80
|
+
const toolchain = await inspectAndroidToolchain(root);
|
|
81
|
+
assert(toolchain.ok, "ANDROID_TOOLCHAIN_REQUIRED", `Android toolchain is not ready: ${formatAndroidToolchainFailure(toolchain)}`);
|
|
82
|
+
await runRealAndroidBuild(job, {
|
|
83
|
+
root,
|
|
84
|
+
profile: resolvedBuildProfile,
|
|
85
|
+
uploadArtifacts: !skipUpload,
|
|
86
|
+
skipBundleBuild,
|
|
87
|
+
quiet: json,
|
|
88
|
+
onEvent: reportEvent,
|
|
89
|
+
onProgress: reportProgress,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
else if (realIos) {
|
|
93
|
+
const toolchain = await inspectIosToolchain(root, resolveProfile(effectiveConfig, profile), simulator ? "simulator" : "device");
|
|
94
|
+
assert(toolchain.ok, "IOS_TOOLCHAIN_REQUIRED", `iOS toolchain is not ready: ${formatIosToolchainFailure(toolchain)}`);
|
|
95
|
+
await runRealIosBuild(job, {
|
|
96
|
+
root,
|
|
97
|
+
profile: resolvedBuildProfile,
|
|
98
|
+
simulator,
|
|
99
|
+
simulatorDevice,
|
|
100
|
+
simulatorAutostart,
|
|
101
|
+
uploadArtifacts: !skipUpload,
|
|
102
|
+
skipBundleBuild,
|
|
103
|
+
quiet: json,
|
|
104
|
+
onEvent: reportEvent,
|
|
105
|
+
onProgress: reportProgress,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
else if (!local && platform === "web") {
|
|
109
|
+
await runRealWebBuild(job, {
|
|
110
|
+
root,
|
|
111
|
+
profile: resolveProfile(effectiveConfig, profile),
|
|
112
|
+
uploadArtifacts: !skipUpload,
|
|
113
|
+
skipBundleBuild,
|
|
114
|
+
quiet: json,
|
|
115
|
+
onEvent: reportEvent,
|
|
116
|
+
onProgress: reportProgress,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
else if (!local && platform === "harmony") {
|
|
120
|
+
await runRealHarmonyBuild(job, {
|
|
121
|
+
root,
|
|
122
|
+
profile: resolveProfile(effectiveConfig, profile),
|
|
123
|
+
uploadArtifacts: !skipUpload,
|
|
124
|
+
skipBundleBuild,
|
|
125
|
+
quiet: json,
|
|
126
|
+
onEvent: reportEvent,
|
|
127
|
+
onProgress: reportProgress,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else if (!local && platform === "desktop") {
|
|
131
|
+
await runRealDesktopBuild(job, {
|
|
132
|
+
root,
|
|
133
|
+
profile: resolveProfile(effectiveConfig, profile),
|
|
134
|
+
uploadArtifacts: !skipUpload,
|
|
135
|
+
allowUnsigned: options.allowUnsigned,
|
|
136
|
+
skipBundleBuild,
|
|
137
|
+
quiet: json,
|
|
138
|
+
onEvent: reportEvent,
|
|
139
|
+
onProgress: reportProgress,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
else {
|
|
143
|
+
await builds.run(job.id);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
reportProgress(100);
|
|
147
|
+
await saveState(state, repository, builds, submissions);
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
await saveState(state, repository, builds, submissions);
|
|
151
|
+
if (error instanceof Error) {
|
|
152
|
+
Object.assign(error, { buildId: job.id });
|
|
153
|
+
}
|
|
154
|
+
throw error;
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
if (ownsProgress)
|
|
158
|
+
progress.stop();
|
|
159
|
+
}
|
|
160
|
+
const result = builds.get(job.id);
|
|
161
|
+
if (showResult) {
|
|
162
|
+
printValue(result, {
|
|
163
|
+
title: `${platform[0].toUpperCase()}${platform.slice(1)} build result`,
|
|
164
|
+
rows: [
|
|
165
|
+
{ label: "Build ID", value: result.id, valueColor: "purple" },
|
|
166
|
+
{ label: "Platform", value: result.platform, valueColor: "blue" },
|
|
167
|
+
{ label: "Profile", value: result.profile, valueColor: "text" },
|
|
168
|
+
{
|
|
169
|
+
label: "Status",
|
|
170
|
+
value: result.state,
|
|
171
|
+
valueColor: result.state === "success" ? "green" : "yellow",
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
done: result.state === "success"
|
|
175
|
+
? "Build complete. Run lynxship submit to publish."
|
|
176
|
+
: "Build queued.",
|
|
177
|
+
});
|
|
178
|
+
if (result.state === "success" && result.artifact?.url)
|
|
179
|
+
ui.downloadArtifact(result.artifact.url, result.artifact.expiresAt);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProjectCommandContext } from "./project.js";
|
|
2
|
+
import type { BoxRow, CliUi } from "../ui/index.js";
|
|
3
|
+
export interface ConfigurationCommandContext {
|
|
4
|
+
root: string;
|
|
5
|
+
args: string[];
|
|
6
|
+
ui: CliUi;
|
|
7
|
+
flag: (name: string, fallback?: string | null) => string | null;
|
|
8
|
+
printValue: (value: unknown, view?: {
|
|
9
|
+
title: string;
|
|
10
|
+
rows: BoxRow[];
|
|
11
|
+
done: string;
|
|
12
|
+
}) => void;
|
|
13
|
+
project: ProjectCommandContext;
|
|
14
|
+
assertInteractivePrompt: (label: string, fallback: string, optionName: string) => Promise<string>;
|
|
15
|
+
initializeBuildProject: (context: ProjectCommandContext) => Promise<unknown>;
|
|
16
|
+
mobilePlatformValue: (value: string) => "android" | "ios";
|
|
17
|
+
}
|
|
18
|
+
export declare function runConfigurationCommands(context: ConfigurationCommandContext, command: string): Promise<void>;
|
|
19
|
+
//# sourceMappingURL=configuration.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"configuration.d.ts","sourceRoot":"","sources":["../../src/commands/configuration.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAEpD,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,KAAK,MAAM,GAAG,IAAI,CAAC;IAChE,UAAU,EAAE,CACV,KAAK,EAAE,OAAO,EACd,IAAI,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,EAAE,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KACnD,IAAI,CAAC;IACV,OAAO,EAAE,qBAAqB,CAAC;IAC/B,uBAAuB,EAAE,CACvB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,KACf,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,sBAAsB,EAAE,CAAC,OAAO,EAAE,qBAAqB,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC7E,mBAAmB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,GAAG,KAAK,CAAC;CAC3D;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,2BAA2B,EACpC,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAmPf"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { basename, join } from "node:path";
|
|
2
|
+
import { assert } from "@lynxship/contracts";
|
|
3
|
+
import { configureAndroid, configureAppStoreConnect, configureGooglePlay, configureR2, } from "../configure.js";
|
|
4
|
+
import { initializeAndroidHost, suggestedAndroidApplicationId, } from "../android-host.js";
|
|
5
|
+
import { initializeIosHost, suggestedIosBundleIdentifier, } from "../ios-host.js";
|
|
6
|
+
import { credentialStorageDescription } from "../secure-store.js";
|
|
7
|
+
export async function runConfigurationCommands(context, command) {
|
|
8
|
+
const { root, args, ui, flag, printValue, project, assertInteractivePrompt, initializeBuildProject, mobilePlatformValue, } = context;
|
|
9
|
+
if (command === "storage") {
|
|
10
|
+
assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run `lynxship storage configure` in an interactive terminal");
|
|
11
|
+
assert((args.shift() ?? "configure") === "configure", "CLI_STORAGE_COMMAND", "Only storage configure is available");
|
|
12
|
+
ui.info("Configuring Cloudflare R2. Secret fields will stay invisible…");
|
|
13
|
+
const config = await configureR2(root);
|
|
14
|
+
ui.success(`R2 bucket verified: ${config.bucket}`);
|
|
15
|
+
printValue({
|
|
16
|
+
status: "configured",
|
|
17
|
+
provider: "cloudflare-r2",
|
|
18
|
+
bucket: config.bucket,
|
|
19
|
+
}, {
|
|
20
|
+
title: "Cloudflare R2",
|
|
21
|
+
rows: [
|
|
22
|
+
{ label: "Provider", value: "Cloudflare R2", valueColor: "orange" },
|
|
23
|
+
{ label: "Bucket", value: config.bucket, valueColor: "blue" },
|
|
24
|
+
{
|
|
25
|
+
label: "Credentials",
|
|
26
|
+
value: credentialStorageDescription(),
|
|
27
|
+
valueColor: "green",
|
|
28
|
+
},
|
|
29
|
+
],
|
|
30
|
+
done: "R2 is ready for signed build artifacts.",
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
if (command === "ios") {
|
|
35
|
+
const subcommand = args.shift() ?? "host";
|
|
36
|
+
assert(subcommand === "host" && (args.shift() ?? "init") === "init", "CLI_IOS_HOST_COMMAND", "Use `lynxship ios host init` to create an iOS host.");
|
|
37
|
+
await initializeBuildProject(project);
|
|
38
|
+
const suggestedId = suggestedIosBundleIdentifier(root);
|
|
39
|
+
const bundleIdentifier = flag("--bundle-identifier") ??
|
|
40
|
+
(await assertInteractivePrompt("iOS bundle identifier", suggestedId, "--bundle-identifier"));
|
|
41
|
+
const result = await initializeIosHost(root, {
|
|
42
|
+
bundleIdentifier,
|
|
43
|
+
appName: basename(root),
|
|
44
|
+
appIcon: flag("--icon") ?? undefined,
|
|
45
|
+
});
|
|
46
|
+
ui.success(`iOS host created: ${result.directory}`);
|
|
47
|
+
printValue({
|
|
48
|
+
status: "created",
|
|
49
|
+
platform: "ios",
|
|
50
|
+
directory: result.directory,
|
|
51
|
+
bundleIdentifier: result.bundleIdentifier,
|
|
52
|
+
project: result.project,
|
|
53
|
+
scheme: result.scheme,
|
|
54
|
+
configUpdated: result.configUpdated,
|
|
55
|
+
}, {
|
|
56
|
+
title: "iOS host",
|
|
57
|
+
rows: [
|
|
58
|
+
{
|
|
59
|
+
label: "Bundle identifier",
|
|
60
|
+
value: result.bundleIdentifier,
|
|
61
|
+
valueColor: "blue",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: "Xcode project",
|
|
65
|
+
value: result.project,
|
|
66
|
+
valueColor: "green",
|
|
67
|
+
},
|
|
68
|
+
{ label: "Scheme", value: result.scheme, valueColor: "purple" },
|
|
69
|
+
{
|
|
70
|
+
label: "CocoaPods",
|
|
71
|
+
value: "Run pod install on macOS before the first build",
|
|
72
|
+
valueColor: "yellow",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
done: "iOS host is ready. Run lynxship doctor --platform ios on macOS.",
|
|
76
|
+
});
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (command === "android") {
|
|
80
|
+
const subcommand = args.shift() ?? "configure";
|
|
81
|
+
if (subcommand === "host") {
|
|
82
|
+
assert((args.shift() ?? "init") === "init", "CLI_ANDROID_HOST_COMMAND", "Use `lynxship android host init` to create an Android host.");
|
|
83
|
+
await initializeBuildProject(project);
|
|
84
|
+
const suggestedId = suggestedAndroidApplicationId(root);
|
|
85
|
+
const applicationId = flag("--application-id") ??
|
|
86
|
+
(await assertInteractivePrompt("Android application ID", suggestedId, "--application-id"));
|
|
87
|
+
const result = await initializeAndroidHost(root, {
|
|
88
|
+
applicationId,
|
|
89
|
+
appName: basename(root),
|
|
90
|
+
});
|
|
91
|
+
ui.success(`Android host created: ${result.directory}`);
|
|
92
|
+
printValue({
|
|
93
|
+
status: "created",
|
|
94
|
+
platform: "android",
|
|
95
|
+
directory: result.directory,
|
|
96
|
+
applicationId: result.applicationId,
|
|
97
|
+
}, {
|
|
98
|
+
title: "Android host",
|
|
99
|
+
rows: [
|
|
100
|
+
{
|
|
101
|
+
label: "Application ID",
|
|
102
|
+
value: result.applicationId,
|
|
103
|
+
valueColor: "blue",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
label: "Gradle wrapper",
|
|
107
|
+
value: join(result.directory, "gradlew"),
|
|
108
|
+
valueColor: "green",
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
done: "Android host is ready. Run lynxship doctor, then lynxship build.",
|
|
112
|
+
});
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run `lynxship android configure` in an interactive terminal");
|
|
116
|
+
assert(subcommand === "configure", "CLI_ANDROID_COMMAND", "Only android configure is available");
|
|
117
|
+
ui.info("Configuring Android signing. Secret fields will stay invisible…");
|
|
118
|
+
const result = await configureAndroid(root);
|
|
119
|
+
ui.success(result.generated
|
|
120
|
+
? `Android keystore generated: ${result.keystorePath}`
|
|
121
|
+
: `Android signing credentials saved in ${credentialStorageDescription()}`);
|
|
122
|
+
printValue({ status: "configured", provider: "android-keystore" }, {
|
|
123
|
+
title: "Android signing",
|
|
124
|
+
rows: [
|
|
125
|
+
{ label: "Keystore", value: "Configured", valueColor: "green" },
|
|
126
|
+
{
|
|
127
|
+
label: "Storage",
|
|
128
|
+
value: credentialStorageDescription(),
|
|
129
|
+
valueColor: "green",
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
done: "Android signing is ready for the next build.",
|
|
133
|
+
});
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (command === "store") {
|
|
137
|
+
assert(ui.interactive, "CLI_INTERACTIVE_REQUIRED", "Run store configure in an interactive terminal");
|
|
138
|
+
assert((args.shift() ?? "configure") === "configure", "CLI_STORE_COMMAND", "Only store configure is available");
|
|
139
|
+
const platform = mobilePlatformValue(flag("--platform", "android"));
|
|
140
|
+
ui.info(platform === "android"
|
|
141
|
+
? "Configuring Google Play submission. Secret fields will stay invisible…"
|
|
142
|
+
: "Configuring App Store Connect submission. Private key contents stay protected…");
|
|
143
|
+
if (platform === "android")
|
|
144
|
+
await configureGooglePlay(root);
|
|
145
|
+
else
|
|
146
|
+
await configureAppStoreConnect(root);
|
|
147
|
+
ui.success(platform === "android"
|
|
148
|
+
? "Google Play submission credentials saved securely"
|
|
149
|
+
: "App Store Connect submission credentials saved securely");
|
|
150
|
+
printValue({
|
|
151
|
+
status: "configured",
|
|
152
|
+
provider: platform === "android" ? "google-play" : "app-store-connect",
|
|
153
|
+
storage: credentialStorageDescription(),
|
|
154
|
+
}, {
|
|
155
|
+
title: platform === "android"
|
|
156
|
+
? "Google Play submission"
|
|
157
|
+
: "App Store Connect submission",
|
|
158
|
+
rows: [
|
|
159
|
+
{
|
|
160
|
+
label: "Provider",
|
|
161
|
+
value: platform === "android" ? "Google Play" : "App Store Connect",
|
|
162
|
+
valueColor: "blue",
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
label: "Credentials",
|
|
166
|
+
value: credentialStorageDescription(),
|
|
167
|
+
valueColor: "green",
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
done: "Store submission is ready for the next build.",
|
|
171
|
+
});
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type MobilePlatform } from "@lynxship/contracts";
|
|
2
|
+
import { type ProjectCommandContext } from "./project.js";
|
|
3
|
+
import type { BoxRow, CliUi } from "../ui/index.js";
|
|
4
|
+
export interface DevelopmentCommandContext {
|
|
5
|
+
root: string;
|
|
6
|
+
args: readonly string[];
|
|
7
|
+
ui: CliUi;
|
|
8
|
+
json: boolean;
|
|
9
|
+
flag: (name: string, fallback?: string | null) => string | null;
|
|
10
|
+
project: ProjectCommandContext;
|
|
11
|
+
printValue: (value: unknown, view?: {
|
|
12
|
+
title: string;
|
|
13
|
+
rows: BoxRow[];
|
|
14
|
+
done: string;
|
|
15
|
+
}) => void;
|
|
16
|
+
mobilePlatformValue: (value: string) => MobilePlatform;
|
|
17
|
+
}
|
|
18
|
+
export declare function forwardedToolArgs(values: readonly string[]): string[];
|
|
19
|
+
export declare function runRspeedyCommand(context: DevelopmentCommandContext, subcommand: "dev" | "preview" | "inspect" | "build", environment?: NodeJS.ProcessEnv): Promise<void>;
|
|
20
|
+
export declare function runAutolinkCodegen(context: DevelopmentCommandContext): Promise<void>;
|
|
21
|
+
export declare function runDevToolDoctor(context: DevelopmentCommandContext, kind: "devtool" | "trace" | "recorder"): Promise<void>;
|
|
22
|
+
//# sourceMappingURL=development.d.ts.map
|