catalyst-core 1.2.0-canary.4 → 1.2.0
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/changelog.md +1 -1
- package/dist/errors/registry.js +2 -2
- package/dist/errors-index.json +2 -2
- package/dist/native/buildIos/build.js +3 -1
- package/dist/native/buildIos/index.js +7 -4
- package/dist/native/buildIos/signing.js +36 -3
- package/dist/native/iosToolchain.js +2 -1
- package/dist/native/setupServer.js +5 -3
- package/dist/native/utils.js +1 -1
- package/dist/vite/resolveDevServerConfig.js +3 -1
- package/mcp_v2/knowledge-base.json +2 -2
- package/package.json +1 -1
- package/test/native/iosInstalledSigning.test.ts +24 -0
- package/test/native/iosSigning.test.ts +15 -0
- package/test/native/setupServer.test.ts +7 -1
- package/test/vite/resolveDevServerConfig.test.js +8 -0
package/changelog.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [1.2.0] -
|
|
3
|
+
## [1.2.0] - 2026-11-13
|
|
4
4
|
|
|
5
5
|
This release builds, runs and releases the Android and iOS apps from the CLI, without opening Android Studio or Xcode. The web runtime has no breaking changes. Universal apps should read the breaking changes below and the 1.1.x to 1.2.0 section of the Version Upgrades guide. After upgrading, run `npm audit fix`: the `body-parser` floor is raised to `^1.20.9`.
|
|
6
6
|
|
package/dist/errors/registry.js
CHANGED
|
@@ -330,9 +330,9 @@ export const ERROR_DEFINITIONS = {
|
|
|
330
330
|
category: "IOS",
|
|
331
331
|
defaultMessage: "iOS code signing is not set up",
|
|
332
332
|
defaultDetails:
|
|
333
|
-
"A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
|
|
333
|
+
"A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. A debug build for an iPhone signs with WEBVIEW_CONFIG.ios.developmentTeam from config/config.json, and with the installed development profile ios.provisioningProfile names when that is set. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
|
|
334
334
|
suggestedAction:
|
|
335
|
-
"For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise,
|
|
335
|
+
"For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
|
|
336
336
|
},
|
|
337
337
|
[ERROR_CODES.IOS_ASC_API_KEY_INVALID]: {
|
|
338
338
|
category: "IOS",
|
package/dist/errors-index.json
CHANGED
|
@@ -226,8 +226,8 @@
|
|
|
226
226
|
"IOS-010": {
|
|
227
227
|
"category": "IOS",
|
|
228
228
|
"message": "iOS code signing is not set up",
|
|
229
|
-
"details": "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
|
|
230
|
-
"suggestedAction": "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise,
|
|
229
|
+
"details": "A device or release build needs a signing identity and provisioning profile. A release signs with the first of: a .p12 and its profile in credentials/ios/, an installed ios.provisioningProfile, an App Store Connect API key in credentials/ios/, an App Store profile installed on this Mac for the app id (with its certificate in the keychain), or the Apple account signed in to Xcode for WEBVIEW_CONFIG.ios.developmentTeam. xcodebuild reported a signing failure, or a release build has no team to sign for. A debug build for an iPhone signs with WEBVIEW_CONFIG.ios.developmentTeam from config/config.json, and with the installed development profile ios.provisioningProfile names when that is set. When an App Store release finds no App Store profile for its bundle id and has nothing else to sign with, the error says so first.",
|
|
230
|
+
"suggestedAction": "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios.",
|
|
231
231
|
"docUrl": "https://github.com/tata1mg/catalyst-core/blob/main/errors/IOS/IOS-010.md"
|
|
232
232
|
},
|
|
233
233
|
"IOS-011": {
|
|
@@ -11,6 +11,7 @@ const {
|
|
|
11
11
|
runInteractiveCommand
|
|
12
12
|
} = require("../utils.js");
|
|
13
13
|
const {
|
|
14
|
+
DEBUG_BUILD_SETTINGS,
|
|
14
15
|
xcodeAuthArgs
|
|
15
16
|
} = require("./signing.js");
|
|
16
17
|
module.exports = function createBuildPhase(ctx) {
|
|
@@ -138,6 +139,7 @@ module.exports = function createBuildPhase(ctx) {
|
|
|
138
139
|
CODE_SIGNING_REQUIRED=NO \
|
|
139
140
|
CODE_SIGNING_ALLOWED=NO \
|
|
140
141
|
ONLY_ACTIVE_ARCH=YES \
|
|
142
|
+
${DEBUG_BUILD_SETTINGS.join(" ")} \
|
|
141
143
|
BUILD_DIR="${derivedDataPath}/${projectName}-Build/Build/Products" \
|
|
142
144
|
CONFIGURATION_BUILD_DIR="${derivedDataPath}/${projectName}-Build/Build/Products/Debug-iphonesimulator" \
|
|
143
145
|
OS_ACTIVITY_MODE=debug \
|
|
@@ -319,7 +321,7 @@ module.exports = function createBuildPhase(ctx) {
|
|
|
319
321
|
const signingArgs = ctx.signingPlan?.style === "manual" ? [] : xcodeAuthArgs(ctx.apiKey);
|
|
320
322
|
// The streamed output is what tells you how it is going; argv, not a
|
|
321
323
|
// shell string, so a key path with spaces needs no quoting.
|
|
322
|
-
return runCommandStreaming("xcodebuild", ["-scheme", scheme, "-sdk", "iphoneos", "-configuration", "Debug", "-destination", `platform=iOS,id=${device.udid}`, `PRODUCT_BUNDLE_IDENTIFIER=${bundleId}`, "ONLY_ACTIVE_ARCH=YES", ...signingArgs, "build"]);
|
|
324
|
+
return runCommandStreaming("xcodebuild", ["-scheme", scheme, "-sdk", "iphoneos", "-configuration", "Debug", "-destination", `platform=iOS,id=${device.udid}`, `PRODUCT_BUNDLE_IDENTIFIER=${bundleId}`, "ONLY_ACTIVE_ARCH=YES", ...DEBUG_BUILD_SETTINGS, ...signingArgs, "build"]);
|
|
323
325
|
}
|
|
324
326
|
async function findPhysicalDeviceAppPath() {
|
|
325
327
|
const DERIVED_DATA_DIR = path.join(os.homedir(), "Library/Developer/Xcode/DerivedData");
|
|
@@ -35,6 +35,7 @@ const {
|
|
|
35
35
|
createProfileScanner,
|
|
36
36
|
pickInstalledProfile,
|
|
37
37
|
XCODE_TEAM_HINT,
|
|
38
|
+
deviceSigningHint,
|
|
38
39
|
missingProfileError,
|
|
39
40
|
buildExportOptions,
|
|
40
41
|
writeExportOptionsPlist,
|
|
@@ -469,10 +470,12 @@ function createIosBuild(config, {
|
|
|
469
470
|
progress.complete("build");
|
|
470
471
|
} catch (error) {
|
|
471
472
|
// The diagnostic in the catch below shows what xcodebuild
|
|
472
|
-
// said. A signing failure gets IOS-010
|
|
473
|
-
//
|
|
474
|
-
//
|
|
475
|
-
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the device build", error
|
|
473
|
+
// said. A signing failure gets IOS-010 with the
|
|
474
|
+
// development hint (ios.developmentTeam, or what to check
|
|
475
|
+
// in ios.provisioningProfile), not the App Store one.
|
|
476
|
+
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the device build", error, {
|
|
477
|
+
hint: deviceSigningHint(signingPlan)
|
|
478
|
+
}) : error;
|
|
476
479
|
failure.stage = failure.stage || "Building for the device";
|
|
477
480
|
throw failure;
|
|
478
481
|
}
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.XCODE_TEAM_HINT = exports.SIGNING_PROPERTIES = exports.EXPORT_METHODS = void 0;
|
|
6
|
+
exports.XCODE_TEAM_HINT = exports.SIGNING_PROPERTIES = exports.EXPORT_METHODS = exports.DEVICE_TEAM_HINT = exports.DEBUG_BUILD_SETTINGS = void 0;
|
|
7
7
|
exports.buildExportOptions = buildExportOptions;
|
|
8
8
|
exports.chooseXcodeTeam = chooseXcodeTeam;
|
|
9
9
|
exports.createProfileScanner = createProfileScanner;
|
|
10
|
+
exports.deviceSigningHint = deviceSigningHint;
|
|
10
11
|
exports.findInstalledProfileIdentity = findInstalledProfileIdentity;
|
|
11
12
|
exports.isReleaseBuildType = isReleaseBuildType;
|
|
12
13
|
exports.isSigningFailure = isSigningFailure;
|
|
@@ -222,6 +223,15 @@ function readIosCredentials(dir, {
|
|
|
222
223
|
};
|
|
223
224
|
}
|
|
224
225
|
|
|
226
|
+
/**
|
|
227
|
+
* xcodebuild build settings for every debug build, simulator and iPhone.
|
|
228
|
+
* ENABLE_DEBUG_DYLIB=NO: for Xcode's Previews a Debug app is split into the
|
|
229
|
+
* app, <app>.debug.dylib and __preview.dylib, each signed on its own, so an
|
|
230
|
+
* iPhone build asked for the Keychain password three times. A command line
|
|
231
|
+
* build has no use for Previews. The release archive does not use them.
|
|
232
|
+
*/
|
|
233
|
+
const DEBUG_BUILD_SETTINGS = exports.DEBUG_BUILD_SETTINGS = ["ENABLE_DEBUG_DYLIB=NO"];
|
|
234
|
+
|
|
225
235
|
/**
|
|
226
236
|
* xcodebuild flags that let automatic signing create and fetch certificates
|
|
227
237
|
* and profiles: with the API key when there is one (cloud signing),
|
|
@@ -701,6 +711,25 @@ function findInstalledProfileIdentity({
|
|
|
701
711
|
|
|
702
712
|
const XCODE_TEAM_HINT = exports.XCODE_TEAM_HINT = "Open Xcode > Settings > Accounts and sign in with the Apple account that has your company's team, or set WEBVIEW_CONFIG.ios.developmentTeam.";
|
|
703
713
|
|
|
714
|
+
/**
|
|
715
|
+
* IOS-010's hint when Xcode could not sign a debug build for an iPhone. That
|
|
716
|
+
* build signs with what config/config.json sets, nothing else: the team, and
|
|
717
|
+
* an installed development profile when ios.provisioningProfile names one.
|
|
718
|
+
*/
|
|
719
|
+
const DEVICE_TEAM_HINT = exports.DEVICE_TEAM_HINT = "Set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID. With Xcode signed in to that team, Xcode signs automatically. Or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone.";
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* DEVICE_TEAM_HINT, or for a plan that signs with ios.provisioningProfile
|
|
723
|
+
* what to check in that profile.
|
|
724
|
+
*
|
|
725
|
+
* @param {ReturnType<typeof planIosSigning>} [plan]
|
|
726
|
+
* @returns {string}
|
|
727
|
+
*/
|
|
728
|
+
function deviceSigningHint(plan) {
|
|
729
|
+
if (!plan?.provisioningProfile) return DEVICE_TEAM_HINT;
|
|
730
|
+
return `Check that the profile "${plan.provisioningProfile}" set in WEBVIEW_CONFIG.ios.provisioningProfile includes this iPhone, matches the bundle id, and that its certificate is in your keychain.`;
|
|
731
|
+
}
|
|
732
|
+
|
|
704
733
|
/**
|
|
705
734
|
* IOS-010's detail and hint for an App Store release that found no installed
|
|
706
735
|
* App Store profile for its bundle id (plan.missingProfileFor) and has no
|
|
@@ -1188,17 +1217,21 @@ function signingFailureLine(output) {
|
|
|
1188
1217
|
* @param {any} [cause]
|
|
1189
1218
|
* @param {object} [options]
|
|
1190
1219
|
* @param {string} [options.missingProfileFor] - plan.missingProfileFor
|
|
1220
|
+
* @param {string} [options.hint] - over the registry's hint, e.g. deviceSigningHint(plan)
|
|
1191
1221
|
* @returns {any}
|
|
1192
1222
|
*/
|
|
1193
1223
|
function signingSetupError(detail, cause, {
|
|
1194
|
-
missingProfileFor
|
|
1224
|
+
missingProfileFor,
|
|
1225
|
+
hint
|
|
1195
1226
|
} = {}) {
|
|
1196
1227
|
const said = signingFailureLine(cause?.output);
|
|
1197
1228
|
const failed = said ? `${detail}: ${said}` : detail;
|
|
1198
1229
|
const missing = missingProfileError(missingProfileFor, failed);
|
|
1199
1230
|
const error = missing ? (0, _utils.nativeError)("IOS-010", missing.detail, {
|
|
1200
1231
|
hint: missing.hint
|
|
1201
|
-
}) : (0, _utils.nativeError)("IOS-010", failed
|
|
1232
|
+
}) : (0, _utils.nativeError)("IOS-010", failed, {
|
|
1233
|
+
hint
|
|
1234
|
+
});
|
|
1202
1235
|
if (cause) {
|
|
1203
1236
|
error.cause = cause;
|
|
1204
1237
|
if (cause.output) error.output = cause.output;
|
|
@@ -489,7 +489,8 @@ async function chooseIosTarget({
|
|
|
489
489
|
/**
|
|
490
490
|
* The host a debug build reaches the dev server on. The simulator shares the
|
|
491
491
|
* Mac's network, so localhost. An iPhone uses the Mac's Bonjour name
|
|
492
|
-
* (`<
|
|
492
|
+
* (`<localhostname>.local`, lowercased), else its LAN IP; IOS-015 when there
|
|
493
|
+
* is neither.
|
|
493
494
|
* It goes into the build only, never into config.json.
|
|
494
495
|
*
|
|
495
496
|
* @param {{ target: string, name?: string } | null} target
|
|
@@ -72,8 +72,8 @@ function getLocalIPAddress(interfaces = _os.default.networkInterfaces()) {
|
|
|
72
72
|
|
|
73
73
|
/**
|
|
74
74
|
* The host a physical device on the same network reaches this Mac on:
|
|
75
|
-
* `<
|
|
76
|
-
* Null when neither is available.
|
|
75
|
+
* `<localhostname>.local` (Bonjour, survives DHCP changes, lowercased), else
|
|
76
|
+
* the LAN IP. Null when neither is available.
|
|
77
77
|
*
|
|
78
78
|
* @param {object} [options]
|
|
79
79
|
* @param {() => string} [options.localHostName] - returns `scutil --get LocalHostName`
|
|
@@ -90,8 +90,10 @@ function getDeviceReachableHost({
|
|
|
90
90
|
} catch (error) {
|
|
91
91
|
// Not macOS, or scutil failed: fall back to the LAN IP.
|
|
92
92
|
}
|
|
93
|
+
// Lowercase: WebKit sends the Host header lowercased, and Vite's
|
|
94
|
+
// server.allowedHosts compares it exactly.
|
|
93
95
|
if (/^[A-Za-z0-9-]+$/.test(name)) return {
|
|
94
|
-
host: `${name}.local`,
|
|
96
|
+
host: `${name.toLowerCase()}.local`,
|
|
95
97
|
source: "bonjour"
|
|
96
98
|
};
|
|
97
99
|
const ip = getLocalIPAddress(interfaces);
|
package/dist/native/utils.js
CHANGED
|
@@ -508,7 +508,7 @@ const NATIVE_ERRORS = exports.NATIVE_ERRORS = {
|
|
|
508
508
|
},
|
|
509
509
|
"IOS-010": {
|
|
510
510
|
message: "iOS code signing is not set up",
|
|
511
|
-
hint: "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise,
|
|
511
|
+
hint: "For an App Store release, install the App Store provisioning profile for your bundle id (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles) or set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name. Otherwise, sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team. For a debug build on an iPhone, set WEBVIEW_CONFIG.ios.developmentTeam in config/config.json to your 10-character team ID (with Xcode signed in to that team, Xcode signs automatically), or also set ios.provisioningProfile to the name of an installed development profile that includes this iPhone. When ios.provisioningProfile is set, check that the profile includes this iPhone, matches the bundle id, and that its certificate is in your keychain. In CI, put the signing files in credentials/ios/ and set CATALYST_IOS_CREDENTIALS to the output of catalyst credentials:export ios."
|
|
512
512
|
},
|
|
513
513
|
"IOS-011": {
|
|
514
514
|
message: "App Store Connect API key settings are incomplete or wrong",
|
|
@@ -38,11 +38,13 @@ export function resolveDevFsAllow(devFs, frameworkPaths) {
|
|
|
38
38
|
* CATALYST_DEV_ALLOWED_HOSTS. `catalyst buildApp:ios` sets it to the Mac's
|
|
39
39
|
* `<name>.local` when a physical iPhone loads the dev server by that name:
|
|
40
40
|
* Vite answers 403 to any Host header that is not localhost or an IP.
|
|
41
|
+
* Hosts are lowercased: browsers send the Host header lowercased and Vite
|
|
42
|
+
* compares it exactly.
|
|
41
43
|
*/
|
|
42
44
|
export function withExtraAllowedHosts(allowedHosts, extra) {
|
|
43
45
|
const hosts = String(extra || "")
|
|
44
46
|
.split(",")
|
|
45
|
-
.map((host) => host.trim())
|
|
47
|
+
.map((host) => host.trim().toLowerCase())
|
|
46
48
|
.filter(Boolean)
|
|
47
49
|
if (allowedHosts === true || hosts.length === 0) return allowedHosts
|
|
48
50
|
const base = Array.isArray(allowedHosts) ? allowedHosts : []
|
|
@@ -170,7 +170,7 @@
|
|
|
170
170
|
{
|
|
171
171
|
"section": "build_system",
|
|
172
172
|
"title": "iOS Build Pipeline",
|
|
173
|
-
"content": "npm run ios (catalyst buildApp:ios) checks full Xcode and offers the licence and first-launch steps, installs a simulator runtime, uses an existing iPhone simulator (ios.simulatorName, else the last pick, a booted one or the newest; 'Catalyst iPhone' is created after asking only when there is none), picks the target (simulator by default; a terminal run asks when an iPhone is connected; --device uses the iPhone; the pick is remembered in .catalyst/local.json), starts catalyst start when nothing answers and waits for it, generates ConfigConstants.swift and plugin metadata from WEBVIEW_CONFIG, syncs public/ios assets into the package-owned Xcode project, compiles with the Debug configuration, and installs and launches the .app. The simulator loads the dev server on localhost; an iPhone on the Mac's <
|
|
173
|
+
"content": "npm run ios (catalyst buildApp:ios) checks full Xcode and offers the licence and first-launch steps, installs a simulator runtime, uses an existing iPhone simulator (ios.simulatorName, else the last pick, a booted one or the newest; 'Catalyst iPhone' is created after asking only when there is none), picks the target (simulator by default; a terminal run asks when an iPhone is connected; --device uses the iPhone; the pick is remembered in .catalyst/local.json), starts catalyst start when nothing answers and waits for it, generates ConfigConstants.swift and plugin metadata from WEBVIEW_CONFIG, syncs public/ios assets into the package-owned Xcode project, compiles with the Debug configuration, and installs and launches the .app. The simulator loads the dev server on localhost; an iPhone on the Mac's <localhostname>.local, lowercased (else the LAN IP), with NSAllowsLocalNetworking in debug. An iPhone build signs with what config.json sets and looks nothing up: ios.developmentTeam (with Xcode signed in to that team, Xcode signs automatically; a free account works), plus ios.provisioningProfile to sign with an installed development profile that includes the iPhone (and ios.bundleIdentifier when the profile is for another id); a signing failure is IOS-010. A debug build never writes config.json. With --release (or the deprecated ios.buildType release) it archives and exports the signed store IPA instead. The generated project is located under the resolved catalyst-core package at dist/native/iosnativeWebView/iosnativeWebView.xcodeproj.",
|
|
174
174
|
"layer": "Build",
|
|
175
175
|
"source": "static",
|
|
176
176
|
"tags": ["ios", "build", "buildApp:ios", "debug", "xcode"],
|
|
@@ -484,7 +484,7 @@
|
|
|
484
484
|
{
|
|
485
485
|
"section": "config_structure",
|
|
486
486
|
"title": "WEBVIEW_CONFIG Required Fields",
|
|
487
|
-
"content": "WEBVIEW_CONFIG field reference (catalyst-core 1.2.0+, from nativeConfig.js):\n\nTOP-LEVEL (both platforms):\n- appId (string, required for release): Android application id and iOS bundle id, e.g. com.company.app. android.packageName / ios.bundleIdentifier / ios.appBundleId win over it when set. Release refuses com.example.* (NATIVE-003); a release in a terminal asks for it once and saves it. An iOS bundle id allows only letters, digits, - and ., so an appId with an underscore fails every iOS build (NATIVE-010): set ios.bundleIdentifier\n- appName (string, optional): name under the icon; wins over android.appName / ios.appName\n- url (string, required for release): https origin the release app loads, no path. --url overrides. Debug builds ignore it\n- allowPrivateHost (boolean, optional): lets a release load a private host\n- LOCAL_IP (string, optional): 'localhost' makes debug builds use the dev server (they work out its address). A real host (public, or a private VPN/LAN staging server, not this machine's own address) is loaded by debug builds instead; apps without url still use LOCAL_IP/port/useHttps as the release host\n- port (string, optional): dev server port when NODE_SERVER_PORT is not set\n- appInfo (string, optional): free-form text useDeviceInfo() reports, e.g. \"android-5Feb2026-v2.1.0\". The iOS build writes \"\" when it is missing\n- cachePattern (optional): set it per platform instead. A top-level cachePattern wins over android.cachePattern and ios.cachePattern\n- accessControl.enabled / allowedUrls (optional): URL allowlist, off when the block is missing; debug builds add the dev server origin\n\nANDROID (WEBVIEW_CONFIG.android, all optional):\n- sdkPath: else ANDROID_HOME, ANDROID_SDK_ROOT, default location; installed when missing\n- emulatorName: else the last used or catalyst-api36, created when missing\n- buildType: deprecated; 'release' builds a release with a warning\n- outputPath: release folder instead of build-output/android\n- buildOptimisation\n- cachePattern: comma-separated string of globs, e.g. \"*.css,*.js,*.png\"\n\nIOS (WEBVIEW_CONFIG.ios, all optional):\n- developmentTeam: Apple team ID, needed for an iPhone and for cloud or Xcode-account signing (IOS-010)\n- simulatorName: simulator to use when it exists; else the last pick, a booted iPhone simulator or the newest one. 'Catalyst iPhone' is created only when there is no iPhone simulator\n- deviceUDID: which iPhone --device uses without a terminal\n- provisioningProfile: installed profile name (or UUID) for manual signing; a terminal release saves the installed App Store profile it picked here, pinning later releases to it (remove the key to let the build pick again)\n- exportMethod: app-store-connect (default), release-testing, enterprise or debugging\n- buildType: deprecated; 'release' builds the store IPA with a warning\n- cachePattern: array of globs, e.g. [\"*.css\", \"*.js\"]; a string is read as one pattern, not split on commas\n\nVersion comes from package.json; signing files live in credentials/android/ and credentials/ios/, never in config.json.",
|
|
487
|
+
"content": "WEBVIEW_CONFIG field reference (catalyst-core 1.2.0+, from nativeConfig.js):\n\nTOP-LEVEL (both platforms):\n- appId (string, required for release): Android application id and iOS bundle id, e.g. com.company.app. android.packageName / ios.bundleIdentifier / ios.appBundleId win over it when set. Release refuses com.example.* (NATIVE-003); a release in a terminal asks for it once and saves it. An iOS bundle id allows only letters, digits, - and ., so an appId with an underscore fails every iOS build (NATIVE-010): set ios.bundleIdentifier\n- appName (string, optional): name under the icon; wins over android.appName / ios.appName\n- url (string, required for release): https origin the release app loads, no path. --url overrides. Debug builds ignore it\n- allowPrivateHost (boolean, optional): lets a release load a private host\n- LOCAL_IP (string, optional): 'localhost' makes debug builds use the dev server (they work out its address). A real host (public, or a private VPN/LAN staging server, not this machine's own address) is loaded by debug builds instead; apps without url still use LOCAL_IP/port/useHttps as the release host\n- port (string, optional): dev server port when NODE_SERVER_PORT is not set\n- appInfo (string, optional): free-form text useDeviceInfo() reports, e.g. \"android-5Feb2026-v2.1.0\". The iOS build writes \"\" when it is missing\n- cachePattern (optional): set it per platform instead. A top-level cachePattern wins over android.cachePattern and ios.cachePattern\n- accessControl.enabled / allowedUrls (optional): URL allowlist, off when the block is missing; debug builds add the dev server origin\n\nANDROID (WEBVIEW_CONFIG.android, all optional):\n- sdkPath: else ANDROID_HOME, ANDROID_SDK_ROOT, default location; installed when missing\n- emulatorName: else the last used or catalyst-api36, created when missing\n- buildType: deprecated; 'release' builds a release with a warning\n- outputPath: release folder instead of build-output/android\n- buildOptimisation\n- cachePattern: comma-separated string of globs, e.g. \"*.css,*.js,*.png\"\n\nIOS (WEBVIEW_CONFIG.ios, all optional):\n- developmentTeam: Apple team ID, needed for an iPhone and for cloud or Xcode-account signing (IOS-010); an iPhone debug build reads it from here only, a release in a terminal reads it from Xcode and saves it here when unset\n- simulatorName: simulator to use when it exists; else the last pick, a booted iPhone simulator or the newest one. 'Catalyst iPhone' is created only when there is no iPhone simulator\n- deviceUDID: which iPhone --device uses without a terminal\n- provisioningProfile: installed profile name (or UUID) for manual signing; an iPhone debug build signs with the development profile named here, which must include the iPhone; a terminal release saves the installed App Store profile it picked here, pinning later releases to it (remove the key to let the build pick again)\n- exportMethod: app-store-connect (default), release-testing, enterprise or debugging\n- buildType: deprecated; 'release' builds the store IPA with a warning\n- cachePattern: array of globs, e.g. [\"*.css\", \"*.js\"]; a string is read as one pattern, not split on commas\n\nVersion comes from package.json; signing files live in credentials/android/ and credentials/ios/, never in config.json.",
|
|
488
488
|
"layer": "Config",
|
|
489
489
|
"source": "static",
|
|
490
490
|
"tags": ["config", "WEBVIEW_CONFIG", "android", "ios", "required", "fields"]
|
package/package.json
CHANGED
|
@@ -5,8 +5,10 @@ import path from "path"
|
|
|
5
5
|
import { afterEach, describe, expect, it } from "vitest"
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
+
DEVICE_TEAM_HINT,
|
|
8
9
|
XCODE_TEAM_HINT,
|
|
9
10
|
chooseXcodeTeam,
|
|
11
|
+
deviceSigningHint,
|
|
10
12
|
missingProfileError,
|
|
11
13
|
createProfileScanner,
|
|
12
14
|
findInstalledProfileIdentity,
|
|
@@ -743,4 +745,26 @@ describe("signing failure detail", () => {
|
|
|
743
745
|
/^For an App Store release/
|
|
744
746
|
)
|
|
745
747
|
})
|
|
748
|
+
|
|
749
|
+
it("gives a device build the config hint: the team, or what to check in the profile", () => {
|
|
750
|
+
const output = 'error: Signing for "iosnativeWebView" requires a development team.'
|
|
751
|
+
const automatic = planIosSigning({ iosConfig: {} })
|
|
752
|
+
const error = signingSetupError(
|
|
753
|
+
"Xcode could not sign the device build",
|
|
754
|
+
{ output },
|
|
755
|
+
{
|
|
756
|
+
hint: deviceSigningHint(automatic),
|
|
757
|
+
}
|
|
758
|
+
)
|
|
759
|
+
expect(error.hint).toBe(DEVICE_TEAM_HINT)
|
|
760
|
+
expect(error.hint).toMatch(/WEBVIEW_CONFIG\.ios\.developmentTeam in config\/config\.json/)
|
|
761
|
+
expect(error.hint).not.toMatch(/App Store/)
|
|
762
|
+
const manual = planIosSigning({
|
|
763
|
+
iosConfig: { developmentTeam: "ABCDE12345", provisioningProfile: "Shop Dev" },
|
|
764
|
+
})
|
|
765
|
+
expect(deviceSigningHint(manual)).toBe(
|
|
766
|
+
'Check that the profile "Shop Dev" set in WEBVIEW_CONFIG.ios.provisioningProfile includes this iPhone, matches the bundle id, and that its certificate is in your keychain.'
|
|
767
|
+
)
|
|
768
|
+
expect(deviceSigningHint(undefined)).toBe(DEVICE_TEAM_HINT)
|
|
769
|
+
})
|
|
746
770
|
})
|
|
@@ -8,6 +8,7 @@ import { fileURLToPath } from "url"
|
|
|
8
8
|
import { afterEach, describe, expect, it } from "vitest"
|
|
9
9
|
|
|
10
10
|
import {
|
|
11
|
+
DEBUG_BUILD_SETTINGS,
|
|
11
12
|
buildExportOptions,
|
|
12
13
|
findInstalledProfileIdentity,
|
|
13
14
|
isReleaseBuildType,
|
|
@@ -784,6 +785,20 @@ describe("signing failures", () => {
|
|
|
784
785
|
})
|
|
785
786
|
})
|
|
786
787
|
|
|
788
|
+
describe("debug xcodebuild settings", () => {
|
|
789
|
+
it("turn off Xcode's debug dylib, so a debug build signs only the app", () => {
|
|
790
|
+
expect(DEBUG_BUILD_SETTINGS).toEqual(["ENABLE_DEBUG_DYLIB=NO"])
|
|
791
|
+
})
|
|
792
|
+
|
|
793
|
+
it("go into the simulator command and the iPhone arguments, not the release archive", () => {
|
|
794
|
+
const build = fs.readFileSync(path.join(here, "../../src/native/buildIos/build.js"), "utf8")
|
|
795
|
+
expect(build).toContain('${DEBUG_BUILD_SETTINGS.join(" ")} \\')
|
|
796
|
+
expect(build).toMatch(/"ONLY_ACTIVE_ARCH=YES",\s+\.\.\.DEBUG_BUILD_SETTINGS,/)
|
|
797
|
+
const index = fs.readFileSync(path.join(here, "../../src/native/buildIos/index.js"), "utf8")
|
|
798
|
+
expect(index).not.toContain("DEBUG_BUILD_SETTINGS")
|
|
799
|
+
})
|
|
800
|
+
})
|
|
801
|
+
|
|
787
802
|
describe("generateXCConfig signing and version", () => {
|
|
788
803
|
function phase(iosConfig: Record<string, any>, extra: Record<string, any> = {}) {
|
|
789
804
|
const projectDir = tempDir()
|
|
@@ -56,11 +56,17 @@ describe("getDeviceReachableHost", () => {
|
|
|
56
56
|
expect(
|
|
57
57
|
getDeviceReachableHost({ localHostName: () => "Janes-MacBook-Pro\n", interfaces: {} as any })
|
|
58
58
|
).toEqual({
|
|
59
|
-
host: "
|
|
59
|
+
host: "janes-macbook-pro.local",
|
|
60
60
|
source: "bonjour",
|
|
61
61
|
})
|
|
62
62
|
})
|
|
63
63
|
|
|
64
|
+
it("lowercases the Bonjour name, since WebKit sends the Host header lowercased", () => {
|
|
65
|
+
expect(
|
|
66
|
+
getDeviceReachableHost({ localHostName: () => "G206F1WHVF", interfaces: {} as any })?.host
|
|
67
|
+
).toBe("g206f1whvf.local")
|
|
68
|
+
})
|
|
69
|
+
|
|
64
70
|
it("falls back to the LAN IP, then null", () => {
|
|
65
71
|
const fails = () => {
|
|
66
72
|
throw new Error("no scutil")
|
|
@@ -125,6 +125,14 @@ test("withExtraAllowedHosts: adds the env hosts to the app's list", () => {
|
|
|
125
125
|
assert.equal(withExtraAllowedHosts(undefined, ""), undefined)
|
|
126
126
|
})
|
|
127
127
|
|
|
128
|
+
test("withExtraAllowedHosts: lowercases the env hosts and compares them exactly, as Vite does", () => {
|
|
129
|
+
assert.deepEqual(withExtraAllowedHosts(undefined, "G206F1WHVF.local"), ["g206f1whvf.local"])
|
|
130
|
+
assert.deepEqual(withExtraAllowedHosts(["My-Mac.local"], "my-mac.local"), [
|
|
131
|
+
"My-Mac.local",
|
|
132
|
+
"my-mac.local",
|
|
133
|
+
])
|
|
134
|
+
})
|
|
135
|
+
|
|
128
136
|
test("buildDevServer: CATALYST_DEV_ALLOWED_HOSTS reaches server.allowedHosts", () => {
|
|
129
137
|
const before = process.env.CATALYST_DEV_ALLOWED_HOSTS
|
|
130
138
|
process.env.CATALYST_DEV_ALLOWED_HOSTS = "my-mac.local"
|