catalyst-core 1.2.0-canary.3 → 1.2.0-canary.5
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 +34 -0
- package/dist/cli/hints.js +5 -0
- package/dist/errors/registry.js +2 -2
- package/dist/errors-index.json +2 -2
- package/dist/native/buildAppIos.js +17 -5
- package/dist/native/buildIos/build.js +3 -1
- package/dist/native/buildIos/index.js +19 -7
- package/dist/native/buildIos/signing.js +142 -22
- package/dist/native/iosToolchain.js +2 -1
- package/dist/native/setupServer.js +23 -11
- package/dist/native/utils.js +1 -1
- package/dist/server/expressServer.js +21 -13
- package/dist/vite/resolveDevServerConfig.js +3 -1
- package/mcp_v2/conversion-tasks.json +29 -24
- package/mcp_v2/knowledge-base.json +37 -9
- package/mcp_v2/test/build.test.ts +32 -0
- package/mcp_v2/test/config.test.ts +140 -1
- package/mcp_v2/test/native-scripts.test.ts +129 -0
- package/mcp_v2/tools/build.js +9 -2
- package/mcp_v2/tools/config.d.ts +3 -0
- package/mcp_v2/tools/config.js +100 -44
- package/mcp_v2/tools/conversion.js +123 -21
- package/mcp_v2/tools/tasks.js +7 -0
- package/package.json +2 -2
- package/test/cli/buildErrors.test.ts +12 -0
- package/test/native/iosInstalledSigning.test.ts +164 -1
- package/test/native/iosSigning.test.ts +15 -0
- package/test/native/setupServer.test.ts +62 -2
- package/test/vite/resolveDevServerConfig.test.js +8 -0
package/changelog.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [1.2.0] - Unreleased
|
|
4
|
+
|
|
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
|
+
|
|
7
|
+
### One command to run, one to release
|
|
8
|
+
|
|
9
|
+
New apps have four scripts. `npm run android` and `npm run ios` check the toolchain and install what is missing after asking, start the dev server when nothing answers on `NODE_SERVER_PORT`, and build, install and open the app on an emulator, simulator or phone. Android also saves the app as a debug APK in `build-output/android/debug/` that can be shared. `npm run android:release` writes a signed App Bundle, a signed APK and `upload_certificate.pem` to `build-output/android/<version>-<build>/`, and `npm run ios:release` writes a signed IPA and a zip of its dSYMs to `build-output/ios/<version>-<build>/`. The scripts run `catalyst buildApp:android` and `catalyst buildApp:ios`, with `--release` for the store builds, so older apps keep `buildApp:<platform>` and build a release with `npm run buildApp:<platform> -- --release`. `setupEmulator:android` and `setupEmulator:ios` still work and are no longer needed.
|
|
10
|
+
|
|
11
|
+
### App config
|
|
12
|
+
|
|
13
|
+
A native app needs `WEBVIEW_CONFIG.appId`, `appName` and `url`. `appId` is the Android application id and the iOS bundle id, and `android.packageName` and `ios.bundleIdentifier` (then `ios.appBundleId`) win over it. `url` is the deployed site a release opens; debug builds load the dev server. New apps get `appId` and `appName` from the project name. A release run in a terminal asks once for an app id or website that is missing or a placeholder, and saves the answer to `config/config.json`.
|
|
14
|
+
|
|
15
|
+
### Signing
|
|
16
|
+
|
|
17
|
+
Signing files live in `credentials/android/` and `credentials/ios/`, which new apps keep out of git. The first Android release in a terminal creates one upload key per app after asking; back it up, because Google Play only accepts updates signed with it. `npx catalyst credentials:export <platform>` prints the folder as one value for the `CATALYST_ANDROID_CREDENTIALS` or `CATALYST_IOS_CREDENTIALS` CI secret. On a Mac signed in to Xcode, an iOS release signs with the Apple account or an installed App Store profile and needs no files; the order the build tries is in the iOS release guide. The version comes from `package.json`, and the build number is `CATALYST_BUILD_NUMBER` or the minutes since 2024-01-01 UTC. Android apps target API 36 and align their native libraries for 16 KB memory pages.
|
|
18
|
+
|
|
19
|
+
### Breaking changes
|
|
20
|
+
|
|
21
|
+
- An iOS bundle id with an underscore fails with `NATIVE-010`. Set `ios.bundleIdentifier` to an id without one.
|
|
22
|
+
- A release with a placeholder app id (any `com.example.` id or a template id) fails with `NATIVE-003`. A release whose start URL is `localhost`, a private or link-local address, a `.local` name or this machine fails with `NATIVE-001`, unless `WEBVIEW_CONFIG.allowPrivateHost` is `true`. Run in a terminal, the build asks for the value instead.
|
|
23
|
+
- A debug build no longer installs on a connected phone by itself. A terminal run asks once where to run the app and remembers the answer in `.catalyst/local.json`; without a terminal the emulator or simulator is used. Pass `--device` for the phone.
|
|
24
|
+
- The first debug build rewrites `config/config.json` once: a value equal to this machine's LAN address, and a private `LOCAL_IP` that `NODE_SERVER_HOSTNAME` also holds, go back to `localhost`. Each change is printed. It never runs for a release, in CI or with `allowPrivateHost: true`.
|
|
25
|
+
- Keystore passwords in `android.keystoreConfig` and `android.keystore` are no longer used. Apps that uploaded a build signed with the keystore shipped in the template, or with a key generated by an earlier release build, need an upload key reset in Play Console.
|
|
26
|
+
|
|
27
|
+
### create-catalyst-app
|
|
28
|
+
|
|
29
|
+
New apps get a new starter page, an animated HTML splash screen that is on by default (`splashScreen.minDuration` `7000`), a restyled offline page, a placeholder `public/icon.png`, and a `server/index.js` with an empty stub for `preServerInit` and each per-request hook. Their `.gitignore` covers `credentials/`, `.catalyst/` and `build-output/`.
|
|
30
|
+
|
|
31
|
+
### Fixes
|
|
32
|
+
|
|
33
|
+
- `MetaTag` waits for a `split()` route's chunk before it replaces the head tags, so the server's tags are no longer removed before the route's `setMetaData` is available.
|
|
34
|
+
- Startup warns when `server/index.js` puts a lifecycle hook inside `export default { ... }`, where Catalyst never calls it. Export each hook by name.
|
|
35
|
+
- `npm run start` prints a hint when `NODE_SERVER_HOSTNAME` is not an address of this machine.
|
|
36
|
+
|
|
3
37
|
## [1.1.0] - 2026-10-22
|
|
4
38
|
|
|
5
39
|
This release contains everything in 1.0.2, namely the HTML splash screen, experimental WebMCP support and the new CLI output, plus one fix. There are no breaking API changes.
|
package/dist/cli/hints.js
CHANGED
|
@@ -27,6 +27,11 @@ const HINTS = [
|
|
|
27
27
|
: `Something is already on ${where}. Stop it, or set NODE_SERVER_PORT in config/config.json.`
|
|
28
28
|
},
|
|
29
29
|
},
|
|
30
|
+
{
|
|
31
|
+
test: /listen EADDRNOTAVAIL\b(?:.*?address not available\s+(\S+):\d+)?/i,
|
|
32
|
+
hint: (match) =>
|
|
33
|
+
`NODE_SERVER_HOSTNAME ${match[1] ? `${match[1]} ` : ""}is not an address of this machine; set it to localhost (or this machine's address) in config/config.json.`,
|
|
34
|
+
},
|
|
30
35
|
{
|
|
31
36
|
test: /SDK location not found|ANDROID_HOME/i,
|
|
32
37
|
hint: () =>
|
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.",
|
|
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
|
-
"
|
|
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.",
|
|
230
|
-
"suggestedAction": "
|
|
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": {
|
|
@@ -44,6 +44,7 @@ const {
|
|
|
44
44
|
planSigningForConfig,
|
|
45
45
|
readIosCredentials,
|
|
46
46
|
resolveExportMethod,
|
|
47
|
+
settleInstalledProfile,
|
|
47
48
|
settleXcodeTeam
|
|
48
49
|
} = require("./buildIos/signing.js");
|
|
49
50
|
const {
|
|
@@ -96,10 +97,11 @@ async function askAndSave(options) {
|
|
|
96
97
|
/**
|
|
97
98
|
* A release that will sign with the Apple account in Xcode needs
|
|
98
99
|
* ios.developmentTeam. When it is unset, use the one team Xcode has that can
|
|
99
|
-
* sign this export (or ask which, in a terminal).
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* sign this export (or ask which, in a terminal). A release that auto-picked
|
|
101
|
+
* an installed App Store profile pins it as ios.provisioningProfile. In a
|
|
102
|
+
* terminal the team or profile is saved to config/config.json; without one
|
|
103
|
+
* it is used for this build only. Plans with planSigningForConfig, the same
|
|
104
|
+
* function createIosBuild signs with. Returns the appConfig to build with.
|
|
103
105
|
*
|
|
104
106
|
* Config and credential errors are left for createIosBuild, which reports
|
|
105
107
|
* them the usual way.
|
|
@@ -140,6 +142,16 @@ async function ensureSigningTeam({
|
|
|
140
142
|
found?.cleanup();
|
|
141
143
|
}
|
|
142
144
|
const file = path.join(cwd, "config", "config.json");
|
|
145
|
+
const profile = settleInstalledProfile({
|
|
146
|
+
plan,
|
|
147
|
+
log,
|
|
148
|
+
save: name => writeConfigKeys(file, {
|
|
149
|
+
"WEBVIEW_CONFIG.ios.provisioningProfile": name
|
|
150
|
+
}, {
|
|
151
|
+
log
|
|
152
|
+
})
|
|
153
|
+
});
|
|
154
|
+
if (profile?.saved) return loadAppConfig();
|
|
143
155
|
const team = await settleXcodeTeam({
|
|
144
156
|
plan,
|
|
145
157
|
release,
|
|
@@ -193,7 +205,7 @@ async function main() {
|
|
|
193
205
|
BUILD_OUTPUT_PATH
|
|
194
206
|
};
|
|
195
207
|
for (const change of migration.changes) {
|
|
196
|
-
log(`config.json ${change.path}: ${change.from} -> ${change.to}`, "
|
|
208
|
+
log(`config.json ${change.path}: ${change.from} -> ${change.to}`, "note");
|
|
197
209
|
}
|
|
198
210
|
const release = flags.release || isReleaseBuildType(appConfig.WEBVIEW_CONFIG?.ios?.buildType);
|
|
199
211
|
// A store build in a terminal asks once for a missing app id or
|
|
@@ -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,8 @@ const {
|
|
|
35
35
|
createProfileScanner,
|
|
36
36
|
pickInstalledProfile,
|
|
37
37
|
XCODE_TEAM_HINT,
|
|
38
|
+
deviceSigningHint,
|
|
39
|
+
missingProfileError,
|
|
38
40
|
buildExportOptions,
|
|
39
41
|
writeExportOptionsPlist,
|
|
40
42
|
isSigningFailure,
|
|
@@ -186,7 +188,11 @@ function createIosBuild(config, {
|
|
|
186
188
|
const apiKey = release ? resolveApiKey() : null;
|
|
187
189
|
if (release && signingPlan.style === "automatic" && !signingPlan.teamId) {
|
|
188
190
|
credentials?.cleanup();
|
|
189
|
-
|
|
191
|
+
const detail = "a signed release with automatic signing needs WEBVIEW_CONFIG.ios.developmentTeam";
|
|
192
|
+
const missing = missingProfileError(signingPlan.missingProfileFor, detail);
|
|
193
|
+
throw missing ? nativeError("IOS-010", missing.detail, {
|
|
194
|
+
hint: missing.hint
|
|
195
|
+
}) : nativeError("IOS-010", detail, signingPlan.source === "Xcode account" ? {
|
|
190
196
|
hint: XCODE_TEAM_HINT
|
|
191
197
|
} : {});
|
|
192
198
|
}
|
|
@@ -464,10 +470,12 @@ function createIosBuild(config, {
|
|
|
464
470
|
progress.complete("build");
|
|
465
471
|
} catch (error) {
|
|
466
472
|
// The diagnostic in the catch below shows what xcodebuild
|
|
467
|
-
// said. A signing failure gets IOS-010
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
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;
|
|
471
479
|
failure.stage = failure.stage || "Building for the device";
|
|
472
480
|
throw failure;
|
|
473
481
|
}
|
|
@@ -611,7 +619,9 @@ function createIosBuild(config, {
|
|
|
611
619
|
try {
|
|
612
620
|
await runCommandStreaming("xcodebuild", ["archive", "-project", `${PROJECT_NAME}.xcodeproj`, "-scheme", SCHEME_NAME, "-configuration", "Release", "-destination", "generic/platform=iOS", "-archivePath", archivePath, ...signingArgs]);
|
|
613
621
|
} catch (error) {
|
|
614
|
-
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the release archive", error
|
|
622
|
+
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the release archive", error, {
|
|
623
|
+
missingProfileFor: signing.missingProfileFor
|
|
624
|
+
}) : error;
|
|
615
625
|
failure.stage = "Archiving the release build";
|
|
616
626
|
throw failure;
|
|
617
627
|
}
|
|
@@ -629,7 +639,9 @@ function createIosBuild(config, {
|
|
|
629
639
|
try {
|
|
630
640
|
await runCommandStreaming("xcodebuild", ["-exportArchive", "-archivePath", archivePath, "-exportPath", exportPath, "-exportOptionsPlist", optionsPlist, ...(signing.style === "automatic" ? xcodeAuthArgs(ctx.apiKey) : [])]);
|
|
631
641
|
} catch (error) {
|
|
632
|
-
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the IPA for export", error
|
|
642
|
+
const failure = isSigningFailure(error.output) ? signingSetupError("Xcode could not sign the IPA for export", error, {
|
|
643
|
+
missingProfileFor: signing.missingProfileFor
|
|
644
|
+
}) : error;
|
|
633
645
|
failure.stage = "Exporting the IPA";
|
|
634
646
|
throw failure;
|
|
635
647
|
}
|
|
@@ -3,13 +3,15 @@
|
|
|
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;
|
|
14
|
+
exports.missingProfileError = missingProfileError;
|
|
13
15
|
exports.needsXcodeTeam = needsXcodeTeam;
|
|
14
16
|
exports.parseIdentities = parseIdentities;
|
|
15
17
|
exports.parseIdentity = parseIdentity;
|
|
@@ -27,6 +29,7 @@ exports.readProvisioningProfile = readProvisioningProfile;
|
|
|
27
29
|
exports.readXcodeTeams = readXcodeTeams;
|
|
28
30
|
exports.resolveExportMethod = resolveExportMethod;
|
|
29
31
|
exports.scanInstalledProfiles = scanInstalledProfiles;
|
|
32
|
+
exports.settleInstalledProfile = settleInstalledProfile;
|
|
30
33
|
exports.settleXcodeTeam = settleXcodeTeam;
|
|
31
34
|
exports.setupP12Signing = setupP12Signing;
|
|
32
35
|
exports.signingFailureLine = signingFailureLine;
|
|
@@ -220,6 +223,15 @@ function readIosCredentials(dir, {
|
|
|
220
223
|
};
|
|
221
224
|
}
|
|
222
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
|
+
|
|
223
235
|
/**
|
|
224
236
|
* xcodebuild flags that let automatic signing create and fetch certificates
|
|
225
237
|
* and profiles: with the API key when there is one (cloud signing),
|
|
@@ -296,13 +308,19 @@ function planIosSigning({
|
|
|
296
308
|
if (release && exportMethod === "app-store-connect" && bundleId && scanProfiles) {
|
|
297
309
|
const scan = scanProfiles();
|
|
298
310
|
const {
|
|
299
|
-
identity
|
|
311
|
+
identity,
|
|
312
|
+
missing
|
|
300
313
|
} = pickInstalledProfile(scan, {
|
|
301
314
|
bundleId,
|
|
302
315
|
teamId,
|
|
303
316
|
needsPush: notifications,
|
|
304
317
|
now
|
|
305
318
|
});
|
|
319
|
+
// Nothing else to sign with: IOS-010 leads with the missing profile.
|
|
320
|
+
if (missing) return {
|
|
321
|
+
...automatic("Xcode account"),
|
|
322
|
+
missingProfileFor: bundleId
|
|
323
|
+
};
|
|
306
324
|
if (identity) {
|
|
307
325
|
// By name, as people know it, unless another installed profile shares it.
|
|
308
326
|
const sameName = scan.profiles.filter(profile => profile.name === identity.profileName).length;
|
|
@@ -580,7 +598,9 @@ function isAppStoreProfile(profile) {
|
|
|
580
598
|
* profile (.mobileprovision, no devices, get-task-allow false) for exactly
|
|
581
599
|
* this bundle id, for `teamId` when set, with aps-environment when
|
|
582
600
|
* `needsPush`. Expired profiles are skipped; the newest one whose
|
|
583
|
-
* certificate is in the keychain wins. Without a match, `reason` says why
|
|
601
|
+
* certificate is in the keychain wins. Without a match, `reason` says why,
|
|
602
|
+
* and `missing` is true when no App Store profile for the bundle id is
|
|
603
|
+
* installed at all.
|
|
584
604
|
*
|
|
585
605
|
* @param {ReturnType<typeof scanInstalledProfiles>} scan
|
|
586
606
|
* @param {object} options
|
|
@@ -589,7 +609,7 @@ function isAppStoreProfile(profile) {
|
|
|
589
609
|
* @param {string} [options.teamId]
|
|
590
610
|
* @param {boolean} [options.needsPush]
|
|
591
611
|
* @param {Date} [options.now]
|
|
592
|
-
* @returns {{ identity: { sha1: string, name: string, teamId?: string, uuid: string, profileName?: string } | null, reason?: string }}
|
|
612
|
+
* @returns {{ identity: { sha1: string, name: string, teamId?: string, uuid: string, profileName?: string } | null, reason?: string, missing?: boolean }}
|
|
593
613
|
*/
|
|
594
614
|
function pickInstalledProfile(scan, {
|
|
595
615
|
profile,
|
|
@@ -611,13 +631,19 @@ function pickInstalledProfile(scan, {
|
|
|
611
631
|
};
|
|
612
632
|
let candidates = scan?.profiles || [];
|
|
613
633
|
/** Keep the candidates that pass; when none does, that check is the reason. */
|
|
614
|
-
const steps = wanted ? [[c => c.name === wanted || String(c.uuid).toLowerCase() === wanted.toLowerCase(), () => "no installed profile has that name or UUID"], [c => !(c.expires && c.expires <= now.getTime()), left => `it expired on ${left[0].expirationDate}`], [c => profileMatchesBundleId(c.applicationIdentifier, bundleId), left => `it is for ${appIdOf(left[0])}, not ${bundleId}`]] : [[c => c.file.endsWith(".mobileprovision") && c.applicationIdentifier && appIdOf(c) === bundleId, () => `no installed profile is for exactly ${bundleId}
|
|
615
|
-
for (const [keep, reason] of steps) {
|
|
634
|
+
const steps = wanted ? [[c => c.name === wanted || String(c.uuid).toLowerCase() === wanted.toLowerCase(), () => "no installed profile has that name or UUID"], [c => !(c.expires && c.expires <= now.getTime()), left => `it expired on ${left[0].expirationDate}`], [c => profileMatchesBundleId(c.applicationIdentifier, bundleId), left => `it is for ${appIdOf(left[0])}, not ${bundleId}`]] : [[c => c.file.endsWith(".mobileprovision") && c.applicationIdentifier && appIdOf(c) === bundleId, () => `no installed profile is for exactly ${bundleId}`, true], [isAppStoreProfile, () => `no installed App Store profile is for ${bundleId}`, true], [c => !teamId || c.teamId === teamId, () => `no installed App Store profile for ${bundleId} is for team ${teamId}`], [c => !needsPush || Boolean(c.apsEnvironment), () => `no installed App Store profile for ${bundleId} has push notifications`], [c => !(c.expires && c.expires <= now.getTime()), () => `every installed App Store profile for ${bundleId} has expired`]];
|
|
635
|
+
for (const [keep, reason, missing] of steps) {
|
|
616
636
|
const left = candidates.filter(keep);
|
|
617
|
-
if (!left.length)
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
637
|
+
if (!left.length) {
|
|
638
|
+
return missing ? {
|
|
639
|
+
identity: null,
|
|
640
|
+
reason: reason(candidates),
|
|
641
|
+
missing: true
|
|
642
|
+
} : {
|
|
643
|
+
identity: null,
|
|
644
|
+
reason: reason(candidates)
|
|
645
|
+
};
|
|
646
|
+
}
|
|
621
647
|
candidates = left;
|
|
622
648
|
}
|
|
623
649
|
const newestFirst = [...candidates].sort((a, b) => b.expires - a.expires);
|
|
@@ -685,6 +711,44 @@ function findInstalledProfileIdentity({
|
|
|
685
711
|
|
|
686
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.";
|
|
687
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
|
+
|
|
733
|
+
/**
|
|
734
|
+
* IOS-010's detail and hint for an App Store release that found no installed
|
|
735
|
+
* App Store profile for its bundle id (plan.missingProfileFor) and has no
|
|
736
|
+
* other signing source: the missing profile first, then every way to sign,
|
|
737
|
+
* the Xcode account last. Null for any other plan.
|
|
738
|
+
*
|
|
739
|
+
* @param {string | undefined} bundleId - plan.missingProfileFor
|
|
740
|
+
* @param {string} [detail] - what failed after that, e.g. the Xcode account
|
|
741
|
+
* @returns {{ detail: string, hint: string } | null}
|
|
742
|
+
*/
|
|
743
|
+
function missingProfileError(bundleId, detail) {
|
|
744
|
+
if (!bundleId) return null;
|
|
745
|
+
const missing = `no App Store provisioning profile for ${bundleId} is installed on this Mac`;
|
|
746
|
+
return {
|
|
747
|
+
detail: detail ? `${missing}, and ${detail}` : missing,
|
|
748
|
+
hint: `Install the App Store profile for ${bundleId} (download it from developer.apple.com, or with Xcode > Settings > Accounts > Download Manual Profiles), set WEBVIEW_CONFIG.ios.provisioningProfile to an installed profile's name, put a .p12 and its profile or an App Store Connect API key in credentials/ios/, or sign in to Xcode (Xcode > Settings > Accounts) with the Apple account that has your company's team.`
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
|
|
688
752
|
/**
|
|
689
753
|
* The teams in IDEProvisioningTeamByIdentifier (account id -> teams), one
|
|
690
754
|
* per team id. `free` is isFreeProvisioningTeam, undefined when Xcode did
|
|
@@ -753,31 +817,35 @@ function needsXcodeTeam(plan, {
|
|
|
753
817
|
* Store, release-testing or enterprise export, so it is left out unless the
|
|
754
818
|
* export is for debugging; a team Xcode did not mark either way is kept.
|
|
755
819
|
* One team is used; several are a question, NATIVE-005 without a terminal;
|
|
756
|
-
* none is IOS-010.
|
|
820
|
+
* none is IOS-010, which leads with `missingProfileFor` when that is set.
|
|
757
821
|
*
|
|
758
822
|
* @param {object} options
|
|
759
823
|
* @param {ReturnType<typeof parseXcodeTeams>} options.teams - from readXcodeTeams
|
|
760
824
|
* @param {string} [options.exportMethod]
|
|
761
825
|
* @param {boolean} [options.isTTY]
|
|
762
826
|
* @param {typeof select} [options.choose]
|
|
827
|
+
* @param {string} [options.missingProfileFor] - plan.missingProfileFor
|
|
763
828
|
* @returns {Promise<{ teamId: string, teamName: string, free: boolean | undefined }>}
|
|
764
829
|
*/
|
|
765
830
|
async function chooseXcodeTeam({
|
|
766
831
|
teams,
|
|
767
832
|
exportMethod = "app-store-connect",
|
|
768
833
|
isTTY,
|
|
769
|
-
choose = _utils.select
|
|
834
|
+
choose = _utils.select,
|
|
835
|
+
missingProfileFor
|
|
770
836
|
}) {
|
|
771
|
-
|
|
772
|
-
|
|
837
|
+
const noTeam = detail => {
|
|
838
|
+
const missing = missingProfileError(missingProfileFor, detail);
|
|
839
|
+
return missing ? (0, _utils.nativeError)("IOS-010", missing.detail, {
|
|
840
|
+
hint: missing.hint
|
|
841
|
+
}) : (0, _utils.nativeError)("IOS-010", detail, {
|
|
773
842
|
hint: XCODE_TEAM_HINT
|
|
774
843
|
});
|
|
775
|
-
}
|
|
844
|
+
};
|
|
845
|
+
if (!teams) throw noTeam("could not read which teams are signed in to Xcode");
|
|
776
846
|
const usable = exportMethod === "debugging" ? teams : teams.filter(team => team.free !== true);
|
|
777
847
|
if (!usable.length) {
|
|
778
|
-
throw (
|
|
779
|
-
hint: XCODE_TEAM_HINT
|
|
780
|
-
});
|
|
848
|
+
throw noTeam(teams.length ? `Xcode is signed in with a personal team only, and a personal team cannot sign ${exportMethod} builds` : "no Apple account with a team is signed in to Xcode");
|
|
781
849
|
}
|
|
782
850
|
if (usable.length === 1) return usable[0];
|
|
783
851
|
const label = team => `${team.teamName || "Unnamed team"} (${team.teamId})`;
|
|
@@ -831,7 +899,8 @@ async function settleXcodeTeam({
|
|
|
831
899
|
teams: readTeams(),
|
|
832
900
|
exportMethod,
|
|
833
901
|
isTTY: interactive,
|
|
834
|
-
choose
|
|
902
|
+
choose,
|
|
903
|
+
missingProfileFor: plan.missingProfileFor
|
|
835
904
|
});
|
|
836
905
|
const label = `${team.teamName || "Unnamed team"} (${team.teamId})`;
|
|
837
906
|
if (!interactive) {
|
|
@@ -849,6 +918,44 @@ async function settleXcodeTeam({
|
|
|
849
918
|
};
|
|
850
919
|
}
|
|
851
920
|
|
|
921
|
+
/**
|
|
922
|
+
* The profile step for a release that auto-picked an installed App Store
|
|
923
|
+
* profile (planIosSigning's "installed profile"): in a terminal save it as
|
|
924
|
+
* ios.provisioningProfile with `save`, so later builds sign with the same
|
|
925
|
+
* one; without a terminal (CI, agents) only use it for this build, since
|
|
926
|
+
* config.json is never written there. Null for any other plan.
|
|
927
|
+
*
|
|
928
|
+
* @param {object} options
|
|
929
|
+
* @param {ReturnType<typeof planIosSigning>} options.plan - from planSigningForConfig
|
|
930
|
+
* @param {boolean} [options.isTTY] - defaults to isInteractive()
|
|
931
|
+
* @param {(profile: string) => void} options.save - writes WEBVIEW_CONFIG.ios.provisioningProfile
|
|
932
|
+
* @param {(message: string, type?: string) => void} [options.log]
|
|
933
|
+
* @returns {{ provisioningProfile: string, saved: boolean } | null}
|
|
934
|
+
*/
|
|
935
|
+
function settleInstalledProfile({
|
|
936
|
+
plan,
|
|
937
|
+
isTTY,
|
|
938
|
+
save,
|
|
939
|
+
log = () => {}
|
|
940
|
+
}) {
|
|
941
|
+
if (plan?.source !== "installed profile") return null;
|
|
942
|
+
// The name, or the UUID when another installed profile shares the name.
|
|
943
|
+
const profile = plan.provisioningProfile;
|
|
944
|
+
if (!(isTTY ?? (0, _utils.isInteractive)())) {
|
|
945
|
+
log(`Using the installed profile "${plan.profileName}". Set WEBVIEW_CONFIG.ios.provisioningProfile to keep it.`, "note");
|
|
946
|
+
return {
|
|
947
|
+
provisioningProfile: profile,
|
|
948
|
+
saved: false
|
|
949
|
+
};
|
|
950
|
+
}
|
|
951
|
+
log(`Signing with the installed profile "${plan.profileName}" (team ${plan.teamId})`, "info");
|
|
952
|
+
save(profile);
|
|
953
|
+
return {
|
|
954
|
+
provisioningProfile: profile,
|
|
955
|
+
saved: true
|
|
956
|
+
};
|
|
957
|
+
}
|
|
958
|
+
|
|
852
959
|
/**
|
|
853
960
|
* Local signing with credentials/ios/: import the .p12 into a temporary
|
|
854
961
|
* keychain, put that keychain on the user search list, and install the
|
|
@@ -1103,15 +1210,28 @@ function signingFailureLine(output) {
|
|
|
1103
1210
|
|
|
1104
1211
|
/**
|
|
1105
1212
|
* The coded error for a signing failure, keeping xcodebuild's transcript for
|
|
1106
|
-
* the diagnostic. The detail ends with the line that says what went wrong
|
|
1213
|
+
* the diagnostic. The detail ends with the line that says what went wrong,
|
|
1214
|
+
* and starts with the missing App Store profile when `missingProfileFor` is set.
|
|
1107
1215
|
*
|
|
1108
1216
|
* @param {string} detail
|
|
1109
1217
|
* @param {any} [cause]
|
|
1218
|
+
* @param {object} [options]
|
|
1219
|
+
* @param {string} [options.missingProfileFor] - plan.missingProfileFor
|
|
1220
|
+
* @param {string} [options.hint] - over the registry's hint, e.g. deviceSigningHint(plan)
|
|
1110
1221
|
* @returns {any}
|
|
1111
1222
|
*/
|
|
1112
|
-
function signingSetupError(detail, cause
|
|
1223
|
+
function signingSetupError(detail, cause, {
|
|
1224
|
+
missingProfileFor,
|
|
1225
|
+
hint
|
|
1226
|
+
} = {}) {
|
|
1113
1227
|
const said = signingFailureLine(cause?.output);
|
|
1114
|
-
const
|
|
1228
|
+
const failed = said ? `${detail}: ${said}` : detail;
|
|
1229
|
+
const missing = missingProfileError(missingProfileFor, failed);
|
|
1230
|
+
const error = missing ? (0, _utils.nativeError)("IOS-010", missing.detail, {
|
|
1231
|
+
hint: missing.hint
|
|
1232
|
+
}) : (0, _utils.nativeError)("IOS-010", failed, {
|
|
1233
|
+
hint
|
|
1234
|
+
});
|
|
1115
1235
|
if (cause) {
|
|
1116
1236
|
error.cause = cause;
|
|
1117
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);
|
|
@@ -415,12 +417,14 @@ async function ensureDevServer({
|
|
|
415
417
|
// One-time migration: older setups wrote this machine's LAN address into
|
|
416
418
|
// config.json (every "localhost", and any http://<ip>), which makes Express
|
|
417
419
|
// bind only that address and breaks adb reverse. Put localhost back, but only
|
|
418
|
-
// for this machine's own addresses
|
|
419
|
-
// address is the app's own setting.
|
|
420
|
+
// for this machine's own addresses (or the one it had on another network):
|
|
421
|
+
// a container IP, 0.0.0.0 or a loopback address is the app's own setting.
|
|
420
422
|
// ---------------------------------------------------------------------------
|
|
421
423
|
|
|
422
424
|
const MIGRATION_KEY = "lanAddresses";
|
|
423
425
|
const IPV4 = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
426
|
+
// 10/8, 172.16/12, 192.168/16 and link-local 169.254/16.
|
|
427
|
+
const PRIVATE_LAN_IPV4 = /^(?:10|172\.(?:1[6-9]|2\d|3[01])|192\.168|169\.254)\./;
|
|
424
428
|
const isCI = env => {
|
|
425
429
|
const value = env.CI;
|
|
426
430
|
return value !== undefined && value !== "" && value !== "0" && String(value).toLowerCase() !== "false";
|
|
@@ -429,10 +433,13 @@ const isCI = env => {
|
|
|
429
433
|
/**
|
|
430
434
|
* The changes the migration would make, without writing anything. Only a
|
|
431
435
|
* value equal to one of this machine's current IPv4 addresses, or an
|
|
432
|
-
* http:// URL on one of them, goes back to localhost.
|
|
433
|
-
*
|
|
434
|
-
*
|
|
435
|
-
*
|
|
436
|
+
* http:// URL on one of them, goes back to localhost. So does a private
|
|
437
|
+
* LOCAL_IP that NODE_SERVER_HOSTNAME also holds: an earlier setup wrote this
|
|
438
|
+
* machine's address on another network into both, and the dev server can't
|
|
439
|
+
* bind it here. Any other address, private ones included (a LOCAL_IP
|
|
440
|
+
* pointing at a VPN or LAN staging server), is the app's own setting.
|
|
441
|
+
* WEBVIEW_CONFIG.url is never touched, and nothing is when
|
|
442
|
+
* WEBVIEW_CONFIG.allowPrivateHost is true.
|
|
436
443
|
*
|
|
437
444
|
* @param {object} config - the parsed config.json (not modified)
|
|
438
445
|
* @param {object} [options]
|
|
@@ -451,6 +458,11 @@ function planLanAddressMigration(config, {
|
|
|
451
458
|
};
|
|
452
459
|
// This machine's IPv4 addresses, without loopback and 0.0.0.0/8.
|
|
453
460
|
const saved = new Set([...localHosts].filter(host => IPV4.test(host) && !/^(?:127|0)\./.test(host)));
|
|
461
|
+
// The LAN address this machine had when LOCAL_IP was written.
|
|
462
|
+
const leftover = typeof wv?.LOCAL_IP === "string" ? wv.LOCAL_IP.trim() : "";
|
|
463
|
+
if (IPV4.test(leftover) && PRIVATE_LAN_IPV4.test(leftover) && String(config.NODE_SERVER_HOSTNAME ?? "").trim() === leftover) {
|
|
464
|
+
saved.add(leftover);
|
|
465
|
+
}
|
|
454
466
|
if (!saved.size) return {
|
|
455
467
|
config,
|
|
456
468
|
changes
|
|
@@ -532,8 +544,8 @@ function migrateLanAddresses({
|
|
|
532
544
|
} : undefined).changes;
|
|
533
545
|
const changes = planned.length ? writeConfigKeys(file, Object.fromEntries(planned.map(change => [change.path, change.to]))) : [];
|
|
534
546
|
if (changes.length) {
|
|
535
|
-
log(`Put localhost back in config/config.json where an earlier setup saved this machine's LAN address:`, "
|
|
536
|
-
for (const change of changes) log(` ${change.path}: ${change.from} -> ${change.to}`, "
|
|
547
|
+
log(`Put localhost back in config/config.json where an earlier setup saved this machine's LAN address:`, "note");
|
|
548
|
+
for (const change of changes) log(` ${change.path}: ${change.from} -> ${change.to}`, "note");
|
|
537
549
|
}
|
|
538
550
|
const state = (0, _localState.readLocalState)({
|
|
539
551
|
cwd
|
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: "
|
|
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",
|