@absolutejs/absolute 0.20.0-beta.112 → 0.20.0-beta.113
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 +25 -0
- package/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/cli/index.js +1 -1
- package/dist/cli/{mobile-d5wbqynh.js → mobile-v11nwcj9.js} +564 -178
- package/dist/mobile/index.js +423 -197
- package/dist/mobile/index.js.map +5 -4
- package/dist/src/mobile/ciPromotionLedger.d.ts +49 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/package.json +1 -1
|
@@ -16283,9 +16283,9 @@ var require_pkcs7 = __commonJS(function(exports, module) {
|
|
|
16283
16283
|
var jan_1_2050 = new Date("2050-01-01T00:00:00Z");
|
|
16284
16284
|
var date = attr.value;
|
|
16285
16285
|
if (typeof date === "string") {
|
|
16286
|
-
var
|
|
16287
|
-
if (!isNaN(
|
|
16288
|
-
date = new Date(
|
|
16286
|
+
var timestamp2 = Date.parse(date);
|
|
16287
|
+
if (!isNaN(timestamp2)) {
|
|
16288
|
+
date = new Date(timestamp2);
|
|
16289
16289
|
} else if (date.length === 13) {
|
|
16290
16290
|
date = asn1.utcTimeToDate(date);
|
|
16291
16291
|
} else {
|
|
@@ -16779,15 +16779,15 @@ var require_main = __commonJS(function(exports) {
|
|
|
16779
16779
|
|
|
16780
16780
|
// src/cli/scripts/mobile.ts
|
|
16781
16781
|
import {
|
|
16782
|
-
access as
|
|
16783
|
-
mkdir as
|
|
16782
|
+
access as access18,
|
|
16783
|
+
mkdir as mkdir19,
|
|
16784
16784
|
mkdtemp as mkdtemp7,
|
|
16785
|
-
readFile as
|
|
16786
|
-
rm as
|
|
16785
|
+
readFile as readFile23,
|
|
16786
|
+
rm as rm11,
|
|
16787
16787
|
writeFile as writeFile19
|
|
16788
16788
|
} from "fs/promises";
|
|
16789
|
-
import { createPublicKey as createPublicKey2, randomUUID } from "crypto";
|
|
16790
|
-
import { join as
|
|
16789
|
+
import { createHash as createHash7, createPublicKey as createPublicKey2, randomUUID as randomUUID2 } from "crypto";
|
|
16790
|
+
import { join as join19, relative as relative17, resolve as resolve18 } from "path";
|
|
16791
16791
|
import { createInterface } from "readline/promises";
|
|
16792
16792
|
|
|
16793
16793
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -24279,47 +24279,266 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
|
|
|
24279
24279
|
}
|
|
24280
24280
|
};
|
|
24281
24281
|
|
|
24282
|
+
// src/mobile/ciPromotionLedger.ts
|
|
24283
|
+
import {
|
|
24284
|
+
access as access14,
|
|
24285
|
+
link,
|
|
24286
|
+
lstat,
|
|
24287
|
+
mkdir as mkdir15,
|
|
24288
|
+
open,
|
|
24289
|
+
readFile as readFile20,
|
|
24290
|
+
readdir as readdir7,
|
|
24291
|
+
rename as rename12,
|
|
24292
|
+
rm as rm9
|
|
24293
|
+
} from "fs/promises";
|
|
24294
|
+
import { dirname as dirname13, isAbsolute as isAbsolute9, join as join16, relative as relative13, resolve as resolve14, sep as sep10 } from "path";
|
|
24295
|
+
import { randomUUID } from "crypto";
|
|
24296
|
+
var ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT = 1;
|
|
24297
|
+
var MAX_OPERATION_BYTES = 65536;
|
|
24298
|
+
var MAX_OPERATIONS = 1000;
|
|
24299
|
+
var DISPATCH_ID_PATTERN2 = /^amp_[a-f0-9]{32}$/u;
|
|
24300
|
+
var RUN_ID_PATTERN2 = /^[1-9][0-9]*$/u;
|
|
24301
|
+
var SHA256_PATTERN2 = /^[a-f0-9]{64}$/u;
|
|
24302
|
+
var CERTIFICATION_ID_PATTERN = /^amobile_cert_[a-f0-9]{64}$/u;
|
|
24303
|
+
var RELEASE_ID_PATTERN = /^amobile_(?:android|ios)_[a-f0-9]{64}$/u;
|
|
24304
|
+
var REPOSITORY_PATTERN3 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
|
24305
|
+
var LAST_ASCII_CONTROL = 31;
|
|
24306
|
+
var DELETE_CHARACTER = 127;
|
|
24307
|
+
var PHASES = [
|
|
24308
|
+
"dispatching",
|
|
24309
|
+
"dispatched",
|
|
24310
|
+
"discovered",
|
|
24311
|
+
"completed",
|
|
24312
|
+
"audited"
|
|
24313
|
+
];
|
|
24314
|
+
var isRecord10 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24315
|
+
var requiredString2 = (value, label, max = 512) => {
|
|
24316
|
+
const hasControlCharacter = typeof value === "string" && Array.from(value).some((character) => {
|
|
24317
|
+
const code = character.codePointAt(0);
|
|
24318
|
+
return code !== undefined && (code <= LAST_ASCII_CONTROL || code === DELETE_CHARACTER);
|
|
24319
|
+
});
|
|
24320
|
+
if (typeof value !== "string" || value.trim() === "" || value !== value.trim() || value.length > max || hasControlCharacter)
|
|
24321
|
+
throw new TypeError(`${label} is invalid.`);
|
|
24322
|
+
return value;
|
|
24323
|
+
};
|
|
24324
|
+
var optionalString2 = (value, label, max = 512) => {
|
|
24325
|
+
if (value === null)
|
|
24326
|
+
return null;
|
|
24327
|
+
return requiredString2(value, label, max);
|
|
24328
|
+
};
|
|
24329
|
+
var boolean = (value, label) => {
|
|
24330
|
+
if (typeof value !== "boolean")
|
|
24331
|
+
throw new TypeError(`${label} is invalid.`);
|
|
24332
|
+
return value;
|
|
24333
|
+
};
|
|
24334
|
+
var timestamp = (value, label) => {
|
|
24335
|
+
const parsed = requiredString2(value, label, 64);
|
|
24336
|
+
if (!Number.isFinite(Date.parse(parsed)))
|
|
24337
|
+
throw new TypeError(`${label} is invalid.`);
|
|
24338
|
+
return parsed;
|
|
24339
|
+
};
|
|
24340
|
+
var projectRelativePath = (value, label) => {
|
|
24341
|
+
const parsed = requiredString2(value, label, 512);
|
|
24342
|
+
if (isAbsolute9(parsed) || parsed === ".." || parsed.startsWith(`..${sep10}`) || parsed.split(/[\\/]/u).includes(".."))
|
|
24343
|
+
throw new TypeError(`${label} must be project-relative.`);
|
|
24344
|
+
return parsed.replaceAll("\\", "/");
|
|
24345
|
+
};
|
|
24346
|
+
var nullableProjectRelativePath = (value, label) => value === null ? null : projectRelativePath(value, label);
|
|
24347
|
+
var dispatchId2 = (value) => {
|
|
24348
|
+
const parsed = requiredString2(value, "Promotion dispatch ID", 36);
|
|
24349
|
+
if (!DISPATCH_ID_PATTERN2.test(parsed))
|
|
24350
|
+
throw new TypeError("Promotion dispatch ID is invalid.");
|
|
24351
|
+
return parsed;
|
|
24352
|
+
};
|
|
24353
|
+
var runId2 = (value, label) => {
|
|
24354
|
+
const parsed = requiredString2(value, label, 32);
|
|
24355
|
+
if (!RUN_ID_PATTERN2.test(parsed))
|
|
24356
|
+
throw new TypeError(`${label} is invalid.`);
|
|
24357
|
+
return parsed;
|
|
24358
|
+
};
|
|
24359
|
+
var nullableRunId = (value, label) => value === null ? null : runId2(value, label);
|
|
24360
|
+
var operationRoot = (projectRoot) => join16(resolve14(projectRoot), ".absolutejs", "mobile-ci", "promotions");
|
|
24361
|
+
var absoluteMobilePromotionOperationPath = (projectRoot, requestedDispatchId) => join16(operationRoot(projectRoot), dispatchId2(requestedDispatchId), "operation.json");
|
|
24362
|
+
var operationPhase = (value) => {
|
|
24363
|
+
const phase = PHASES.find((candidate) => candidate === value);
|
|
24364
|
+
if (!phase)
|
|
24365
|
+
throw new TypeError("Mobile promotion operation phase is invalid.");
|
|
24366
|
+
return phase;
|
|
24367
|
+
};
|
|
24368
|
+
var parseAbsoluteMobilePromotionOperation = (value) => {
|
|
24369
|
+
if (!isRecord10(value) || value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT || value.platform !== "android" && value.platform !== "ios")
|
|
24370
|
+
throw new TypeError("Mobile promotion operation is invalid.");
|
|
24371
|
+
const parsed = {
|
|
24372
|
+
auditDirectory: nullableProjectRelativePath(value.auditDirectory, "Promotion audit directory"),
|
|
24373
|
+
auditOutputDirectory: nullableProjectRelativePath(value.auditOutputDirectory, "Promotion requested audit directory"),
|
|
24374
|
+
auditRequested: boolean(value.auditRequested, "Promotion audit request"),
|
|
24375
|
+
certificationId: requiredString2(value.certificationId, "Promotion certification ID", 256),
|
|
24376
|
+
certificationSha256: requiredString2(value.certificationSha256, "Promotion certification digest", 64),
|
|
24377
|
+
channel: optionalString2(value.channel, "Promotion channel", 128),
|
|
24378
|
+
conclusion: optionalString2(value.conclusion, "Promotion conclusion", 64),
|
|
24379
|
+
createdAt: timestamp(value.createdAt, "Promotion creation time"),
|
|
24380
|
+
dispatchId: dispatchId2(value.dispatchId),
|
|
24381
|
+
format: ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT,
|
|
24382
|
+
githubStatus: optionalString2(value.githubStatus, "Promotion GitHub status", 64),
|
|
24383
|
+
phase: operationPhase(value.phase),
|
|
24384
|
+
platform: value.platform,
|
|
24385
|
+
playTrack: optionalString2(value.playTrack, "Promotion Play track", 128),
|
|
24386
|
+
ref: optionalString2(value.ref, "Promotion ref", 256),
|
|
24387
|
+
releaseId: requiredString2(value.releaseId, "Promotion release ID", 256),
|
|
24388
|
+
repository: optionalString2(value.repository, "Promotion repository", 256),
|
|
24389
|
+
runId: nullableRunId(value.runId, "Promotion run ID"),
|
|
24390
|
+
sourceRunId: runId2(value.sourceRunId, "Promotion source run ID"),
|
|
24391
|
+
testflightGroup: optionalString2(value.testflightGroup, "Promotion TestFlight group", 128),
|
|
24392
|
+
testflightSubmitReview: boolean(value.testflightSubmitReview, "Promotion TestFlight review setting"),
|
|
24393
|
+
updatedAt: timestamp(value.updatedAt, "Promotion update time"),
|
|
24394
|
+
url: optionalString2(value.url, "Promotion run URL", 1024),
|
|
24395
|
+
watchRequested: boolean(value.watchRequested, "Promotion watch request"),
|
|
24396
|
+
workflow: projectRelativePath(value.workflow, "Promotion workflow")
|
|
24397
|
+
};
|
|
24398
|
+
if (!SHA256_PATTERN2.test(parsed.certificationSha256))
|
|
24399
|
+
throw new TypeError("Promotion certification digest is invalid.");
|
|
24400
|
+
if (!CERTIFICATION_ID_PATTERN.test(parsed.certificationId))
|
|
24401
|
+
throw new TypeError("Promotion certification ID is invalid.");
|
|
24402
|
+
if (!RELEASE_ID_PATTERN.test(parsed.releaseId))
|
|
24403
|
+
throw new TypeError("Promotion release ID is invalid.");
|
|
24404
|
+
if (parsed.repository !== null && !REPOSITORY_PATTERN3.test(parsed.repository))
|
|
24405
|
+
throw new TypeError("Promotion repository is invalid.");
|
|
24406
|
+
const discovered = PHASES.indexOf(parsed.phase) >= PHASES.indexOf("discovered");
|
|
24407
|
+
const completed = PHASES.indexOf(parsed.phase) >= PHASES.indexOf("completed");
|
|
24408
|
+
if (discovered && (!parsed.runId || !parsed.url) || !discovered && (parsed.runId || parsed.url) || completed && (parsed.githubStatus !== "completed" || !parsed.conclusion) || !completed && (parsed.githubStatus || parsed.conclusion) || parsed.phase === "audited" && !parsed.auditDirectory || parsed.phase !== "audited" && parsed.auditDirectory)
|
|
24409
|
+
throw new TypeError("Mobile promotion operation phase is incomplete.");
|
|
24410
|
+
if (parsed.url && parsed.runId) {
|
|
24411
|
+
const url = new URL(parsed.url);
|
|
24412
|
+
if (url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !url.pathname.endsWith(`/actions/runs/${parsed.runId}`))
|
|
24413
|
+
throw new TypeError("Promotion run URL is invalid.");
|
|
24414
|
+
}
|
|
24415
|
+
if (Date.parse(parsed.updatedAt) < Date.parse(parsed.createdAt))
|
|
24416
|
+
throw new TypeError("Promotion operation time moved backwards.");
|
|
24417
|
+
return parsed;
|
|
24418
|
+
};
|
|
24419
|
+
var serialize = (operation) => `${JSON.stringify(parseAbsoluteMobilePromotionOperation(operation), null, 2)}
|
|
24420
|
+
`;
|
|
24421
|
+
var writeSynced = async (path, source) => {
|
|
24422
|
+
const handle = await open(path, "wx", 384);
|
|
24423
|
+
try {
|
|
24424
|
+
await handle.writeFile(source);
|
|
24425
|
+
await handle.sync();
|
|
24426
|
+
} finally {
|
|
24427
|
+
await handle.close();
|
|
24428
|
+
}
|
|
24429
|
+
};
|
|
24430
|
+
var atomicWrite = async (path, source, exclusive) => {
|
|
24431
|
+
await mkdir15(dirname13(path), { recursive: true });
|
|
24432
|
+
const temporary = join16(dirname13(path), `.operation-${randomUUID()}.tmp`);
|
|
24433
|
+
try {
|
|
24434
|
+
await writeSynced(temporary, source);
|
|
24435
|
+
if (exclusive)
|
|
24436
|
+
await link(temporary, path);
|
|
24437
|
+
else
|
|
24438
|
+
await rename12(temporary, path);
|
|
24439
|
+
} finally {
|
|
24440
|
+
await rm9(temporary, { force: true });
|
|
24441
|
+
}
|
|
24442
|
+
};
|
|
24443
|
+
var advanceAbsoluteMobilePromotionOperation = async (projectRoot, requestedDispatchId, options, now = () => new Date) => {
|
|
24444
|
+
const current = await readAbsoluteMobilePromotionOperation(projectRoot, requestedDispatchId);
|
|
24445
|
+
if (PHASES.indexOf(options.phase) < PHASES.indexOf(current.phase))
|
|
24446
|
+
throw new TypeError("Mobile promotion operation cannot move backwards.");
|
|
24447
|
+
if (current.runId && options.runId && current.runId !== options.runId || current.url && options.url && current.url !== options.url || current.auditDirectory && options.auditDirectory && current.auditDirectory !== options.auditDirectory)
|
|
24448
|
+
throw new TypeError("Mobile promotion operation identity is immutable.");
|
|
24449
|
+
const next = parseAbsoluteMobilePromotionOperation({
|
|
24450
|
+
...current,
|
|
24451
|
+
auditDirectory: options.auditDirectory === undefined ? current.auditDirectory : options.auditDirectory,
|
|
24452
|
+
conclusion: options.conclusion === undefined ? current.conclusion : options.conclusion,
|
|
24453
|
+
githubStatus: options.githubStatus === undefined ? current.githubStatus : options.githubStatus,
|
|
24454
|
+
phase: options.phase,
|
|
24455
|
+
runId: options.runId === undefined ? current.runId : options.runId,
|
|
24456
|
+
updatedAt: now().toISOString(),
|
|
24457
|
+
url: options.url === undefined ? current.url : options.url
|
|
24458
|
+
});
|
|
24459
|
+
await atomicWrite(absoluteMobilePromotionOperationPath(projectRoot, requestedDispatchId), serialize(next), false);
|
|
24460
|
+
return next;
|
|
24461
|
+
};
|
|
24462
|
+
var createAbsoluteMobilePromotionOperation = async (projectRoot, options) => {
|
|
24463
|
+
const now = (options.now ?? (() => new Date))().toISOString();
|
|
24464
|
+
const operation = parseAbsoluteMobilePromotionOperation({
|
|
24465
|
+
...options,
|
|
24466
|
+
auditDirectory: null,
|
|
24467
|
+
conclusion: null,
|
|
24468
|
+
createdAt: now,
|
|
24469
|
+
format: ABSOLUTE_MOBILE_CI_PROMOTION_OPERATION_FORMAT,
|
|
24470
|
+
githubStatus: null,
|
|
24471
|
+
phase: "dispatching",
|
|
24472
|
+
runId: null,
|
|
24473
|
+
updatedAt: now,
|
|
24474
|
+
url: null
|
|
24475
|
+
});
|
|
24476
|
+
await atomicWrite(absoluteMobilePromotionOperationPath(projectRoot, operation.dispatchId), serialize(operation), true);
|
|
24477
|
+
return operation;
|
|
24478
|
+
};
|
|
24479
|
+
var listAbsoluteMobilePromotionOperations = async (projectRoot) => {
|
|
24480
|
+
const root = operationRoot(projectRoot);
|
|
24481
|
+
if (!await access14(root).then(() => true).catch(() => false))
|
|
24482
|
+
return [];
|
|
24483
|
+
const entries = await readdir7(root, { withFileTypes: true });
|
|
24484
|
+
if (entries.length > MAX_OPERATIONS)
|
|
24485
|
+
throw new TypeError("Mobile promotion operation count exceeds the limit.");
|
|
24486
|
+
const operations = await Promise.all(entries.filter((entry) => entry.isDirectory() && DISPATCH_ID_PATTERN2.test(entry.name)).map((entry) => readAbsoluteMobilePromotionOperation(projectRoot, entry.name)));
|
|
24487
|
+
return operations.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
|
24488
|
+
};
|
|
24489
|
+
var readAbsoluteMobilePromotionOperation = async (projectRoot, requestedDispatchId) => {
|
|
24490
|
+
const path = absoluteMobilePromotionOperationPath(projectRoot, requestedDispatchId);
|
|
24491
|
+
const metadata = await lstat(path).catch(() => null);
|
|
24492
|
+
if (!metadata?.isFile() || metadata.size > MAX_OPERATION_BYTES)
|
|
24493
|
+
throw new TypeError(`Mobile promotion operation ${requestedDispatchId} was not found or is invalid.`);
|
|
24494
|
+
const operation = parseAbsoluteMobilePromotionOperation(JSON.parse(await readFile20(path, "utf8")));
|
|
24495
|
+
if (operation.dispatchId !== requestedDispatchId)
|
|
24496
|
+
throw new TypeError("Mobile promotion operation identity does not match.");
|
|
24497
|
+
return operation;
|
|
24498
|
+
};
|
|
24499
|
+
var relativeAbsoluteMobilePromotionOperationPath = (projectRoot, dispatchIdValue) => relative13(resolve14(projectRoot), absoluteMobilePromotionOperationPath(projectRoot, dispatchIdValue)).replaceAll("\\", "/");
|
|
24500
|
+
|
|
24282
24501
|
// src/mobile/updateSigning.ts
|
|
24283
24502
|
import { createHash as createHash6, sign, verify } from "crypto";
|
|
24284
24503
|
import {
|
|
24285
24504
|
cp,
|
|
24286
|
-
mkdir as
|
|
24505
|
+
mkdir as mkdir16,
|
|
24287
24506
|
mkdtemp as mkdtemp6,
|
|
24288
|
-
readdir as
|
|
24289
|
-
readFile as
|
|
24290
|
-
rename as
|
|
24291
|
-
rm as
|
|
24507
|
+
readdir as readdir8,
|
|
24508
|
+
readFile as readFile21,
|
|
24509
|
+
rename as rename13,
|
|
24510
|
+
rm as rm10,
|
|
24292
24511
|
stat as stat8,
|
|
24293
24512
|
writeFile as writeFile16
|
|
24294
24513
|
} from "fs/promises";
|
|
24295
|
-
import { dirname as
|
|
24514
|
+
import { dirname as dirname14, join as join17, relative as relative14, resolve as resolve15 } from "path";
|
|
24296
24515
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
24297
24516
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
24298
24517
|
var sha2564 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
24299
24518
|
var listFiles = async (root, directory = root) => {
|
|
24300
|
-
const entries = await
|
|
24519
|
+
const entries = await readdir8(directory, { withFileTypes: true });
|
|
24301
24520
|
const paths = await Promise.all(entries.map(async (entry) => {
|
|
24302
|
-
const path =
|
|
24521
|
+
const path = join17(directory, entry.name);
|
|
24303
24522
|
if (entry.isDirectory())
|
|
24304
24523
|
return listFiles(root, path);
|
|
24305
24524
|
if (!entry.isFile())
|
|
24306
24525
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
24307
|
-
return [
|
|
24526
|
+
return [relative14(root, path).replaceAll("\\", "/")];
|
|
24308
24527
|
}));
|
|
24309
24528
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
24310
24529
|
};
|
|
24311
24530
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
24312
|
-
const source =
|
|
24531
|
+
const source = join17(root, path);
|
|
24313
24532
|
const [metadata, contents] = await Promise.all([
|
|
24314
24533
|
stat8(source),
|
|
24315
|
-
|
|
24534
|
+
readFile21(source)
|
|
24316
24535
|
]);
|
|
24317
24536
|
return { bytes: metadata.size, path, sha256: sha2564(contents) };
|
|
24318
24537
|
}));
|
|
24319
24538
|
var releaseIdFor = (value) => `amu_${sha2564(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
24320
24539
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
24321
|
-
const bundleDirectory =
|
|
24322
|
-
const outputRoot =
|
|
24540
|
+
const bundleDirectory = resolve15(options.bundleDirectory);
|
|
24541
|
+
const outputRoot = resolve15(options.outputDirectory);
|
|
24323
24542
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
24324
24543
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
24325
24544
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -24347,30 +24566,30 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
24347
24566
|
value: signature.toString("base64")
|
|
24348
24567
|
}
|
|
24349
24568
|
});
|
|
24350
|
-
await
|
|
24351
|
-
const outputDirectory =
|
|
24352
|
-
const staging = await mkdtemp6(
|
|
24569
|
+
await mkdir16(outputRoot, { recursive: true });
|
|
24570
|
+
const outputDirectory = join17(outputRoot, manifest.releaseId);
|
|
24571
|
+
const staging = await mkdtemp6(join17(outputRoot, ".stage-"));
|
|
24353
24572
|
try {
|
|
24354
|
-
await cp(bundleDirectory,
|
|
24573
|
+
await cp(bundleDirectory, join17(staging, UPDATE_FILES_DIRECTORY), {
|
|
24355
24574
|
force: true,
|
|
24356
24575
|
recursive: true
|
|
24357
24576
|
});
|
|
24358
|
-
await writeFile16(
|
|
24577
|
+
await writeFile16(join17(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
24359
24578
|
`);
|
|
24360
|
-
await
|
|
24579
|
+
await rename13(staging, outputDirectory);
|
|
24361
24580
|
} catch (error) {
|
|
24362
|
-
await
|
|
24581
|
+
await rm10(staging, { force: true, recursive: true });
|
|
24363
24582
|
if (typeof error === "object" && error !== null && Reflect.get(error, "code") === "EEXIST")
|
|
24364
24583
|
throw new TypeError(`Mobile update ${manifest.releaseId} already exists.`, { cause: error });
|
|
24365
24584
|
throw error;
|
|
24366
24585
|
}
|
|
24367
24586
|
return {
|
|
24368
24587
|
manifest,
|
|
24369
|
-
manifestPath:
|
|
24588
|
+
manifestPath: join17(outputDirectory, UPDATE_MANIFEST_FILE),
|
|
24370
24589
|
outputDirectory
|
|
24371
24590
|
};
|
|
24372
24591
|
};
|
|
24373
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
24592
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile21(join17(resolve15(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
24374
24593
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
24375
24594
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
24376
24595
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -24380,8 +24599,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
24380
24599
|
};
|
|
24381
24600
|
|
|
24382
24601
|
// src/mobile/expoUpdate.ts
|
|
24383
|
-
import { access as
|
|
24384
|
-
import { dirname as
|
|
24602
|
+
import { access as access15, mkdir as mkdir17, readFile as readFile22, writeFile as writeFile17 } from "fs/promises";
|
|
24603
|
+
import { dirname as dirname15, extname as extname3, join as join18 } from "path";
|
|
24385
24604
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
24386
24605
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
24387
24606
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -24447,7 +24666,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
24447
24666
|
};
|
|
24448
24667
|
};
|
|
24449
24668
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
24450
|
-
const metadata = JSON.parse(await
|
|
24669
|
+
const metadata = JSON.parse(await readFile22(join18(options.exportDirectory, "metadata.json"), "utf8"));
|
|
24451
24670
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
24452
24671
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
24453
24672
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -24458,11 +24677,11 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
24458
24677
|
runtimeVersion: options.runtimeVersion
|
|
24459
24678
|
});
|
|
24460
24679
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
24461
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
24680
|
+
await Promise.all(referenced.map(({ path }) => access15(join18(options.exportDirectory, path)).catch(() => {
|
|
24462
24681
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
24463
24682
|
})));
|
|
24464
|
-
const destination =
|
|
24465
|
-
await
|
|
24683
|
+
const destination = join18(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
24684
|
+
await mkdir17(dirname15(destination), { recursive: true });
|
|
24466
24685
|
await writeFile17(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
24467
24686
|
`);
|
|
24468
24687
|
return { descriptor, path: destination };
|
|
@@ -24470,18 +24689,18 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
24470
24689
|
|
|
24471
24690
|
// src/mobile/expoCodeSigning.ts
|
|
24472
24691
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
24473
|
-
import { access as
|
|
24474
|
-
import { dirname as
|
|
24475
|
-
var exists6 = async (path) =>
|
|
24692
|
+
import { access as access16, mkdir as mkdir18, writeFile as writeFile18 } from "fs/promises";
|
|
24693
|
+
import { dirname as dirname16, isAbsolute as isAbsolute10, relative as relative15, resolve as resolve16, sep as sep11 } from "path";
|
|
24694
|
+
var exists6 = async (path) => access16(path).then(() => true).catch(() => false);
|
|
24476
24695
|
var inside4 = (root, path) => {
|
|
24477
|
-
const location =
|
|
24478
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
24696
|
+
const location = relative15(root, path);
|
|
24697
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep11}`) && !isAbsolute10(location);
|
|
24479
24698
|
};
|
|
24480
24699
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
24481
|
-
const root =
|
|
24482
|
-
const certificatePath =
|
|
24483
|
-
const privateKeyPath =
|
|
24484
|
-
const publicKeyPath =
|
|
24700
|
+
const root = resolve16(options.projectRoot);
|
|
24701
|
+
const certificatePath = resolve16(root, options.certificatePath);
|
|
24702
|
+
const privateKeyPath = resolve16(root, options.privateKeyPath);
|
|
24703
|
+
const publicKeyPath = resolve16(root, options.publicKeyPath ?? resolve16(dirname16(privateKeyPath), "public-key.pem"));
|
|
24485
24704
|
if (!inside4(root, certificatePath))
|
|
24486
24705
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
24487
24706
|
if (inside4(root, privateKeyPath) || inside4(root, publicKeyPath))
|
|
@@ -24511,7 +24730,7 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
24511
24730
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
24512
24731
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
24513
24732
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
24514
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
24733
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir18(dirname16(path), { recursive: true })));
|
|
24515
24734
|
await Promise.all([
|
|
24516
24735
|
writeFile18(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
24517
24736
|
writeFile18(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
@@ -24526,8 +24745,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
24526
24745
|
};
|
|
24527
24746
|
|
|
24528
24747
|
// src/mobile/updatePublisher.ts
|
|
24529
|
-
import { access as
|
|
24530
|
-
import { isAbsolute as
|
|
24748
|
+
import { access as access17 } from "fs/promises";
|
|
24749
|
+
import { isAbsolute as isAbsolute11, relative as relative16, resolve as resolve17, sep as sep12 } from "path";
|
|
24531
24750
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
24532
24751
|
var lifecycleMethod = (publisher, name) => {
|
|
24533
24752
|
const method = publisher[name];
|
|
@@ -24636,16 +24855,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
24636
24855
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24637
24856
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
24638
24857
|
var projectPath4 = (projectRoot, requested, label) => {
|
|
24639
|
-
const root =
|
|
24640
|
-
const path =
|
|
24641
|
-
const projectRelative =
|
|
24642
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
24858
|
+
const root = resolve17(projectRoot);
|
|
24859
|
+
const path = resolve17(root, requested);
|
|
24860
|
+
const projectRelative = relative16(root, path);
|
|
24861
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep12}`) || isAbsolute11(projectRelative))
|
|
24643
24862
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
24644
24863
|
return path;
|
|
24645
24864
|
};
|
|
24646
24865
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
24647
24866
|
const modulePath = projectPath4(projectRoot, requestedModulePath, "mobile update registry");
|
|
24648
|
-
await
|
|
24867
|
+
await access17(modulePath).catch(() => {
|
|
24649
24868
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
24650
24869
|
});
|
|
24651
24870
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -24700,10 +24919,10 @@ var rollbackAbsoluteMobileUpdate = async (options) => {
|
|
|
24700
24919
|
var NOT_FOUND4 = -1;
|
|
24701
24920
|
var ANDROID_EMULATOR_BOOT_POLL_MS = 500;
|
|
24702
24921
|
var ANDROID_EMULATOR_BOOT_TIMEOUT_MS = 180000;
|
|
24703
|
-
var
|
|
24922
|
+
var isRecord11 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
24704
24923
|
var expoPublicConfig = (config) => {
|
|
24705
24924
|
const result = { ...config };
|
|
24706
|
-
if (!
|
|
24925
|
+
if (!isRecord11(result.updates))
|
|
24707
24926
|
return result;
|
|
24708
24927
|
const {
|
|
24709
24928
|
codeSigningCertificate: _codeSigningCertificate,
|
|
@@ -24743,13 +24962,13 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
24743
24962
|
];
|
|
24744
24963
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
24745
24964
|
var directProjectPackages = async (projectRoot) => {
|
|
24746
|
-
const manifest = JSON.parse(await
|
|
24747
|
-
if (!
|
|
24965
|
+
const manifest = JSON.parse(await readFile23(join19(projectRoot, "package.json"), "utf8"));
|
|
24966
|
+
if (!isRecord11(manifest))
|
|
24748
24967
|
throw new TypeError("Application package.json must contain an object.");
|
|
24749
24968
|
const names = new Set;
|
|
24750
24969
|
for (const field2 of ["dependencies", "devDependencies"]) {
|
|
24751
24970
|
const dependencies = Reflect.get(manifest, field2);
|
|
24752
|
-
if (
|
|
24971
|
+
if (isRecord11(dependencies))
|
|
24753
24972
|
for (const name of Object.keys(dependencies))
|
|
24754
24973
|
names.add(name);
|
|
24755
24974
|
}
|
|
@@ -24757,8 +24976,8 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
24757
24976
|
};
|
|
24758
24977
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
24759
24978
|
try {
|
|
24760
|
-
const manifest = JSON.parse(await
|
|
24761
|
-
return
|
|
24979
|
+
const manifest = JSON.parse(await readFile23(join19(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
24980
|
+
return isRecord11(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
24762
24981
|
} catch {
|
|
24763
24982
|
return;
|
|
24764
24983
|
}
|
|
@@ -24821,9 +25040,9 @@ var runCapacitor = async (projectRoot, args) => {
|
|
|
24821
25040
|
};
|
|
24822
25041
|
var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.reduce((pending, platform2) => pending.then(() => runCapacitor(projectRoot, [command, platform2])), Promise.resolve());
|
|
24823
25042
|
var expoExecutable = async (project) => {
|
|
24824
|
-
const executable =
|
|
25043
|
+
const executable = join19(project, "node_modules", ".bin", "expo");
|
|
24825
25044
|
try {
|
|
24826
|
-
await
|
|
25045
|
+
await access18(executable);
|
|
24827
25046
|
return executable;
|
|
24828
25047
|
} catch {
|
|
24829
25048
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -24855,14 +25074,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
24855
25074
|
};
|
|
24856
25075
|
var ensureExpoPackages = async (project, args) => {
|
|
24857
25076
|
try {
|
|
24858
|
-
const manifest = JSON.parse(await
|
|
25077
|
+
const manifest = JSON.parse(await readFile23(join19(project, "package.json"), "utf8"));
|
|
24859
25078
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
24860
25079
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
24861
25080
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
24862
25081
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
24863
25082
|
if (typeof expected !== "string")
|
|
24864
25083
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
24865
|
-
const installed = JSON.parse(await
|
|
25084
|
+
const installed = JSON.parse(await readFile23(join19(project, "node_modules", name, "package.json"), "utf8"));
|
|
24866
25085
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
24867
25086
|
if (typeof actual !== "string")
|
|
24868
25087
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -25060,7 +25279,7 @@ var sync = async (args) => {
|
|
|
25060
25279
|
};
|
|
25061
25280
|
var associations = async (args) => {
|
|
25062
25281
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
25063
|
-
const outputDirectory =
|
|
25282
|
+
const outputDirectory = resolve18(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
25064
25283
|
if (args.includes("--verify")) {
|
|
25065
25284
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
25066
25285
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -25112,7 +25331,7 @@ var generateGithubCi = async (args) => {
|
|
|
25112
25331
|
const publicResult = {
|
|
25113
25332
|
changed: result.changed,
|
|
25114
25333
|
format: result.format,
|
|
25115
|
-
path:
|
|
25334
|
+
path: relative17(projectRoot, result.path).replaceAll("\\", "/"),
|
|
25116
25335
|
platforms: result.platforms,
|
|
25117
25336
|
publishing: result.publishing,
|
|
25118
25337
|
requiredSecrets: result.requiredSecrets
|
|
@@ -25143,14 +25362,14 @@ var createGithubCertificationVerification = async (args) => {
|
|
|
25143
25362
|
GITHUB_WORKFLOW_REF: process.env.GITHUB_WORKFLOW_REF
|
|
25144
25363
|
});
|
|
25145
25364
|
const path = await writeAbsoluteMobileCertificationVerification(projectRoot, outputPath, verification);
|
|
25146
|
-
console.log(
|
|
25365
|
+
console.log(relative17(projectRoot, path).replaceAll("\\", "/"));
|
|
25147
25366
|
return verification;
|
|
25148
25367
|
};
|
|
25149
25368
|
var auditDispatchedGithubMobilePromotion = async (options) => {
|
|
25150
25369
|
const startedAt = performance.now();
|
|
25151
25370
|
try {
|
|
25152
25371
|
const audited = await auditAbsoluteMobileCiPromotion({
|
|
25153
|
-
outputDirectory:
|
|
25372
|
+
outputDirectory: options.outputDirectory,
|
|
25154
25373
|
projectRoot: options.projectRoot,
|
|
25155
25374
|
...options.repository ? { repository: options.repository } : {},
|
|
25156
25375
|
runId: options.runId
|
|
@@ -25172,7 +25391,125 @@ var auditDispatchedGithubMobilePromotion = async (options) => {
|
|
|
25172
25391
|
throw error;
|
|
25173
25392
|
}
|
|
25174
25393
|
};
|
|
25394
|
+
var promotionPhaseRank = (phase) => ["dispatching", "dispatched", "discovered", "completed", "audited"].indexOf(phase);
|
|
25395
|
+
var promotionRecoveryCommand = (dispatchId3) => `absolute mobile ci promote --resume ${dispatchId3}`;
|
|
25396
|
+
var promotionRecoveryError = (error, dispatchId3) => {
|
|
25397
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
25398
|
+
return new TypeError(`${message}
|
|
25399
|
+
Promotion ${dispatchId3} is recorded. Resume without redispatching: ${promotionRecoveryCommand(dispatchId3)}
|
|
25400
|
+
If immutable audit output already exists, add --outdir with a new project-local directory.`, { cause: error });
|
|
25401
|
+
};
|
|
25402
|
+
var watchAndPersistGithubMobilePromotion = async (options) => {
|
|
25403
|
+
let status;
|
|
25404
|
+
try {
|
|
25405
|
+
status = await watchAbsoluteMobileCiRun({
|
|
25406
|
+
projectRoot: options.projectRoot,
|
|
25407
|
+
...options.operation.repository ? { repository: options.operation.repository } : {},
|
|
25408
|
+
runId: options.runId
|
|
25409
|
+
});
|
|
25410
|
+
} catch (error) {
|
|
25411
|
+
const inspected = await inspectAbsoluteMobileCiRun({
|
|
25412
|
+
projectRoot: options.projectRoot,
|
|
25413
|
+
...options.operation.repository ? { repository: options.operation.repository } : {},
|
|
25414
|
+
runId: options.runId
|
|
25415
|
+
}).catch(() => null);
|
|
25416
|
+
if (inspected?.status === "completed")
|
|
25417
|
+
await advanceAbsoluteMobilePromotionOperation(options.projectRoot, options.operation.dispatchId, {
|
|
25418
|
+
conclusion: inspected.conclusion,
|
|
25419
|
+
githubStatus: inspected.status,
|
|
25420
|
+
phase: "completed"
|
|
25421
|
+
});
|
|
25422
|
+
throw error;
|
|
25423
|
+
}
|
|
25424
|
+
const operation = await advanceAbsoluteMobilePromotionOperation(options.projectRoot, options.operation.dispatchId, {
|
|
25425
|
+
conclusion: status.conclusion,
|
|
25426
|
+
githubStatus: status.status,
|
|
25427
|
+
phase: "completed"
|
|
25428
|
+
});
|
|
25429
|
+
return { operation, status };
|
|
25430
|
+
};
|
|
25431
|
+
var completeGithubMobilePromotion = async (options) => {
|
|
25432
|
+
let { operation } = options;
|
|
25433
|
+
if (!operation.runId || !operation.url) {
|
|
25434
|
+
const promotion = await discoverAbsoluteMobilePromotionRun({
|
|
25435
|
+
dispatchId: operation.dispatchId,
|
|
25436
|
+
projectRoot: options.projectRoot,
|
|
25437
|
+
...operation.repository ? { repository: operation.repository } : {},
|
|
25438
|
+
workflow: operation.workflow
|
|
25439
|
+
});
|
|
25440
|
+
operation = await advanceAbsoluteMobilePromotionOperation(options.projectRoot, operation.dispatchId, {
|
|
25441
|
+
phase: "discovered",
|
|
25442
|
+
runId: promotion.runId,
|
|
25443
|
+
url: promotion.url
|
|
25444
|
+
});
|
|
25445
|
+
}
|
|
25446
|
+
if (!operation.runId || !operation.url)
|
|
25447
|
+
throw new TypeError("Promotion run discovery did not produce an identity.");
|
|
25448
|
+
const promotionRunId = operation.runId;
|
|
25449
|
+
const promotionUrl = operation.url;
|
|
25450
|
+
if (!options.json) {
|
|
25451
|
+
console.log(`Promotion ${operation.dispatchId} is GitHub run ${promotionRunId}.`);
|
|
25452
|
+
console.log(promotionUrl);
|
|
25453
|
+
}
|
|
25454
|
+
let status = null;
|
|
25455
|
+
if (options.watch && promotionPhaseRank(operation.phase) < promotionPhaseRank("completed")) {
|
|
25456
|
+
if (!options.json)
|
|
25457
|
+
console.log(`Watching GitHub run ${promotionRunId} until completion\u2026`);
|
|
25458
|
+
({ operation, status } = await watchAndPersistGithubMobilePromotion({
|
|
25459
|
+
operation,
|
|
25460
|
+
projectRoot: options.projectRoot,
|
|
25461
|
+
runId: promotionRunId
|
|
25462
|
+
}));
|
|
25463
|
+
} else if (options.watch) {
|
|
25464
|
+
status = await inspectAbsoluteMobileCiRun({
|
|
25465
|
+
projectRoot: options.projectRoot,
|
|
25466
|
+
...operation.repository ? { repository: operation.repository } : {},
|
|
25467
|
+
runId: promotionRunId
|
|
25468
|
+
});
|
|
25469
|
+
}
|
|
25470
|
+
if (options.audit && operation.conclusion !== "success")
|
|
25471
|
+
throw new TypeError("Promotion audit requires a successfully completed workflow run.");
|
|
25472
|
+
let audited = null;
|
|
25473
|
+
if (options.audit && operation.phase !== "audited") {
|
|
25474
|
+
audited = await auditDispatchedGithubMobilePromotion({
|
|
25475
|
+
outputDirectory: options.auditOutputDirectory ?? operation.auditOutputDirectory ?? undefined,
|
|
25476
|
+
platform: operation.platform,
|
|
25477
|
+
projectRoot: options.projectRoot,
|
|
25478
|
+
...operation.repository ? { repository: operation.repository } : {},
|
|
25479
|
+
runId: promotionRunId
|
|
25480
|
+
});
|
|
25481
|
+
operation = await advanceAbsoluteMobilePromotionOperation(options.projectRoot, operation.dispatchId, {
|
|
25482
|
+
auditDirectory: relative17(options.projectRoot, audited.directory).replaceAll("\\", "/"),
|
|
25483
|
+
phase: "audited"
|
|
25484
|
+
});
|
|
25485
|
+
}
|
|
25486
|
+
const result = {
|
|
25487
|
+
auditDirectory: operation.auditDirectory,
|
|
25488
|
+
dispatchId: operation.dispatchId,
|
|
25489
|
+
platform: operation.platform,
|
|
25490
|
+
releaseId: operation.releaseId,
|
|
25491
|
+
runId: promotionRunId,
|
|
25492
|
+
sourceRunId: operation.sourceRunId,
|
|
25493
|
+
status: operation.githubStatus ?? operation.phase,
|
|
25494
|
+
url: promotionUrl
|
|
25495
|
+
};
|
|
25496
|
+
if (options.json)
|
|
25497
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25498
|
+
else {
|
|
25499
|
+
if (status)
|
|
25500
|
+
renderGithubMobileRunStatus(status);
|
|
25501
|
+
if (audited)
|
|
25502
|
+
renderGithubMobilePromotionAudit(audited, options.projectRoot);
|
|
25503
|
+
else if (operation.auditDirectory)
|
|
25504
|
+
console.log(`Audit: ${operation.auditDirectory}`);
|
|
25505
|
+
console.log(`Promotion record: ${relativeAbsoluteMobilePromotionOperationPath(options.projectRoot, operation.dispatchId)}`);
|
|
25506
|
+
}
|
|
25507
|
+
return result;
|
|
25508
|
+
};
|
|
25175
25509
|
var dispatchGithubMobilePromotion = async (args) => {
|
|
25510
|
+
const resumeDispatchId = valueAfter(args, "--resume");
|
|
25511
|
+
if (resumeDispatchId)
|
|
25512
|
+
return resumeGithubMobilePromotion(args, resumeDispatchId);
|
|
25176
25513
|
const [platform2] = args;
|
|
25177
25514
|
if (platform2 !== "android" && platform2 !== "ios")
|
|
25178
25515
|
throw new TypeError("Usage: absolute mobile ci promote <android|ios> --run-id id --certification directory-or-json [--channel name] [store options] [--watch] [--audit]");
|
|
@@ -25196,8 +25533,31 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25196
25533
|
if (certificationBase64.length > 48000)
|
|
25197
25534
|
throw new TypeError("Mobile certification is too large for protected GitHub dispatch.");
|
|
25198
25535
|
const workflow = valueAfter(args, "--workflow") ?? ".github/workflows/absolute-mobile.yml";
|
|
25199
|
-
const
|
|
25536
|
+
const dispatchId3 = `amp_${randomUUID2().replaceAll("-", "")}`;
|
|
25200
25537
|
const repository = valueAfter(args, "--repo");
|
|
25538
|
+
const ref = valueAfter(args, "--ref");
|
|
25539
|
+
const auditRequested = args.includes("--audit");
|
|
25540
|
+
const watchRequested = args.includes("--watch") || auditRequested;
|
|
25541
|
+
const requestedAuditOutputDirectory = valueAfter(args, "--outdir");
|
|
25542
|
+
const auditOutputDirectory = requestedAuditOutputDirectory ? relative17(projectRoot, resolve18(projectRoot, requestedAuditOutputDirectory)).replaceAll("\\", "/") : null;
|
|
25543
|
+
await createAbsoluteMobilePromotionOperation(projectRoot, {
|
|
25544
|
+
auditOutputDirectory,
|
|
25545
|
+
auditRequested,
|
|
25546
|
+
certificationId: certification.certificationId,
|
|
25547
|
+
certificationSha256: createHash7("sha256").update(serializeAbsoluteMobileReleaseCertification(certification)).digest("hex"),
|
|
25548
|
+
channel: channel || null,
|
|
25549
|
+
dispatchId: dispatchId3,
|
|
25550
|
+
platform: platform2,
|
|
25551
|
+
playTrack: platform2 === "android" ? playTrack : null,
|
|
25552
|
+
ref: ref ?? null,
|
|
25553
|
+
releaseId: certification.release.releaseId,
|
|
25554
|
+
repository: repository ?? null,
|
|
25555
|
+
sourceRunId,
|
|
25556
|
+
testflightGroup: platform2 === "ios" ? testflightGroup || null : null,
|
|
25557
|
+
testflightSubmitReview: platform2 === "ios" && args.includes("--testflight-submit-review"),
|
|
25558
|
+
watchRequested,
|
|
25559
|
+
workflow
|
|
25560
|
+
});
|
|
25201
25561
|
const command = [
|
|
25202
25562
|
"workflow",
|
|
25203
25563
|
"run",
|
|
@@ -25209,7 +25569,7 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25209
25569
|
"-f",
|
|
25210
25570
|
`source_run_id=${sourceRunId}`,
|
|
25211
25571
|
"-f",
|
|
25212
|
-
`dispatch_id=${
|
|
25572
|
+
`dispatch_id=${dispatchId3}`,
|
|
25213
25573
|
"-f",
|
|
25214
25574
|
`certification_base64=${certificationBase64}`,
|
|
25215
25575
|
"-f",
|
|
@@ -25221,74 +25581,98 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25221
25581
|
command.push("-f", `testflight_group=${testflightGroup}`);
|
|
25222
25582
|
command.push("-f", `submit_testflight_review=${args.includes("--testflight-submit-review")}`);
|
|
25223
25583
|
}
|
|
25224
|
-
const ref = valueAfter(args, "--ref");
|
|
25225
25584
|
if (ref)
|
|
25226
25585
|
command.push("--ref", ref);
|
|
25227
25586
|
if (repository)
|
|
25228
25587
|
command.push("--repo", repository);
|
|
25229
|
-
|
|
25230
|
-
|
|
25231
|
-
|
|
25232
|
-
|
|
25233
|
-
|
|
25234
|
-
|
|
25235
|
-
|
|
25236
|
-
|
|
25237
|
-
|
|
25238
|
-
|
|
25239
|
-
|
|
25240
|
-
|
|
25241
|
-
|
|
25242
|
-
|
|
25243
|
-
|
|
25244
|
-
|
|
25245
|
-
|
|
25246
|
-
|
|
25247
|
-
|
|
25248
|
-
|
|
25249
|
-
|
|
25250
|
-
|
|
25251
|
-
|
|
25252
|
-
|
|
25253
|
-
|
|
25254
|
-
|
|
25255
|
-
|
|
25256
|
-
|
|
25257
|
-
}
|
|
25258
|
-
|
|
25259
|
-
|
|
25260
|
-
|
|
25261
|
-
|
|
25262
|
-
|
|
25263
|
-
|
|
25264
|
-
|
|
25265
|
-
|
|
25266
|
-
|
|
25267
|
-
|
|
25268
|
-
|
|
25269
|
-
|
|
25270
|
-
|
|
25271
|
-
|
|
25272
|
-
|
|
25273
|
-
|
|
25274
|
-
|
|
25275
|
-
|
|
25276
|
-
|
|
25277
|
-
|
|
25278
|
-
|
|
25279
|
-
|
|
25280
|
-
|
|
25281
|
-
|
|
25282
|
-
|
|
25283
|
-
|
|
25284
|
-
|
|
25285
|
-
|
|
25588
|
+
try {
|
|
25589
|
+
const child = Bun.spawn(["gh", ...command], {
|
|
25590
|
+
cwd: projectRoot,
|
|
25591
|
+
stderr: "pipe",
|
|
25592
|
+
stdin: "inherit",
|
|
25593
|
+
stdout: "pipe"
|
|
25594
|
+
});
|
|
25595
|
+
const [exitCode, stderr] = await Promise.all([
|
|
25596
|
+
child.exited,
|
|
25597
|
+
new Response(child.stderr).text(),
|
|
25598
|
+
new Response(child.stdout).text()
|
|
25599
|
+
]);
|
|
25600
|
+
if (exitCode !== 0)
|
|
25601
|
+
throw new TypeError(`GitHub promotion dispatch exited with status ${exitCode}: ${stderr.trim() || "unknown GitHub CLI error"}`);
|
|
25602
|
+
const operation = await advanceAbsoluteMobilePromotionOperation(projectRoot, dispatchId3, { phase: "dispatched" });
|
|
25603
|
+
sendTelemetryEvent("mobile:ci-promotion-dispatched", {
|
|
25604
|
+
discovery: "correlated",
|
|
25605
|
+
platform: platform2,
|
|
25606
|
+
provider: "github-actions",
|
|
25607
|
+
storeTarget: platform2 === "android" ? playTrack !== "registry-only" : testflightGroup !== ""
|
|
25608
|
+
});
|
|
25609
|
+
return await completeGithubMobilePromotion({
|
|
25610
|
+
audit: operation.auditRequested,
|
|
25611
|
+
json: args.includes("--json"),
|
|
25612
|
+
operation,
|
|
25613
|
+
projectRoot,
|
|
25614
|
+
watch: operation.watchRequested
|
|
25615
|
+
});
|
|
25616
|
+
} catch (error) {
|
|
25617
|
+
throw promotionRecoveryError(error, dispatchId3);
|
|
25618
|
+
}
|
|
25619
|
+
};
|
|
25620
|
+
var resumeGithubMobilePromotion = async (args, dispatchId3) => {
|
|
25621
|
+
const projectRoot = process.cwd();
|
|
25622
|
+
const startedAt = performance.now();
|
|
25623
|
+
let resumedPhase = "unread";
|
|
25624
|
+
try {
|
|
25625
|
+
const operation = await readAbsoluteMobilePromotionOperation(projectRoot, dispatchId3);
|
|
25626
|
+
resumedPhase = operation.phase;
|
|
25627
|
+
const audit = args.includes("--audit") || operation.auditRequested;
|
|
25628
|
+
const watch = args.includes("--watch") || audit || operation.watchRequested;
|
|
25629
|
+
if (!args.includes("--json"))
|
|
25630
|
+
console.log(`Resuming ${operation.platform} promotion ${operation.dispatchId} from ${operation.phase}.`);
|
|
25631
|
+
const result = await completeGithubMobilePromotion({
|
|
25632
|
+
audit,
|
|
25633
|
+
auditOutputDirectory: valueAfter(args, "--outdir"),
|
|
25634
|
+
json: args.includes("--json"),
|
|
25635
|
+
operation,
|
|
25636
|
+
projectRoot,
|
|
25637
|
+
watch
|
|
25638
|
+
});
|
|
25639
|
+
sendTelemetryEvent("mobile:ci-promotion-resumed", {
|
|
25640
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
25641
|
+
phase: resumedPhase,
|
|
25642
|
+
provider: "github-actions",
|
|
25643
|
+
success: true
|
|
25644
|
+
});
|
|
25286
25645
|
return result;
|
|
25287
|
-
|
|
25288
|
-
|
|
25289
|
-
|
|
25290
|
-
|
|
25291
|
-
|
|
25646
|
+
} catch (error) {
|
|
25647
|
+
sendTelemetryEvent("mobile:ci-promotion-resumed", {
|
|
25648
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
25649
|
+
phase: resumedPhase,
|
|
25650
|
+
provider: "github-actions",
|
|
25651
|
+
success: false
|
|
25652
|
+
});
|
|
25653
|
+
throw promotionRecoveryError(error, dispatchId3);
|
|
25654
|
+
}
|
|
25655
|
+
};
|
|
25656
|
+
var listGithubMobilePromotions = async (args) => {
|
|
25657
|
+
const projectRoot = process.cwd();
|
|
25658
|
+
const operations = await listAbsoluteMobilePromotionOperations(projectRoot);
|
|
25659
|
+
if (args.includes("--json")) {
|
|
25660
|
+
console.log(JSON.stringify(operations, null, 2));
|
|
25661
|
+
return operations;
|
|
25662
|
+
}
|
|
25663
|
+
if (operations.length === 0) {
|
|
25664
|
+
console.log("No local mobile promotion operations were found.");
|
|
25665
|
+
return operations;
|
|
25666
|
+
}
|
|
25667
|
+
operations.forEach((operation) => {
|
|
25668
|
+
console.log(`${operation.dispatchId} ${operation.platform} ${operation.phase} ${operation.releaseId}`);
|
|
25669
|
+
console.log(` source ${operation.sourceRunId}${operation.runId ? ` \u2192 promotion ${operation.runId}` : ""}`);
|
|
25670
|
+
if (operation.url)
|
|
25671
|
+
console.log(` ${operation.url}`);
|
|
25672
|
+
if (operation.phase !== "audited")
|
|
25673
|
+
console.log(` Resume: ${promotionRecoveryCommand(operation.dispatchId)}`);
|
|
25674
|
+
});
|
|
25675
|
+
return operations;
|
|
25292
25676
|
};
|
|
25293
25677
|
var githubMobileRunId = (args) => {
|
|
25294
25678
|
const value = valueAfter(args, "--run-id");
|
|
@@ -25332,7 +25716,7 @@ var inspectGithubMobileRun = async (args) => {
|
|
|
25332
25716
|
};
|
|
25333
25717
|
var renderGithubMobilePromotionAudit = (result, projectRoot) => {
|
|
25334
25718
|
console.log(`\u2713 Verified ${result.audit.platform} promotion ${result.audit.github.promotionRunId} for ${result.audit.release.releaseId}.`);
|
|
25335
|
-
console.log(`Audit: ${
|
|
25719
|
+
console.log(`Audit: ${relative17(projectRoot, result.directory)}`);
|
|
25336
25720
|
};
|
|
25337
25721
|
var auditGithubMobilePromotion = async (args) => {
|
|
25338
25722
|
const projectRoot = process.cwd();
|
|
@@ -25464,9 +25848,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
25464
25848
|
temporaryDirectory: undefined
|
|
25465
25849
|
};
|
|
25466
25850
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
25467
|
-
const temporaryParent =
|
|
25468
|
-
await
|
|
25469
|
-
const temporaryDirectory = await mkdtemp7(
|
|
25851
|
+
const temporaryParent = join19(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
25852
|
+
await mkdir19(temporaryParent, { recursive: true });
|
|
25853
|
+
const temporaryDirectory = await mkdtemp7(join19(temporaryParent, ".stage-"));
|
|
25470
25854
|
try {
|
|
25471
25855
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
25472
25856
|
"export",
|
|
@@ -25478,9 +25862,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
25478
25862
|
temporaryDirectory,
|
|
25479
25863
|
"--clear"
|
|
25480
25864
|
], { production: true });
|
|
25481
|
-
const generatedApp = JSON.parse(await
|
|
25482
|
-
const expoConfig =
|
|
25483
|
-
if (!
|
|
25865
|
+
const generatedApp = JSON.parse(await readFile23(join19(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
25866
|
+
const expoConfig = isRecord11(generatedApp) ? generatedApp.expo : undefined;
|
|
25867
|
+
if (!isRecord11(expoConfig))
|
|
25484
25868
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
25485
25869
|
const publicExpoConfig = expoPublicConfig(expoConfig);
|
|
25486
25870
|
const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
|
|
@@ -25496,7 +25880,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
25496
25880
|
temporaryDirectory
|
|
25497
25881
|
};
|
|
25498
25882
|
} catch (error) {
|
|
25499
|
-
await
|
|
25883
|
+
await rm11(temporaryDirectory, { force: true, recursive: true });
|
|
25500
25884
|
throw error;
|
|
25501
25885
|
}
|
|
25502
25886
|
};
|
|
@@ -25521,11 +25905,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
25521
25905
|
let expoExportDirectory;
|
|
25522
25906
|
try {
|
|
25523
25907
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
25524
|
-
const embedded = JSON.parse(await
|
|
25525
|
-
const runtimeFingerprint =
|
|
25908
|
+
const embedded = JSON.parse(await readFile23(join19(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
25909
|
+
const runtimeFingerprint = isRecord11(embedded) ? embedded.nativeRuntime : undefined;
|
|
25526
25910
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
25527
25911
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
25528
|
-
const privateKey = await
|
|
25912
|
+
const privateKey = await readFile23(resolve18(projectRoot, signingKeyPath));
|
|
25529
25913
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
25530
25914
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
25531
25915
|
format: "der",
|
|
@@ -25546,7 +25930,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
25546
25930
|
channel: mobile.updates.channel,
|
|
25547
25931
|
classification,
|
|
25548
25932
|
keyId,
|
|
25549
|
-
outputDirectory: valueAfter(args, "--outdir") ??
|
|
25933
|
+
outputDirectory: valueAfter(args, "--outdir") ?? join19(projectRoot, ".absolutejs", "mobile", "updates"),
|
|
25550
25934
|
privateKey,
|
|
25551
25935
|
runtimeFingerprint
|
|
25552
25936
|
});
|
|
@@ -25558,7 +25942,7 @@ var buildMobileUpdate = async (args) => {
|
|
|
25558
25942
|
return result;
|
|
25559
25943
|
} finally {
|
|
25560
25944
|
if (expoExportDirectory)
|
|
25561
|
-
await
|
|
25945
|
+
await rm11(expoExportDirectory, { force: true, recursive: true });
|
|
25562
25946
|
sendTelemetryEvent("mobile:update-build", {
|
|
25563
25947
|
classification: classification ?? "invalid",
|
|
25564
25948
|
durationMs: Math.round(performance.now() - startedAt),
|
|
@@ -25588,7 +25972,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
25588
25972
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
25589
25973
|
...validityYears === undefined ? {} : { validityYears }
|
|
25590
25974
|
});
|
|
25591
|
-
const certificatePath =
|
|
25975
|
+
const certificatePath = relative17(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
25592
25976
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
25593
25977
|
engine: "expo",
|
|
25594
25978
|
validityYears: validityYears ?? 10
|
|
@@ -25650,7 +26034,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
25650
26034
|
publicKeys: mobile.updates.publicKeys,
|
|
25651
26035
|
storage: requestedStorage
|
|
25652
26036
|
});
|
|
25653
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
26037
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative17(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
25654
26038
|
if (requestedStorage === "local")
|
|
25655
26039
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
25656
26040
|
else
|
|
@@ -26127,7 +26511,7 @@ var buildAndroid = async (args, prepareVersionCode) => {
|
|
|
26127
26511
|
const durationMs = Math.round(performance.now() - startedAt);
|
|
26128
26512
|
console.log(`Built ${release.metadata.signed ? "signed" : "unsigned"} Android App Bundle in ${getDurationString(durationMs)}.`);
|
|
26129
26513
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
26130
|
-
console.log(`Metadata: ${
|
|
26514
|
+
console.log(`Metadata: ${join19(release.releaseRoot, "release.json")}`);
|
|
26131
26515
|
return release;
|
|
26132
26516
|
} finally {
|
|
26133
26517
|
sendTelemetryEvent("mobile:android-release-build", {
|
|
@@ -26350,7 +26734,7 @@ var buildIos = async (args, prepareBuildNumber) => {
|
|
|
26350
26734
|
console.log(`Artifact: ${release.artifactPath}`);
|
|
26351
26735
|
if (release.registeredArtifactPath)
|
|
26352
26736
|
console.log(`Registered-device artifact: ${release.registeredArtifactPath}`);
|
|
26353
|
-
console.log(`Metadata: ${
|
|
26737
|
+
console.log(`Metadata: ${join19(release.releaseRoot, "release.json")}`);
|
|
26354
26738
|
return release;
|
|
26355
26739
|
} finally {
|
|
26356
26740
|
stopListeningForCancellation();
|
|
@@ -26501,7 +26885,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
26501
26885
|
checks.push({
|
|
26502
26886
|
id: "sync.storage-schema",
|
|
26503
26887
|
label: `Offline schema ${schema.components.map((component) => `${component.id}@${component.version}`).join(", ")}`,
|
|
26504
|
-
path:
|
|
26888
|
+
path: join19(projectRoot, "package.json"),
|
|
26505
26889
|
platform: "host",
|
|
26506
26890
|
status: "pass"
|
|
26507
26891
|
});
|
|
@@ -26509,7 +26893,7 @@ var appendSyncSchemaDoctorCheck = (checks, projectRoot) => {
|
|
|
26509
26893
|
checks.push({
|
|
26510
26894
|
id: "sync.storage-schema",
|
|
26511
26895
|
label: "Offline schema metadata is invalid",
|
|
26512
|
-
path:
|
|
26896
|
+
path: join19(projectRoot, "package.json"),
|
|
26513
26897
|
platform: "host",
|
|
26514
26898
|
remediation: error instanceof Error ? error.message : String(error),
|
|
26515
26899
|
status: "fail"
|
|
@@ -26597,7 +26981,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
26597
26981
|
}
|
|
26598
26982
|
return { https: args.includes("--https"), port };
|
|
26599
26983
|
}
|
|
26600
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
26984
|
+
const instances = listLiveInstances().filter((instance2) => resolve18(instance2.cwd) === resolve18(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
26601
26985
|
if (instances.length !== 1) {
|
|
26602
26986
|
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>.");
|
|
26603
26987
|
}
|
|
@@ -26691,8 +27075,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
26691
27075
|
return waitForBoot();
|
|
26692
27076
|
};
|
|
26693
27077
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
26694
|
-
const root =
|
|
26695
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
27078
|
+
const root = resolve18(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
27079
|
+
if (root !== projectRoot && !root.startsWith(`${resolve18(projectRoot)}/`)) {
|
|
26696
27080
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
26697
27081
|
}
|
|
26698
27082
|
return root;
|
|
@@ -26720,11 +27104,11 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
26720
27104
|
});
|
|
26721
27105
|
};
|
|
26722
27106
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
26723
|
-
await
|
|
26724
|
-
const screenshot = options.session ? await options.session.screenshot(
|
|
27107
|
+
await mkdir19(options.artifactRoot, { recursive: true });
|
|
27108
|
+
const screenshot = options.session ? await options.session.screenshot(join19(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
26725
27109
|
return;
|
|
26726
27110
|
}) : undefined;
|
|
26727
|
-
const diagnosticPath =
|
|
27111
|
+
const diagnosticPath = join19(options.artifactRoot, "android-failure.json");
|
|
26728
27112
|
await writeFile19(diagnosticPath, `${JSON.stringify({
|
|
26729
27113
|
diagnostics: options.session?.diagnostics ?? [],
|
|
26730
27114
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -26800,7 +27184,7 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
26800
27184
|
release,
|
|
26801
27185
|
serial
|
|
26802
27186
|
});
|
|
26803
|
-
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial,
|
|
27187
|
+
const screenshot = reportRoot ? await captureAndroidReleaseScreenshot(adb2, serial, join19(reportRoot, "android-release.png")) : undefined;
|
|
26804
27188
|
sendTelemetryEvent("mobile:android-release-conformance", {
|
|
26805
27189
|
durationMs: result.durationMs,
|
|
26806
27190
|
engine: result.engine,
|
|
@@ -26847,8 +27231,8 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
26847
27231
|
platform: "android",
|
|
26848
27232
|
success: false
|
|
26849
27233
|
});
|
|
26850
|
-
await
|
|
26851
|
-
const diagnosticPath =
|
|
27234
|
+
await mkdir19(artifactRoot, { recursive: true });
|
|
27235
|
+
const diagnosticPath = join19(artifactRoot, "android-release-failure.json");
|
|
26852
27236
|
await writeFile19(diagnosticPath, `${JSON.stringify({
|
|
26853
27237
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
26854
27238
|
platform: "android",
|
|
@@ -26943,7 +27327,7 @@ var testAndroid = async (args) => {
|
|
|
26943
27327
|
console.log(JSON.stringify(report, null, 2));
|
|
26944
27328
|
else
|
|
26945
27329
|
printAndroidTestReport(report);
|
|
26946
|
-
const screenshot = reportRoot ? await session.screenshot(
|
|
27330
|
+
const screenshot = reportRoot ? await session.screenshot(join19(artifactRoot, "android-emulator.png")) : undefined;
|
|
26947
27331
|
await writeRequestedAndroidReport({
|
|
26948
27332
|
adb: adb2,
|
|
26949
27333
|
args,
|
|
@@ -27012,14 +27396,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
27012
27396
|
const port = Number(explicit);
|
|
27013
27397
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
27014
27398
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
27015
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
27399
|
+
const instance2 = listLiveInstances().find((candidate) => resolve18(candidate.cwd) === resolve18(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
27016
27400
|
return {
|
|
27017
27401
|
https: instance2?.https ?? args.includes("--https"),
|
|
27018
27402
|
instance: instance2,
|
|
27019
27403
|
port
|
|
27020
27404
|
};
|
|
27021
27405
|
}
|
|
27022
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
27406
|
+
const instances = listLiveInstances().filter((instance2) => resolve18(instance2.cwd) === resolve18(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
27023
27407
|
if (instances.length !== 1)
|
|
27024
27408
|
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>.");
|
|
27025
27409
|
const [instance] = instances;
|
|
@@ -27037,7 +27421,7 @@ var waitForIosHmrClient = async (options) => {
|
|
|
27037
27421
|
return;
|
|
27038
27422
|
});
|
|
27039
27423
|
const status = response?.ok ? await response.json().catch(() => null) : null;
|
|
27040
|
-
const targets =
|
|
27424
|
+
const targets = isRecord11(status) && isRecord11(status.connectedTargets) ? status.connectedTargets : undefined;
|
|
27041
27425
|
if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
|
|
27042
27426
|
return;
|
|
27043
27427
|
await Bun.sleep(100);
|
|
@@ -27163,8 +27547,8 @@ var requireCapturedCommand = (command, label) => {
|
|
|
27163
27547
|
return result;
|
|
27164
27548
|
};
|
|
27165
27549
|
var writeIosFailureArtifacts = async (options) => {
|
|
27166
|
-
await
|
|
27167
|
-
const screenshot =
|
|
27550
|
+
await mkdir19(options.artifactRoot, { recursive: true });
|
|
27551
|
+
const screenshot = join19(options.artifactRoot, "ios-failure.png");
|
|
27168
27552
|
const screenshotResult = captureCommand4([
|
|
27169
27553
|
options.xcrun,
|
|
27170
27554
|
"simctl",
|
|
@@ -27173,7 +27557,7 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
27173
27557
|
"screenshot",
|
|
27174
27558
|
screenshot
|
|
27175
27559
|
]);
|
|
27176
|
-
const diagnosticPath =
|
|
27560
|
+
const diagnosticPath = join19(options.artifactRoot, "ios-failure.json");
|
|
27177
27561
|
await writeFile19(diagnosticPath, `${JSON.stringify({
|
|
27178
27562
|
appId: options.appId,
|
|
27179
27563
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
@@ -27195,14 +27579,14 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
27195
27579
|
return;
|
|
27196
27580
|
const candidate = args[index + 1];
|
|
27197
27581
|
const explicit = candidate?.startsWith("--") ? undefined : candidate;
|
|
27198
|
-
const
|
|
27199
|
-
return safeArtifactRoot(projectRoot, explicit ?? `.absolutejs/mobile/test-reports/${platform2}-${
|
|
27582
|
+
const timestamp2 = new Date().toISOString().replaceAll(":", "-");
|
|
27583
|
+
return safeArtifactRoot(projectRoot, explicit ?? `.absolutejs/mobile/test-reports/${platform2}-${timestamp2}`);
|
|
27200
27584
|
};
|
|
27201
27585
|
var absolutejsVersionForReport = async () => {
|
|
27202
27586
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
27203
27587
|
const versions = await Promise.all([
|
|
27204
|
-
|
|
27205
|
-
|
|
27588
|
+
resolve18(import.meta.dir, "..", "..", "package.json"),
|
|
27589
|
+
resolve18(import.meta.dir, "..", "..", "..", "package.json")
|
|
27206
27590
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
27207
27591
|
for (const version of versions) {
|
|
27208
27592
|
if (version === "unknown")
|
|
@@ -27579,8 +27963,8 @@ var testIosRelease = async (args, mobile, projectRoot) => {
|
|
|
27579
27963
|
remote: remoteProfile !== undefined,
|
|
27580
27964
|
success: false
|
|
27581
27965
|
});
|
|
27582
|
-
await
|
|
27583
|
-
const diagnosticPath =
|
|
27966
|
+
await mkdir19(artifactRoot, { recursive: true });
|
|
27967
|
+
const diagnosticPath = join19(artifactRoot, "ios-release-failure.json");
|
|
27584
27968
|
await writeFile19(diagnosticPath, `${JSON.stringify({
|
|
27585
27969
|
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
27586
27970
|
platform: "ios",
|
|
@@ -27674,8 +28058,8 @@ var testIos = async (args) => {
|
|
|
27674
28058
|
mobile.appId
|
|
27675
28059
|
], "iOS app launch");
|
|
27676
28060
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
27677
|
-
await
|
|
27678
|
-
const screenshot =
|
|
28061
|
+
await mkdir19(artifactRoot, { recursive: true });
|
|
28062
|
+
const screenshot = join19(artifactRoot, "ios-simulator.png");
|
|
27679
28063
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
27680
28064
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
27681
28065
|
const report = {
|
|
@@ -27892,6 +28276,8 @@ var runMobile = async (args) => {
|
|
|
27892
28276
|
if (command === "ci") {
|
|
27893
28277
|
if (args[1] === "promote")
|
|
27894
28278
|
await dispatchGithubMobilePromotion(args.slice(2));
|
|
28279
|
+
else if (args[1] === "promotions")
|
|
28280
|
+
await listGithubMobilePromotions(args.slice(2));
|
|
27895
28281
|
else if (args[1] === "status")
|
|
27896
28282
|
await inspectGithubMobileRun(args.slice(2));
|
|
27897
28283
|
else if (args[1] === "audit")
|
|
@@ -27978,7 +28364,7 @@ var runMobile = async (args) => {
|
|
|
27978
28364
|
await publishIos(args.slice(2));
|
|
27979
28365
|
return;
|
|
27980
28366
|
}
|
|
27981
|
-
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] | certify <release-dir> [--evidence report-dir]... [--require installed|simulator|device|store] [--outdir dir] [--json] | certify <release-dir> --verify certification-dir [--require installed|simulator|device|store] [--json] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | ci promote <android|ios> --run-id id --certification path [--channel name] [--play-track track|--testflight-group group] [--ref branch] [--repo owner/name] [--watch] [--audit] [--outdir path] [--json] | ci status --run-id id [--watch] [--repo owner/name] [--json] | ci audit --run-id id [--source-run-id id] [--repo owner/name] [--outdir path] [--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] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--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] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--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 [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
|
|
28367
|
+
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] | certify <release-dir> [--evidence report-dir]... [--require installed|simulator|device|store] [--outdir dir] [--json] | certify <release-dir> --verify certification-dir [--require installed|simulator|device|store] [--json] | associations [--outdir dir] [--verify] | ci github [server-entry] [--publish] [--registry module] [--secret-env NAME] [--output path] [--force] [--json] | ci promote <android|ios> --run-id id --certification path [--channel name] [--play-track track|--testflight-group group] [--ref branch] [--repo owner/name] [--watch] [--audit] [--outdir path] [--json] | ci promote --resume dispatch-id [--watch] [--audit] [--outdir path] [--json] | ci promotions [--json] | ci status --run-id id [--watch] [--repo owner/name] [--json] | ci audit --run-id id [--source-run-id id] [--repo owner/name] [--outdir path] [--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] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--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] [--release release-dir] [--certification certification-dir] [--certification-attestation verification-json] [--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 [--remote name] [--device id [--testflight] --yes] | --wait-for-hmr] [--report [dir]] [--udid id] [--artifacts dir] [--json] [--config path]");
|
|
27982
28368
|
};
|
|
27983
28369
|
export {
|
|
27984
28370
|
runMobile
|