@absolutejs/absolute 0.20.0-beta.109 → 0.20.0-beta.110
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/README.md +4 -3
- package/changelog.json +26 -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-e4ygngfk.js → mobile-12y4gtsq.js} +571 -131
- package/dist/dev/serverBootstrap.js +35 -6
- package/dist/mobile/index.js +417 -85
- package/dist/mobile/index.js.map +8 -7
- package/dist/src/dev/serverEntryCopies.d.ts +6 -0
- package/dist/src/mobile/certificationVerification.d.ts +24 -0
- package/dist/src/mobile/ciWorkflow.d.ts +1 -1
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/releaseCertification.d.ts +1 -0
- package/dist/src/mobile/releasePublisher.d.ts +10 -0
- package/package.json +2 -2
|
@@ -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 access16,
|
|
16783
|
+
mkdir as mkdir17,
|
|
16784
16784
|
mkdtemp as mkdtemp6,
|
|
16785
|
-
readFile as
|
|
16785
|
+
readFile as readFile21,
|
|
16786
16786
|
rm as rm9,
|
|
16787
|
-
writeFile as
|
|
16787
|
+
writeFile as writeFile18
|
|
16788
16788
|
} from "fs/promises";
|
|
16789
16789
|
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16790
|
-
import { join as join17, relative as
|
|
16790
|
+
import { join as join17, relative as relative15, resolve as resolve16 } from "path";
|
|
16791
16791
|
import { createInterface } from "readline/promises";
|
|
16792
16792
|
|
|
16793
16793
|
// src/mobile/nativeDeepLinks.ts
|
|
@@ -22521,6 +22521,8 @@ This certification is local and contains no signing credentials, device identifi
|
|
|
22521
22521
|
${evidence}
|
|
22522
22522
|
`;
|
|
22523
22523
|
};
|
|
22524
|
+
var serializeAbsoluteMobileReleaseCertification = (certification) => `${JSON.stringify(certification, null, 2)}
|
|
22525
|
+
`;
|
|
22524
22526
|
var verifyAbsoluteMobileReleaseCertification = (certification, release, requirement) => {
|
|
22525
22527
|
const parsed = parseCertification(certification);
|
|
22526
22528
|
const expected = releaseIdentity(release);
|
|
@@ -22545,8 +22547,7 @@ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification,
|
|
|
22545
22547
|
jsonPath: join13(destination, "certification.json"),
|
|
22546
22548
|
markdownPath: join13(destination, "certification.md")
|
|
22547
22549
|
};
|
|
22548
|
-
const json =
|
|
22549
|
-
`;
|
|
22550
|
+
const json = serializeAbsoluteMobileReleaseCertification(certification);
|
|
22550
22551
|
const markdown = renderAbsoluteMobileReleaseCertification(certification);
|
|
22551
22552
|
const verifyExisting = async () => {
|
|
22552
22553
|
const [existingJson, existingMarkdown] = await Promise.all([
|
|
@@ -22582,9 +22583,111 @@ var writeAbsoluteMobileReleaseCertification = async (projectRoot, certification,
|
|
|
22582
22583
|
return paths;
|
|
22583
22584
|
};
|
|
22584
22585
|
|
|
22586
|
+
// src/mobile/certificationVerification.ts
|
|
22587
|
+
import { access as access9, mkdir as mkdir12, readFile as readFile16, stat as stat6, writeFile as writeFile13 } from "fs/promises";
|
|
22588
|
+
import { dirname as dirname10, isAbsolute as isAbsolute6, relative as relative8, resolve as resolve9, sep as sep6 } from "path";
|
|
22589
|
+
var ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT = 1;
|
|
22590
|
+
var MAX_VERIFICATION_BYTES = 1048576;
|
|
22591
|
+
var SHA_PATTERN = /^[a-f0-9]{40}$/u;
|
|
22592
|
+
var REPOSITORY_PATTERN = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
|
22593
|
+
var REF_PATTERN = /^refs\/(?:heads|tags)\/[^@\s]+$/u;
|
|
22594
|
+
var WORKFLOW_PATTERN = /^\.github\/workflows\/[^/@]+\.ya?ml$/u;
|
|
22595
|
+
var isRecord7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22596
|
+
var required = (value, name) => {
|
|
22597
|
+
if (typeof value !== "string" || value.trim() === "")
|
|
22598
|
+
throw new TypeError(`${name} is required.`);
|
|
22599
|
+
return value.trim();
|
|
22600
|
+
};
|
|
22601
|
+
var requireMatch = (value, pattern, name) => {
|
|
22602
|
+
if (!pattern.test(value))
|
|
22603
|
+
throw new TypeError(`${name} is invalid.`);
|
|
22604
|
+
return value;
|
|
22605
|
+
};
|
|
22606
|
+
var createAbsoluteMobileCertificationVerification = (bundle, environment) => {
|
|
22607
|
+
const repository = required(environment.GITHUB_REPOSITORY, "GITHUB_REPOSITORY");
|
|
22608
|
+
const workflowReference = required(environment.GITHUB_WORKFLOW_REF, "GITHUB_WORKFLOW_REF");
|
|
22609
|
+
const prefix = `${repository}/`;
|
|
22610
|
+
const separator = workflowReference.lastIndexOf("@");
|
|
22611
|
+
if (!workflowReference.startsWith(prefix) || separator <= prefix.length)
|
|
22612
|
+
throw new TypeError("GITHUB_WORKFLOW_REF is invalid.");
|
|
22613
|
+
return parseAbsoluteMobileCertificationVerification({
|
|
22614
|
+
bundle,
|
|
22615
|
+
format: ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
|
|
22616
|
+
identity: {
|
|
22617
|
+
issuer: "https://token.actions.githubusercontent.com",
|
|
22618
|
+
ref: environment.GITHUB_REF,
|
|
22619
|
+
repository,
|
|
22620
|
+
sha: environment.GITHUB_SHA,
|
|
22621
|
+
workflowPath: workflowReference.slice(prefix.length, separator)
|
|
22622
|
+
},
|
|
22623
|
+
kind: "sigstore-bundle"
|
|
22624
|
+
});
|
|
22625
|
+
};
|
|
22626
|
+
var parseAbsoluteMobileCertificationVerification = (value) => {
|
|
22627
|
+
if (!isRecord7(value) || value.format !== ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT || value.kind !== "sigstore-bundle" || !isRecord7(value.bundle) || !isRecord7(value.identity))
|
|
22628
|
+
throw new TypeError("Mobile certification verification contract is invalid.");
|
|
22629
|
+
const issuer = required(value.identity.issuer, "verification issuer");
|
|
22630
|
+
if (issuer !== "https://token.actions.githubusercontent.com")
|
|
22631
|
+
throw new TypeError("verification issuer is invalid.");
|
|
22632
|
+
const verification = {
|
|
22633
|
+
bundle: value.bundle,
|
|
22634
|
+
format: ABSOLUTE_MOBILE_CERTIFICATION_VERIFICATION_FORMAT,
|
|
22635
|
+
identity: {
|
|
22636
|
+
issuer,
|
|
22637
|
+
ref: requireMatch(required(value.identity.ref, "verification ref"), REF_PATTERN, "verification ref"),
|
|
22638
|
+
repository: requireMatch(required(value.identity.repository, "verification repository"), REPOSITORY_PATTERN, "verification repository"),
|
|
22639
|
+
sha: requireMatch(required(value.identity.sha, "verification sha"), SHA_PATTERN, "verification sha"),
|
|
22640
|
+
workflowPath: requireMatch(required(value.identity.workflowPath, "verification workflowPath"), WORKFLOW_PATTERN, "verification workflowPath")
|
|
22641
|
+
},
|
|
22642
|
+
kind: "sigstore-bundle"
|
|
22643
|
+
};
|
|
22644
|
+
const bytes = new TextEncoder().encode(JSON.stringify(verification));
|
|
22645
|
+
if (bytes.byteLength > MAX_VERIFICATION_BYTES)
|
|
22646
|
+
throw new TypeError("Mobile certification verification exceeds the 1 MiB limit.");
|
|
22647
|
+
return verification;
|
|
22648
|
+
};
|
|
22649
|
+
var projectFile = (projectRoot, requested) => {
|
|
22650
|
+
const root = resolve9(projectRoot);
|
|
22651
|
+
const path = resolve9(root, requested);
|
|
22652
|
+
const portable = relative8(root, path);
|
|
22653
|
+
if (portable === ".." || portable.startsWith(`..${sep6}`) || isAbsolute6(portable))
|
|
22654
|
+
throw new TypeError("Mobile certification verification path must remain inside the project.");
|
|
22655
|
+
return path;
|
|
22656
|
+
};
|
|
22657
|
+
var readAbsoluteMobileCertificationVerification = async (projectRoot, requested) => {
|
|
22658
|
+
const path = projectFile(projectRoot, requested);
|
|
22659
|
+
await access9(path).catch(() => {
|
|
22660
|
+
throw new TypeError(`Mobile certification verification does not exist: ${path}`);
|
|
22661
|
+
});
|
|
22662
|
+
const metadata = await stat6(path);
|
|
22663
|
+
if (!metadata.isFile() || metadata.size > MAX_VERIFICATION_BYTES)
|
|
22664
|
+
throw new TypeError("Mobile certification verification file is invalid.");
|
|
22665
|
+
return parseAbsoluteMobileCertificationVerification(JSON.parse(await readFile16(path, "utf8")));
|
|
22666
|
+
};
|
|
22667
|
+
var readAbsoluteSigstoreBundle = async (projectRoot, requested) => {
|
|
22668
|
+
const path = projectFile(projectRoot, requested);
|
|
22669
|
+
const metadata = await stat6(path).catch(() => null);
|
|
22670
|
+
if (!metadata?.isFile() || metadata.size > MAX_VERIFICATION_BYTES)
|
|
22671
|
+
throw new TypeError("Sigstore bundle file is invalid.");
|
|
22672
|
+
const value = JSON.parse(await readFile16(path, "utf8"));
|
|
22673
|
+
if (!isRecord7(value))
|
|
22674
|
+
throw new TypeError("Sigstore bundle is invalid.");
|
|
22675
|
+
return value;
|
|
22676
|
+
};
|
|
22677
|
+
var writeAbsoluteMobileCertificationVerification = async (projectRoot, requested, verification) => {
|
|
22678
|
+
const path = projectFile(projectRoot, requested);
|
|
22679
|
+
const parsed = parseAbsoluteMobileCertificationVerification(verification);
|
|
22680
|
+
await mkdir12(dirname10(path), { recursive: true });
|
|
22681
|
+
await writeFile13(path, `${JSON.stringify(parsed, null, 2)}
|
|
22682
|
+
`, {
|
|
22683
|
+
mode: 384
|
|
22684
|
+
});
|
|
22685
|
+
return path;
|
|
22686
|
+
};
|
|
22687
|
+
|
|
22585
22688
|
// src/mobile/releasePublisher.ts
|
|
22586
|
-
import { access as
|
|
22587
|
-
import { isAbsolute as
|
|
22689
|
+
import { access as access10 } from "fs/promises";
|
|
22690
|
+
import { isAbsolute as isAbsolute7, relative as relative9, resolve as resolve10, sep as sep7 } from "path";
|
|
22588
22691
|
import { pathToFileURL as pathToFileURL2 } from "url";
|
|
22589
22692
|
var prepareAbsoluteIosRelease = async (publisher, options) => {
|
|
22590
22693
|
if (typeof publisher.prepareIosRelease !== "function") {
|
|
@@ -22607,33 +22710,35 @@ var prepareAbsoluteAndroidRelease = async (publisher, options) => {
|
|
|
22607
22710
|
}
|
|
22608
22711
|
return versionCode;
|
|
22609
22712
|
};
|
|
22610
|
-
var requireCertificationReceipt = (publication, certification, requirement, releaseId) => {
|
|
22611
|
-
if (!certification && !requirement)
|
|
22713
|
+
var requireCertificationReceipt = (publication, certification, requirement, releaseId, verification) => {
|
|
22714
|
+
if (!certification && !requirement && !verification)
|
|
22612
22715
|
return;
|
|
22613
22716
|
if (!certification || !requirement)
|
|
22614
22717
|
throw new TypeError("Native release publication certification contract is incomplete.");
|
|
22615
22718
|
const receipt = publication.certification;
|
|
22616
22719
|
if (!receipt || receipt.certificationId !== certification.certificationId || receipt.releaseId !== releaseId || receipt.requirement !== requirement || receipt.strength !== certification.strength)
|
|
22617
22720
|
throw new TypeError("Native release registry did not retain the required release certification.");
|
|
22721
|
+
if (verification && (!receipt.provenance || receipt.provenance.subject !== releaseId))
|
|
22722
|
+
throw new TypeError("Native release registry did not retain trusted certification provenance.");
|
|
22618
22723
|
};
|
|
22619
|
-
var
|
|
22620
|
-
var isPublisher = (value) =>
|
|
22724
|
+
var isRecord8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22725
|
+
var isPublisher = (value) => isRecord8(value) && typeof value.publish === "function";
|
|
22621
22726
|
var publisherModulePath = (projectRoot, requested) => {
|
|
22622
|
-
const root =
|
|
22623
|
-
const path =
|
|
22624
|
-
const projectRelative =
|
|
22625
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
22727
|
+
const root = resolve10(projectRoot);
|
|
22728
|
+
const path = resolve10(root, requested);
|
|
22729
|
+
const projectRelative = relative9(root, path);
|
|
22730
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep7}`) || isAbsolute7(projectRelative)) {
|
|
22626
22731
|
throw new TypeError("mobile publish --registry must remain inside the project.");
|
|
22627
22732
|
}
|
|
22628
22733
|
return path;
|
|
22629
22734
|
};
|
|
22630
22735
|
var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath) => {
|
|
22631
22736
|
const modulePath = publisherModulePath(projectRoot, requestedModulePath);
|
|
22632
|
-
await
|
|
22737
|
+
await access10(modulePath).catch(() => {
|
|
22633
22738
|
throw new TypeError(`Native release registry module does not exist: ${modulePath}`);
|
|
22634
22739
|
});
|
|
22635
22740
|
const loaded = await import(pathToFileURL2(modulePath).href);
|
|
22636
|
-
const publisher =
|
|
22741
|
+
const publisher = isRecord8(loaded) ? loaded.default ?? loaded.registry : undefined;
|
|
22637
22742
|
if (!isPublisher(publisher)) {
|
|
22638
22743
|
throw new TypeError("Native release registry module must default-export a registry with publish(options).");
|
|
22639
22744
|
}
|
|
@@ -22641,6 +22746,8 @@ var loadAbsoluteNativeReleasePublisher = async (projectRoot, requestedModulePath
|
|
|
22641
22746
|
};
|
|
22642
22747
|
var publishAbsoluteAndroidRelease = async (options) => {
|
|
22643
22748
|
const certificationRequirement = options.certificationRequirement ?? options.certification?.requirement;
|
|
22749
|
+
if (options.certificationVerification && !options.certification)
|
|
22750
|
+
throw new TypeError("Android certification verification requires certification.");
|
|
22644
22751
|
if (options.certification)
|
|
22645
22752
|
verifyAbsoluteMobileReleaseCertification(options.certification, options.release, certificationRequirement);
|
|
22646
22753
|
else if (certificationRequirement)
|
|
@@ -22650,6 +22757,7 @@ var publishAbsoluteAndroidRelease = async (options) => {
|
|
|
22650
22757
|
allowUnsigned: options.allowUnsigned,
|
|
22651
22758
|
certification: options.certification,
|
|
22652
22759
|
certificationRequirement,
|
|
22760
|
+
certificationVerification: options.certificationVerification,
|
|
22653
22761
|
channel: options.channel,
|
|
22654
22762
|
googlePlay: options.googlePlay,
|
|
22655
22763
|
releaseRoot: options.release.releaseRoot,
|
|
@@ -22660,7 +22768,7 @@ var publishAbsoluteAndroidRelease = async (options) => {
|
|
|
22660
22768
|
if (!actual || actual.appId !== expected.appId || actual.platform !== "android" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.versionCode !== expected.versionCode || typeof publication.reused !== "boolean") {
|
|
22661
22769
|
throw new TypeError("Native release registry returned a different Android release identity.");
|
|
22662
22770
|
}
|
|
22663
|
-
requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId);
|
|
22771
|
+
requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId, options.certificationVerification);
|
|
22664
22772
|
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
22665
22773
|
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
22666
22774
|
}
|
|
@@ -22674,6 +22782,8 @@ var publishAbsoluteAndroidRelease = async (options) => {
|
|
|
22674
22782
|
};
|
|
22675
22783
|
var publishAbsoluteIosRelease = async (options) => {
|
|
22676
22784
|
const certificationRequirement = options.certificationRequirement ?? options.certification?.requirement;
|
|
22785
|
+
if (options.certificationVerification && !options.certification)
|
|
22786
|
+
throw new TypeError("iOS certification verification requires certification.");
|
|
22677
22787
|
if (options.certification)
|
|
22678
22788
|
verifyAbsoluteMobileReleaseCertification(options.certification, options.release, certificationRequirement);
|
|
22679
22789
|
else if (certificationRequirement)
|
|
@@ -22684,6 +22794,7 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
22684
22794
|
appStoreConnect: options.appStoreConnect,
|
|
22685
22795
|
certification: options.certification,
|
|
22686
22796
|
certificationRequirement,
|
|
22797
|
+
certificationVerification: options.certificationVerification,
|
|
22687
22798
|
channel: options.channel,
|
|
22688
22799
|
releaseRoot: options.release.releaseRoot,
|
|
22689
22800
|
signal: options.signal
|
|
@@ -22693,7 +22804,7 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
22693
22804
|
if (!actual || actual.appId !== expected.appId || actual.platform !== "ios" || actual.releaseId !== expected.releaseId || actual.sha256 !== expected.sha256 || actual.signed !== expected.signed || actual.buildNumber !== expected.buildNumber || actual.marketingVersion !== expected.marketingVersion || typeof publication.reused !== "boolean") {
|
|
22694
22805
|
throw new TypeError("Native release registry returned a different iOS release identity.");
|
|
22695
22806
|
}
|
|
22696
|
-
requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId);
|
|
22807
|
+
requireCertificationReceipt(publication, options.certification, certificationRequirement, expected.releaseId, options.certificationVerification);
|
|
22697
22808
|
if (options.channel !== undefined && (publication.channel?.channel !== options.channel || publication.channel.releaseId !== expected.releaseId)) {
|
|
22698
22809
|
throw new TypeError("Native release registry did not promote the requested channel.");
|
|
22699
22810
|
}
|
|
@@ -22707,8 +22818,8 @@ var publishAbsoluteIosRelease = async (options) => {
|
|
|
22707
22818
|
};
|
|
22708
22819
|
|
|
22709
22820
|
// src/mobile/mobileInspect.ts
|
|
22710
|
-
import { access as
|
|
22711
|
-
import { join as join14, relative as
|
|
22821
|
+
import { access as access11, readFile as readFile17 } from "fs/promises";
|
|
22822
|
+
import { join as join14, relative as relative10, resolve as resolve11 } from "path";
|
|
22712
22823
|
var ABSOLUTE_MOBILE_INSPECTION_FORMAT = 1;
|
|
22713
22824
|
var MOBILE_PACKAGE_NAMES = new Set([
|
|
22714
22825
|
"@absolutejs/absolute",
|
|
@@ -22723,19 +22834,19 @@ var MOBILE_PACKAGE_NAMES = new Set([
|
|
|
22723
22834
|
]);
|
|
22724
22835
|
var isObject2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22725
22836
|
var portablePath2 = (projectRoot, path) => {
|
|
22726
|
-
const value =
|
|
22837
|
+
const value = relative10(resolve11(projectRoot), resolve11(path)).replaceAll("\\", "/");
|
|
22727
22838
|
return value || ".";
|
|
22728
22839
|
};
|
|
22729
22840
|
var pathExists4 = async (path) => {
|
|
22730
22841
|
try {
|
|
22731
|
-
await
|
|
22842
|
+
await access11(path);
|
|
22732
22843
|
return true;
|
|
22733
22844
|
} catch {
|
|
22734
22845
|
return false;
|
|
22735
22846
|
}
|
|
22736
22847
|
};
|
|
22737
22848
|
var readObject2 = async (path) => {
|
|
22738
|
-
const value = JSON.parse(await
|
|
22849
|
+
const value = JSON.parse(await readFile17(path, "utf8"));
|
|
22739
22850
|
if (!isObject2(value))
|
|
22740
22851
|
throw new TypeError("JSON root must be an object.");
|
|
22741
22852
|
return value;
|
|
@@ -22869,9 +22980,9 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
22869
22980
|
|
|
22870
22981
|
// src/mobile/ciWorkflow.ts
|
|
22871
22982
|
import { existsSync } from "fs";
|
|
22872
|
-
import { access as
|
|
22873
|
-
import { dirname as
|
|
22874
|
-
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT =
|
|
22983
|
+
import { access as access12, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
|
|
22984
|
+
import { dirname as dirname11, extname as extname2, relative as relative11, resolve as resolve12, sep as sep8 } from "path";
|
|
22985
|
+
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 2;
|
|
22875
22986
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
22876
22987
|
var CI_ENV_INDENTATION = 6;
|
|
22877
22988
|
var RESERVED_SECRET_NAMES = new Set([
|
|
@@ -22891,7 +23002,7 @@ var RESERVED_SECRET_NAMES = new Set([
|
|
|
22891
23002
|
]);
|
|
22892
23003
|
var exists5 = async (path) => {
|
|
22893
23004
|
try {
|
|
22894
|
-
await
|
|
23005
|
+
await access12(path);
|
|
22895
23006
|
return true;
|
|
22896
23007
|
} catch {
|
|
22897
23008
|
return false;
|
|
@@ -22899,10 +23010,10 @@ var exists5 = async (path) => {
|
|
|
22899
23010
|
};
|
|
22900
23011
|
var yamlString = (value) => `'${value.replaceAll("'", "''")}'`;
|
|
22901
23012
|
var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
22902
|
-
const root =
|
|
22903
|
-
const path =
|
|
22904
|
-
const portable =
|
|
22905
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
23013
|
+
const root = resolve12(projectRoot);
|
|
23014
|
+
const path = resolve12(root, value);
|
|
23015
|
+
const portable = relative11(root, path).replaceAll("\\", "/");
|
|
23016
|
+
if (portable === ".." || portable.startsWith(`..${sep8}`) || portable.startsWith("../") || portable === "") {
|
|
22906
23017
|
throw new TypeError(`${field2} must remain inside the project root.`);
|
|
22907
23018
|
}
|
|
22908
23019
|
if (/\r|\n/u.test(portable) || portable.startsWith("-"))
|
|
@@ -22912,11 +23023,11 @@ var projectPath2 = (projectRoot, value, field2, options = {}) => {
|
|
|
22912
23023
|
return portable;
|
|
22913
23024
|
};
|
|
22914
23025
|
var workflowOutputPath = (projectRoot, value) => {
|
|
22915
|
-
const root =
|
|
22916
|
-
const workflows =
|
|
22917
|
-
const path =
|
|
22918
|
-
const portable =
|
|
22919
|
-
if (portable === ".." || portable.startsWith(`..${
|
|
23026
|
+
const root = resolve12(projectRoot);
|
|
23027
|
+
const workflows = resolve12(root, ".github/workflows");
|
|
23028
|
+
const path = resolve12(root, value ?? ".github/workflows/absolute-mobile.yml");
|
|
23029
|
+
const portable = relative11(workflows, path);
|
|
23030
|
+
if (portable === ".." || portable.startsWith(`..${sep8}`) || extname2(path) !== ".yml" && extname2(path) !== ".yaml") {
|
|
22920
23031
|
throw new TypeError("mobile ci github --output must be a .yml or .yaml file inside .github/workflows.");
|
|
22921
23032
|
}
|
|
22922
23033
|
return path;
|
|
@@ -23009,6 +23120,14 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
23009
23120
|
if (!includePublishing)
|
|
23010
23121
|
return "";
|
|
23011
23122
|
const fields = [
|
|
23123
|
+
` operation:
|
|
23124
|
+
description: Build a release or promote an exact artifact from an earlier run
|
|
23125
|
+
required: true
|
|
23126
|
+
type: choice
|
|
23127
|
+
default: build
|
|
23128
|
+
options:
|
|
23129
|
+
- build
|
|
23130
|
+
- promote`,
|
|
23012
23131
|
` publish:
|
|
23013
23132
|
description: Publish through mobile.release.ts after the signed build
|
|
23014
23133
|
required: true
|
|
@@ -23017,6 +23136,14 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
23017
23136
|
` channel:
|
|
23018
23137
|
description: Optional AbsoluteJS immutable release channel
|
|
23019
23138
|
required: false
|
|
23139
|
+
type: string`,
|
|
23140
|
+
` source_run_id:
|
|
23141
|
+
description: Source workflow run ID for promotion without rebuilding
|
|
23142
|
+
required: false
|
|
23143
|
+
type: string`,
|
|
23144
|
+
` certification_base64:
|
|
23145
|
+
description: AbsoluteJS certification supplied by the promotion CLI
|
|
23146
|
+
required: false
|
|
23020
23147
|
type: string`
|
|
23021
23148
|
];
|
|
23022
23149
|
if (platforms.includes("android"))
|
|
@@ -23046,7 +23173,7 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
23046
23173
|
${fields.join(`
|
|
23047
23174
|
`)}`;
|
|
23048
23175
|
};
|
|
23049
|
-
var jobCondition = (platform) => `github.event_name == 'workflow_dispatch' && (inputs.platform == 'all' || inputs.platform == '${platform}')`;
|
|
23176
|
+
var jobCondition = (platform, includePublishing) => `github.event_name == 'workflow_dispatch' && ${includePublishing ? "inputs.operation == 'build' && " : ""}(inputs.platform == 'all' || inputs.platform == '${platform}')`;
|
|
23050
23177
|
var androidJob = (options) => {
|
|
23051
23178
|
const custom = customSecretEnvironment(options.customSecrets);
|
|
23052
23179
|
const publishEnvironment = options.includePublishing ? `
|
|
@@ -23073,6 +23200,11 @@ var androidJob = (options) => {
|
|
|
23073
23200
|
chmod 600 "$GOOGLE_APPLICATION_CREDENTIALS"
|
|
23074
23201
|
fi` : "";
|
|
23075
23202
|
const certificationSteps = options.includePublishing ? `
|
|
23203
|
+
- name: Install pinned Cosign for Android certification
|
|
23204
|
+
if: inputs.publish
|
|
23205
|
+
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
|
|
23206
|
+
with:
|
|
23207
|
+
cosign-release: v3.1.2
|
|
23076
23208
|
- name: Install-test and certify exact Android release
|
|
23077
23209
|
if: inputs.publish
|
|
23078
23210
|
shell: bash
|
|
@@ -23092,6 +23224,12 @@ var androidJob = (options) => {
|
|
|
23092
23224
|
"\${args[@]}"
|
|
23093
23225
|
args=(bunx absolute mobile certify "$RELEASE_DIRECTORY" --evidence .absolutejs/mobile-ci/acceptance/android --require installed --outdir .absolutejs/mobile-ci/certifications/android --json)
|
|
23094
23226
|
"\${args[@]}" > .absolutejs/mobile-ci/android-certification.json
|
|
23227
|
+
shopt -s nullglob
|
|
23228
|
+
certifications=(.absolutejs/mobile-ci/certifications/android/*)
|
|
23229
|
+
if [[ "\${#certifications[@]}" -ne 1 ]]; then echo "Expected exactly one Android certification." >&2; exit 1; fi
|
|
23230
|
+
CERTIFICATION_PATH="\${certifications[0]}/certification.json"
|
|
23231
|
+
bunx @absolutejs/attest@0.2.0 sign-blobs "$CERTIFICATION_PATH"
|
|
23232
|
+
bunx absolute mobile ci verification --bundle "$CERTIFICATION_PATH.sigstore.json" --out "\${certifications[0]}/verification.json"
|
|
23095
23233
|
- name: Publish certified Android release
|
|
23096
23234
|
if: inputs.publish
|
|
23097
23235
|
shell: bash
|
|
@@ -23103,7 +23241,7 @@ var androidJob = (options) => {
|
|
|
23103
23241
|
echo "Expected exactly one Android release and certification." >&2
|
|
23104
23242
|
exit 1
|
|
23105
23243
|
fi
|
|
23106
|
-
args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification "\${certifications[0]}" --registry "$ABSOLUTE_REGISTRY_MODULE")
|
|
23244
|
+
args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification "\${certifications[0]}" --certification-attestation "\${certifications[0]}/verification.json" --registry "$ABSOLUTE_REGISTRY_MODULE")
|
|
23107
23245
|
if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then
|
|
23108
23246
|
args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL")
|
|
23109
23247
|
fi
|
|
@@ -23128,7 +23266,7 @@ var androidJob = (options) => {
|
|
|
23128
23266
|
android:
|
|
23129
23267
|
name: Signed Android release
|
|
23130
23268
|
needs: validate
|
|
23131
|
-
if: \${{ ${jobCondition("android")} }}
|
|
23269
|
+
if: \${{ ${jobCondition("android", options.includePublishing)} }}
|
|
23132
23270
|
runs-on: ubuntu-latest
|
|
23133
23271
|
environment: absolute-mobile-release
|
|
23134
23272
|
permissions:
|
|
@@ -23219,6 +23357,11 @@ var iosJob = (options) => {
|
|
|
23219
23357
|
chmod 600 "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
|
|
23220
23358
|
fi` : "";
|
|
23221
23359
|
const certificationSteps = options.includePublishing ? `
|
|
23360
|
+
- name: Install pinned Cosign for iOS certification
|
|
23361
|
+
if: inputs.publish
|
|
23362
|
+
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
|
|
23363
|
+
with:
|
|
23364
|
+
cosign-release: v3.1.2
|
|
23222
23365
|
- name: Simulator-test and certify exact iOS release
|
|
23223
23366
|
if: inputs.publish
|
|
23224
23367
|
shell: bash
|
|
@@ -23235,6 +23378,12 @@ var iosJob = (options) => {
|
|
|
23235
23378
|
"\${args[@]}"
|
|
23236
23379
|
args=(bunx absolute mobile certify "$RELEASE_DIRECTORY" --evidence .absolutejs/mobile-ci/acceptance/ios --require simulator --outdir .absolutejs/mobile-ci/certifications/ios --json)
|
|
23237
23380
|
"\${args[@]}" > .absolutejs/mobile-ci/ios-certification.json
|
|
23381
|
+
shopt -s nullglob
|
|
23382
|
+
certifications=(.absolutejs/mobile-ci/certifications/ios/*)
|
|
23383
|
+
if [[ "\${#certifications[@]}" -ne 1 ]]; then echo "Expected exactly one iOS certification." >&2; exit 1; fi
|
|
23384
|
+
CERTIFICATION_PATH="\${certifications[0]}/certification.json"
|
|
23385
|
+
bunx @absolutejs/attest@0.2.0 sign-blobs "$CERTIFICATION_PATH"
|
|
23386
|
+
bunx absolute mobile ci verification --bundle "$CERTIFICATION_PATH.sigstore.json" --out "\${certifications[0]}/verification.json"
|
|
23238
23387
|
- name: Publish certified iOS release
|
|
23239
23388
|
if: inputs.publish
|
|
23240
23389
|
shell: bash
|
|
@@ -23246,7 +23395,7 @@ var iosJob = (options) => {
|
|
|
23246
23395
|
echo "Expected exactly one iOS release and certification." >&2
|
|
23247
23396
|
exit 1
|
|
23248
23397
|
fi
|
|
23249
|
-
args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification "\${certifications[0]}" --registry "$ABSOLUTE_REGISTRY_MODULE")
|
|
23398
|
+
args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification "\${certifications[0]}" --certification-attestation "\${certifications[0]}/verification.json" --registry "$ABSOLUTE_REGISTRY_MODULE")
|
|
23250
23399
|
if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then
|
|
23251
23400
|
args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL")
|
|
23252
23401
|
fi
|
|
@@ -23274,7 +23423,7 @@ var iosJob = (options) => {
|
|
|
23274
23423
|
ios:
|
|
23275
23424
|
name: Signed iOS release
|
|
23276
23425
|
needs: validate
|
|
23277
|
-
if: \${{ ${jobCondition("ios")} }}
|
|
23426
|
+
if: \${{ ${jobCondition("ios", options.includePublishing)} }}
|
|
23278
23427
|
runs-on: macos-latest
|
|
23279
23428
|
environment: absolute-mobile-release
|
|
23280
23429
|
permissions:
|
|
@@ -23352,6 +23501,179 @@ ${certificationSteps}
|
|
|
23352
23501
|
rm -f "\${{ runner.temp }}/absolute.mobileprovision"
|
|
23353
23502
|
rm -f "\${{ runner.temp }}/AuthKey_AbsoluteJS.p8"`;
|
|
23354
23503
|
};
|
|
23504
|
+
var promotionValidationSteps = ` - name: Validate promotion handoff
|
|
23505
|
+
if: github.event_name == 'workflow_dispatch' && inputs.operation == 'promote'
|
|
23506
|
+
shell: bash
|
|
23507
|
+
run: |
|
|
23508
|
+
if [[ "$ABSOLUTE_PROMOTION_PLATFORM" == "all" ]]; then
|
|
23509
|
+
echo "Promotion requires one exact platform." >&2
|
|
23510
|
+
exit 1
|
|
23511
|
+
fi
|
|
23512
|
+
if [[ ! "$ABSOLUTE_SOURCE_RUN_ID" =~ ^[1-9][0-9]*$ ]]; then
|
|
23513
|
+
echo "Promotion requires a numeric source workflow run ID." >&2
|
|
23514
|
+
exit 1
|
|
23515
|
+
fi
|
|
23516
|
+
if [[ -z "$ABSOLUTE_CERTIFICATION_BASE64" ]]; then
|
|
23517
|
+
echo "Promotion requires certification supplied by absolute mobile ci promote." >&2
|
|
23518
|
+
exit 1
|
|
23519
|
+
fi`;
|
|
23520
|
+
var promotionSharedSteps = (platform) => ` - name: Restore exact ${platform === "android" ? "Android" : "iOS"} release from source run
|
|
23521
|
+
uses: actions/download-artifact@v8
|
|
23522
|
+
with:
|
|
23523
|
+
name: absolute-mobile-${platform}
|
|
23524
|
+
path: .absolutejs/mobile/releases/${platform}
|
|
23525
|
+
run-id: \${{ inputs.source_run_id }}
|
|
23526
|
+
github-token: \${{ github.token }}
|
|
23527
|
+
- name: Import and verify exact certification
|
|
23528
|
+
shell: bash
|
|
23529
|
+
run: |
|
|
23530
|
+
mkdir -p .absolutejs/mobile-ci/imported-certification
|
|
23531
|
+
printf '%s' "$ABSOLUTE_CERTIFICATION_BASE64" | base64 --decode > .absolutejs/mobile-ci/imported-certification/certification.json
|
|
23532
|
+
shopt -s nullglob
|
|
23533
|
+
releases=(.absolutejs/mobile/releases/${platform}/*)
|
|
23534
|
+
if [[ "\${#releases[@]}" -ne 1 || ! -d "\${releases[0]}" ]]; then
|
|
23535
|
+
echo "Expected exactly one immutable ${platform} release directory from the source run." >&2
|
|
23536
|
+
exit 1
|
|
23537
|
+
fi
|
|
23538
|
+
args=(bunx absolute mobile certify "\${releases[0]}" --verify .absolutejs/mobile-ci/imported-certification/certification.json --json)
|
|
23539
|
+
"\${args[@]}" > .absolutejs/mobile-ci/imported-certification/verified.json
|
|
23540
|
+
- name: Install pinned Cosign
|
|
23541
|
+
uses: sigstore/cosign-installer@6f9f17788090df1f26f669e9d70d6ae9567deba6
|
|
23542
|
+
with:
|
|
23543
|
+
cosign-release: v3.1.2
|
|
23544
|
+
- name: Sign certification with GitHub OIDC
|
|
23545
|
+
shell: bash
|
|
23546
|
+
run: |
|
|
23547
|
+
bunx @absolutejs/attest@0.2.0 sign-blobs .absolutejs/mobile-ci/imported-certification/certification.json
|
|
23548
|
+
bunx absolute mobile ci verification --bundle .absolutejs/mobile-ci/imported-certification/certification.json.sigstore.json --out .absolutejs/mobile-ci/imported-certification/verification.json`;
|
|
23549
|
+
var androidPromotionJob = (options) => {
|
|
23550
|
+
const custom = customSecretEnvironment(options.customSecrets);
|
|
23551
|
+
return `
|
|
23552
|
+
promote_android:
|
|
23553
|
+
name: Promote exact certified Android release
|
|
23554
|
+
needs: validate
|
|
23555
|
+
if: \${{ github.event_name == 'workflow_dispatch' && inputs.operation == 'promote' && inputs.platform == 'android' }}
|
|
23556
|
+
runs-on: ubuntu-latest
|
|
23557
|
+
environment: absolute-mobile-release
|
|
23558
|
+
permissions:
|
|
23559
|
+
actions: read
|
|
23560
|
+
contents: read
|
|
23561
|
+
id-token: write
|
|
23562
|
+
env:
|
|
23563
|
+
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
|
|
23564
|
+
ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
|
|
23565
|
+
ABSOLUTE_PLAY_TRACK: \${{ inputs.play_track }}
|
|
23566
|
+
ABSOLUTE_GOOGLE_CREDENTIALS_BASE64: \${{ secrets.ABSOLUTE_GOOGLE_CREDENTIALS_BASE64 }}
|
|
23567
|
+
GOOGLE_APPLICATION_CREDENTIALS: \${{ runner.temp }}/absolute-google-credentials.json${custom ? `
|
|
23568
|
+
${custom}` : ""}
|
|
23569
|
+
${commandEnvironment({ configPath: undefined, registryModule: "", serverEntry: "" })}
|
|
23570
|
+
steps:
|
|
23571
|
+
${installSteps}
|
|
23572
|
+
${promotionSharedSteps("android")}
|
|
23573
|
+
- name: Publish restored Android release
|
|
23574
|
+
shell: bash
|
|
23575
|
+
run: |
|
|
23576
|
+
if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" || "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then
|
|
23577
|
+
true
|
|
23578
|
+
else
|
|
23579
|
+
echo "Promotion requires a release channel or Google Play track." >&2
|
|
23580
|
+
exit 1
|
|
23581
|
+
fi
|
|
23582
|
+
if [[ "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then
|
|
23583
|
+
if [[ -z "$ABSOLUTE_GOOGLE_CREDENTIALS_BASE64" ]]; then
|
|
23584
|
+
echo "ABSOLUTE_GOOGLE_CREDENTIALS_BASE64 is required for Google Play publication." >&2
|
|
23585
|
+
exit 1
|
|
23586
|
+
fi
|
|
23587
|
+
printf '%s' "$ABSOLUTE_GOOGLE_CREDENTIALS_BASE64" | base64 --decode > "$GOOGLE_APPLICATION_CREDENTIALS"
|
|
23588
|
+
chmod 600 "$GOOGLE_APPLICATION_CREDENTIALS"
|
|
23589
|
+
fi
|
|
23590
|
+
shopt -s nullglob
|
|
23591
|
+
releases=(.absolutejs/mobile/releases/android/*)
|
|
23592
|
+
args=(bunx absolute mobile publish android --release "\${releases[0]}" --certification .absolutejs/mobile-ci/imported-certification/certification.json --certification-attestation .absolutejs/mobile-ci/imported-certification/verification.json --registry "$ABSOLUTE_REGISTRY_MODULE" --json)
|
|
23593
|
+
if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL"); fi
|
|
23594
|
+
if [[ "$ABSOLUTE_PLAY_TRACK" != "registry-only" ]]; then args+=(--play-track "$ABSOLUTE_PLAY_TRACK"); fi
|
|
23595
|
+
${appendConfigArgument}
|
|
23596
|
+
"\${args[@]}" > .absolutejs/mobile-ci/promotion-receipt.json
|
|
23597
|
+
- name: Upload Android promotion audit
|
|
23598
|
+
if: always()
|
|
23599
|
+
uses: actions/upload-artifact@v7
|
|
23600
|
+
with:
|
|
23601
|
+
name: absolute-mobile-android-promotion-\${{ github.run_id }}
|
|
23602
|
+
path: |
|
|
23603
|
+
.absolutejs/mobile-ci/imported-certification/
|
|
23604
|
+
.absolutejs/mobile-ci/promotion-receipt.json
|
|
23605
|
+
if-no-files-found: error
|
|
23606
|
+
retention-days: 90
|
|
23607
|
+
include-hidden-files: true
|
|
23608
|
+
- name: Remove Google credentials
|
|
23609
|
+
if: always()
|
|
23610
|
+
run: rm -f "\${{ runner.temp }}/absolute-google-credentials.json"`;
|
|
23611
|
+
};
|
|
23612
|
+
var iosPromotionJob = (options) => {
|
|
23613
|
+
const custom = customSecretEnvironment(options.customSecrets);
|
|
23614
|
+
return `
|
|
23615
|
+
promote_ios:
|
|
23616
|
+
name: Promote exact certified iOS release
|
|
23617
|
+
needs: validate
|
|
23618
|
+
if: \${{ github.event_name == 'workflow_dispatch' && inputs.operation == 'promote' && inputs.platform == 'ios' }}
|
|
23619
|
+
runs-on: ubuntu-latest
|
|
23620
|
+
environment: absolute-mobile-release
|
|
23621
|
+
permissions:
|
|
23622
|
+
actions: read
|
|
23623
|
+
contents: read
|
|
23624
|
+
id-token: write
|
|
23625
|
+
env:
|
|
23626
|
+
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
|
|
23627
|
+
ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
|
|
23628
|
+
ABSOLUTE_TESTFLIGHT_GROUP: \${{ inputs.testflight_group }}
|
|
23629
|
+
ABSOLUTE_TESTFLIGHT_SUBMIT_REVIEW: \${{ inputs.submit_testflight_review }}
|
|
23630
|
+
APP_STORE_CONNECT_ISSUER_ID: \${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
|
|
23631
|
+
APP_STORE_CONNECT_KEY_ID: \${{ secrets.APP_STORE_CONNECT_KEY_ID }}
|
|
23632
|
+
APP_STORE_CONNECT_PRIVATE_KEY_BASE64: \${{ secrets.APP_STORE_CONNECT_PRIVATE_KEY_BASE64 }}
|
|
23633
|
+
APP_STORE_CONNECT_PRIVATE_KEY_PATH: \${{ runner.temp }}/AuthKey_AbsoluteJS.p8${custom ? `
|
|
23634
|
+
${custom}` : ""}
|
|
23635
|
+
${commandEnvironment({ configPath: undefined, registryModule: "", serverEntry: "" })}
|
|
23636
|
+
steps:
|
|
23637
|
+
${installSteps}
|
|
23638
|
+
${promotionSharedSteps("ios")}
|
|
23639
|
+
- name: Publish restored iOS release
|
|
23640
|
+
shell: bash
|
|
23641
|
+
run: |
|
|
23642
|
+
if [[ -z "$ABSOLUTE_RELEASE_CHANNEL" && -z "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then
|
|
23643
|
+
echo "Promotion requires a release channel or TestFlight group." >&2
|
|
23644
|
+
exit 1
|
|
23645
|
+
fi
|
|
23646
|
+
if [[ -n "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then
|
|
23647
|
+
required=(APP_STORE_CONNECT_ISSUER_ID APP_STORE_CONNECT_KEY_ID APP_STORE_CONNECT_PRIVATE_KEY_BASE64)
|
|
23648
|
+
for name in "\${required[@]}"; do
|
|
23649
|
+
if [[ -z "\${!name}" ]]; then echo "$name is required for TestFlight publication." >&2; exit 1; fi
|
|
23650
|
+
done
|
|
23651
|
+
printf '%s' "$APP_STORE_CONNECT_PRIVATE_KEY_BASE64" | base64 --decode > "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
|
|
23652
|
+
chmod 600 "$APP_STORE_CONNECT_PRIVATE_KEY_PATH"
|
|
23653
|
+
fi
|
|
23654
|
+
shopt -s nullglob
|
|
23655
|
+
releases=(.absolutejs/mobile/releases/ios/*)
|
|
23656
|
+
args=(bunx absolute mobile publish ios --release "\${releases[0]}" --certification .absolutejs/mobile-ci/imported-certification/certification.json --certification-attestation .absolutejs/mobile-ci/imported-certification/verification.json --registry "$ABSOLUTE_REGISTRY_MODULE" --json)
|
|
23657
|
+
if [[ -n "$ABSOLUTE_RELEASE_CHANNEL" ]]; then args+=(--channel "$ABSOLUTE_RELEASE_CHANNEL"); fi
|
|
23658
|
+
if [[ -n "$ABSOLUTE_TESTFLIGHT_GROUP" ]]; then args+=(--testflight-group "$ABSOLUTE_TESTFLIGHT_GROUP"); fi
|
|
23659
|
+
if [[ "$ABSOLUTE_TESTFLIGHT_SUBMIT_REVIEW" == "true" ]]; then args+=(--testflight-submit-review); fi
|
|
23660
|
+
${appendConfigArgument}
|
|
23661
|
+
"\${args[@]}" > .absolutejs/mobile-ci/promotion-receipt.json
|
|
23662
|
+
- name: Upload iOS promotion audit
|
|
23663
|
+
if: always()
|
|
23664
|
+
uses: actions/upload-artifact@v7
|
|
23665
|
+
with:
|
|
23666
|
+
name: absolute-mobile-ios-promotion-\${{ github.run_id }}
|
|
23667
|
+
path: |
|
|
23668
|
+
.absolutejs/mobile-ci/imported-certification/
|
|
23669
|
+
.absolutejs/mobile-ci/promotion-receipt.json
|
|
23670
|
+
if-no-files-found: error
|
|
23671
|
+
retention-days: 90
|
|
23672
|
+
include-hidden-files: true
|
|
23673
|
+
- name: Remove App Store Connect credentials
|
|
23674
|
+
if: always()
|
|
23675
|
+
run: rm -f "\${{ runner.temp }}/AuthKey_AbsoluteJS.p8"`;
|
|
23676
|
+
};
|
|
23355
23677
|
var requiredSecrets = (platforms, includePublishing, custom) => [
|
|
23356
23678
|
...platforms.includes("android") ? [
|
|
23357
23679
|
"ABSOLUTE_ANDROID_KEYSTORE_BASE64",
|
|
@@ -23413,10 +23735,14 @@ jobs:
|
|
|
23413
23735
|
permissions:
|
|
23414
23736
|
contents: read
|
|
23415
23737
|
env:
|
|
23416
|
-
${environment}
|
|
23738
|
+
${environment}${includePublishing ? `
|
|
23739
|
+
ABSOLUTE_PROMOTION_PLATFORM: \${{ inputs.platform }}
|
|
23740
|
+
ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
|
|
23741
|
+
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}` : ""}
|
|
23417
23742
|
steps:
|
|
23418
|
-
${installSteps}
|
|
23419
|
-
${
|
|
23743
|
+
${installSteps}${includePublishing ? `
|
|
23744
|
+
${promotionValidationSteps}` : ""}
|
|
23745
|
+
${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets, includePublishing }) : ""}${platforms.includes("ios") ? iosJob({ customSecrets, includePublishing }) : ""}${includePublishing && platforms.includes("android") ? androidPromotionJob({ customSecrets, includePublishing }) : ""}${includePublishing && platforms.includes("ios") ? iosPromotionJob({ customSecrets, includePublishing }) : ""}
|
|
23420
23746
|
`;
|
|
23421
23747
|
const replacements = new Map([
|
|
23422
23748
|
[
|
|
@@ -23442,12 +23768,12 @@ ${bundleAuditSteps}${platforms.includes("android") ? androidJob({ customSecrets,
|
|
|
23442
23768
|
var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
23443
23769
|
const path = workflowOutputPath(options.projectRoot, options.outputPath);
|
|
23444
23770
|
const generated = createAbsoluteMobileGithubWorkflow(options);
|
|
23445
|
-
const previous = await exists5(path) ? await
|
|
23771
|
+
const previous = await exists5(path) ? await readFile18(path, "utf8") : undefined;
|
|
23446
23772
|
if (previous !== undefined && previous !== generated.workflow && !options.force)
|
|
23447
|
-
throw new TypeError(`${
|
|
23773
|
+
throw new TypeError(`${relative11(options.projectRoot, path)} already exists and differs. Rerun with --force to replace the generated workflow.`);
|
|
23448
23774
|
if (previous !== generated.workflow) {
|
|
23449
|
-
await
|
|
23450
|
-
await
|
|
23775
|
+
await mkdir13(dirname11(path), { recursive: true });
|
|
23776
|
+
await writeFile14(path, generated.workflow);
|
|
23451
23777
|
}
|
|
23452
23778
|
return {
|
|
23453
23779
|
changed: previous !== generated.workflow,
|
|
@@ -23463,16 +23789,16 @@ var writeAbsoluteMobileGithubWorkflow = async (options) => {
|
|
|
23463
23789
|
import { createHash as createHash6, sign, verify } from "crypto";
|
|
23464
23790
|
import {
|
|
23465
23791
|
cp,
|
|
23466
|
-
mkdir as
|
|
23792
|
+
mkdir as mkdir14,
|
|
23467
23793
|
mkdtemp as mkdtemp5,
|
|
23468
23794
|
readdir as readdir6,
|
|
23469
|
-
readFile as
|
|
23795
|
+
readFile as readFile19,
|
|
23470
23796
|
rename as rename11,
|
|
23471
23797
|
rm as rm8,
|
|
23472
|
-
stat as
|
|
23473
|
-
writeFile as
|
|
23798
|
+
stat as stat7,
|
|
23799
|
+
writeFile as writeFile15
|
|
23474
23800
|
} from "fs/promises";
|
|
23475
|
-
import { dirname as
|
|
23801
|
+
import { dirname as dirname12, join as join15, relative as relative12, resolve as resolve13 } from "path";
|
|
23476
23802
|
var UPDATE_MANIFEST_FILE = "update.json";
|
|
23477
23803
|
var UPDATE_FILES_DIRECTORY = "files";
|
|
23478
23804
|
var sha2564 = (value) => createHash6("sha256").update(value).digest("hex");
|
|
@@ -23484,22 +23810,22 @@ var listFiles = async (root, directory = root) => {
|
|
|
23484
23810
|
return listFiles(root, path);
|
|
23485
23811
|
if (!entry.isFile())
|
|
23486
23812
|
throw new TypeError("Mobile updates cannot contain links or special files.");
|
|
23487
|
-
return [
|
|
23813
|
+
return [relative12(root, path).replaceAll("\\", "/")];
|
|
23488
23814
|
}));
|
|
23489
23815
|
return paths.flat().sort((left, right) => left.localeCompare(right));
|
|
23490
23816
|
};
|
|
23491
23817
|
var inspectFiles = async (root, paths) => Promise.all(paths.map(async (path) => {
|
|
23492
23818
|
const source = join15(root, path);
|
|
23493
23819
|
const [metadata, contents] = await Promise.all([
|
|
23494
|
-
|
|
23495
|
-
|
|
23820
|
+
stat7(source),
|
|
23821
|
+
readFile19(source)
|
|
23496
23822
|
]);
|
|
23497
23823
|
return { bytes: metadata.size, path, sha256: sha2564(contents) };
|
|
23498
23824
|
}));
|
|
23499
23825
|
var releaseIdFor = (value) => `amu_${sha2564(canonicalizeAbsoluteMobileUpdate(value))}`;
|
|
23500
23826
|
var buildAbsoluteMobileUpdate = async (options) => {
|
|
23501
|
-
const bundleDirectory =
|
|
23502
|
-
const outputRoot =
|
|
23827
|
+
const bundleDirectory = resolve13(options.bundleDirectory);
|
|
23828
|
+
const outputRoot = resolve13(options.outputDirectory);
|
|
23503
23829
|
if (outputRoot === bundleDirectory || outputRoot.startsWith(`${bundleDirectory}/`))
|
|
23504
23830
|
throw new TypeError("Mobile update output must be outside the embedded bundle.");
|
|
23505
23831
|
const paths = await listFiles(bundleDirectory);
|
|
@@ -23527,7 +23853,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
23527
23853
|
value: signature.toString("base64")
|
|
23528
23854
|
}
|
|
23529
23855
|
});
|
|
23530
|
-
await
|
|
23856
|
+
await mkdir14(outputRoot, { recursive: true });
|
|
23531
23857
|
const outputDirectory = join15(outputRoot, manifest.releaseId);
|
|
23532
23858
|
const staging = await mkdtemp5(join15(outputRoot, ".stage-"));
|
|
23533
23859
|
try {
|
|
@@ -23535,7 +23861,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
23535
23861
|
force: true,
|
|
23536
23862
|
recursive: true
|
|
23537
23863
|
});
|
|
23538
|
-
await
|
|
23864
|
+
await writeFile15(join15(staging, UPDATE_MANIFEST_FILE), `${JSON.stringify(manifest, null, "\t")}
|
|
23539
23865
|
`);
|
|
23540
23866
|
await rename11(staging, outputDirectory);
|
|
23541
23867
|
} catch (error) {
|
|
@@ -23550,7 +23876,7 @@ var buildAbsoluteMobileUpdate = async (options) => {
|
|
|
23550
23876
|
outputDirectory
|
|
23551
23877
|
};
|
|
23552
23878
|
};
|
|
23553
|
-
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await
|
|
23879
|
+
var readAbsoluteMobileUpdate = async (directory) => parseAbsoluteMobileUpdateManifest(JSON.parse(await readFile19(join15(resolve13(directory), UPDATE_MANIFEST_FILE), "utf8")));
|
|
23554
23880
|
var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
23555
23881
|
const manifest = parseAbsoluteMobileUpdateManifest(manifestValue);
|
|
23556
23882
|
const valid = verify("sha256", absoluteMobileUpdateSigningPayload(unsignedAbsoluteMobileUpdate(manifest)), { dsaEncoding: "ieee-p1363", key: publicKey }, Buffer.from(manifest.signature.value, "base64"));
|
|
@@ -23560,8 +23886,8 @@ var verifyAbsoluteMobileUpdateSignature = (manifestValue, publicKey) => {
|
|
|
23560
23886
|
};
|
|
23561
23887
|
|
|
23562
23888
|
// src/mobile/expoUpdate.ts
|
|
23563
|
-
import { access as
|
|
23564
|
-
import { dirname as
|
|
23889
|
+
import { access as access13, mkdir as mkdir15, readFile as readFile20, writeFile as writeFile16 } from "fs/promises";
|
|
23890
|
+
import { dirname as dirname13, extname as extname3, join as join16 } from "path";
|
|
23565
23891
|
var ABSOLUTE_EXPO_UPDATE_DESCRIPTOR = "_absolute/expo-update.json";
|
|
23566
23892
|
var ABSOLUTE_EXPO_UPDATE_FORMAT = 1;
|
|
23567
23893
|
var object2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -23627,7 +23953,7 @@ var parseAbsoluteExpoUpdateDescriptor = (value) => {
|
|
|
23627
23953
|
};
|
|
23628
23954
|
};
|
|
23629
23955
|
var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
23630
|
-
const metadata = JSON.parse(await
|
|
23956
|
+
const metadata = JSON.parse(await readFile20(join16(options.exportDirectory, "metadata.json"), "utf8"));
|
|
23631
23957
|
if (!object2(metadata) || !object2(metadata.fileMetadata))
|
|
23632
23958
|
throw new TypeError("Expo export metadata.json is invalid.");
|
|
23633
23959
|
const descriptor = parseAbsoluteExpoUpdateDescriptor({
|
|
@@ -23638,30 +23964,30 @@ var finalizeAbsoluteExpoUpdateExport = async (options) => {
|
|
|
23638
23964
|
runtimeVersion: options.runtimeVersion
|
|
23639
23965
|
});
|
|
23640
23966
|
const referenced = Object.values(descriptor.platforms).flatMap((entry) => entry ? [entry.launchAsset, ...entry.assets] : []);
|
|
23641
|
-
await Promise.all(referenced.map(({ path }) =>
|
|
23967
|
+
await Promise.all(referenced.map(({ path }) => access13(join16(options.exportDirectory, path)).catch(() => {
|
|
23642
23968
|
throw new TypeError(`Expo export metadata references missing asset ${path}.`);
|
|
23643
23969
|
})));
|
|
23644
23970
|
const destination = join16(options.exportDirectory, ABSOLUTE_EXPO_UPDATE_DESCRIPTOR);
|
|
23645
|
-
await
|
|
23646
|
-
await
|
|
23971
|
+
await mkdir15(dirname13(destination), { recursive: true });
|
|
23972
|
+
await writeFile16(destination, `${JSON.stringify(descriptor, null, "\t")}
|
|
23647
23973
|
`);
|
|
23648
23974
|
return { descriptor, path: destination };
|
|
23649
23975
|
};
|
|
23650
23976
|
|
|
23651
23977
|
// src/mobile/expoCodeSigning.ts
|
|
23652
23978
|
var import_code_signing_certificates = __toESM(require_main(), 1);
|
|
23653
|
-
import { access as
|
|
23654
|
-
import { dirname as
|
|
23655
|
-
var exists6 = async (path) =>
|
|
23979
|
+
import { access as access14, mkdir as mkdir16, writeFile as writeFile17 } from "fs/promises";
|
|
23980
|
+
import { dirname as dirname14, isAbsolute as isAbsolute8, relative as relative13, resolve as resolve14, sep as sep9 } from "path";
|
|
23981
|
+
var exists6 = async (path) => access14(path).then(() => true).catch(() => false);
|
|
23656
23982
|
var inside4 = (root, path) => {
|
|
23657
|
-
const location =
|
|
23658
|
-
return location === "" || location !== ".." && !location.startsWith(`..${
|
|
23983
|
+
const location = relative13(root, path);
|
|
23984
|
+
return location === "" || location !== ".." && !location.startsWith(`..${sep9}`) && !isAbsolute8(location);
|
|
23659
23985
|
};
|
|
23660
23986
|
var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
23661
|
-
const root =
|
|
23662
|
-
const certificatePath =
|
|
23663
|
-
const privateKeyPath =
|
|
23664
|
-
const publicKeyPath =
|
|
23987
|
+
const root = resolve14(options.projectRoot);
|
|
23988
|
+
const certificatePath = resolve14(root, options.certificatePath);
|
|
23989
|
+
const privateKeyPath = resolve14(root, options.privateKeyPath);
|
|
23990
|
+
const publicKeyPath = resolve14(root, options.publicKeyPath ?? resolve14(dirname14(privateKeyPath), "public-key.pem"));
|
|
23665
23991
|
if (!inside4(root, certificatePath))
|
|
23666
23992
|
throw new TypeError("Expo code-signing certificate must be written inside the project so store builds can embed it.");
|
|
23667
23993
|
if (inside4(root, privateKeyPath) || inside4(root, publicKeyPath))
|
|
@@ -23691,11 +24017,11 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
23691
24017
|
import_code_signing_certificates.validateSelfSignedCertificate(certificate, keyPair);
|
|
23692
24018
|
const { privateKeyPEM, publicKeyPEM } = import_code_signing_certificates.convertKeyPairToPEM(keyPair);
|
|
23693
24019
|
const certificatePem = import_code_signing_certificates.convertCertificateToCertificatePEM(certificate);
|
|
23694
|
-
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) =>
|
|
24020
|
+
await Promise.all([certificatePath, privateKeyPath, publicKeyPath].map((path) => mkdir16(dirname14(path), { recursive: true })));
|
|
23695
24021
|
await Promise.all([
|
|
23696
|
-
|
|
23697
|
-
|
|
23698
|
-
|
|
24022
|
+
writeFile17(certificatePath, certificatePem, { flag: "wx", mode: 420 }),
|
|
24023
|
+
writeFile17(privateKeyPath, privateKeyPEM, { flag: "wx", mode: 384 }),
|
|
24024
|
+
writeFile17(publicKeyPath, publicKeyPEM, { flag: "wx", mode: 420 })
|
|
23699
24025
|
]);
|
|
23700
24026
|
return {
|
|
23701
24027
|
certificatePath,
|
|
@@ -23706,8 +24032,8 @@ var generateAbsoluteExpoCodeSigning = async (options) => {
|
|
|
23706
24032
|
};
|
|
23707
24033
|
|
|
23708
24034
|
// src/mobile/updatePublisher.ts
|
|
23709
|
-
import { access as
|
|
23710
|
-
import { isAbsolute as
|
|
24035
|
+
import { access as access15 } from "fs/promises";
|
|
24036
|
+
import { isAbsolute as isAbsolute9, relative as relative14, resolve as resolve15, sep as sep10 } from "path";
|
|
23711
24037
|
import { pathToFileURL as pathToFileURL3 } from "url";
|
|
23712
24038
|
var lifecycleMethod = (publisher, name) => {
|
|
23713
24039
|
const method = publisher[name];
|
|
@@ -23816,16 +24142,16 @@ var pruneAbsoluteMobileUpdates = async (options) => {
|
|
|
23816
24142
|
var object3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23817
24143
|
var isPublisher2 = (value) => object3(value) && typeof value.publishUpdate === "function" && typeof value.promoteUpdate === "function" && typeof value.rollbackUpdate === "function";
|
|
23818
24144
|
var projectPath3 = (projectRoot, requested, label) => {
|
|
23819
|
-
const root =
|
|
23820
|
-
const path =
|
|
23821
|
-
const projectRelative =
|
|
23822
|
-
if (projectRelative === ".." || projectRelative.startsWith(`..${
|
|
24145
|
+
const root = resolve15(projectRoot);
|
|
24146
|
+
const path = resolve15(root, requested);
|
|
24147
|
+
const projectRelative = relative14(root, path);
|
|
24148
|
+
if (projectRelative === ".." || projectRelative.startsWith(`..${sep10}`) || isAbsolute9(projectRelative))
|
|
23823
24149
|
throw new TypeError(`${label} must remain inside the project.`);
|
|
23824
24150
|
return path;
|
|
23825
24151
|
};
|
|
23826
24152
|
var loadAbsoluteMobileUpdatePublisher = async (projectRoot, requestedModulePath) => {
|
|
23827
24153
|
const modulePath = projectPath3(projectRoot, requestedModulePath, "mobile update registry");
|
|
23828
|
-
await
|
|
24154
|
+
await access15(modulePath).catch(() => {
|
|
23829
24155
|
throw new TypeError(`Mobile update registry does not exist: ${modulePath}`);
|
|
23830
24156
|
});
|
|
23831
24157
|
const loaded = await import(pathToFileURL3(modulePath).href);
|
|
@@ -23880,10 +24206,10 @@ var rollbackAbsoluteMobileUpdate = async (options) => {
|
|
|
23880
24206
|
var NOT_FOUND4 = -1;
|
|
23881
24207
|
var ANDROID_EMULATOR_BOOT_POLL_MS = 500;
|
|
23882
24208
|
var ANDROID_EMULATOR_BOOT_TIMEOUT_MS = 180000;
|
|
23883
|
-
var
|
|
24209
|
+
var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23884
24210
|
var expoPublicConfig = (config) => {
|
|
23885
24211
|
const result = { ...config };
|
|
23886
|
-
if (!
|
|
24212
|
+
if (!isRecord9(result.updates))
|
|
23887
24213
|
return result;
|
|
23888
24214
|
const {
|
|
23889
24215
|
codeSigningCertificate: _codeSigningCertificate,
|
|
@@ -23923,13 +24249,13 @@ var CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
|
23923
24249
|
];
|
|
23924
24250
|
var packageNameFromSpec = (spec) => spec.slice(0, spec.lastIndexOf("@"));
|
|
23925
24251
|
var directProjectPackages = async (projectRoot) => {
|
|
23926
|
-
const manifest = JSON.parse(await
|
|
23927
|
-
if (!
|
|
24252
|
+
const manifest = JSON.parse(await readFile21(join17(projectRoot, "package.json"), "utf8"));
|
|
24253
|
+
if (!isRecord9(manifest))
|
|
23928
24254
|
throw new TypeError("Application package.json must contain an object.");
|
|
23929
24255
|
const names = new Set;
|
|
23930
24256
|
for (const field2 of ["dependencies", "devDependencies"]) {
|
|
23931
24257
|
const dependencies = Reflect.get(manifest, field2);
|
|
23932
|
-
if (
|
|
24258
|
+
if (isRecord9(dependencies))
|
|
23933
24259
|
for (const name of Object.keys(dependencies))
|
|
23934
24260
|
names.add(name);
|
|
23935
24261
|
}
|
|
@@ -23937,8 +24263,8 @@ var directProjectPackages = async (projectRoot) => {
|
|
|
23937
24263
|
};
|
|
23938
24264
|
var resolvedPackageVersion = async (projectRoot, packageName) => {
|
|
23939
24265
|
try {
|
|
23940
|
-
const manifest = JSON.parse(await
|
|
23941
|
-
return
|
|
24266
|
+
const manifest = JSON.parse(await readFile21(join17(projectRoot, "node_modules", packageName, "package.json"), "utf8"));
|
|
24267
|
+
return isRecord9(manifest) && typeof manifest.version === "string" ? manifest.version : undefined;
|
|
23942
24268
|
} catch {
|
|
23943
24269
|
return;
|
|
23944
24270
|
}
|
|
@@ -24003,7 +24329,7 @@ var runCapacitorForPlatforms = (projectRoot, command, platforms) => platforms.re
|
|
|
24003
24329
|
var expoExecutable = async (project) => {
|
|
24004
24330
|
const executable = join17(project, "node_modules", ".bin", "expo");
|
|
24005
24331
|
try {
|
|
24006
|
-
await
|
|
24332
|
+
await access16(executable);
|
|
24007
24333
|
return executable;
|
|
24008
24334
|
} catch {
|
|
24009
24335
|
throw new TypeError("Expo dependencies are not installed in the generated shell. Run `absolute mobile init --yes`.");
|
|
@@ -24035,14 +24361,14 @@ var runExpo = async (project, args, options = {}) => {
|
|
|
24035
24361
|
};
|
|
24036
24362
|
var ensureExpoPackages = async (project, args) => {
|
|
24037
24363
|
try {
|
|
24038
|
-
const manifest = JSON.parse(await
|
|
24364
|
+
const manifest = JSON.parse(await readFile21(join17(project, "package.json"), "utf8"));
|
|
24039
24365
|
const dependencies = typeof manifest === "object" && manifest !== null ? Reflect.get(manifest, "dependencies") : undefined;
|
|
24040
24366
|
if (typeof dependencies !== "object" || dependencies === null || Array.isArray(dependencies))
|
|
24041
24367
|
throw new TypeError("Generated Expo dependencies are invalid.");
|
|
24042
24368
|
await Promise.all(Object.entries(dependencies).map(async ([name, expected]) => {
|
|
24043
24369
|
if (typeof expected !== "string")
|
|
24044
24370
|
throw new TypeError("Generated Expo dependency version is invalid.");
|
|
24045
|
-
const installed = JSON.parse(await
|
|
24371
|
+
const installed = JSON.parse(await readFile21(join17(project, "node_modules", name, "package.json"), "utf8"));
|
|
24046
24372
|
const actual = typeof installed === "object" && installed !== null ? Reflect.get(installed, "version") : undefined;
|
|
24047
24373
|
if (typeof actual !== "string")
|
|
24048
24374
|
throw new TypeError("Installed Expo dependency version is invalid.");
|
|
@@ -24240,7 +24566,7 @@ var sync = async (args) => {
|
|
|
24240
24566
|
};
|
|
24241
24567
|
var associations = async (args) => {
|
|
24242
24568
|
const { mobile, projectRoot } = await loadMobile(valueAfter(args, "--config"));
|
|
24243
|
-
const outputDirectory =
|
|
24569
|
+
const outputDirectory = resolve16(projectRoot, valueAfter(args, "--outdir") ?? ".absolutejs/mobile/associations");
|
|
24244
24570
|
if (args.includes("--verify")) {
|
|
24245
24571
|
const result2 = await verifyAbsoluteMobileAssociationFiles(mobile);
|
|
24246
24572
|
console.log(`Verified ${result2.results.length} hosted association files`);
|
|
@@ -24292,7 +24618,7 @@ var generateGithubCi = async (args) => {
|
|
|
24292
24618
|
const publicResult = {
|
|
24293
24619
|
changed: result.changed,
|
|
24294
24620
|
format: result.format,
|
|
24295
|
-
path:
|
|
24621
|
+
path: relative15(projectRoot, result.path).replaceAll("\\", "/"),
|
|
24296
24622
|
platforms: result.platforms,
|
|
24297
24623
|
publishing: result.publishing,
|
|
24298
24624
|
requiredSecrets: result.requiredSecrets
|
|
@@ -24305,8 +24631,91 @@ var generateGithubCi = async (args) => {
|
|
|
24305
24631
|
console.log("Create a protected GitHub environment named absolute-mobile-release, then add these secrets:");
|
|
24306
24632
|
result.requiredSecrets.forEach((name) => console.log(` ${name}`));
|
|
24307
24633
|
console.log("Pull requests run the secret-free release audit. Signed builds and optional publishing run only through manual workflow dispatch.");
|
|
24634
|
+
if (result.publishing)
|
|
24635
|
+
console.log("After later installed-app certification, run `absolute mobile ci promote <platform> --run-id <source-run> --certification <path> ...` to publish the exact retained artifact without rebuilding.");
|
|
24308
24636
|
return publicResult;
|
|
24309
24637
|
};
|
|
24638
|
+
var createGithubCertificationVerification = async (args) => {
|
|
24639
|
+
const bundlePath = valueAfter(args, "--bundle");
|
|
24640
|
+
const outputPath = valueAfter(args, "--out");
|
|
24641
|
+
if (!bundlePath || bundlePath.startsWith("-") || !outputPath || outputPath.startsWith("-"))
|
|
24642
|
+
throw new TypeError("Usage: absolute mobile ci verification --bundle bundle.json --out verification.json");
|
|
24643
|
+
const projectRoot = process.cwd();
|
|
24644
|
+
const bundle = await readAbsoluteSigstoreBundle(projectRoot, bundlePath);
|
|
24645
|
+
const verification = createAbsoluteMobileCertificationVerification(bundle, {
|
|
24646
|
+
GITHUB_REF: process.env.GITHUB_REF,
|
|
24647
|
+
GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY,
|
|
24648
|
+
GITHUB_SHA: process.env.GITHUB_SHA,
|
|
24649
|
+
GITHUB_WORKFLOW_REF: process.env.GITHUB_WORKFLOW_REF
|
|
24650
|
+
});
|
|
24651
|
+
const path = await writeAbsoluteMobileCertificationVerification(projectRoot, outputPath, verification);
|
|
24652
|
+
console.log(relative15(projectRoot, path).replaceAll("\\", "/"));
|
|
24653
|
+
return verification;
|
|
24654
|
+
};
|
|
24655
|
+
var dispatchGithubMobilePromotion = async (args) => {
|
|
24656
|
+
const [platform2] = args;
|
|
24657
|
+
if (platform2 !== "android" && platform2 !== "ios")
|
|
24658
|
+
throw new TypeError("Usage: absolute mobile ci promote <android|ios> --run-id id --certification directory-or-json [--channel name] [store options]");
|
|
24659
|
+
const runId = valueAfter(args, "--run-id");
|
|
24660
|
+
if (!runId || !/^[1-9][0-9]*$/u.test(runId))
|
|
24661
|
+
throw new TypeError("mobile ci promote requires a numeric --run-id.");
|
|
24662
|
+
const requestedCertification = valueAfter(args, "--certification");
|
|
24663
|
+
if (!requestedCertification || requestedCertification.startsWith("-"))
|
|
24664
|
+
throw new TypeError("mobile ci promote requires --certification <directory-or-json>.");
|
|
24665
|
+
const configPath = valueAfter(args, "--config");
|
|
24666
|
+
const { mobile, projectRoot } = await loadMobile(configPath);
|
|
24667
|
+
const { certification } = await readAbsoluteMobileReleaseCertification(projectRoot, requestedCertification);
|
|
24668
|
+
if (certification.release.platform !== platform2 || certification.release.appId !== mobile.appId || certification.release.engine !== mobile.engine)
|
|
24669
|
+
throw new TypeError("The certification does not match this application platform, app ID, or engine.");
|
|
24670
|
+
const channel = valueAfter(args, "--channel") ?? "";
|
|
24671
|
+
const playTrack = valueAfter(args, "--play-track") ?? "registry-only";
|
|
24672
|
+
const testflightGroup = valueAfter(args, "--testflight-group") ?? "";
|
|
24673
|
+
if (channel === "" && (platform2 === "android" ? playTrack === "registry-only" : testflightGroup === ""))
|
|
24674
|
+
throw new TypeError("mobile ci promote requires --channel or a platform store target.");
|
|
24675
|
+
const certificationBase64 = Buffer.from(serializeAbsoluteMobileReleaseCertification(certification)).toString("base64");
|
|
24676
|
+
if (certificationBase64.length > 48000)
|
|
24677
|
+
throw new TypeError("Mobile certification is too large for protected GitHub dispatch.");
|
|
24678
|
+
const workflow = valueAfter(args, "--workflow") ?? ".github/workflows/absolute-mobile.yml";
|
|
24679
|
+
const command = [
|
|
24680
|
+
"workflow",
|
|
24681
|
+
"run",
|
|
24682
|
+
workflow,
|
|
24683
|
+
"-f",
|
|
24684
|
+
"operation=promote",
|
|
24685
|
+
"-f",
|
|
24686
|
+
`platform=${platform2}`,
|
|
24687
|
+
"-f",
|
|
24688
|
+
`source_run_id=${runId}`,
|
|
24689
|
+
"-f",
|
|
24690
|
+
`certification_base64=${certificationBase64}`,
|
|
24691
|
+
"-f",
|
|
24692
|
+
`channel=${channel}`
|
|
24693
|
+
];
|
|
24694
|
+
if (platform2 === "android")
|
|
24695
|
+
command.push("-f", `play_track=${playTrack}`);
|
|
24696
|
+
else {
|
|
24697
|
+
command.push("-f", `testflight_group=${testflightGroup}`);
|
|
24698
|
+
command.push("-f", `submit_testflight_review=${args.includes("--testflight-submit-review")}`);
|
|
24699
|
+
}
|
|
24700
|
+
const ref = valueAfter(args, "--ref");
|
|
24701
|
+
if (ref)
|
|
24702
|
+
command.push("--ref", ref);
|
|
24703
|
+
const child = Bun.spawn(["gh", ...command], {
|
|
24704
|
+
cwd: projectRoot,
|
|
24705
|
+
stderr: "inherit",
|
|
24706
|
+
stdin: "inherit",
|
|
24707
|
+
stdout: "inherit"
|
|
24708
|
+
});
|
|
24709
|
+
const exitCode = await child.exited;
|
|
24710
|
+
if (exitCode !== 0)
|
|
24711
|
+
throw new TypeError(`GitHub promotion dispatch exited with status ${exitCode}.`);
|
|
24712
|
+
sendTelemetryEvent("mobile:ci-promotion-dispatched", {
|
|
24713
|
+
platform: platform2,
|
|
24714
|
+
provider: "github-actions",
|
|
24715
|
+
storeTarget: platform2 === "android" ? playTrack !== "registry-only" : testflightGroup !== ""
|
|
24716
|
+
});
|
|
24717
|
+
console.log(`Dispatched protected ${platform2} promotion for ${certification.release.releaseId} from workflow run ${runId}.`);
|
|
24718
|
+
};
|
|
24310
24719
|
var doctorMark = (status) => {
|
|
24311
24720
|
if (status === "pass")
|
|
24312
24721
|
return "\x1B[32m\u2713\x1B[0m";
|
|
@@ -24404,7 +24813,7 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
24404
24813
|
};
|
|
24405
24814
|
await ensureExpoPackages(mobile.nativeProjectDirectory, options.args);
|
|
24406
24815
|
const temporaryParent = join17(options.projectRoot, ".absolutejs", "mobile", "expo-update-exports");
|
|
24407
|
-
await
|
|
24816
|
+
await mkdir17(temporaryParent, { recursive: true });
|
|
24408
24817
|
const temporaryDirectory = await mkdtemp6(join17(temporaryParent, ".stage-"));
|
|
24409
24818
|
try {
|
|
24410
24819
|
await runExpo(mobile.nativeProjectDirectory, [
|
|
@@ -24417,9 +24826,9 @@ var prepareExpoMobileUpdateExport = async (options) => {
|
|
|
24417
24826
|
temporaryDirectory,
|
|
24418
24827
|
"--clear"
|
|
24419
24828
|
], { production: true });
|
|
24420
|
-
const generatedApp = JSON.parse(await
|
|
24421
|
-
const expoConfig =
|
|
24422
|
-
if (!
|
|
24829
|
+
const generatedApp = JSON.parse(await readFile21(join17(mobile.nativeProjectDirectory, "app.json"), "utf8"));
|
|
24830
|
+
const expoConfig = isRecord9(generatedApp) ? generatedApp.expo : undefined;
|
|
24831
|
+
if (!isRecord9(expoConfig))
|
|
24423
24832
|
throw new TypeError("Generated Expo app configuration is invalid.");
|
|
24424
24833
|
const publicExpoConfig = expoPublicConfig(expoConfig);
|
|
24425
24834
|
const computedRuntime = resolveAbsoluteMobileUpdateRuntime(mobile, options.projectRoot).fingerprint;
|
|
@@ -24460,11 +24869,11 @@ var buildMobileUpdate = async (args) => {
|
|
|
24460
24869
|
let expoExportDirectory;
|
|
24461
24870
|
try {
|
|
24462
24871
|
await start(mobileBuildServerEntry(args), valueAfter(args, "--web-outdir"), configPath, { prepareOnly: true });
|
|
24463
|
-
const embedded = JSON.parse(await
|
|
24464
|
-
const runtimeFingerprint =
|
|
24872
|
+
const embedded = JSON.parse(await readFile21(join17(mobile.bundleDirectory, "absolute-mobile-manifest.json"), "utf8"));
|
|
24873
|
+
const runtimeFingerprint = isRecord9(embedded) ? embedded.nativeRuntime : undefined;
|
|
24465
24874
|
if (typeof runtimeFingerprint !== "string" || !/^[a-f0-9]{64}$/u.test(runtimeFingerprint))
|
|
24466
24875
|
throw new TypeError("Prepared mobile bundle is missing its native runtime fingerprint.");
|
|
24467
|
-
const privateKey = await
|
|
24876
|
+
const privateKey = await readFile21(resolve16(projectRoot, signingKeyPath));
|
|
24468
24877
|
const configuredPublicKey = Buffer.from(mobile.updates.publicKeys[keyId], "base64");
|
|
24469
24878
|
const derivedPublicKey = createPublicKey2(privateKey).export({
|
|
24470
24879
|
format: "der",
|
|
@@ -24527,7 +24936,7 @@ var generateExpoUpdateSigning = async (args) => {
|
|
|
24527
24936
|
...valueAfter(args, "--public-key") ? { publicKeyPath: valueAfter(args, "--public-key") } : {},
|
|
24528
24937
|
...validityYears === undefined ? {} : { validityYears }
|
|
24529
24938
|
});
|
|
24530
|
-
const certificatePath =
|
|
24939
|
+
const certificatePath = relative15(projectRoot, result.certificatePath).replaceAll("\\", "/");
|
|
24531
24940
|
sendTelemetryEvent("mobile:update-code-signing-generated", {
|
|
24532
24941
|
engine: "expo",
|
|
24533
24942
|
validityYears: validityYears ?? 10
|
|
@@ -24589,7 +24998,7 @@ var provisionMobileUpdate = async (args) => {
|
|
|
24589
24998
|
publicKeys: mobile.updates.publicKeys,
|
|
24590
24999
|
storage: requestedStorage
|
|
24591
25000
|
});
|
|
24592
|
-
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${
|
|
25001
|
+
console.log(`Provisioned ${requestedStorage === "local" ? "local development" : "durable S3-compatible"} mobile update storage in ${relative15(projectRoot, path).replaceAll("\\", "/")}.`);
|
|
24593
25002
|
if (requestedStorage === "local")
|
|
24594
25003
|
console.log("Production release checks will require durable storage. Re-run with --storage s3 --force before deploying.");
|
|
24595
25004
|
else
|
|
@@ -25116,6 +25525,16 @@ var loadPublicationCertification = async (options) => {
|
|
|
25116
25525
|
const loaded = await readAbsoluteMobileReleaseCertification(options.projectRoot, requested);
|
|
25117
25526
|
return verifyAbsoluteMobileReleaseCertification(loaded.certification, options.release, options.requirement);
|
|
25118
25527
|
};
|
|
25528
|
+
var loadPublicationCertificationVerification = async (options) => {
|
|
25529
|
+
const requested = valueAfter(options.args, "--certification-attestation");
|
|
25530
|
+
if (options.args.includes("--certification-attestation") && (!requested || requested.startsWith("-")))
|
|
25531
|
+
throw new TypeError("mobile publish requires --certification-attestation <verification-json>.");
|
|
25532
|
+
if (!requested)
|
|
25533
|
+
return;
|
|
25534
|
+
if (!options.certificationLoaded)
|
|
25535
|
+
throw new TypeError("mobile publish --certification-attestation requires --certification.");
|
|
25536
|
+
return readAbsoluteMobileCertificationVerification(options.projectRoot, requested);
|
|
25537
|
+
};
|
|
25119
25538
|
var publishAndroid = async (args) => {
|
|
25120
25539
|
const registryModule = args.includes("--registry") ? requireValueAfter(args, "--registry") : "mobile.release.ts";
|
|
25121
25540
|
const channel = args.includes("--channel") ? requireValueAfter(args, "--channel") : undefined;
|
|
@@ -25146,11 +25565,17 @@ var publishAndroid = async (args) => {
|
|
|
25146
25565
|
release,
|
|
25147
25566
|
...certificationRequirement ? { requirement: certificationRequirement } : {}
|
|
25148
25567
|
});
|
|
25568
|
+
const certificationVerification = await loadPublicationCertificationVerification({
|
|
25569
|
+
args,
|
|
25570
|
+
certificationLoaded: certification !== undefined,
|
|
25571
|
+
projectRoot
|
|
25572
|
+
});
|
|
25149
25573
|
const publication = await publishAbsoluteAndroidRelease({
|
|
25150
25574
|
allowUnsigned: args.includes("--unsigned"),
|
|
25151
25575
|
channel,
|
|
25152
25576
|
...certification ? { certification } : {},
|
|
25153
25577
|
...certificationRequirement ? { certificationRequirement } : {},
|
|
25578
|
+
...certificationVerification ? { certificationVerification } : {},
|
|
25154
25579
|
googlePlay,
|
|
25155
25580
|
modulePath: registryModule,
|
|
25156
25581
|
projectRoot,
|
|
@@ -25159,6 +25584,8 @@ var publishAndroid = async (args) => {
|
|
|
25159
25584
|
const { reused: publicationReused } = publication;
|
|
25160
25585
|
reused = publicationReused;
|
|
25161
25586
|
success = true;
|
|
25587
|
+
if (args.includes("--json"))
|
|
25588
|
+
return console.log(JSON.stringify(publication, null, 2)), publication;
|
|
25162
25589
|
console.log(`${publication.reused ? "Reused" : "Published"} Android release ${release.metadata.releaseId}${publication.channel ? ` on ${publication.channel.channel}` : ""}.`);
|
|
25163
25590
|
if (publication.googlePlay)
|
|
25164
25591
|
printGooglePlayPublication(publication.googlePlay);
|
|
@@ -25335,12 +25762,18 @@ var publishIos = async (args) => {
|
|
|
25335
25762
|
release,
|
|
25336
25763
|
...certificationRequirement ? { requirement: certificationRequirement } : {}
|
|
25337
25764
|
});
|
|
25765
|
+
const certificationVerification = await loadPublicationCertificationVerification({
|
|
25766
|
+
args,
|
|
25767
|
+
certificationLoaded: certification !== undefined,
|
|
25768
|
+
projectRoot
|
|
25769
|
+
});
|
|
25338
25770
|
const publication = await publishAbsoluteIosRelease({
|
|
25339
25771
|
allowUnsigned: args.includes("--unsigned"),
|
|
25340
25772
|
appStoreConnect,
|
|
25341
25773
|
channel,
|
|
25342
25774
|
...certification ? { certification } : {},
|
|
25343
25775
|
...certificationRequirement ? { certificationRequirement } : {},
|
|
25776
|
+
...certificationVerification ? { certificationVerification } : {},
|
|
25344
25777
|
modulePath: registryModule,
|
|
25345
25778
|
projectRoot,
|
|
25346
25779
|
release
|
|
@@ -25351,6 +25784,8 @@ var publishIos = async (args) => {
|
|
|
25351
25784
|
} = publication;
|
|
25352
25785
|
reused = publicationReused;
|
|
25353
25786
|
success = true;
|
|
25787
|
+
if (args.includes("--json"))
|
|
25788
|
+
return console.log(JSON.stringify(publication, null, 2)), publication;
|
|
25354
25789
|
console.log(`${publication.reused ? "Reused" : "Published"} iOS release ${release.metadata.releaseId}${publication.channel ? ` on ${publication.channel.channel}` : ""}.`);
|
|
25355
25790
|
if (appStoreConnectPublication)
|
|
25356
25791
|
printAppStoreConnectPublication(appStoreConnectPublication);
|
|
@@ -25510,7 +25945,7 @@ var requireAndroidTestPort = (args, projectRoot) => {
|
|
|
25510
25945
|
}
|
|
25511
25946
|
return { https: args.includes("--https"), port };
|
|
25512
25947
|
}
|
|
25513
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
25948
|
+
const instances = listLiveInstances().filter((instance2) => resolve16(instance2.cwd) === resolve16(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
25514
25949
|
if (instances.length !== 1) {
|
|
25515
25950
|
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>.");
|
|
25516
25951
|
}
|
|
@@ -25604,8 +26039,8 @@ var selectOrStartAndroidReleaseEmulator = async (adb2, emulator, explicitSerial,
|
|
|
25604
26039
|
return waitForBoot();
|
|
25605
26040
|
};
|
|
25606
26041
|
var safeArtifactRoot = (projectRoot, value) => {
|
|
25607
|
-
const root =
|
|
25608
|
-
if (root !== projectRoot && !root.startsWith(`${
|
|
26042
|
+
const root = resolve16(projectRoot, value ?? ".absolutejs/mobile/test-artifacts");
|
|
26043
|
+
if (root !== projectRoot && !root.startsWith(`${resolve16(projectRoot)}/`)) {
|
|
25609
26044
|
throw new TypeError("mobile test --artifacts must remain inside the project.");
|
|
25610
26045
|
}
|
|
25611
26046
|
return root;
|
|
@@ -25633,12 +26068,12 @@ var waitForRequestedAndroidHmr = async (args, session, checks, timeoutMs) => {
|
|
|
25633
26068
|
});
|
|
25634
26069
|
};
|
|
25635
26070
|
var writeAndroidFailureArtifacts = async (options) => {
|
|
25636
|
-
await
|
|
26071
|
+
await mkdir17(options.artifactRoot, { recursive: true });
|
|
25637
26072
|
const screenshot = options.session ? await options.session.screenshot(join17(options.artifactRoot, "android-failure.png")).catch(() => {
|
|
25638
26073
|
return;
|
|
25639
26074
|
}) : undefined;
|
|
25640
26075
|
const diagnosticPath = join17(options.artifactRoot, "android-failure.json");
|
|
25641
|
-
await
|
|
26076
|
+
await writeFile18(diagnosticPath, `${JSON.stringify({
|
|
25642
26077
|
diagnostics: options.session?.diagnostics ?? [],
|
|
25643
26078
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
25644
26079
|
platform: "android",
|
|
@@ -25659,7 +26094,7 @@ var captureAndroidReleaseScreenshot = async (adb2, serial, destination) => {
|
|
|
25659
26094
|
]);
|
|
25660
26095
|
if (exitCode !== 0)
|
|
25661
26096
|
throw new Error(`Android release screenshot failed: ${stderr.trim() || `status ${exitCode}`}`);
|
|
25662
|
-
await
|
|
26097
|
+
await writeFile18(destination, bytes);
|
|
25663
26098
|
return destination;
|
|
25664
26099
|
};
|
|
25665
26100
|
var printAndroidReleaseAcceptance = (result, json) => {
|
|
@@ -25760,9 +26195,9 @@ var testAndroidRelease = async (args, mobile, projectRoot) => {
|
|
|
25760
26195
|
platform: "android",
|
|
25761
26196
|
success: false
|
|
25762
26197
|
});
|
|
25763
|
-
await
|
|
26198
|
+
await mkdir17(artifactRoot, { recursive: true });
|
|
25764
26199
|
const diagnosticPath = join17(artifactRoot, "android-release-failure.json");
|
|
25765
|
-
await
|
|
26200
|
+
await writeFile18(diagnosticPath, `${JSON.stringify({
|
|
25766
26201
|
error: sanitizeNativeReportText(error instanceof Error ? error.message : String(error)),
|
|
25767
26202
|
platform: "android",
|
|
25768
26203
|
provider: mobile.engine,
|
|
@@ -25925,14 +26360,14 @@ var requireIosTestContext = (args, projectRoot) => {
|
|
|
25925
26360
|
const port = Number(explicit);
|
|
25926
26361
|
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
25927
26362
|
throw new TypeError("mobile test --port must be a valid TCP port.");
|
|
25928
|
-
const instance2 = listLiveInstances().find((candidate) =>
|
|
26363
|
+
const instance2 = listLiveInstances().find((candidate) => resolve16(candidate.cwd) === resolve16(projectRoot) && candidate.source === "dev" && candidate.port === port);
|
|
25929
26364
|
return {
|
|
25930
26365
|
https: instance2?.https ?? args.includes("--https"),
|
|
25931
26366
|
instance: instance2,
|
|
25932
26367
|
port
|
|
25933
26368
|
};
|
|
25934
26369
|
}
|
|
25935
|
-
const instances = listLiveInstances().filter((instance2) =>
|
|
26370
|
+
const instances = listLiveInstances().filter((instance2) => resolve16(instance2.cwd) === resolve16(projectRoot) && instance2.source === "dev" && instance2.port !== null);
|
|
25936
26371
|
if (instances.length !== 1)
|
|
25937
26372
|
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>.");
|
|
25938
26373
|
const [instance] = instances;
|
|
@@ -25950,7 +26385,7 @@ var waitForIosHmrClient = async (options) => {
|
|
|
25950
26385
|
return;
|
|
25951
26386
|
});
|
|
25952
26387
|
const status = response?.ok ? await response.json().catch(() => null) : null;
|
|
25953
|
-
const targets =
|
|
26388
|
+
const targets = isRecord9(status) && isRecord9(status.connectedTargets) ? status.connectedTargets : undefined;
|
|
25954
26389
|
if (targets && typeof targets["capacitor-ios"] === "number" && targets["capacitor-ios"] > 0)
|
|
25955
26390
|
return;
|
|
25956
26391
|
await Bun.sleep(100);
|
|
@@ -26076,7 +26511,7 @@ var requireCapturedCommand = (command, label) => {
|
|
|
26076
26511
|
return result;
|
|
26077
26512
|
};
|
|
26078
26513
|
var writeIosFailureArtifacts = async (options) => {
|
|
26079
|
-
await
|
|
26514
|
+
await mkdir17(options.artifactRoot, { recursive: true });
|
|
26080
26515
|
const screenshot = join17(options.artifactRoot, "ios-failure.png");
|
|
26081
26516
|
const screenshotResult = captureCommand4([
|
|
26082
26517
|
options.xcrun,
|
|
@@ -26087,7 +26522,7 @@ var writeIosFailureArtifacts = async (options) => {
|
|
|
26087
26522
|
screenshot
|
|
26088
26523
|
]);
|
|
26089
26524
|
const diagnosticPath = join17(options.artifactRoot, "ios-failure.json");
|
|
26090
|
-
await
|
|
26525
|
+
await writeFile18(diagnosticPath, `${JSON.stringify({
|
|
26091
26526
|
appId: options.appId,
|
|
26092
26527
|
error: options.error instanceof Error ? options.error.message : String(options.error),
|
|
26093
26528
|
platform: "ios",
|
|
@@ -26114,8 +26549,8 @@ var nativeReportRoot = (args, projectRoot, platform2) => {
|
|
|
26114
26549
|
var absolutejsVersionForReport = async () => {
|
|
26115
26550
|
let absolutejsVersion = process.env.ABSOLUTE_VERSION ?? "unknown";
|
|
26116
26551
|
const versions = await Promise.all([
|
|
26117
|
-
|
|
26118
|
-
|
|
26552
|
+
resolve16(import.meta.dir, "..", "..", "package.json"),
|
|
26553
|
+
resolve16(import.meta.dir, "..", "..", "..", "package.json")
|
|
26119
26554
|
].map((candidate) => readPackageVersionForIosReport(candidate).catch(() => "unknown")));
|
|
26120
26555
|
for (const version of versions) {
|
|
26121
26556
|
if (version === "unknown")
|
|
@@ -26492,9 +26927,9 @@ var testIosRelease = async (args, mobile, projectRoot) => {
|
|
|
26492
26927
|
remote: remoteProfile !== undefined,
|
|
26493
26928
|
success: false
|
|
26494
26929
|
});
|
|
26495
|
-
await
|
|
26930
|
+
await mkdir17(artifactRoot, { recursive: true });
|
|
26496
26931
|
const diagnosticPath = join17(artifactRoot, "ios-release-failure.json");
|
|
26497
|
-
await
|
|
26932
|
+
await writeFile18(diagnosticPath, `${JSON.stringify({
|
|
26498
26933
|
error: sanitizeIosReportText(error instanceof Error ? error.message : String(error)),
|
|
26499
26934
|
platform: "ios",
|
|
26500
26935
|
provider: mobile.engine,
|
|
@@ -26587,7 +27022,7 @@ var testIos = async (args) => {
|
|
|
26587
27022
|
mobile.appId
|
|
26588
27023
|
], "iOS app launch");
|
|
26589
27024
|
await waitForIosHmrClient({ https, port, timeoutMs });
|
|
26590
|
-
await
|
|
27025
|
+
await mkdir17(artifactRoot, { recursive: true });
|
|
26591
27026
|
const screenshot = join17(artifactRoot, "ios-simulator.png");
|
|
26592
27027
|
requireCapturedCommand([xcrun, "simctl", "io", simulator.udid, "screenshot", screenshot], "iOS simulator screenshot");
|
|
26593
27028
|
const hmrApply = await waitForRequestedIosHmr(args, instance, timeoutMs);
|
|
@@ -26803,7 +27238,12 @@ var runMobile = async (args) => {
|
|
|
26803
27238
|
return;
|
|
26804
27239
|
}
|
|
26805
27240
|
if (command === "ci") {
|
|
26806
|
-
|
|
27241
|
+
if (args[1] === "promote")
|
|
27242
|
+
await dispatchGithubMobilePromotion(args.slice(2));
|
|
27243
|
+
else if (args[1] === "verification")
|
|
27244
|
+
await createGithubCertificationVerification(args.slice(2));
|
|
27245
|
+
else
|
|
27246
|
+
await generateGithubCi(args.slice(1));
|
|
26807
27247
|
return;
|
|
26808
27248
|
}
|
|
26809
27249
|
if (command === "doctor") {
|
|
@@ -26882,7 +27322,7 @@ var runMobile = async (args) => {
|
|
|
26882
27322
|
await publishIos(args.slice(2));
|
|
26883
27323
|
return;
|
|
26884
27324
|
}
|
|
26885
|
-
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] | 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] [--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] [--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]");
|
|
27325
|
+
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] | 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]");
|
|
26886
27326
|
};
|
|
26887
27327
|
export {
|
|
26888
27328
|
runMobile
|