@absolutejs/absolute 0.20.0-beta.104 → 0.20.0-beta.105
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 +7 -0
- package/changelog.json +22 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/{compile-g8vmvr0w.js → compile-hsbs1xf6.js} +2 -2
- package/dist/cli/{dev-m4r4zs2k.js → dev-9cp5t2n1.js} +4 -4
- package/dist/cli/{expoProject-780thyfd.js → expoProject-zkprmjjz.js} +1 -1
- package/dist/cli/{index-d5fr1xp3.js → index-0zcebhm4.js} +59 -1
- package/dist/cli/{index-wpfvpsk7.js → index-5skbydh0.js} +1 -1
- package/dist/cli/{index-3sdvzrv2.js → index-7fxrgx5q.js} +1 -1
- package/dist/cli/{index-67kedtn7.js → index-hrzd7e5x.js} +1 -1
- package/dist/cli/{index-27hhdgtg.js → index-mazxtfh4.js} +1 -1
- package/dist/cli/{index-qxnptvwn.js → index-q1d4ns4n.js} +100 -9
- package/dist/cli/index.js +5 -5
- package/dist/cli/{iosNativeWatcher-3k715hq0.js → iosNativeWatcher-t6gv0p1w.js} +2 -2
- package/dist/cli/{iosSimulatorController-dr98a0p9.js → iosSimulatorController-5n2a9fxv.js} +2 -2
- package/dist/cli/{mobile-njmnd7vc.js → mobile-fbtq3s5x.js} +896 -172
- package/dist/cli/{remoteMacProtocol-kqvkp0qd.js → remoteMacProtocol-bbramzh6.js} +2 -2
- package/dist/cli/{start-39aqmr5n.js → start-5grq7nfr.js} +3 -3
- package/dist/mobile/index.js +1191 -573
- package/dist/mobile/index.js.map +8 -7
- package/dist/mobile/remoteMacAgentEntry.js +296 -249
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/iosRelease.d.ts +31 -7
- package/dist/src/mobile/iosReleaseAcceptance.d.ts +63 -0
- package/dist/src/mobile/iosTestReport.d.ts +1 -0
- package/dist/src/mobile/nativeReleaseReadiness.d.ts +1 -1
- package/dist/src/mobile/nativeTestReport.d.ts +15 -0
- package/dist/src/mobile/remoteMacProtocol.d.ts +7 -1
- package/dist/src/mobile/shellReleaseReadiness.d.ts +1 -1
- package/package.json +1 -1
|
@@ -7,12 +7,12 @@ import {
|
|
|
7
7
|
} from "./index-zh6hhrwy.js";
|
|
8
8
|
import {
|
|
9
9
|
start
|
|
10
|
-
} from "./index-
|
|
10
|
+
} from "./index-mazxtfh4.js";
|
|
11
11
|
import {
|
|
12
12
|
ABSOLUTE_MOBILE_CLIENT_MANIFEST_FORMAT,
|
|
13
13
|
ABSOLUTE_MOBILE_PAGE_PROTOCOL_VERSION,
|
|
14
14
|
resolveAbsoluteMobileRoute
|
|
15
|
-
} from "./index-
|
|
15
|
+
} from "./index-hrzd7e5x.js";
|
|
16
16
|
import"./index-bzs19w3r.js";
|
|
17
17
|
import"./index-s2tazax7.js";
|
|
18
18
|
import {
|
|
@@ -30,15 +30,17 @@ import {
|
|
|
30
30
|
listAbsoluteRemoteMacProfiles,
|
|
31
31
|
pairAbsoluteRemoteMac,
|
|
32
32
|
removeAbsoluteRemoteMacProfile
|
|
33
|
-
} from "./index-
|
|
33
|
+
} from "./index-7fxrgx5q.js";
|
|
34
34
|
import {
|
|
35
35
|
ABSOLUTE_IOS_SIMULATOR_NAME,
|
|
36
36
|
parseIosSimulators,
|
|
37
37
|
repairAbsoluteIosDevSession
|
|
38
|
-
} from "./index-
|
|
38
|
+
} from "./index-5skbydh0.js";
|
|
39
39
|
import {
|
|
40
|
-
buildAbsoluteIosRelease
|
|
41
|
-
|
|
40
|
+
buildAbsoluteIosRelease,
|
|
41
|
+
requireAbsoluteIosReleaseMetadata,
|
|
42
|
+
resolveAbsoluteIosXcodeProject
|
|
43
|
+
} from "./index-q1d4ns4n.js";
|
|
42
44
|
import"./index-fc9vxw56.js";
|
|
43
45
|
import {
|
|
44
46
|
listLiveInstances
|
|
@@ -67,7 +69,7 @@ import {
|
|
|
67
69
|
syncAbsoluteExpoWebAssets,
|
|
68
70
|
unsignedAbsoluteMobileUpdate,
|
|
69
71
|
writeAbsoluteExpoProject
|
|
70
|
-
} from "./index-
|
|
72
|
+
} from "./index-0zcebhm4.js";
|
|
71
73
|
import {
|
|
72
74
|
buildAbsoluteExpoAndroidWslBundle
|
|
73
75
|
} from "./index-28nxv57v.js";
|
|
@@ -4412,9 +4414,9 @@ var require_sha256 = __commonJS(function(exports, module) {
|
|
|
4412
4414
|
var forge = require_forge();
|
|
4413
4415
|
require_md();
|
|
4414
4416
|
require_util();
|
|
4415
|
-
var
|
|
4416
|
-
forge.md.sha256 = forge.md.algorithms.sha256 =
|
|
4417
|
-
|
|
4417
|
+
var sha2564 = module.exports = forge.sha256 = forge.sha256 || {};
|
|
4418
|
+
forge.md.sha256 = forge.md.algorithms.sha256 = sha2564;
|
|
4419
|
+
sha2564.create = function() {
|
|
4418
4420
|
if (!_initialized) {
|
|
4419
4421
|
_init();
|
|
4420
4422
|
}
|
|
@@ -16776,15 +16778,15 @@ var require_main = __commonJS(function(exports) {
|
|
|
16776
16778
|
|
|
16777
16779
|
// src/cli/scripts/mobile.ts
|
|
16778
16780
|
import {
|
|
16779
|
-
access as
|
|
16780
|
-
mkdir as
|
|
16781
|
-
mkdtemp as
|
|
16782
|
-
readFile as
|
|
16783
|
-
rm as
|
|
16781
|
+
access as access14,
|
|
16782
|
+
mkdir as mkdir15,
|
|
16783
|
+
mkdtemp as mkdtemp5,
|
|
16784
|
+
readFile as readFile19,
|
|
16785
|
+
rm as rm8,
|
|
16784
16786
|
writeFile as writeFile16
|
|
16785
16787
|
} from "fs/promises";
|
|
16786
16788
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16787
|
-
import { join as
|
|
16789
|
+
import { join as join16, relative as relative13, resolve as resolve14 } from "path";
|
|
16788
16790
|
import { createInterface } from "readline/promises";
|
|
16789
16791
|
|
|
16790
16792
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -18459,6 +18461,8 @@ import { mkdir as mkdir3, readFile as readFile6, readdir as readdir3, rename as
|
|
|
18459
18461
|
import { dirname as dirname3, join as join6 } from "path";
|
|
18460
18462
|
var START3 = "// absolutejs:release-readiness:start";
|
|
18461
18463
|
var END3 = "// absolutejs:release-readiness:end";
|
|
18464
|
+
var IOS_PLUGIN_START = "// absolutejs:release-readiness-plugin:start";
|
|
18465
|
+
var IOS_PLUGIN_END = "// absolutejs:release-readiness-plugin:end";
|
|
18462
18466
|
var ANDROID_PLUGIN3 = "AbsoluteReleaseReadinessPlugin.java";
|
|
18463
18467
|
var optionalSource4 = async (path) => {
|
|
18464
18468
|
try {
|
|
@@ -18580,9 +18584,70 @@ public final class AbsoluteReleaseReadinessPlugin extends Plugin {
|
|
|
18580
18584
|
}
|
|
18581
18585
|
}
|
|
18582
18586
|
`;
|
|
18583
|
-
var
|
|
18584
|
-
|
|
18585
|
-
|
|
18587
|
+
var iosPluginRegion3 = `${IOS_PLUGIN_START}
|
|
18588
|
+
@objc(AbsoluteReleaseReadinessPlugin)
|
|
18589
|
+
public final class AbsoluteReleaseReadinessPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
18590
|
+
public let identifier = "AbsoluteReleaseReadinessPlugin"
|
|
18591
|
+
public let jsName = "AbsoluteReleaseReadiness"
|
|
18592
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
18593
|
+
CAPPluginMethod(name: "ready", returnType: CAPPluginReturnPromise)
|
|
18594
|
+
]
|
|
18595
|
+
|
|
18596
|
+
@objc public func ready(_ call: CAPPluginCall) {
|
|
18597
|
+
let info = Bundle.main.infoDictionary ?? [:]
|
|
18598
|
+
let version = info["CFBundleShortVersionString"] as? String ?? "unknown"
|
|
18599
|
+
let build = info["CFBundleVersion"] as? String ?? "unknown"
|
|
18600
|
+
NSLog("AbsoluteJS: Capacitor embedded web content ready; version=%@; build=%@", version, build)
|
|
18601
|
+
call.resolve(["ready": true])
|
|
18602
|
+
}
|
|
18603
|
+
}
|
|
18604
|
+
${IOS_PLUGIN_END}
|
|
18605
|
+
`;
|
|
18606
|
+
var replaceIosPluginRegion = (appDelegate, existingStart, existingEnd) => {
|
|
18607
|
+
const from = appDelegate.lastIndexOf(`
|
|
18608
|
+
`, existingStart) + 1;
|
|
18609
|
+
const newline = appDelegate.indexOf(`
|
|
18610
|
+
`, existingEnd + IOS_PLUGIN_END.length);
|
|
18611
|
+
const through = newline < 0 ? appDelegate.length : newline + 1;
|
|
18612
|
+
return `${appDelegate.slice(0, from)}${iosPluginRegion3}${appDelegate.slice(through)}`;
|
|
18613
|
+
};
|
|
18614
|
+
var configureIos5 = async (config) => {
|
|
18615
|
+
const appDelegatePath = join6(config.nativeProjectDirectory, "ios/App/App/AppDelegate.swift");
|
|
18616
|
+
const capacitorConfigPath = join6(config.nativeProjectDirectory, "ios/App/App/capacitor.config.json");
|
|
18617
|
+
const appDelegate = await readFile6(appDelegatePath, "utf8");
|
|
18618
|
+
const existingStart = appDelegate.indexOf(IOS_PLUGIN_START);
|
|
18619
|
+
const existingEnd = appDelegate.indexOf(IOS_PLUGIN_END);
|
|
18620
|
+
if (existingStart < 0 !== existingEnd < 0 || existingStart >= 0 && existingEnd < existingStart)
|
|
18621
|
+
throw new TypeError("AbsoluteJS iOS release-readiness markers are malformed.");
|
|
18622
|
+
const source = existingStart < 0 ? `${appDelegate.trimEnd()}
|
|
18623
|
+
|
|
18624
|
+
${iosPluginRegion3}` : replaceIosPluginRegion(appDelegate, existingStart, existingEnd);
|
|
18625
|
+
let capacitorConfig;
|
|
18626
|
+
try {
|
|
18627
|
+
capacitorConfig = JSON.parse(await readFile6(capacitorConfigPath, "utf8"));
|
|
18628
|
+
} catch {
|
|
18629
|
+
throw new TypeError("iOS capacitor.config.json is invalid; run Capacitor sync before projecting release readiness.");
|
|
18630
|
+
}
|
|
18631
|
+
if (typeof capacitorConfig !== "object" || capacitorConfig === null)
|
|
18632
|
+
throw new TypeError("iOS capacitor.config.json must contain an object.");
|
|
18633
|
+
const packageClassList = Reflect.get(capacitorConfig, "packageClassList");
|
|
18634
|
+
if (!Array.isArray(packageClassList))
|
|
18635
|
+
throw new TypeError("iOS capacitor.config.json has no packageClassList; run Capacitor sync before projecting release readiness.");
|
|
18636
|
+
const classes = packageClassList.filter((value) => typeof value === "string");
|
|
18637
|
+
const nextConfig = `${JSON.stringify({
|
|
18638
|
+
...capacitorConfig,
|
|
18639
|
+
packageClassList: [
|
|
18640
|
+
...new Set([...classes, "AbsoluteReleaseReadinessPlugin"])
|
|
18641
|
+
]
|
|
18642
|
+
}, null, "\t")}
|
|
18643
|
+
`;
|
|
18644
|
+
const changed = await Promise.all([
|
|
18645
|
+
writeChanged4(appDelegatePath, source),
|
|
18646
|
+
writeChanged4(capacitorConfigPath, nextConfig)
|
|
18647
|
+
]);
|
|
18648
|
+
return changed.some(Boolean);
|
|
18649
|
+
};
|
|
18650
|
+
var configureAndroid5 = async (config) => {
|
|
18586
18651
|
const mainActivityPath = await activityPath(config);
|
|
18587
18652
|
const activity = await readFile6(mainActivityPath, "utf8");
|
|
18588
18653
|
const packageName = activity.match(/^\s*package\s+([A-Za-z0-9_.]+)\s*[;\n]/mu)?.[1];
|
|
@@ -18593,6 +18658,15 @@ var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.plat
|
|
|
18593
18658
|
writeChanged4(mainActivityPath, injectRegistration(activity)),
|
|
18594
18659
|
writeChanged4(pluginPath, pluginSource(packageName))
|
|
18595
18660
|
]);
|
|
18661
|
+
return changed.some(Boolean);
|
|
18662
|
+
};
|
|
18663
|
+
var applyAbsoluteNativeReleaseReadiness = async (config, platforms = config.platforms) => {
|
|
18664
|
+
if (config.engine !== "capacitor")
|
|
18665
|
+
return { changed: false };
|
|
18666
|
+
const changed = await Promise.all([
|
|
18667
|
+
platforms.includes("android") ? configureAndroid5(config) : false,
|
|
18668
|
+
platforms.includes("ios") ? configureIos5(config) : false
|
|
18669
|
+
]);
|
|
18596
18670
|
return { changed: changed.some(Boolean) };
|
|
18597
18671
|
};
|
|
18598
18672
|
|
|
@@ -21083,6 +21157,452 @@ var runAbsoluteAndroidReleaseAcceptance = async (options) => {
|
|
|
21083
21157
|
};
|
|
21084
21158
|
};
|
|
21085
21159
|
|
|
21160
|
+
// src/mobile/iosReleaseAcceptance.ts
|
|
21161
|
+
import { createHash as createHash4 } from "crypto";
|
|
21162
|
+
import {
|
|
21163
|
+
access as access7,
|
|
21164
|
+
mkdir as mkdir9,
|
|
21165
|
+
mkdtemp as mkdtemp3,
|
|
21166
|
+
readFile as readFile12,
|
|
21167
|
+
readdir as readdir5,
|
|
21168
|
+
rm as rm6,
|
|
21169
|
+
stat as stat4
|
|
21170
|
+
} from "fs/promises";
|
|
21171
|
+
import {
|
|
21172
|
+
basename as basename2,
|
|
21173
|
+
dirname as dirname9,
|
|
21174
|
+
isAbsolute as isAbsolute4,
|
|
21175
|
+
join as join11,
|
|
21176
|
+
relative as relative6,
|
|
21177
|
+
resolve as resolve7,
|
|
21178
|
+
sep as sep4
|
|
21179
|
+
} from "path";
|
|
21180
|
+
|
|
21181
|
+
// src/mobile/iosUpgradeConformance.ts
|
|
21182
|
+
var captureCommand3 = async (command) => {
|
|
21183
|
+
const process2 = Bun.spawn(command, { stderr: "pipe", stdout: "pipe" });
|
|
21184
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
21185
|
+
process2.exited,
|
|
21186
|
+
new Response(process2.stderr).text(),
|
|
21187
|
+
new Response(process2.stdout).text()
|
|
21188
|
+
]);
|
|
21189
|
+
return { exitCode, stderr, stdout };
|
|
21190
|
+
};
|
|
21191
|
+
var requiredOutput = (result, message) => {
|
|
21192
|
+
const value = result.stdout.trim();
|
|
21193
|
+
if (result.exitCode !== 0 || !value)
|
|
21194
|
+
throw new Error(`${message}: ${result.stderr.trim() || value || "no output"}`);
|
|
21195
|
+
return value;
|
|
21196
|
+
};
|
|
21197
|
+
var inspectAbsoluteIosInstalledApp = async (xcrun, udid, appId, run = captureCommand3, plutil = "/usr/bin/plutil") => {
|
|
21198
|
+
const dataContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "data"]), `Could not inspect the installed iOS data container for ${appId}`);
|
|
21199
|
+
const appContainer = requiredOutput(await run([xcrun, "simctl", "get_app_container", udid, appId, "app"]), `Could not inspect the installed iOS application container for ${appId}`);
|
|
21200
|
+
const info = requiredOutput(await run([
|
|
21201
|
+
plutil,
|
|
21202
|
+
"-convert",
|
|
21203
|
+
"json",
|
|
21204
|
+
"-o",
|
|
21205
|
+
"-",
|
|
21206
|
+
`${appContainer}/Info.plist`
|
|
21207
|
+
]), `Could not inspect the installed iOS Info.plist for ${appId}`);
|
|
21208
|
+
return parseAbsoluteIosInstalledAppInfo(appId, appContainer, dataContainer, info);
|
|
21209
|
+
};
|
|
21210
|
+
var parseAbsoluteIosInstalledAppInfo = (appId, appContainer, dataContainer, output) => {
|
|
21211
|
+
let value;
|
|
21212
|
+
try {
|
|
21213
|
+
value = JSON.parse(output);
|
|
21214
|
+
} catch {
|
|
21215
|
+
throw new TypeError("Invalid installed iOS application Info.plist JSON.");
|
|
21216
|
+
}
|
|
21217
|
+
if (!value || typeof value !== "object")
|
|
21218
|
+
throw new TypeError("Invalid installed iOS application Info.plist JSON.");
|
|
21219
|
+
const bundleId = Reflect.get(value, "CFBundleIdentifier");
|
|
21220
|
+
if (bundleId !== appId)
|
|
21221
|
+
throw new Error(`Installed iOS application identifier mismatch (${String(bundleId)} != ${appId}).`);
|
|
21222
|
+
const buildNumber = Reflect.get(value, "CFBundleVersion");
|
|
21223
|
+
const version = Reflect.get(value, "CFBundleShortVersionString");
|
|
21224
|
+
return {
|
|
21225
|
+
appContainer,
|
|
21226
|
+
appId,
|
|
21227
|
+
...typeof buildNumber === "string" || typeof buildNumber === "number" ? { buildNumber: String(buildNumber) } : {},
|
|
21228
|
+
dataContainer,
|
|
21229
|
+
...typeof version === "string" ? { version } : {}
|
|
21230
|
+
};
|
|
21231
|
+
};
|
|
21232
|
+
|
|
21233
|
+
// src/mobile/iosReleaseAcceptance.ts
|
|
21234
|
+
var READY_MARKERS = {
|
|
21235
|
+
capacitor: "Capacitor embedded web content ready",
|
|
21236
|
+
expo: "Expo embedded web content ready"
|
|
21237
|
+
};
|
|
21238
|
+
var READY_TIMEOUT_MS = 30000;
|
|
21239
|
+
var READY_POLL_MS = 250;
|
|
21240
|
+
var defaultRun2 = async (command, options = {}) => {
|
|
21241
|
+
const process2 = Bun.spawn(command, {
|
|
21242
|
+
cwd: options.cwd,
|
|
21243
|
+
env: options.env,
|
|
21244
|
+
stderr: "pipe",
|
|
21245
|
+
stdin: "ignore",
|
|
21246
|
+
stdout: "pipe"
|
|
21247
|
+
});
|
|
21248
|
+
const [exitCode, stderr, stdout] = await Promise.all([
|
|
21249
|
+
process2.exited,
|
|
21250
|
+
new Response(process2.stderr).text(),
|
|
21251
|
+
new Response(process2.stdout).text()
|
|
21252
|
+
]);
|
|
21253
|
+
return { exitCode, stderr, stdout };
|
|
21254
|
+
};
|
|
21255
|
+
var exists3 = async (path) => {
|
|
21256
|
+
try {
|
|
21257
|
+
await access7(path);
|
|
21258
|
+
return true;
|
|
21259
|
+
} catch {
|
|
21260
|
+
return false;
|
|
21261
|
+
}
|
|
21262
|
+
};
|
|
21263
|
+
var sha2562 = async (path) => createHash4("sha256").update(await readFile12(path)).digest("hex");
|
|
21264
|
+
var inside2 = (root, candidate) => {
|
|
21265
|
+
const path = relative6(resolve7(root), resolve7(candidate));
|
|
21266
|
+
return path === "" || !isAbsolute4(path) && path !== ".." && !path.startsWith(`..${sep4}`);
|
|
21267
|
+
};
|
|
21268
|
+
var requireSuccess2 = async (run, command, label, options) => {
|
|
21269
|
+
const result = await run(command, options);
|
|
21270
|
+
if (result.exitCode !== 0)
|
|
21271
|
+
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
21272
|
+
return result;
|
|
21273
|
+
};
|
|
21274
|
+
var findByExtension = async (root, extension) => {
|
|
21275
|
+
if (!await exists3(root))
|
|
21276
|
+
return;
|
|
21277
|
+
const entries = await readdir5(root, { withFileTypes: true });
|
|
21278
|
+
const matches = await Promise.all(entries.map(async (entry) => {
|
|
21279
|
+
const path = join11(root, entry.name);
|
|
21280
|
+
if (entry.name.endsWith(extension))
|
|
21281
|
+
return path;
|
|
21282
|
+
return entry.isDirectory() ? findByExtension(path, extension) : undefined;
|
|
21283
|
+
}));
|
|
21284
|
+
return matches.find((match) => match !== undefined);
|
|
21285
|
+
};
|
|
21286
|
+
var readAbsoluteIosRelease = async (projectRoot, requested) => {
|
|
21287
|
+
const root = resolve7(projectRoot);
|
|
21288
|
+
const selected = resolve7(root, requested);
|
|
21289
|
+
if (!inside2(root, selected))
|
|
21290
|
+
throw new TypeError("mobile test ios --release must remain inside the project.");
|
|
21291
|
+
const metadataPath = basename2(selected) === "release.json" ? selected : join11(selected, "release.json");
|
|
21292
|
+
const releaseRoot = dirname9(metadataPath);
|
|
21293
|
+
const metadata = requireAbsoluteIosReleaseMetadata(JSON.parse(await readFile12(metadataPath, "utf8")));
|
|
21294
|
+
const artifactPath = resolve7(releaseRoot, metadata.artifact);
|
|
21295
|
+
if (!inside2(releaseRoot, artifactPath))
|
|
21296
|
+
throw new TypeError("iOS release artifact path is invalid.");
|
|
21297
|
+
const [artifactStat, artifactSha256] = await Promise.all([
|
|
21298
|
+
stat4(artifactPath),
|
|
21299
|
+
sha2562(artifactPath)
|
|
21300
|
+
]);
|
|
21301
|
+
if (artifactStat.size !== metadata.bytes || artifactSha256 !== metadata.sha256)
|
|
21302
|
+
throw new TypeError(`Immutable iOS release ${metadata.releaseId} is missing or modified.`);
|
|
21303
|
+
const registered = metadata.registeredDevice;
|
|
21304
|
+
let registeredArtifactPath;
|
|
21305
|
+
if (registered) {
|
|
21306
|
+
registeredArtifactPath = resolve7(releaseRoot, registered.artifact);
|
|
21307
|
+
if (!inside2(releaseRoot, registeredArtifactPath))
|
|
21308
|
+
throw new TypeError("iOS registered-device artifact path is invalid.");
|
|
21309
|
+
const [registeredStat, registeredSha256] = await Promise.all([
|
|
21310
|
+
stat4(registeredArtifactPath),
|
|
21311
|
+
sha2562(registeredArtifactPath)
|
|
21312
|
+
]);
|
|
21313
|
+
if (registeredStat.size !== registered.bytes || registeredSha256 !== registered.sha256)
|
|
21314
|
+
throw new TypeError(`Registered-device export for ${metadata.releaseId} is missing or modified.`);
|
|
21315
|
+
}
|
|
21316
|
+
return {
|
|
21317
|
+
artifactPath,
|
|
21318
|
+
metadata,
|
|
21319
|
+
metadataPath,
|
|
21320
|
+
registeredArtifactPath,
|
|
21321
|
+
releaseRoot
|
|
21322
|
+
};
|
|
21323
|
+
};
|
|
21324
|
+
var releaseMarker = (release) => {
|
|
21325
|
+
const marker = READY_MARKERS[release.metadata.engine];
|
|
21326
|
+
return release.metadata.buildNumber === undefined ? marker : `${marker}; version=${release.metadata.marketingVersion}; build=${release.metadata.buildNumber}`;
|
|
21327
|
+
};
|
|
21328
|
+
var waitForSimulatorMarker = async (options) => {
|
|
21329
|
+
const startedAt = performance.now();
|
|
21330
|
+
const poll = async () => {
|
|
21331
|
+
if (performance.now() - startedAt >= READY_TIMEOUT_MS)
|
|
21332
|
+
throw new Error("The installed iOS Release app launched but its embedded content did not become ready.");
|
|
21333
|
+
const logs = await options.run([
|
|
21334
|
+
options.xcrun,
|
|
21335
|
+
"simctl",
|
|
21336
|
+
"spawn",
|
|
21337
|
+
options.udid,
|
|
21338
|
+
"log",
|
|
21339
|
+
"show",
|
|
21340
|
+
"--style",
|
|
21341
|
+
"compact",
|
|
21342
|
+
"--start",
|
|
21343
|
+
options.startedAt.toISOString(),
|
|
21344
|
+
"--predicate",
|
|
21345
|
+
`eventMessage CONTAINS "${releaseMarker(options.release)}"`
|
|
21346
|
+
]);
|
|
21347
|
+
if (logs.exitCode === 0 && logs.stdout.includes(releaseMarker(options.release)))
|
|
21348
|
+
return;
|
|
21349
|
+
await Bun.sleep(READY_POLL_MS);
|
|
21350
|
+
await poll();
|
|
21351
|
+
};
|
|
21352
|
+
await poll();
|
|
21353
|
+
};
|
|
21354
|
+
var launchSimulator = async (options) => {
|
|
21355
|
+
const startedAt = performance.now();
|
|
21356
|
+
const logStartedAt = new Date;
|
|
21357
|
+
await requireSuccess2(options.run, [
|
|
21358
|
+
options.xcrun,
|
|
21359
|
+
"simctl",
|
|
21360
|
+
"launch",
|
|
21361
|
+
"--terminate-running-process",
|
|
21362
|
+
options.udid,
|
|
21363
|
+
options.release.metadata.appId
|
|
21364
|
+
], "iOS Simulator Release launch");
|
|
21365
|
+
await waitForSimulatorMarker({ ...options, startedAt: logStartedAt });
|
|
21366
|
+
return Math.round(performance.now() - startedAt);
|
|
21367
|
+
};
|
|
21368
|
+
var runAbsoluteIosSimulatorReleaseAcceptance = async (options) => {
|
|
21369
|
+
const run = options.run ?? defaultRun2;
|
|
21370
|
+
const xcodebuild = options.xcodebuild ?? "/usr/bin/xcodebuild";
|
|
21371
|
+
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
21372
|
+
const startedAt = performance.now();
|
|
21373
|
+
const iosRoot = join11(options.config.nativeProjectDirectory, "ios");
|
|
21374
|
+
const xcode = await resolveAbsoluteIosXcodeProject(iosRoot);
|
|
21375
|
+
const derivedData = join11(options.artifactDirectory, "DerivedData");
|
|
21376
|
+
await mkdir9(options.artifactDirectory, { recursive: true });
|
|
21377
|
+
await requireSuccess2(run, [
|
|
21378
|
+
xcodebuild,
|
|
21379
|
+
"-workspace",
|
|
21380
|
+
xcode.workspacePath,
|
|
21381
|
+
"-scheme",
|
|
21382
|
+
xcode.scheme,
|
|
21383
|
+
"-configuration",
|
|
21384
|
+
"Release",
|
|
21385
|
+
"-destination",
|
|
21386
|
+
`platform=iOS Simulator,id=${options.udid}`,
|
|
21387
|
+
"-derivedDataPath",
|
|
21388
|
+
derivedData,
|
|
21389
|
+
`MARKETING_VERSION=${options.release.metadata.marketingVersion}`,
|
|
21390
|
+
...options.release.metadata.buildNumber === undefined ? [] : [
|
|
21391
|
+
`CURRENT_PROJECT_VERSION=${options.release.metadata.buildNumber}`
|
|
21392
|
+
],
|
|
21393
|
+
"build"
|
|
21394
|
+
], "iOS Simulator Release build", { cwd: iosRoot });
|
|
21395
|
+
const app = await findByExtension(join11(derivedData, "Build", "Products"), ".app");
|
|
21396
|
+
if (!app)
|
|
21397
|
+
throw new Error("Xcode did not produce a Simulator Release app.");
|
|
21398
|
+
const installStartedAt = performance.now();
|
|
21399
|
+
await requireSuccess2(run, [xcrun, "simctl", "install", options.udid, app], "iOS Simulator Release installation");
|
|
21400
|
+
const installMs = Math.round(performance.now() - installStartedAt);
|
|
21401
|
+
const installed = await inspectAbsoluteIosInstalledApp(xcrun, options.udid, options.release.metadata.appId, (command) => run(command));
|
|
21402
|
+
if (installed.version !== options.release.metadata.marketingVersion)
|
|
21403
|
+
throw new Error(`Installed iOS version ${installed.version ?? "unknown"} does not match release ${options.release.metadata.marketingVersion}.`);
|
|
21404
|
+
if (options.release.metadata.buildNumber !== undefined && installed.buildNumber !== String(options.release.metadata.buildNumber))
|
|
21405
|
+
throw new Error(`Installed iOS build ${installed.buildNumber ?? "unknown"} does not match release ${options.release.metadata.buildNumber}.`);
|
|
21406
|
+
const launchMs = await launchSimulator({
|
|
21407
|
+
release: options.release,
|
|
21408
|
+
run,
|
|
21409
|
+
udid: options.udid,
|
|
21410
|
+
xcrun
|
|
21411
|
+
});
|
|
21412
|
+
await requireSuccess2(run, [
|
|
21413
|
+
xcrun,
|
|
21414
|
+
"simctl",
|
|
21415
|
+
"terminate",
|
|
21416
|
+
options.udid,
|
|
21417
|
+
options.release.metadata.appId
|
|
21418
|
+
], "iOS Simulator Release termination");
|
|
21419
|
+
const relaunchMs = await launchSimulator({
|
|
21420
|
+
release: options.release,
|
|
21421
|
+
run,
|
|
21422
|
+
udid: options.udid,
|
|
21423
|
+
xcrun
|
|
21424
|
+
});
|
|
21425
|
+
return {
|
|
21426
|
+
artifactBytes: options.release.metadata.bytes,
|
|
21427
|
+
artifactExactness: "source-equivalent",
|
|
21428
|
+
artifactSha256: options.release.metadata.sha256,
|
|
21429
|
+
...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
|
|
21430
|
+
distribution: "simulator-release",
|
|
21431
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
21432
|
+
embeddedLocal: true,
|
|
21433
|
+
engine: options.release.metadata.engine,
|
|
21434
|
+
installMs,
|
|
21435
|
+
launchMs,
|
|
21436
|
+
marketingVersion: options.release.metadata.marketingVersion,
|
|
21437
|
+
networkUnavailable: "not-proven",
|
|
21438
|
+
relaunchMs,
|
|
21439
|
+
releaseId: options.release.metadata.releaseId,
|
|
21440
|
+
signed: options.release.metadata.signed,
|
|
21441
|
+
status: "pass",
|
|
21442
|
+
target: "simulator"
|
|
21443
|
+
};
|
|
21444
|
+
};
|
|
21445
|
+
var readUntilMarker = async (reader, marker, output = "") => {
|
|
21446
|
+
const { done, value } = await reader.read();
|
|
21447
|
+
if (done)
|
|
21448
|
+
return { found: false, output };
|
|
21449
|
+
const next = `${output}${new TextDecoder().decode(value)}`;
|
|
21450
|
+
if (next.includes(marker))
|
|
21451
|
+
return { found: true, output: next };
|
|
21452
|
+
return readUntilMarker(reader, marker, next);
|
|
21453
|
+
};
|
|
21454
|
+
var observeDeviceLaunch = async (options) => {
|
|
21455
|
+
const startedAt = performance.now();
|
|
21456
|
+
const process2 = Bun.spawn([
|
|
21457
|
+
options.xcrun,
|
|
21458
|
+
"devicectl",
|
|
21459
|
+
"device",
|
|
21460
|
+
"process",
|
|
21461
|
+
"launch",
|
|
21462
|
+
"--console",
|
|
21463
|
+
"--terminate-existing",
|
|
21464
|
+
"--device",
|
|
21465
|
+
options.device,
|
|
21466
|
+
options.appId
|
|
21467
|
+
], { stderr: "pipe", stdin: "ignore", stdout: "pipe" });
|
|
21468
|
+
const stdoutReader = process2.stdout.getReader();
|
|
21469
|
+
const stderrReader = process2.stderr.getReader();
|
|
21470
|
+
const requireMarker = async (reader) => {
|
|
21471
|
+
const result = await readUntilMarker(reader, options.marker);
|
|
21472
|
+
if (!result.found)
|
|
21473
|
+
throw new Error(`Console stream closed before readiness.${result.output.trim() ? ` Output: ${result.output.trim()}` : ""}`);
|
|
21474
|
+
return result;
|
|
21475
|
+
};
|
|
21476
|
+
const observed = Promise.any([
|
|
21477
|
+
requireMarker(stdoutReader),
|
|
21478
|
+
requireMarker(stderrReader)
|
|
21479
|
+
]);
|
|
21480
|
+
try {
|
|
21481
|
+
const result = await Promise.race([
|
|
21482
|
+
observed.then((value) => ({ type: "output", value })).catch(() => ({ type: "closed" })),
|
|
21483
|
+
Bun.sleep(options.timeoutMs ?? READY_TIMEOUT_MS).then(() => ({
|
|
21484
|
+
type: "timeout"
|
|
21485
|
+
}))
|
|
21486
|
+
]);
|
|
21487
|
+
if (result.type === "output" && result.value.found)
|
|
21488
|
+
return Math.round(performance.now() - startedAt);
|
|
21489
|
+
if (result.type === "closed")
|
|
21490
|
+
throw new Error("The physical iOS app exited before embedded content became ready.");
|
|
21491
|
+
throw new Error("The physical iOS app did not report embedded readiness before the timeout.");
|
|
21492
|
+
} finally {
|
|
21493
|
+
process2.kill();
|
|
21494
|
+
await Promise.all([
|
|
21495
|
+
stdoutReader.cancel().catch(() => {
|
|
21496
|
+
return;
|
|
21497
|
+
}),
|
|
21498
|
+
stderrReader.cancel().catch(() => {
|
|
21499
|
+
return;
|
|
21500
|
+
})
|
|
21501
|
+
]);
|
|
21502
|
+
stdoutReader.releaseLock();
|
|
21503
|
+
stderrReader.releaseLock();
|
|
21504
|
+
}
|
|
21505
|
+
};
|
|
21506
|
+
var installRegisteredDeviceRelease = async (options) => {
|
|
21507
|
+
if (!options.release.registeredArtifactPath)
|
|
21508
|
+
throw new TypeError("This release has no same-archive registered-device IPA. Rebuild with `absolute mobile build ios --registered-device-artifact`.");
|
|
21509
|
+
const staging = await mkdtemp3(join11(dirname9(options.release.releaseRoot), ".ios-device-"));
|
|
21510
|
+
try {
|
|
21511
|
+
await requireSuccess2(options.run, [
|
|
21512
|
+
"/usr/bin/ditto",
|
|
21513
|
+
"-x",
|
|
21514
|
+
"-k",
|
|
21515
|
+
options.release.registeredArtifactPath,
|
|
21516
|
+
staging
|
|
21517
|
+
], "iOS registered-device IPA extraction");
|
|
21518
|
+
const app = await findByExtension(staging, ".app");
|
|
21519
|
+
if (!app)
|
|
21520
|
+
throw new Error("The registered-device IPA contains no installable app.");
|
|
21521
|
+
const startedAt = performance.now();
|
|
21522
|
+
await requireSuccess2(options.run, [
|
|
21523
|
+
options.xcrun,
|
|
21524
|
+
"devicectl",
|
|
21525
|
+
"device",
|
|
21526
|
+
"install",
|
|
21527
|
+
"app",
|
|
21528
|
+
"--device",
|
|
21529
|
+
options.device,
|
|
21530
|
+
app
|
|
21531
|
+
], "iOS registered-device Release installation");
|
|
21532
|
+
return Math.round(performance.now() - startedAt);
|
|
21533
|
+
} finally {
|
|
21534
|
+
await rm6(staging, { force: true, recursive: true });
|
|
21535
|
+
}
|
|
21536
|
+
};
|
|
21537
|
+
var inspectTestFlightInstallation = async (options) => {
|
|
21538
|
+
const apps = await requireSuccess2(options.run, [
|
|
21539
|
+
options.xcrun,
|
|
21540
|
+
"devicectl",
|
|
21541
|
+
"device",
|
|
21542
|
+
"info",
|
|
21543
|
+
"apps",
|
|
21544
|
+
"--device",
|
|
21545
|
+
options.device
|
|
21546
|
+
], "TestFlight installation inspection");
|
|
21547
|
+
if (!apps.stdout.includes(options.release.metadata.appId))
|
|
21548
|
+
throw new Error("The selected device does not have the requested TestFlight app installed.");
|
|
21549
|
+
return 0;
|
|
21550
|
+
};
|
|
21551
|
+
var runAbsoluteIosDeviceReleaseAcceptance = async (options) => {
|
|
21552
|
+
if (!options.networkUnavailableConfirmed)
|
|
21553
|
+
throw new TypeError("Physical iOS offline acceptance requires explicit confirmation that Airplane Mode is enabled and Wi-Fi is disabled in Settings.");
|
|
21554
|
+
const run = options.run ?? defaultRun2;
|
|
21555
|
+
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
21556
|
+
const startedAt = performance.now();
|
|
21557
|
+
await requireSuccess2(run, [
|
|
21558
|
+
xcrun,
|
|
21559
|
+
"devicectl",
|
|
21560
|
+
"device",
|
|
21561
|
+
"info",
|
|
21562
|
+
"details",
|
|
21563
|
+
"--device",
|
|
21564
|
+
options.device
|
|
21565
|
+
], "iOS physical-device availability inspection");
|
|
21566
|
+
const installation = {
|
|
21567
|
+
device: options.device,
|
|
21568
|
+
release: options.release,
|
|
21569
|
+
run,
|
|
21570
|
+
xcrun
|
|
21571
|
+
};
|
|
21572
|
+
const installMs = options.distribution === "registered-device" ? await installRegisteredDeviceRelease(installation) : await inspectTestFlightInstallation(installation);
|
|
21573
|
+
const observe = options.observeLaunch ?? ((launch2) => observeDeviceLaunch({ ...launch2, xcrun }));
|
|
21574
|
+
const marker = releaseMarker(options.release);
|
|
21575
|
+
const launchMs = await observe({
|
|
21576
|
+
appId: options.release.metadata.appId,
|
|
21577
|
+
device: options.device,
|
|
21578
|
+
marker
|
|
21579
|
+
});
|
|
21580
|
+
const relaunchMs = await observe({
|
|
21581
|
+
appId: options.release.metadata.appId,
|
|
21582
|
+
device: options.device,
|
|
21583
|
+
marker
|
|
21584
|
+
});
|
|
21585
|
+
return {
|
|
21586
|
+
artifactBytes: options.release.metadata.bytes,
|
|
21587
|
+
artifactExactness: options.distribution === "testflight" ? "store-delivered" : "archive-equivalent",
|
|
21588
|
+
artifactSha256: options.release.metadata.sha256,
|
|
21589
|
+
...options.release.metadata.buildNumber === undefined ? {} : { buildNumber: options.release.metadata.buildNumber },
|
|
21590
|
+
distribution: options.distribution === "testflight" ? "apple-processed" : "registered-device",
|
|
21591
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
21592
|
+
embeddedLocal: true,
|
|
21593
|
+
engine: options.release.metadata.engine,
|
|
21594
|
+
installMs,
|
|
21595
|
+
launchMs,
|
|
21596
|
+
marketingVersion: options.release.metadata.marketingVersion,
|
|
21597
|
+
networkUnavailable: "user-confirmed",
|
|
21598
|
+
relaunchMs,
|
|
21599
|
+
releaseId: options.release.metadata.releaseId,
|
|
21600
|
+
signed: options.release.metadata.signed,
|
|
21601
|
+
status: "pass",
|
|
21602
|
+
target: "device"
|
|
21603
|
+
};
|
|
21604
|
+
};
|
|
21605
|
+
|
|
21086
21606
|
// src/mobile/iosDeviceAcceptance.ts
|
|
21087
21607
|
var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
21088
21608
|
const xcrun = options.xcrun ?? "/usr/bin/xcrun";
|
|
@@ -21101,20 +21621,20 @@ var absoluteIosDeviceAcceptanceCommands = (options) => {
|
|
|
21101
21621
|
]
|
|
21102
21622
|
};
|
|
21103
21623
|
};
|
|
21104
|
-
var
|
|
21624
|
+
var requireSuccess3 = (result, message) => {
|
|
21105
21625
|
if (result.exitCode !== 0)
|
|
21106
21626
|
throw new Error(message);
|
|
21107
21627
|
return result;
|
|
21108
21628
|
};
|
|
21109
21629
|
var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
21110
21630
|
const commands = absoluteIosDeviceAcceptanceCommands(options);
|
|
21111
|
-
|
|
21112
|
-
const apps =
|
|
21631
|
+
requireSuccess3(await options.capture(commands.details), "The selected physical iOS device is unavailable. Pair it in Xcode Device Hub, trust this Mac, unlock it, and enable Developer Mode.");
|
|
21632
|
+
const apps = requireSuccess3(await options.capture(commands.apps), "AbsoluteJS could not inspect installed apps on the physical iOS device.");
|
|
21113
21633
|
if (!apps.stdout.includes(options.appId))
|
|
21114
21634
|
throw new Error("The AbsoluteJS app is not installed on the selected physical iOS device. Start bun dev with the same --ios-device value first.");
|
|
21115
21635
|
const now = options.now ?? performance.now.bind(performance);
|
|
21116
21636
|
const startedAt = now();
|
|
21117
|
-
|
|
21637
|
+
requireSuccess3(await options.capture(commands.launch), "AbsoluteJS could not relaunch the app on the physical iOS device.");
|
|
21118
21638
|
await options.waitForHmr();
|
|
21119
21639
|
return {
|
|
21120
21640
|
hmrConnected: true,
|
|
@@ -21124,7 +21644,7 @@ var testAbsoluteIosPhysicalDevice = async (options) => {
|
|
|
21124
21644
|
};
|
|
21125
21645
|
|
|
21126
21646
|
// src/mobile/iosConformance.ts
|
|
21127
|
-
import { readFile as
|
|
21647
|
+
import { readFile as readFile13, stat as stat5 } from "fs/promises";
|
|
21128
21648
|
var HMR_LINE = new RegExp(String.raw`\[hmr:ios\]\s+([^\n]*?)\s+(applied in|falling back to reload after|failed after)\s+(\d+)ms(?:; server\s+(\d+)ms, client\s+(\d+)ms)?`, "u");
|
|
21129
21649
|
var parseAbsoluteIosHmrLog = (line) => {
|
|
21130
21650
|
const match = HMR_LINE.exec(line);
|
|
@@ -21156,13 +21676,13 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21156
21676
|
const sleep = options.sleep ?? Bun.sleep;
|
|
21157
21677
|
const timeoutMs = options.timeoutMs ?? 30000;
|
|
21158
21678
|
const deadline = Date.now() + timeoutMs;
|
|
21159
|
-
let offset = options.startOffset ?? await
|
|
21679
|
+
let offset = options.startOffset ?? await stat5(options.logPath).then(({ size }) => size).catch(() => 0);
|
|
21160
21680
|
let buffered = "";
|
|
21161
21681
|
const poll = async () => {
|
|
21162
21682
|
if (Date.now() > deadline)
|
|
21163
21683
|
throw new Error(`No iOS native HMR acknowledgement was observed within ${timeoutMs}ms.`);
|
|
21164
21684
|
options.signal?.throwIfAborted();
|
|
21165
|
-
const contents = await
|
|
21685
|
+
const contents = await readFile13(options.logPath).catch(() => Buffer.alloc(0));
|
|
21166
21686
|
if (contents.byteLength < offset) {
|
|
21167
21687
|
offset = 0;
|
|
21168
21688
|
buffered = "";
|
|
@@ -21183,8 +21703,8 @@ var waitForAbsoluteIosHmrLog = async (options) => {
|
|
|
21183
21703
|
};
|
|
21184
21704
|
|
|
21185
21705
|
// src/mobile/nativeTestReport.ts
|
|
21186
|
-
import { mkdir as
|
|
21187
|
-
import { join as
|
|
21706
|
+
import { mkdir as mkdir10, readFile as readFile14, writeFile as writeFile11 } from "fs/promises";
|
|
21707
|
+
import { join as join12 } from "path";
|
|
21188
21708
|
var secretPattern = /(authorization|access[_ -]?token|refresh[_ -]?token|socket[_ -]?ticket|password|cookie)(\s*[=:]\s*)([^\s,;]+)/giu;
|
|
21189
21709
|
var bearerPattern = /bearer\s+[^\s,;]+/giu;
|
|
21190
21710
|
var coordinatePattern = /\b(latitude|longitude|lat|lng)(\s*[=:]\s*)-?\d+(?:\.\d+)?/giu;
|
|
@@ -21211,8 +21731,10 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
21211
21731
|
let developmentDetails = run.status === "pass" ? `The app launched and connected to native HMR in ${run.durationMs}ms.${routeDetails}` : `The automated native run failed after ${run.durationMs}ms: ${run.error ?? "No error detail was available."}`;
|
|
21212
21732
|
if (run.release)
|
|
21213
21733
|
developmentDetails = run.status === "pass" ? `The ${run.release.engine} release was installed from its generated APK set, launched offline in ${run.release.launchMs}ms, and relaunched in ${run.release.relaunchMs}ms.` : `Installed ${run.release.engine} release acceptance failed after ${run.durationMs}ms.`;
|
|
21734
|
+
if (run.iosRelease)
|
|
21735
|
+
developmentDetails = run.status === "pass" ? `The ${run.iosRelease.engine} iOS release rendered embedded local content in ${run.iosRelease.launchMs}ms and relaunched in ${run.iosRelease.relaunchMs}ms.` : `Installed ${run.iosRelease.engine} iOS release acceptance failed after ${run.durationMs}ms.`;
|
|
21214
21736
|
let hmrDetails = "Correlated edit timing was not requested. Rerun with --wait-for-hmr.";
|
|
21215
|
-
if (run.release)
|
|
21737
|
+
if (run.release || run.iosRelease)
|
|
21216
21738
|
hmrDetails = "HMR is intentionally not part of installed production-release acceptance.";
|
|
21217
21739
|
if (run.hmr)
|
|
21218
21740
|
hmrDetails = `Observed native HMR ${run.hmr.outcome} in ${run.hmr.durationMs}ms${run.hmr.serverMs === undefined ? "" : ` (server ${run.hmr.serverMs}ms, client ${run.hmr.clientMs}ms)`}.`;
|
|
@@ -21251,6 +21773,18 @@ var createAbsoluteNativeAutomatedChecks = (run) => {
|
|
|
21251
21773
|
result: run.release.embeddedOffline ? "PASS" : "FAIL"
|
|
21252
21774
|
});
|
|
21253
21775
|
}
|
|
21776
|
+
if (run.iosRelease) {
|
|
21777
|
+
const release = run.iosRelease;
|
|
21778
|
+
checks.push({
|
|
21779
|
+
details: `Validated immutable release ${release.releaseId} (${release.artifactBytes} bytes, SHA-256 ${release.artifactSha256}); ${release.distribution} evidence is ${release.artifactExactness}, installed in ${release.installMs}ms.`,
|
|
21780
|
+
id: "AUTO-IOS-RELEASE-01",
|
|
21781
|
+
result: run.status === "pass" ? "PASS" : "FAIL"
|
|
21782
|
+
}, {
|
|
21783
|
+
details: release.networkUnavailable === "user-confirmed" ? "The tester explicitly confirmed Airplane Mode was enabled and Wi-Fi was disabled before both physical-device launches." : "Simulator Release proved embedded local rendering, but does not claim physical-device network-unavailable behavior.",
|
|
21784
|
+
id: "AUTO-IOS-RELEASE-OFFLINE-01",
|
|
21785
|
+
result: release.networkUnavailable === "user-confirmed" ? "PASS" : "SKIPPED"
|
|
21786
|
+
});
|
|
21787
|
+
}
|
|
21254
21788
|
if (run.upgrade) {
|
|
21255
21789
|
const { upgrade } = run;
|
|
21256
21790
|
checks.push({
|
|
@@ -21313,7 +21847,7 @@ var createAbsoluteNativeTestReport = (options) => ({
|
|
|
21313
21847
|
run: options.run
|
|
21314
21848
|
});
|
|
21315
21849
|
var readPackageVersionForNativeReport = async (packageJsonPath) => {
|
|
21316
|
-
const manifest = JSON.parse(await
|
|
21850
|
+
const manifest = JSON.parse(await readFile14(packageJsonPath, "utf8"));
|
|
21317
21851
|
if (typeof manifest !== "object" || manifest === null)
|
|
21318
21852
|
return "unknown";
|
|
21319
21853
|
const version = Reflect.get(manifest, "version");
|
|
@@ -21351,9 +21885,9 @@ ${table(report.manualChecks)}
|
|
|
21351
21885
|
`;
|
|
21352
21886
|
};
|
|
21353
21887
|
var writeAbsoluteNativeTestReport = async (directory, report) => {
|
|
21354
|
-
await
|
|
21355
|
-
const jsonPath =
|
|
21356
|
-
const markdownPath =
|
|
21888
|
+
await mkdir10(directory, { recursive: true });
|
|
21889
|
+
const jsonPath = join12(directory, "report.json");
|
|
21890
|
+
const markdownPath = join12(directory, "report.md");
|
|
21357
21891
|
await Promise.all([
|
|
21358
21892
|
writeFile11(jsonPath, `${JSON.stringify(report, null, 2)}
|
|
21359
21893
|
`),
|
|
@@ -21468,7 +22002,7 @@ var createAbsoluteIosPartnerReport = (options) => {
|
|
|
21468
22002
|
absolutejsVersion: options.absolutejsVersion,
|
|
21469
22003
|
bunVersion: options.bunVersion,
|
|
21470
22004
|
macosVersion: options.macosVersion,
|
|
21471
|
-
provider: "capacitor",
|
|
22005
|
+
provider: options.provider ?? "capacitor",
|
|
21472
22006
|
xcodeVersion: options.xcodeVersion
|
|
21473
22007
|
},
|
|
21474
22008
|
run: {
|
|
@@ -21609,8 +22143,8 @@ var createAbsoluteAndroidTestReport = (options) => {
|
|
|
21609
22143
|
};
|
|
21610
22144
|
|
|
21611
22145
|
// src/mobile/releasePublisher.ts
|
|
21612
|
-
import { access as
|
|
21613
|
-
import { isAbsolute as
|
|
22146
|
+
import { access as access8 } from "fs/promises";
|
|
22147
|
+
import { isAbsolute as isAbsolute5, relative as relative7, resolve as resolve8, sep as sep5 } from "path";
|
|
21614
22148
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
21615
22149
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
21616
22150
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -21636,17 +22170,17 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
21636
22170
|
var isRecord6 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21637
22171
|
var isPublisher = (value) => isRecord6(value) && typeof value.publish === "function";
|
|
21638
22172
|
var publisherModulePath = (projectRoot, requested) => {
|
|
21639
|
-
const root =
|
|
21640
|
-
const path =
|
|
21641
|
-
const projectRelative =
|
|
21642
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
22173
|
+
const root = resolve8(projectRoot);
|
|
22174
|
+
const path = resolve8(root, requested);
|
|
22175
|
+
const projectRelative = relative7(root, path);
|
|
22176
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep5}`) || isAbsolute5(projectRelative)) {
|
|
21643
22177
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
21644
22178
|
}
|
|
21645
22179
|
return path;
|
|
21646
22180
|
};
|
|
21647
22181
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
21648
22182
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
21649
|
-
await
|
|
22183
|
+
await access8(modulePath).catch(() => {
|
|
21650
22184
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
21651
22185
|
});
|
|
21652
22186
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
@@ -21708,8 +22242,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
21708
22242
|
};
|
|
21709
22243
|
|
|
21710
22244
|
// src/mobile/mobileInspect.ts
|
|
21711
|
-
import { access as
|
|
21712
|
-
import { join as
|
|
22245
|
+
import { access as access9, readFile as readFile15 } from "fs/promises";
|
|
22246
|
+
import { join as join13, relative as relative8, resolve as resolve9 } from "path";
|
|
21713
22247
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
21714
22248
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
21715
22249
|
"@absolutejs/absolute",
|
|
@@ -21724,19 +22258,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
|
|
|
21724
22258
|
]);
|
|
21725
22259
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21726
22260
|
var portablePath2 = (projectRoot, path) => {
|
|
21727
|
-
const value =
|
|
22261
|
+
const value = relative8(resolve9(projectRoot), resolve9(path)).replaceAll("\\", "/");
|
|
21728
22262
|
return value || ".";
|
|
21729
22263
|
};
|
|
21730
22264
|
var pathExists4 = async (path) => {
|
|
21731
22265
|
try {
|
|
21732
|
-
await
|
|
22266
|
+
await access9(path);
|
|
21733
22267
|
return true;
|
|
21734
22268
|
} catch {
|
|
21735
22269
|
return false;
|
|
21736
22270
|
}
|
|
21737
22271
|
};
|
|
21738
22272
|
var readObject2 = async (path) => {
|
|
21739
|
-
const value = JSON.parse(await
|
|
22273
|
+
const value = JSON.parse(await readFile15(path, "utf8"));
|
|
21740
22274
|
if (!isObject2(value))
|
|
21741
22275
|
throw new TypeError("JSON root must be an object.");
|
|
21742
22276
|
return value;
|
|
@@ -21748,13 +22282,13 @@ var addPackageDeclarations = (declarations, value) => {
|
|
|
21748
22282
|
declarations.set(name, declared);
|
|
21749
22283
|
};
|
|
21750
22284
|
var packageInspections = async (projectRoot, additionalNames) => {
|
|
21751
|
-
const project = await readObject2(
|
|
22285
|
+
const project = await readObject2(join13(projectRoot, "package.json"));
|
|
21752
22286
|
const declarations = new Map;
|
|
21753
22287
|
for (const field2 of ["dependencies", "devDependencies"])
|
|
21754
22288
|
addPackageDeclarations(declarations, project[field2]);
|
|
21755
22289
|
const names = [...new Set([...declarations.keys(), ...additionalNames])].filter((name) => MOBILE_PACKAGE_NAMES.has(name) || name.startsWith("@capacitor/") || additionalNames.includes(name)).sort();
|
|
21756
22290
|
return Promise.all(names.map(async (name) => {
|
|
21757
|
-
const installedManifest = await readObject2(
|
|
22291
|
+
const installedManifest = await readObject2(join13(projectRoot, "node_modules", name, "package.json")).catch(() => {
|
|
21758
22292
|
return;
|
|
21759
22293
|
});
|
|
21760
22294
|
const installed = installedManifest?.version;
|
|
@@ -21813,7 +22347,7 @@ var inspectAbsoluteMobileProject = async (config, projectRoot, options = {}) =>
|
|
|
21813
22347
|
},
|
|
21814
22348
|
format: ABSOLUTE_MOBILE_INSPECTION_FORMAT,
|
|
21815
22349
|
nativeProjects: await Promise.all(config.platforms.map(async (platform) => {
|
|
21816
|
-
const path =
|
|
22350
|
+
const path = join13(config.nativeProjectDirectory, platform);
|
|
21817
22351
|
return {
|
|
21818
22352
|
initialized: await pathExists4(path),
|
|
21819
22353
|
path: portablePath2(projectRoot, path),
|
|
@@ -21870,8 +22404,8 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
21870
22404
|
|
|
21871
22405
|
// src/mobile/ciWorkflow.ts
|
|
21872
22406
|
import { existsSync } from "fs";
|
|
21873
|
-
import { access as
|
|
21874
|
-
import { dirname as
|
|
22407
|
+
import { access as access10, mkdir as mkdir11, readFile as readFile16, writeFile as writeFile12 } from "fs/promises";
|
|
22408
|
+
import { dirname as dirname10, extname as extname2, relative as relative9, resolve as resolve10, sep as sep6 } from "path";
|
|
21875
22409
|
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 1;
|
|
21876
22410
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
21877
22411
|
var CI_ENV_INDENTATION = 6;
|
|
@@ -21890,9 +22424,9 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
21890
22424
|
"APP_STORE_CONNECT_KEY_ID",
|
|
21891
22425
|
"APP_STORE_CONNECT_PRIVATE_KEY_BASE64"
|
|
21892
22426
|
]);
|
|
21893
|
-
var
|
|
22427
|
+
var exists4 = async (path) => {
|
|
21894
22428
|
try {
|
|
21895
|
-
await
|
|
22429
|
+
await access10(path);
|
|
21896
22430
|
return true;
|
|
21897
22431
|
} catch {
|
|
21898
22432
|
return false;
|
|
@@ -21900,10 +22434,10 @@ var exists3 = async (path) => {
|
|
|
21900
22434
|
};
|
|
21901
22435
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
21902
22436
|
var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
21903
|
-
const root =
|
|
21904
|
-
const path =
|
|
21905
|
-
const portable =
|
|
21906
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22437
|
+
const root = resolve10(projectRoot);
|
|
22438
|
+
const path = resolve10(root, value);
|
|
22439
|
+
const portable = relative9(root, path).replaceAll("\\", "/");
|
|
22440
|
+
if (portable === ".." || portable.startsWith(`..${sep6}`) || portable.startsWith("../") || portable === "") {
|
|
21907
22441
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
21908
22442
|
}
|
|
21909
22443
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
@@ -21913,11 +22447,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
|
21913
22447
|
return portable;
|
|
21914
22448
|
};
|
|
21915
22449
|
var workflowOutputPath = (projectRoot, value) => {
|
|
21916
|
-
const root =
|
|
21917
|
-
const workflows =
|
|
21918
|
-
const path =
|
|
21919
|
-
const portable =
|
|
21920
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
22450
|
+
const root = resolve10(projectRoot);
|
|
22451
|
+
const workflows = resolve10(root, ".github/workflows");
|
|
22452
|
+
const path = resolve10(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
22453
|
+
const portable = relative9(workflows, path);
|
|
22454
|
+
if (portable === ".." || portable.startsWith(`..${sep6}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
|
|
21921
22455
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
21922
22456
|
}
|
|
21923
22457
|
return path;
|
|
@@ -22346,11 +22880,11 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
22346
22880
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
22347
22881
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
22348
22882
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
22349
|
-
const previous = await
|
|
22883
|
+
const previous = await exists4(path) ? await readFile16(path, "utf8") : undefined;
|
|
22350
22884
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
22351
|
-
throw new TypeError(`${
|
|
22885
|
+
throw new TypeError(`${relative9(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
22352
22886
|
if (previous !== generated.workflow) {
|
|
22353
|
-
await
|
|
22887
|
+
await mkdir11(dirname10(path), { recursive: true });
|
|
22354
22888
|
await writeFile12(path, generated.workflow);
|
|
22355
22889
|
}
|
|
22356
22890
|
return {
|
|
@@ -22364,46 +22898,46 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
22364
22898
|
};
|
|
22365
22899
|
|
|
22366
22900
|
// src/mobile/updateSigning.ts
|
|
22367
|
-
import { createHash as
|
|
22901
|
+
import { createHash as createHash5, sign, verify } from "crypto";
|
|
22368
22902
|
import {
|
|
22369
22903
|
cp,
|
|
22370
|
-
mkdir as
|
|
22371
|
-
mkdtemp as
|
|
22372
|
-
readdir as
|
|
22373
|
-
readFile as
|
|
22904
|
+
mkdir as mkdir12,
|
|
22905
|
+
mkdtemp as mkdtemp4,
|
|
22906
|
+
readdir as readdir6,
|
|
22907
|
+
readFile as readFile17,
|
|
22374
22908
|
rename as rename10,
|
|
22375
|
-
rm as
|
|
22376
|
-
stat as
|
|
22909
|
+
rm as rm7,
|
|
22910
|
+
stat as stat6,
|
|
22377
22911
|
writeFile as writeFile13
|
|
22378
22912
|
} from "fs/promises";
|
|
22379
|
-
import { dirname as
|
|
22913
|
+
import { dirname as dirname11, join as join14, relative as relative10, resolve as resolve11 } from "path";
|
|
22380
22914
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
22381
22915
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
22382
|
-
var
|
|
22916
|
+
var sha2563 = (value) => createHash5("sha256").update(value).digest("hex");
|
|
22383
22917
|
var listFiles = async (root, directory = root) => {
|
|
22384
|
-
const entries = await
|
|
22918
|
+
const entries = await readdir6(directory, { withFileTypes: true });
|
|
22385
22919
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
22386
|
-
const path =
|
|
22920
|
+
const path = join14(directory, entry.name);
|
|
22387
22921
|
if (entry.isDirectory())
|
|
22388
22922
|
return listFiles(root, path);
|
|
22389
22923
|
if (!entry.isFile())
|
|
22390
22924
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
22391
|
-
return [
|
|
22925
|
+
return [relative10(root, path).replaceAll("\\", "/")];
|
|
22392
22926
|
}));
|
|
22393
22927
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
22394
22928
|
};
|
|
22395
22929
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
22396
|
-
const source =
|
|
22930
|
+
const source = join14(root, path);
|
|
22397
22931
|
const [metadata, contents] = await Promise.all([
|
|
22398
|
-
|
|
22399
|
-
|
|
22932
|
+
stat6(source),
|
|
22933
|
+
readFile17(source)
|
|
22400
22934
|
]);
|
|
22401
|
-
return { bytes: metadata.size, path, sha256:
|
|
22935
|
+
return { bytes: metadata.size, path, sha256: sha2563(contents) };
|
|
22402
22936
|
}));
|
|
22403
|
-
var releaseIdFor = (value) => `amu_${
|
|
22937
|
+
var releaseIdFor = (value) => `amu_${sha2563(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
22404
22938
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
22405
|
-
const bundleDirectory =
|
|
22406
|
-
const outputRoot =
|
|
22939
|
+
const bundleDirectory = resolve11(options.bundleDirectory);
|
|
22940
|
+
const outputRoot = resolve11(options.outputDirectory);
|
|
22407
22941
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
22408
22942
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
22409
22943
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -22431,30 +22965,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
22431
22965
|
value: signature.toString("base64")
|
|
22432
22966
|
}
|
|
22433
22967
|
});
|
|
22434
|
-
await
|
|
22435
|
-
const outputDirectory =
|
|
22436
|
-
const staging = await
|
|
22968
|
+
await mkdir12(outputRoot, { recursive: true });
|
|
22969
|
+
const outputDirectory = join14(outputRoot, manifest.releaseId);
|
|
22970
|
+
const staging = await mkdtemp4(join14(outputRoot, ".stage-"));
|
|
22437
22971
|
try {
|
|
22438
|
-
await cp(bundleDirectory,
|
|
22972
|
+
await cp(bundleDirectory, join14(staging, UPDATE_FILES_DIRECTORY), {
|
|
22439
22973
|
force: true,
|
|
22440
22974
|
recursive: true
|
|
22441
22975
|
});
|
|
22442
|
-
await writeFile13(
|
|
22976
|
+
await writeFile13(join14(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
22443
22977
|
`);
|
|
22444
22978
|
await rename10(staging, outputDirectory);
|
|
22445
22979
|
} catch (error) {
|
|
22446
|
-
await
|
|
22980
|
+
await rm7(staging, { force: true, recursive: true });
|
|
22447
22981
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
22448
22982
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
22449
22983
|
throw error;
|
|
22450
22984
|
}
|
|
22451
22985
|
return {
|
|
22452
22986
|
manifest,
|
|
22453
|
-
manifestPath:
|
|
22987
|
+
manifestPath: join14(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
22454
22988
|
outputDirectory
|
|
22455
22989
|
};
|
|
22456
22990
|
};
|
|
22457
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
22991
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile17(join14(resolve11(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
22458
22992
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
22459
22993
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
22460
22994
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -22464,8 +22998,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
22464
22998
|
};
|
|
22465
22999
|
|
|
22466
23000
|
// src/mobile/expoUpdate.ts
|
|
22467
|
-
import { access as
|
|
22468
|
-
import { dirname as
|
|
23001
|
+
import { access as access11, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
|
|
23002
|
+
import { dirname as dirname12, extname as extname3, join as join15 } from "path";
|
|
22469
23003
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
22470
23004
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
22471
23005
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -22531,7 +23065,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
22531
23065
|
};
|
|
22532
23066
|
};
|
|
22533
23067
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
22534
|
-
const metadata = JSON.parse(await
|
|
23068
|
+
const metadata = JSON.parse(await readFile18(join15(options.exportDirectory, "metadata.json"), "utf8"));
|
|
22535
23069
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
22536
23070
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
22537
23071
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -22542,11 +23076,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22542
23076
|
runtimeVersion: options.runtimeVersion
|
|
22543
23077
|
});
|
|
22544
23078
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
22545
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
23079
|
+
await Promise.all(referenced.map(({ path }) => access11(join15(options.exportDirectory, path)).catch(() => {
|
|
22546
23080
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
22547
23081
|
})));
|
|
22548
|
-
const destination =
|
|
22549
|
-
await
|
|
23082
|
+
const destination = join15(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
23083
|
+
await mkdir13(dirname12(destination), { recursive: true });
|
|
22550
23084
|
await writeFile14(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
22551
23085
|
`);
|
|
22552
23086
|
return { descriptor, path: destination };
|
|
@@ -22554,21 +23088,21 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
22554
23088
|
|
|
22555
23089
|
// src/mobile/expoCodeSigning.ts
|
|
22556
23090
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
22557
|
-
import { access as
|
|
22558
|
-
import { dirname as
|
|
22559
|
-
var
|
|
22560
|
-
var
|
|
22561
|
-
const location =
|
|
22562
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
23091
|
+
import { access as access12, mkdir as mkdir14, writeFile as writeFile15 } from "fs/promises";
|
|
23092
|
+
import { dirname as dirname13, isAbsolute as isAbsolute6, relative as relative11, resolve as resolve12, sep as sep7 } from "path";
|
|
23093
|
+
var exists5 = async (path) => access12(path).then(() => true).catch(() => false);
|
|
23094
|
+
var inside3 = (root, path) => {
|
|
23095
|
+
const location = relative11(root, path);
|
|
23096
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep7}`) && !isAbsolute6(location);
|
|
22563
23097
|
};
|
|
22564
23098
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
22565
|
-
const root =
|
|
22566
|
-
const certificatePath =
|
|
22567
|
-
const privateKeyPath =
|
|
22568
|
-
const publicKeyPath =
|
|
22569
|
-
if (!
|
|
23099
|
+
const root = resolve12(options.projectRoot);
|
|
23100
|
+
const certificatePath = resolve12(root, options.certificatePath);
|
|
23101
|
+
const privateKeyPath = resolve12(root, options.privateKeyPath);
|
|
23102
|
+
const publicKeyPath = resolve12(root, options.publicKeyPath ?? resolve12(dirname13(privateKeyPath), "public-key.pem"));
|
|
23103
|
+
if (!inside3(root, certificatePath))
|
|
22570
23104
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
22571
|
-
if (
|
|
23105
|
+
if (inside3(root, privateKeyPath) || inside3(root, publicKeyPath))
|
|
22572
23106
|
throw new TypeError("Expo code-signing private/public key output must remain outside the project and source control.");
|
|
22573
23107
|
const commonName = options.commonName.trim();
|
|
22574
23108
|
if (!commonName || commonName.length > 64)
|
|
@@ -22577,7 +23111,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22577
23111
|
if (!Number.isSafeInteger(validityYears) || validityYears < 1 || validityYears > 20)
|
|
22578
23112
|
throw new TypeError("Expo code-signing validity must be an integer from 1 through 20 years.");
|
|
22579
23113
|
const collisions = (await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map(async (path) => ({
|
|
22580
|
-
exists: await
|
|
23114
|
+
exists: await exists5(path),
|
|
22581
23115
|
path
|
|
22582
23116
|
})))).filter((entry) => entry.exists);
|
|
22583
23117
|
if (collisions.length > 0)
|
|
@@ -22595,7 +23129,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22595
23129
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
22596
23130
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
22597
23131
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
22598
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
23132
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir14(dirname13(path), { recursive: true })));
|
|
22599
23133
|
await Promise.all([
|
|
22600
23134
|
writeFile15(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
22601
23135
|
writeFile15(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
@@ -22610,8 +23144,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
22610
23144
|
};
|
|
22611
23145
|
|
|
22612
23146
|
// src/mobile/updatePublisher.ts
|
|
22613
|
-
import { access as
|
|
22614
|
-
import { isAbsolute as
|
|
23147
|
+
import { access as access13 } from "fs/promises";
|
|
23148
|
+
import { isAbsolute as isAbsolute7, relative as relative12, resolve as resolve13, sep as sep8 } from "path";
|
|
22615
23149
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
22616
23150
|
var lifecycleMethod = (publisher, name) => {
|
|
22617
23151
|
const method = publisher[name];
|
|
@@ -22720,16 +23254,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
22720
23254
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22721
23255
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
22722
23256
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
22723
|
-
const root =
|
|
22724
|
-
const path =
|
|
22725
|
-
const projectRelative =
|
|
22726
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
23257
|
+
const root = resolve13(projectRoot);
|
|
23258
|
+
const path = resolve13(root, requested);
|
|
23259
|
+
const projectRelative = relative12(root, path);
|
|
23260
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep8}`) || isAbsolute7(projectRelative))
|
|
22727
23261
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
22728
23262
|
return path;
|
|
22729
23263
|
};
|
|
22730
23264
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
22731
23265
|
const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
|
|
22732
|
-
await
|
|
23266
|
+
await access13(modulePath).catch(() => {
|
|
22733
23267
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
22734
23268
|
});
|
|
22735
23269
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -22827,7 +23361,7 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
22827
23361
|
];
|
|
22828
23362
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
22829
23363
|
var directProjectPackages = async (projectRoot) => {
|
|
22830
|
-
const manifest = JSON.parse(await
|
|
23364
|
+
const manifest = JSON.parse(await readFile19(join16(projectRoot, "package.json"), "utf8"));
|
|
22831
23365
|
if (!isRecord7(manifest))
|
|
22832
23366
|
throw new TypeError("Application package.json must contain an object.");
|
|
22833
23367
|
const names = new Set;
|
|
@@ -22841,7 +23375,7 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
22841
23375
|
};
|
|
22842
23376
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
22843
23377
|
try {
|
|
22844
|
-
const manifest = JSON.parse(await
|
|
23378
|
+
const manifest = JSON.parse(await readFile19(join16(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
22845
23379
|
return isRecord7(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
22846
23380
|
} catch {
|
|
22847
23381
|
return;
|
|
@@ -22905,9 +23439,9 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
22905
23439
|
};
|
|
22906
23440
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
22907
23441
|
var expoExecutable = async (project) => {
|
|
22908
|
-
const executable =
|
|
23442
|
+
const executable = join16(project, "node_modules", ".bin", "expo");
|
|
22909
23443
|
try {
|
|
22910
|
-
await
|
|
23444
|
+
await access14(executable);
|
|
22911
23445
|
return executable;
|
|
22912
23446
|
} catch {
|
|
22913
23447
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -22939,14 +23473,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
22939
23473
|
};
|
|
22940
23474
|
var ensureExpoPackages = async (project, args) => {
|
|
22941
23475
|
try {
|
|
22942
|
-
const manifest = JSON.parse(await
|
|
23476
|
+
const manifest = JSON.parse(await readFile19(join16(project, "package.json"), "utf8"));
|
|
22943
23477
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
22944
23478
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
22945
23479
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
22946
23480
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
22947
23481
|
if (typeof expected !== "string")
|
|
22948
23482
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
22949
|
-
const installed = JSON.parse(await
|
|
23483
|
+
const installed = JSON.parse(await readFile19(join16(project, "node_modules", name, "package.json"), "utf8"));
|
|
22950
23484
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
22951
23485
|
if (typeof actual !== "string")
|
|
22952
23486
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -23144,7 +23678,7 @@ var sync = async (args) => {
|
|
|
23144
23678
|
};
|
|
23145
23679
|
var associations = async (args) => {
|
|
23146
23680
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
23147
|
-
const outputDirectory =
|
|
23681
|
+
const outputDirectory = resolve14(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
23148
23682
|
if (args.includes("--verify")) {
|
|
23149
23683
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
23150
23684
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -23196,7 +23730,7 @@ var generateGithubCi = async (args) => {
|
|
|
23196
23730
|
const publicResult = {
|
|
23197
23731
|
changed: result.changed,
|
|
23198
23732
|
format: result.format,
|
|
23199
|
-
path:
|
|
23733
|
+
path: relative13(projectRoot, result.path).replaceAll("\\", "/"),
|
|
23200
23734
|
platforms: result.platforms,
|
|
23201
23735
|
publishing: result.publishing,
|
|
23202
23736
|
requiredSecrets: result.requiredSecrets
|
|
@@ -23307,9 +23841,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23307
23841
|
temporaryDirectory: undefined
|
|
23308
23842
|
};
|
|
23309
23843
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
23310
|
-
const temporaryParent =
|
|
23311
|
-
await
|
|
23312
|
-
const temporaryDirectory = await
|
|
23844
|
+
const temporaryParent = join16(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
23845
|
+
await mkdir15(temporaryParent, { recursive: true });
|
|
23846
|
+
const temporaryDirectory = await mkdtemp5(join16(temporaryParent, ".stage-"));
|
|
23313
23847
|
try {
|
|
23314
23848
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
23315
23849
|
"export",
|
|
@@ -23321,7 +23855,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23321
23855
|
temporaryDirectory,
|
|
23322
23856
|
"--clear"
|
|
23323
23857
|
], { production: true });
|
|
23324
|
-
const generatedApp = JSON.parse(await
|
|
23858
|
+
const generatedApp = JSON.parse(await readFile19(join16(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
23325
23859
|
const expoConfig = isRecord7(generatedApp) ? generatedApp.expo : undefined;
|
|
23326
23860
|
if (!isRecord7(expoConfig))
|
|
23327
23861
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
@@ -23339,7 +23873,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
23339
23873
|
temporaryDirectory
|
|
23340
23874
|
};
|
|
23341
23875
|
} catch (error) {
|
|
23342
|
-
await
|
|
23876
|
+
await rm8(temporaryDirectory, { force: true, recursive: true });
|
|
23343
23877
|
throw error;
|
|
23344
23878
|
}
|
|
23345
23879
|
};
|
|
@@ -23364,11 +23898,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
23364
23898
|
let expoExportDirectory;
|
|
23365
23899
|
try {
|
|
23366
23900
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
23367
|
-
const embedded = JSON.parse(await
|
|
23901
|
+
const embedded = JSON.parse(await readFile19(join16(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
23368
23902
|
const runtimeFingerprint = isRecord7(embedded) ? embedded.nativeRuntime : undefined;
|
|
23369
23903
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
23370
23904
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
23371
|
-
const privateKey = await
|
|
23905
|
+
const privateKey = await readFile19(resolve14(projectRoot, signingKeyPath));
|
|
23372
23906
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
23373
23907
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
23374
23908
|
format: "der",
|
|
@@ -23389,7 +23923,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23389
23923
|
channel: mobile.updates.channel,
|
|
23390
23924
|
classification,
|
|
23391
23925
|
keyId,
|
|
23392
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
23926
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join16(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
23393
23927
|
privateKey,
|
|
23394
23928
|
runtimeFingerprint
|
|
23395
23929
|
});
|
|
@@ -23401,7 +23935,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
23401
23935
|
return result;
|
|
23402
23936
|
} finally {
|
|
23403
23937
|
if (expoExportDirectory)
|
|
23404
|
-
await
|
|
23938
|
+
await rm8(expoExportDirectory, { force: true, recursive: true });
|
|
23405
23939
|
sendTelemetryEvent("mobile:update-build", {
|
|
23406
23940
|
classification: classification ?? "invalid",
|
|
23407
23941
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -23431,7 +23965,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
23431
23965
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
23432
23966
|
...validityYears === undefined ? {} : { validityYears }
|
|
23433
23967
|
});
|
|
23434
|
-
const certificatePath =
|
|
23968
|
+
const certificatePath = relative13(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
23435
23969
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
23436
23970
|
engine: "expo",
|
|
23437
23971
|
validityYears: validityYears ?? 10
|
|
@@ -23493,7 +24027,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
23493
24027
|
publicKeys: mobile.updates.publicKeys,
|
|
23494
24028
|
storage: requestedStorage
|
|
23495
24029
|
});
|
|
23496
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
24030
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative13(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
23497
24031
|
if (requestedStorage === "local")
|
|
23498
24032
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
23499
24033
|
else
|
|
@@ -23905,6 +24439,7 @@ var prepareCapacitorIosReleaseProject = async (mobile, projectRoot) => {
|
|
|
23905
24439
|
await applyAbsoluteNativeBackgroundSync(projectRoot, mobile, ["ios"]);
|
|
23906
24440
|
await applyAbsoluteNativeUpdates(mobile, ["ios"]);
|
|
23907
24441
|
await applyAbsoluteNativeObservability(mobile, ["ios"]);
|
|
24442
|
+
await applyAbsoluteNativeReleaseReadiness(mobile, ["ios"]);
|
|
23908
24443
|
};
|
|
23909
24444
|
var prepareIosReleaseProject = (mobile, projectRoot, args) => mobile.engine === "expo" ? prepareExpoIosReleaseProject(mobile, projectRoot, args) : prepareCapacitorIosReleaseProject(mobile, projectRoot);
|
|
23910
24445
|
var prepareCapacitorAndroidReleaseProject = async (mobile, projectRoot) => {
|
|
@@ -23969,7 +24504,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
23969
24504
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
23970
24505
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
23971
24506
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
23972
|
-
console.log(`Metadata: ${
|
|
24507
|
+
console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
|
|
23973
24508
|
return release;
|
|
23974
24509
|
} finally {
|
|
23975
24510
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -24057,6 +24592,7 @@ var buildLocalIosRelease = async (options) => {
|
|
|
24057
24592
|
developmentTeam: process.env.ABSOLUTE_IOS_DEVELOPMENT_TEAM,
|
|
24058
24593
|
...options.mobile.engine === "expo" ? { env: expoProductionEnvironment() } : {},
|
|
24059
24594
|
outputDirectory: valueAfter(options.args, "--outdir"),
|
|
24595
|
+
registeredDeviceArtifact: options.args.includes("--registered-device-artifact"),
|
|
24060
24596
|
...options.prepareBuildNumber === undefined ? {} : { prepareBuildNumber: options.prepareBuildNumber },
|
|
24061
24597
|
projectRoot: options.projectRoot
|
|
24062
24598
|
});
|
|
@@ -24087,6 +24623,8 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
24087
24623
|
const remoteProfile = requestedRemote !== undefined || process.platform !== "darwin" ? await getAbsoluteRemoteMacProfile(requestedRemote, remoteProfilePath()) : undefined;
|
|
24088
24624
|
if (process.platform !== "darwin" && !remoteProfile)
|
|
24089
24625
|
throw new TypeError("iOS release builds require macOS or a paired Remote Mac. Run `absolute mobile pair mac <name> <user@host>`.");
|
|
24626
|
+
if (remoteProfile && args.includes("--registered-device-artifact"))
|
|
24627
|
+
throw new TypeError("--registered-device-artifact currently requires a local Mac; paired Remote Mac transfer support is not yet available.");
|
|
24090
24628
|
let success = false;
|
|
24091
24629
|
const cancellation = remoteProfile ? new AbortController : undefined;
|
|
24092
24630
|
let stopListeningForCancellation = () => {
|
|
@@ -24125,7 +24663,9 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
24125
24663
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
24126
24664
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} iOS IPA ${release.metadata.marketingVersion}${release.metadata.buildNumber ? ` (${release.metadata.buildNumber})` : ""} in ${getDurationString(durationMs)}.`);
|
|
24127
24665
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
24128
|
-
|
|
24666
|
+
if (release.registeredArtifactPath)
|
|
24667
|
+
console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
|
|
24668
|
+
console.log(`Metadata: ${join16(release.releaseRoot, "release.json")}`);
|
|
24129
24669
|
return release;
|
|
24130
24670
|
} finally {
|
|
24131
24671
|
stopListeningForCancellation();
|
|
@@ -24237,7 +24777,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24237
24777
|
checks.push({
|
|
24238
24778
|
id: "sync.storage-schema",
|
|
24239
24779
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
24240
|
-
path:
|
|
24780
|
+
path: join16(projectRoot, "package.json"),
|
|
24241
24781
|
platform: "host",
|
|
24242
24782
|
status: "pass"
|
|
24243
24783
|
});
|
|
@@ -24245,7 +24785,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
24245
24785
|
checks.push({
|
|
24246
24786
|
id: "sync.storage-schema",
|
|
24247
24787
|
label: "Offline schema metadata is invalid",
|
|
24248
|
-
path:
|
|
24788
|
+
path: join16(projectRoot, "package.json"),
|
|
24249
24789
|
platform: "host",
|
|
24250
24790
|
remediation: error instanceof Error ? error.message : String(error),
|
|
24251
24791
|
status: "fail"
|
|
@@ -24312,7 +24852,7 @@ AbsoluteJS can configure ${installPlatform} emulation:`);
|
|
|
24312
24852
|
Emulator setup verification:`);
|
|
24313
24853
|
printDoctorChecks(result.checks.filter((check) => check.platform === "host" || check.platform === installPlatform));
|
|
24314
24854
|
};
|
|
24315
|
-
var
|
|
24855
|
+
var captureCommand4 = (command) => {
|
|
24316
24856
|
const result = Bun.spawnSync(command, {
|
|
24317
24857
|
stderr: "pipe",
|
|
24318
24858
|
stdin: "ignore",
|
|
@@ -24333,7 +24873,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
24333
24873
|
}
|
|
24334
24874
|
return { https: args.includes("--https"), port };
|
|
24335
24875
|
}
|
|
24336
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
24876
|
+
const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24337
24877
|
if (instances.length !== 1) {
|
|
24338
24878
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for Android to report ready, then run `absolute mobile test android`." : "Multiple dev servers are running for this project. Select one with mobile test android --port <port>.");
|
|
24339
24879
|
}
|
|
@@ -24362,7 +24902,7 @@ var requireAndroidAdb = async () => {
|
|
|
24362
24902
|
return adb2;
|
|
24363
24903
|
};
|
|
24364
24904
|
var selectAndroidSerial = (adb2, explicitSerial) => {
|
|
24365
|
-
const devices =
|
|
24905
|
+
const devices = captureCommand4([adb2, "devices"]);
|
|
24366
24906
|
if (devices.exitCode !== 0) {
|
|
24367
24907
|
throw new Error(`Could not list Android targets: ${devices.stderr.trim() || devices.stdout.trim()}`);
|
|
24368
24908
|
}
|
|
@@ -24382,7 +24922,7 @@ var selectAndroidSerial = (adb2, explicitSerial) => {
|
|
|
24382
24922
|
var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial, json) => {
|
|
24383
24923
|
if (explicitSerial)
|
|
24384
24924
|
return selectAndroidSerial(adb2, explicitSerial);
|
|
24385
|
-
const current =
|
|
24925
|
+
const current = captureCommand4([adb2, "devices"]);
|
|
24386
24926
|
const ready = current.exitCode === 0 ? parseAdbDevices(current.stdout).find((serial) => serial.startsWith("emulator-")) : undefined;
|
|
24387
24927
|
if (ready)
|
|
24388
24928
|
return ready;
|
|
@@ -24401,11 +24941,11 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
24401
24941
|
launched.unref();
|
|
24402
24942
|
const startedAt = performance.now();
|
|
24403
24943
|
const bootedSerial = () => {
|
|
24404
|
-
const devices =
|
|
24944
|
+
const devices = captureCommand4([adb2, "devices"]);
|
|
24405
24945
|
const serial = devices.exitCode === 0 ? parseAdbDevices(devices.stdout).find((candidate) => candidate.startsWith("emulator-")) : undefined;
|
|
24406
24946
|
if (!serial)
|
|
24407
24947
|
return;
|
|
24408
|
-
const booted =
|
|
24948
|
+
const booted = captureCommand4([
|
|
24409
24949
|
adb2,
|
|
24410
24950
|
"-s",
|
|
24411
24951
|
serial,
|
|
@@ -24427,8 +24967,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
24427
24967
|
return waitForBoot();
|
|
24428
24968
|
};
|
|
24429
24969
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
24430
|
-
const root =
|
|
24431
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
24970
|
+
const root = resolve14(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
24971
|
+
if (root !== projectRoot && !root.startsWith(`${resolve14(projectRoot)}/`)) {
|
|
24432
24972
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
24433
24973
|
}
|
|
24434
24974
|
return root;
|
|
@@ -24456,11 +24996,11 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
24456
24996
|
});
|
|
24457
24997
|
};
|
|
24458
24998
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
24459
|
-
await
|
|
24460
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
24999
|
+
await mkdir15(options.artifactRoot, { recursive: true });
|
|
25000
|
+
const screenshot = options.session ? await options.session.screenshot(join16(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
24461
25001
|
return;
|
|
24462
25002
|
}) : undefined;
|
|
24463
|
-
const diagnosticPath =
|
|
25003
|
+
const diagnosticPath = join16(options.artifactRoot, "android-failure.json");
|
|
24464
25004
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24465
25005
|
diagnostics: options.session?.diagnostics ?? [],
|
|
24466
25006
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -24536,7 +25076,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24536
25076
|
release,
|
|
24537
25077
|
serial
|
|
24538
25078
|
});
|
|
24539
|
-
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial,
|
|
25079
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join16(reportRoot, "android-release.png")) : undefined;
|
|
24540
25080
|
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
24541
25081
|
durationMs: result.durationMs,
|
|
24542
25082
|
engine: result.engine,
|
|
@@ -24583,8 +25123,8 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
24583
25123
|
platform: "android",
|
|
24584
25124
|
success: false
|
|
24585
25125
|
});
|
|
24586
|
-
await
|
|
24587
|
-
const diagnosticPath =
|
|
25126
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25127
|
+
const diagnosticPath = join16(artifactRoot, "android-release-failure.json");
|
|
24588
25128
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24589
25129
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
24590
25130
|
platform: "android",
|
|
@@ -24679,7 +25219,7 @@ var testAndroid = async (args) => {
|
|
|
24679
25219
|
console.log(JSON.stringify(report, null, 2));
|
|
24680
25220
|
else
|
|
24681
25221
|
printAndroidTestReport(report);
|
|
24682
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
25222
|
+
const screenshot = reportRoot ? await session.screenshot(join16(artifactRoot, "android-emulator.png")) : undefined;
|
|
24683
25223
|
await writeRequestedAndroidReport({
|
|
24684
25224
|
adb: adb2,
|
|
24685
25225
|
args,
|
|
@@ -24748,14 +25288,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
24748
25288
|
const port = Number(explicit);
|
|
24749
25289
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
24750
25290
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
24751
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
25291
|
+
const instance2 = listLiveInstances().find((candidate) => resolve14(candidate.cwd) === resolve14(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
24752
25292
|
return {
|
|
24753
25293
|
https: instance2?.https ?? args.includes("--https"),
|
|
24754
25294
|
instance: instance2,
|
|
24755
25295
|
port
|
|
24756
25296
|
};
|
|
24757
25297
|
}
|
|
24758
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
25298
|
+
const instances = listLiveInstances().filter((instance2) => resolve14(instance2.cwd) === resolve14(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
24759
25299
|
if (instances.length !== 1)
|
|
24760
25300
|
throw new TypeError(instances.length === 0 ? "No running AbsoluteJS dev server was found for this project. Start `bun dev`, wait for iOS to report ready, then run `absolute mobile test ios`." : "Multiple dev servers are running for this project. Select one with mobile test ios --port <port>.");
|
|
24761
25301
|
const [instance] = instances;
|
|
@@ -24845,11 +25385,11 @@ var physicalIosCapture = async (options) => {
|
|
|
24845
25385
|
remote: false,
|
|
24846
25386
|
xcodeVersion: requireCapturedCommand(["/usr/bin/xcodebuild", "-version"], "Xcode version inspection").stdout.trim(),
|
|
24847
25387
|
xcrun: "/usr/bin/xcrun",
|
|
24848
|
-
capture: async (command) =>
|
|
25388
|
+
capture: async (command) => captureCommand4(command)
|
|
24849
25389
|
};
|
|
24850
25390
|
};
|
|
24851
25391
|
var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
24852
|
-
const result =
|
|
25392
|
+
const result = captureCommand4([
|
|
24853
25393
|
xcrun,
|
|
24854
25394
|
"simctl",
|
|
24855
25395
|
"list",
|
|
@@ -24871,16 +25411,37 @@ var selectIosSimulator = (xcrun, explicitUdid) => {
|
|
|
24871
25411
|
throw new TypeError("No ready AbsoluteJS iOS simulator was found. Start `bun dev` and wait for the iOS target to become ready.");
|
|
24872
25412
|
return selected;
|
|
24873
25413
|
};
|
|
25414
|
+
var selectOrStartIosReleaseSimulator = async (xcrun, explicitUdid) => {
|
|
25415
|
+
const result = captureCommand4([
|
|
25416
|
+
xcrun,
|
|
25417
|
+
"simctl",
|
|
25418
|
+
"list",
|
|
25419
|
+
"devices",
|
|
25420
|
+
"available",
|
|
25421
|
+
"-j"
|
|
25422
|
+
]);
|
|
25423
|
+
if (result.exitCode !== 0)
|
|
25424
|
+
throw new Error(`Could not list iOS simulators: ${result.stderr.trim() || result.stdout.trim()}`);
|
|
25425
|
+
const devices = parseIosSimulators(result.stdout).filter((device) => device.isAvailable);
|
|
25426
|
+
const selected = explicitUdid ? devices.find((device) => device.udid === explicitUdid) : devices.find((device) => device.name === ABSOLUTE_IOS_SIMULATOR_NAME);
|
|
25427
|
+
if (!selected)
|
|
25428
|
+
throw new TypeError(explicitUdid ? `iOS simulator ${explicitUdid} is unavailable.` : "No managed AbsoluteJS iOS simulator was found. Run `absolute mobile doctor ios --fix`.");
|
|
25429
|
+
if (selected.state !== "Booted") {
|
|
25430
|
+
requireCapturedCommand([xcrun, "simctl", "boot", selected.udid], "iOS Simulator boot");
|
|
25431
|
+
requireCapturedCommand([xcrun, "simctl", "bootstatus", selected.udid, "-b"], "iOS Simulator readiness");
|
|
25432
|
+
}
|
|
25433
|
+
return { ...selected, state: "Booted" };
|
|
25434
|
+
};
|
|
24874
25435
|
var requireCapturedCommand = (command, label) => {
|
|
24875
|
-
const result =
|
|
25436
|
+
const result = captureCommand4(command);
|
|
24876
25437
|
if (result.exitCode !== 0)
|
|
24877
25438
|
throw new Error(`${label} failed: ${result.stderr.trim() || result.stdout.trim() || `status ${result.exitCode}`}`);
|
|
24878
25439
|
return result;
|
|
24879
25440
|
};
|
|
24880
25441
|
var writeIosFailureArtifacts = async (options) => {
|
|
24881
|
-
await
|
|
24882
|
-
const screenshot =
|
|
24883
|
-
const screenshotResult =
|
|
25442
|
+
await mkdir15(options.artifactRoot, { recursive: true });
|
|
25443
|
+
const screenshot = join16(options.artifactRoot, "ios-failure.png");
|
|
25444
|
+
const screenshotResult = captureCommand4([
|
|
24884
25445
|
options.xcrun,
|
|
24885
25446
|
"simctl",
|
|
24886
25447
|
"io",
|
|
@@ -24888,7 +25449,7 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
24888
25449
|
"screenshot",
|
|
24889
25450
|
screenshot
|
|
24890
25451
|
]);
|
|
24891
|
-
const diagnosticPath =
|
|
25452
|
+
const diagnosticPath = join16(options.artifactRoot, "ios-failure.json");
|
|
24892
25453
|
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
24893
25454
|
appId: options.appId,
|
|
24894
25455
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -24916,8 +25477,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
24916
25477
|
var absolutejsVersionForReport = async () => {
|
|
24917
25478
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
24918
25479
|
const versions = await Promise.all([
|
|
24919
|
-
|
|
24920
|
-
|
|
25480
|
+
resolve14(import.meta.dir, "..", "..", "package.json"),
|
|
25481
|
+
resolve14(import.meta.dir, "..", "..", "..", "package.json")
|
|
24921
25482
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
24922
25483
|
for (const version of versions) {
|
|
24923
25484
|
if (version === "unknown")
|
|
@@ -24964,7 +25525,11 @@ var writeRequestedIosReport = async (options) => {
|
|
|
24964
25525
|
const metadata = options.metadata ?? (options.xcrun ? await iosReportMetadata(options.xcrun) : undefined);
|
|
24965
25526
|
if (!metadata)
|
|
24966
25527
|
throw new Error("iOS report metadata could not be inspected.");
|
|
24967
|
-
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
|
|
25528
|
+
const paths = await writeAbsoluteIosPartnerReport(reportRoot, createAbsoluteIosPartnerReport({
|
|
25529
|
+
...metadata,
|
|
25530
|
+
...options.provider ? { provider: options.provider } : {},
|
|
25531
|
+
run: options.run
|
|
25532
|
+
}));
|
|
24968
25533
|
const print = options.args.includes("--json") ? console.error : console.log;
|
|
24969
25534
|
print(`iOS partner report: ${paths.markdownPath}`);
|
|
24970
25535
|
print(`Return this report directory: ${paths.directory}`);
|
|
@@ -25081,8 +25646,167 @@ var testPhysicalIos = async (options) => {
|
|
|
25081
25646
|
throw error;
|
|
25082
25647
|
}
|
|
25083
25648
|
};
|
|
25649
|
+
var printIosReleaseAcceptance = (result, json) => {
|
|
25650
|
+
if (json) {
|
|
25651
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25652
|
+
return;
|
|
25653
|
+
}
|
|
25654
|
+
console.log(`\u2713 ${result.artifactExactness} ${result.engine} iOS release evidence passed via ${result.distribution}.`);
|
|
25655
|
+
console.log(`\u2713 Embedded local content launched in ${getDurationString(result.launchMs)} and relaunched in ${getDurationString(result.relaunchMs)}.`);
|
|
25656
|
+
console.log(result.networkUnavailable === "user-confirmed" ? "\u2713 Physical-device network-unavailable state was explicitly confirmed." : "\u2139 Simulator evidence does not claim physical-device offline behavior.");
|
|
25657
|
+
};
|
|
25658
|
+
var iosReleaseRun = (result, appId, targetId) => ({
|
|
25659
|
+
appId,
|
|
25660
|
+
durationMs: result.durationMs,
|
|
25661
|
+
hmrConnected: false,
|
|
25662
|
+
iosRelease: {
|
|
25663
|
+
artifactBytes: result.artifactBytes,
|
|
25664
|
+
artifactExactness: result.artifactExactness,
|
|
25665
|
+
artifactSha256: result.artifactSha256,
|
|
25666
|
+
distribution: result.distribution,
|
|
25667
|
+
embeddedLocal: result.embeddedLocal,
|
|
25668
|
+
engine: result.engine,
|
|
25669
|
+
installMs: result.installMs,
|
|
25670
|
+
launchMs: result.launchMs,
|
|
25671
|
+
networkUnavailable: result.networkUnavailable,
|
|
25672
|
+
relaunchMs: result.relaunchMs,
|
|
25673
|
+
releaseId: result.releaseId,
|
|
25674
|
+
signed: result.signed
|
|
25675
|
+
},
|
|
25676
|
+
status: "pass",
|
|
25677
|
+
targetId,
|
|
25678
|
+
targetKind: result.target
|
|
25679
|
+
});
|
|
25680
|
+
var runIosPhysicalReleaseTarget = async (options) => {
|
|
25681
|
+
const confirmed = options.args.includes("--yes") || await confirmInstall("On the selected iPhone, enable Airplane Mode and then disable Wi-Fi in Settings. Confirm both are still disabled and continue with two release launches?");
|
|
25682
|
+
if (!confirmed)
|
|
25683
|
+
throw new TypeError("Physical iOS offline acceptance was cancelled because network-unavailable state was not confirmed.");
|
|
25684
|
+
const targetId = normalizeAbsoluteIosDeviceIdentifier(options.device);
|
|
25685
|
+
const result = await runAbsoluteIosDeviceReleaseAcceptance({
|
|
25686
|
+
device: targetId,
|
|
25687
|
+
distribution: options.args.includes("--testflight") ? "testflight" : "registered-device",
|
|
25688
|
+
networkUnavailableConfirmed: true,
|
|
25689
|
+
release: options.release,
|
|
25690
|
+
xcrun: options.xcrun
|
|
25691
|
+
});
|
|
25692
|
+
return { result, targetId };
|
|
25693
|
+
};
|
|
25694
|
+
var runIosSimulatorReleaseTarget = async (options) => {
|
|
25695
|
+
const simulator = await selectOrStartIosReleaseSimulator(options.xcrun, valueAfter(options.args, "--udid") ?? valueAfter(options.args, "--serial"));
|
|
25696
|
+
const xcodebuild = requireCapturedCommand([options.xcrun, "--find", "xcodebuild"], "Xcode build-tool inspection").stdout.trim();
|
|
25697
|
+
const result = await runAbsoluteIosSimulatorReleaseAcceptance({
|
|
25698
|
+
artifactDirectory: options.artifactRoot,
|
|
25699
|
+
config: options.mobile,
|
|
25700
|
+
release: options.release,
|
|
25701
|
+
udid: simulator.udid,
|
|
25702
|
+
xcodebuild,
|
|
25703
|
+
xcrun: options.xcrun
|
|
25704
|
+
});
|
|
25705
|
+
return { result, targetId: simulator.udid };
|
|
25706
|
+
};
|
|
25707
|
+
var testIosRelease = async (args, mobile, projectRoot) => {
|
|
25708
|
+
const requested = valueAfter(args, "--release");
|
|
25709
|
+
if (!requested || requested.startsWith("--"))
|
|
25710
|
+
throw new TypeError("mobile test ios --release requires a release directory or release.json path.");
|
|
25711
|
+
if (!mobile.platforms.includes("ios"))
|
|
25712
|
+
throw new TypeError("mobile test ios requires ios in mobile.platforms.");
|
|
25713
|
+
if (args.includes("--remote"))
|
|
25714
|
+
throw new TypeError("Installed iOS release acceptance currently runs on a local Mac. Remote Mac acceptance will be added to the paired-host protocol.");
|
|
25715
|
+
const release = await readAbsoluteIosRelease(projectRoot, requested);
|
|
25716
|
+
if (release.metadata.appId !== mobile.appId)
|
|
25717
|
+
throw new TypeError(`iOS release app ID ${release.metadata.appId} does not match mobile.appId ${mobile.appId}.`);
|
|
25718
|
+
if (release.metadata.engine !== mobile.engine)
|
|
25719
|
+
throw new TypeError(`iOS release engine ${release.metadata.engine} does not match configured engine ${mobile.engine}.`);
|
|
25720
|
+
if (mobile.engine === "expo" && mobile.expoNativeRoutes[mobile.entry])
|
|
25721
|
+
throw new TypeError("Embedded-local Expo release acceptance requires mobile.entry to be a web route; native Expo routes may depend on trusted-server page data.");
|
|
25722
|
+
const requestedDevice = valueAfter(args, "--device");
|
|
25723
|
+
if (args.includes("--device") && !requestedDevice)
|
|
25724
|
+
throw new TypeError("mobile test ios --release --device requires a device identifier or name.");
|
|
25725
|
+
if (args.includes("--testflight") && !requestedDevice)
|
|
25726
|
+
throw new TypeError("mobile test ios --release --testflight requires --device.");
|
|
25727
|
+
if (requestedDevice && (valueAfter(args, "--udid") || valueAfter(args, "--serial")))
|
|
25728
|
+
throw new TypeError("mobile test ios --release --device cannot be combined with a simulator selector.");
|
|
25729
|
+
const xcrun = await requireIosXcrun();
|
|
25730
|
+
const reportRoot = nativeReportRoot(args, projectRoot, "ios");
|
|
25731
|
+
const artifactRoot = reportRoot ?? safeArtifactRoot(projectRoot, valueAfter(args, "--artifacts") ?? `.absolutejs/mobile/test-artifacts/${release.metadata.releaseId}`);
|
|
25732
|
+
const startedAt = performance.now();
|
|
25733
|
+
let targetId = requestedDevice ?? "ios-simulator";
|
|
25734
|
+
try {
|
|
25735
|
+
const acceptance = requestedDevice ? await runIosPhysicalReleaseTarget({
|
|
25736
|
+
args,
|
|
25737
|
+
device: requestedDevice,
|
|
25738
|
+
release,
|
|
25739
|
+
xcrun
|
|
25740
|
+
}) : await runIosSimulatorReleaseTarget({
|
|
25741
|
+
args,
|
|
25742
|
+
artifactRoot,
|
|
25743
|
+
mobile,
|
|
25744
|
+
release,
|
|
25745
|
+
xcrun
|
|
25746
|
+
});
|
|
25747
|
+
const { result, targetId: acceptedTargetId } = acceptance;
|
|
25748
|
+
targetId = acceptedTargetId;
|
|
25749
|
+
sendTelemetryEvent("mobile:ios-release-conformance", {
|
|
25750
|
+
artifactExactness: result.artifactExactness,
|
|
25751
|
+
distribution: result.distribution,
|
|
25752
|
+
durationMs: result.durationMs,
|
|
25753
|
+
engine: result.engine,
|
|
25754
|
+
installMs: result.installMs,
|
|
25755
|
+
launchMs: result.launchMs,
|
|
25756
|
+
platform: "ios",
|
|
25757
|
+
relaunchMs: result.relaunchMs,
|
|
25758
|
+
success: true
|
|
25759
|
+
});
|
|
25760
|
+
await writeRequestedIosReport({
|
|
25761
|
+
args,
|
|
25762
|
+
projectRoot,
|
|
25763
|
+
provider: mobile.engine,
|
|
25764
|
+
run: iosReleaseRun(result, mobile.appId, targetId),
|
|
25765
|
+
xcrun
|
|
25766
|
+
});
|
|
25767
|
+
printIosReleaseAcceptance(result, args.includes("--json"));
|
|
25768
|
+
return result;
|
|
25769
|
+
} catch (error) {
|
|
25770
|
+
const durationMs = Math.round(performance.now() - startedAt);
|
|
25771
|
+
sendTelemetryEvent("mobile:ios-release-conformance", {
|
|
25772
|
+
durationMs,
|
|
25773
|
+
engine: mobile.engine,
|
|
25774
|
+
platform: "ios",
|
|
25775
|
+
success: false
|
|
25776
|
+
});
|
|
25777
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25778
|
+
const diagnosticPath = join16(artifactRoot, "ios-release-failure.json");
|
|
25779
|
+
await writeFile16(diagnosticPath, `${JSON.stringify({
|
|
25780
|
+
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
25781
|
+
platform: "ios",
|
|
25782
|
+
provider: mobile.engine,
|
|
25783
|
+
releaseId: release.metadata.releaseId,
|
|
25784
|
+
status: "fail"
|
|
25785
|
+
}, null, 2)}
|
|
25786
|
+
`);
|
|
25787
|
+
if (reportRoot)
|
|
25788
|
+
await writeRequestedIosReport({
|
|
25789
|
+
args,
|
|
25790
|
+
projectRoot,
|
|
25791
|
+
provider: mobile.engine,
|
|
25792
|
+
run: {
|
|
25793
|
+
appId: mobile.appId,
|
|
25794
|
+
durationMs,
|
|
25795
|
+
error: "Installed iOS release acceptance failed; inspect the local diagnostic artifact before sharing evidence.",
|
|
25796
|
+
hmrConnected: false,
|
|
25797
|
+
status: "fail",
|
|
25798
|
+
targetId,
|
|
25799
|
+
targetKind: requestedDevice ? "device" : "simulator"
|
|
25800
|
+
},
|
|
25801
|
+
xcrun
|
|
25802
|
+
});
|
|
25803
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)} Failure diagnostics: ${diagnosticPath}`, { cause: error });
|
|
25804
|
+
}
|
|
25805
|
+
};
|
|
25084
25806
|
var testIos = async (args) => {
|
|
25085
25807
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
25808
|
+
if (args.includes("--release"))
|
|
25809
|
+
return testIosRelease(args, mobile, projectRoot);
|
|
25086
25810
|
requireCapacitorEngine(mobile, "mobile test ios");
|
|
25087
25811
|
const { https, instance, port } = requireIosTestContext(args, projectRoot);
|
|
25088
25812
|
if (!mobile.platforms.includes("ios"))
|
|
@@ -25142,8 +25866,8 @@ var testIos = async (args) => {
|
|
|
25142
25866
|
mobile.appId
|
|
25143
25867
|
], "iOS app launch");
|
|
25144
25868
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
25145
|
-
await
|
|
25146
|
-
const screenshot =
|
|
25869
|
+
await mkdir15(artifactRoot, { recursive: true });
|
|
25870
|
+
const screenshot = join16(artifactRoot, "ios-simulator.png");
|
|
25147
25871
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
25148
25872
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
25149
25873
|
const report = {
|
|
@@ -25329,7 +26053,7 @@ var runMobile = async (args) => {
|
|
|
25329
26053
|
await publishIos(args.slice(2));
|
|
25330
26054
|
return;
|
|
25331
26055
|
}
|
|
25332
|
-
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path]");
|
|
26056
|
+
throw new TypeError("Usage: absolute mobile <pair mac <name> <user@host> [--port n] [--workspace path] | remotes [inspect [name] [--json] | clean [name] --yes | --json] | unpair mac <name> | init [--no-native] [--force] | sync [ios|android] | inspect [--json] [--require-bundle] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | doctor [ios|android|release [ios|android]] [--remote name] [--json|--fix [--yes]] | build <android|ios> [server-entry] [--remote name] [--registered-device-artifact] [--outdir dir] [--web-outdir dir] [--unsigned] | update provision [--storage local|s3] [--registry module] [--force] [--yes] | update signing generate --private-key path [--certificate path] [--public-key path] [--key-id id] [--common-name name] [--validity-years n] | update build [server-entry] --classification bug-fix|content|security --key-id id --signing-key path --within-submitted-purpose [--outdir dir] [--web-outdir dir] | update publish <release-directory> [--rollout fraction] [--registry module] | update promote --release id --rollout fraction [--registry module] | update rollback [--release id] [--registry module] | update status [--registry module] [--json] | update advance [--rollout fraction] [--registry module] [--json] | update pause|resume|cancel|reconcile [--registry module] [--json] | update storage [--retain count] [--min-age-days days] [--registry module] [--json] | update gc [--retain count] [--min-age-days days] [--grace-days days] [--apply] [--registry module] [--json] | publish android [server-entry] [--registry module] [--channel name] [--play-track track] [--play-status completed|draft|halted|in-progress] [--play-rollout fraction] [--play-name name] [--play-notes language=text] [--play-update-priority 0..5] [--play-hold-review] [--play-cancel-existing-review] [--outdir dir] [--web-outdir dir] [--unsigned] | publish ios [server-entry] [--remote name] [--registry module] [--channel name] [--testflight-group name-or-id] [--testflight-notes locale=text] [--testflight-submit-review] [--outdir dir] [--web-outdir dir] [--unsigned] | test android [--release release-dir [--yes] | --route path [--wait-for-hmr] [--port n]] [--report [dir]] [--serial id] [--artifacts dir] [--json] [--config path] | test ios [--release release-dir [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
|
|
25333
26057
|
};
|
|
25334
26058
|
export {
|
|
25335
26059
|
runMobile
|