@absolutejs/absolute 0.20.0-beta.111 → 0.20.0-beta.112
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 +21 -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-k7gpwfs8.js → mobile-d5wbqynh.js} +182 -17
- package/dist/mobile/index.js +94 -7
- package/dist/mobile/index.js.map +4 -4
- package/dist/src/mobile/ciPromotionAudit.d.ts +26 -2
- package/dist/src/mobile/ciWorkflow.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ This file is generated by `absolute-changelog` from the entries in
|
|
|
6
6
|
`changelog/`. Edit an entry, not this file — and add new ones under
|
|
7
7
|
`changelog/unreleased/`.
|
|
8
8
|
|
|
9
|
+
## 0.20.0-beta.112 — 2026-09-17
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Mobile CI promotions correlate, watch, and audit the exact dispatched workflow run** (`ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT`, `ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT`, `AbsoluteMobileCiRunStatus`, `AbsoluteMobilePromotionAudit`, `AbsoluteMobilePromotionContext`, `AbsoluteMobilePromotionRun`, `discoverAbsoluteMobilePromotionRun`, `parseAbsoluteMobilePromotionRunList`)
|
|
14
|
+
The promotion CLI assigns each protected dispatch a unique non-secret identity, discovers only the GitHub Actions run carrying that identity, returns its run ID and URL, optionally waits for completion, and can chain an independent local audit without racing concurrent promotions; retained format-1 audit context remains readable.
|
|
15
|
+
|
|
9
16
|
## 0.20.0-beta.111 — 2026-09-17
|
|
10
17
|
|
|
11
18
|
### Added
|
package/changelog.json
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
"contract": 1,
|
|
3
3
|
"name": "@absolutejs/absolute",
|
|
4
4
|
"releases": [
|
|
5
|
+
{
|
|
6
|
+
"changes": [
|
|
7
|
+
{
|
|
8
|
+
"detail": "The promotion CLI assigns each protected dispatch a unique non-secret identity, discovers only the GitHub Actions run carrying that identity, returns its run ID and URL, optionally waits for completion, and can chain an independent local audit without racing concurrent promotions; retained format-1 audit context remains readable.",
|
|
9
|
+
"kind": "added",
|
|
10
|
+
"summary": "Mobile CI promotions correlate, watch, and audit the exact dispatched workflow run",
|
|
11
|
+
"symbols": [
|
|
12
|
+
"ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT",
|
|
13
|
+
"ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT",
|
|
14
|
+
"AbsoluteMobileCiRunStatus",
|
|
15
|
+
"AbsoluteMobilePromotionAudit",
|
|
16
|
+
"AbsoluteMobilePromotionContext",
|
|
17
|
+
"AbsoluteMobilePromotionRun",
|
|
18
|
+
"discoverAbsoluteMobilePromotionRun",
|
|
19
|
+
"parseAbsoluteMobilePromotionRunList"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
],
|
|
23
|
+
"date": "2026-09-17",
|
|
24
|
+
"version": "0.20.0-beta.112"
|
|
25
|
+
},
|
|
5
26
|
{
|
|
6
27
|
"changes": [
|
|
7
28
|
{
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
-
// .angular-partial-tmp-
|
|
4
|
+
// .angular-partial-tmp-Pjgrp8/src/core/streamingSlotRegistrar.ts
|
|
5
5
|
var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
|
|
6
6
|
var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
|
|
7
7
|
var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
var __require = import.meta.require;
|
|
3
3
|
|
|
4
|
-
// .angular-partial-tmp-
|
|
4
|
+
// .angular-partial-tmp-Pjgrp8/src/core/streamingSlotRegistrar.ts
|
|
5
5
|
var STREAMING_SLOT_REGISTRAR_KEY = Symbol.for("absolutejs.streamingSlotRegistrar");
|
|
6
6
|
var STREAMING_SLOT_WARNING_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotWarningController");
|
|
7
7
|
var STREAMING_SLOT_COLLECTION_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotCollectionController");
|
|
@@ -48,7 +48,7 @@ var warnMissingStreamingSlotCollector = (primitiveName) => {
|
|
|
48
48
|
getWarningController()?.maybeWarn(primitiveName);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
// .angular-partial-tmp-
|
|
51
|
+
// .angular-partial-tmp-Pjgrp8/src/core/streamingSlotRegistry.ts
|
|
52
52
|
var STREAMING_SLOT_STORAGE_KEY = Symbol.for("absolutejs.streamingSlotAsyncLocalStorage");
|
|
53
53
|
var isObjectRecord2 = (value) => Boolean(value) && typeof value === "object";
|
|
54
54
|
var isAsyncLocalStorage = (value) => isObjectRecord2(value) && ("getStore" in value) && typeof value.getStore === "function" && ("run" in value) && typeof value.run === "function";
|
package/dist/cli/index.js
CHANGED
|
@@ -203,7 +203,7 @@ if (command === "dev") {
|
|
|
203
203
|
sendTelemetryEvent("cli:command", {
|
|
204
204
|
command: `mobile:${workspaceCommand ?? "unknown"}`
|
|
205
205
|
});
|
|
206
|
-
const { runMobile } = await import("./mobile-
|
|
206
|
+
const { runMobile } = await import("./mobile-d5wbqynh.js");
|
|
207
207
|
try {
|
|
208
208
|
await runMobile(args);
|
|
209
209
|
} catch (error) {
|
|
@@ -16786,7 +16786,7 @@ import {
|
|
|
16786
16786
|
rm as rm10,
|
|
16787
16787
|
writeFile as writeFile19
|
|
16788
16788
|
} from "fs/promises";
|
|
16789
|
-
import { createPublicKey as createPublicKey2 } from "crypto";
|
|
16789
|
+
import { createPublicKey as createPublicKey2, randomUUID } from "crypto";
|
|
16790
16790
|
import { join as join18, relative as relative16, resolve as resolve17 } from "path";
|
|
16791
16791
|
import { createInterface } from "readline/promises";
|
|
16792
16792
|
|
|
@@ -22982,7 +22982,7 @@ var renderAbsoluteMobileProjectInspection = (report) => {
|
|
|
22982
22982
|
import { existsSync } from "fs";
|
|
22983
22983
|
import { access as access12, mkdir as mkdir13, readFile as readFile18, writeFile as writeFile14 } from "fs/promises";
|
|
22984
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 =
|
|
22985
|
+
var ABSOLUTE_MOBILE_CI_WORKFLOW_FORMAT = 4;
|
|
22986
22986
|
var SECRET_NAME_PATTERN = /^[A-Z][A-Z0-9_]*$/u;
|
|
22987
22987
|
var CI_ENV_INDENTATION = 6;
|
|
22988
22988
|
var RESERVED_SECRET_NAMES = new Set([
|
|
@@ -23141,6 +23141,10 @@ var publishingInputs = (platforms, includePublishing) => {
|
|
|
23141
23141
|
description: Source workflow run ID for promotion without rebuilding
|
|
23142
23142
|
required: false
|
|
23143
23143
|
type: string`,
|
|
23144
|
+
` dispatch_id:
|
|
23145
|
+
description: Unique promotion correlation ID supplied by the AbsoluteJS CLI
|
|
23146
|
+
required: false
|
|
23147
|
+
type: string`,
|
|
23144
23148
|
` certification_base64:
|
|
23145
23149
|
description: AbsoluteJS certification supplied by the promotion CLI
|
|
23146
23150
|
required: false
|
|
@@ -23513,6 +23517,10 @@ var promotionValidationSteps = ` - name: Validate promotion handoff
|
|
|
23513
23517
|
echo "Promotion requires a numeric source workflow run ID." >&2
|
|
23514
23518
|
exit 1
|
|
23515
23519
|
fi
|
|
23520
|
+
if [[ ! "$ABSOLUTE_PROMOTION_DISPATCH_ID" =~ ^amp_[a-f0-9]{32}$ ]]; then
|
|
23521
|
+
echo "Promotion requires a valid AbsoluteJS dispatch correlation ID." >&2
|
|
23522
|
+
exit 1
|
|
23523
|
+
fi
|
|
23516
23524
|
if [[ -z "$ABSOLUTE_CERTIFICATION_BASE64" ]]; then
|
|
23517
23525
|
echo "Promotion requires certification supplied by absolute mobile ci promote." >&2
|
|
23518
23526
|
exit 1
|
|
@@ -23527,7 +23535,7 @@ var promotionSharedSteps = (platform) => ` - name: Restore exact ${platform
|
|
|
23527
23535
|
- name: Record bounded promotion context
|
|
23528
23536
|
shell: bash
|
|
23529
23537
|
run: |
|
|
23530
|
-
bun -e 'const sourceRunId = process.env.ABSOLUTE_SOURCE_RUN_ID; const promotionRunId = process.env.GITHUB_RUN_ID; if (!sourceRunId?.match(/^[1-9][0-9]*$/) || !promotionRunId?.match(/^[1-9][0-9]*$/)) throw new Error("Invalid promotion run identity"); await Bun.write(".absolutejs/mobile-ci/promotion-context.json", JSON.stringify({ format:
|
|
23538
|
+
bun -e 'const dispatchId = process.env.ABSOLUTE_PROMOTION_DISPATCH_ID; const sourceRunId = process.env.ABSOLUTE_SOURCE_RUN_ID; const promotionRunId = process.env.GITHUB_RUN_ID; if (!dispatchId?.match(/^amp_[a-f0-9]{32}$/) || !sourceRunId?.match(/^[1-9][0-9]*$/) || !promotionRunId?.match(/^[1-9][0-9]*$/)) throw new Error("Invalid promotion run identity"); await Bun.write(".absolutejs/mobile-ci/promotion-context.json", JSON.stringify({ dispatchId, format: 2, platform: ${JSON.stringify(platform)}, promotionRunId, sourceArtifact: ${JSON.stringify(`absolute-mobile-${platform}`)}, sourceRunId }, null, 2) + "\\n");'
|
|
23531
23539
|
- name: Import and verify exact certification
|
|
23532
23540
|
shell: bash
|
|
23533
23541
|
run: |
|
|
@@ -23565,6 +23573,7 @@ var androidPromotionJob = (options) => {
|
|
|
23565
23573
|
id-token: write
|
|
23566
23574
|
env:
|
|
23567
23575
|
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
|
|
23576
|
+
ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
|
|
23568
23577
|
ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
|
|
23569
23578
|
ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
|
|
23570
23579
|
ABSOLUTE_PLAY_TRACK: \${{ inputs.play_track }}
|
|
@@ -23630,6 +23639,7 @@ var iosPromotionJob = (options) => {
|
|
|
23630
23639
|
id-token: write
|
|
23631
23640
|
env:
|
|
23632
23641
|
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}
|
|
23642
|
+
ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
|
|
23633
23643
|
ABSOLUTE_RELEASE_CHANNEL: \${{ inputs.channel }}
|
|
23634
23644
|
ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
|
|
23635
23645
|
ABSOLUTE_TESTFLIGHT_GROUP: \${{ inputs.testflight_group }}
|
|
@@ -23720,6 +23730,7 @@ var createAbsoluteMobileGithubWorkflow = (options) => {
|
|
|
23720
23730
|
});
|
|
23721
23731
|
let workflow = `# Generated by AbsoluteJS. Regenerate with: absolute mobile ci github${includePublishing ? " --publish" : ""}
|
|
23722
23732
|
name: AbsoluteJS Mobile
|
|
23733
|
+
run-name: \${{ inputs.operation == 'promote' && format('AbsoluteJS mobile promotion [{0}]', inputs.dispatch_id) || format('AbsoluteJS mobile {0}', github.event_name) }}
|
|
23723
23734
|
|
|
23724
23735
|
on:
|
|
23725
23736
|
pull_request:
|
|
@@ -23745,6 +23756,7 @@ jobs:
|
|
|
23745
23756
|
env:
|
|
23746
23757
|
${environment}${includePublishing ? `
|
|
23747
23758
|
ABSOLUTE_PROMOTION_PLATFORM: \${{ inputs.platform }}
|
|
23759
|
+
ABSOLUTE_PROMOTION_DISPATCH_ID: \${{ inputs.dispatch_id }}
|
|
23748
23760
|
ABSOLUTE_SOURCE_RUN_ID: \${{ inputs.source_run_id }}
|
|
23749
23761
|
ABSOLUTE_CERTIFICATION_BASE64: \${{ inputs.certification_base64 }}` : ""}
|
|
23750
23762
|
steps:
|
|
@@ -23807,12 +23819,15 @@ import {
|
|
|
23807
23819
|
} from "fs/promises";
|
|
23808
23820
|
import { dirname as dirname12, isAbsolute as isAbsolute8, join as join15, relative as relative12, resolve as resolve13, sep as sep9 } from "path";
|
|
23809
23821
|
var ABSOLUTE_MOBILE_CI_PROMOTION_AUDIT_FORMAT = 1;
|
|
23810
|
-
var ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT =
|
|
23822
|
+
var ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT = 2;
|
|
23811
23823
|
var MAX_GITHUB_OUTPUT_BYTES = 1048576;
|
|
23812
23824
|
var MAX_AUDIT_JSON_BYTES = 1048576;
|
|
23813
23825
|
var MAX_SEARCH_FILES = 20000;
|
|
23814
23826
|
var RUN_ID_PATTERN = /^[1-9][0-9]*$/u;
|
|
23815
23827
|
var REPOSITORY_PATTERN2 = /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/u;
|
|
23828
|
+
var DISPATCH_ID_PATTERN = /^amp_[a-f0-9]{32}$/u;
|
|
23829
|
+
var PROMOTION_DISCOVERY_ATTEMPTS = 60;
|
|
23830
|
+
var PROMOTION_DISCOVERY_INTERVAL_MS = 1000;
|
|
23816
23831
|
var isRecord9 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
23817
23832
|
var requiredString = (value, name) => {
|
|
23818
23833
|
if (typeof value !== "string" || value.trim() === "")
|
|
@@ -23829,6 +23844,26 @@ var runId = (value, name) => {
|
|
|
23829
23844
|
throw new TypeError(`${name} is invalid.`);
|
|
23830
23845
|
return value;
|
|
23831
23846
|
};
|
|
23847
|
+
var dispatchId = (value) => {
|
|
23848
|
+
if (!DISPATCH_ID_PATTERN.test(value))
|
|
23849
|
+
throw new TypeError("Mobile promotion dispatch ID is invalid.");
|
|
23850
|
+
return value;
|
|
23851
|
+
};
|
|
23852
|
+
var promotionRunTitle = (value) => `AbsoluteJS mobile promotion [${dispatchId(value)}]`;
|
|
23853
|
+
var promotionDiscoveryCommand = (workflow, repository) => [
|
|
23854
|
+
"gh",
|
|
23855
|
+
"run",
|
|
23856
|
+
"list",
|
|
23857
|
+
"--workflow",
|
|
23858
|
+
workflow,
|
|
23859
|
+
"--event",
|
|
23860
|
+
"workflow_dispatch",
|
|
23861
|
+
"--limit",
|
|
23862
|
+
"50",
|
|
23863
|
+
...repositoryArgs(repository),
|
|
23864
|
+
"--json",
|
|
23865
|
+
"databaseId,displayTitle,event,url"
|
|
23866
|
+
];
|
|
23832
23867
|
var repositoryArgs = (repository) => {
|
|
23833
23868
|
if (repository === undefined)
|
|
23834
23869
|
return [];
|
|
@@ -23876,6 +23911,27 @@ var parseJob = (value) => {
|
|
|
23876
23911
|
status: requiredString(value.status, "GitHub job status")
|
|
23877
23912
|
};
|
|
23878
23913
|
};
|
|
23914
|
+
var discoverAbsoluteMobilePromotionRun = async (options) => {
|
|
23915
|
+
const requestedDispatchId = dispatchId(options.dispatchId);
|
|
23916
|
+
const attempts = options.attempts ?? PROMOTION_DISCOVERY_ATTEMPTS;
|
|
23917
|
+
if (!Number.isSafeInteger(attempts) || attempts < 1 || attempts > 300)
|
|
23918
|
+
throw new TypeError("Mobile promotion discovery attempts are invalid.");
|
|
23919
|
+
const pollIntervalMs = options.pollIntervalMs ?? PROMOTION_DISCOVERY_INTERVAL_MS;
|
|
23920
|
+
if (!Number.isSafeInteger(pollIntervalMs) || pollIntervalMs < 0 || pollIntervalMs > 1e4)
|
|
23921
|
+
throw new TypeError("Mobile promotion discovery interval is invalid.");
|
|
23922
|
+
const sleep = options.sleep ?? Bun.sleep;
|
|
23923
|
+
const discover = async (attempt) => {
|
|
23924
|
+
const result = await requireCommand2(options.run ?? defaultRunner, promotionDiscoveryCommand(options.workflow, options.repository), { cwd: options.projectRoot }, "GitHub promotion run discovery");
|
|
23925
|
+
const discovered = parseAbsoluteMobilePromotionRunList(JSON.parse(result.stdout), requestedDispatchId);
|
|
23926
|
+
if (discovered)
|
|
23927
|
+
return discovered;
|
|
23928
|
+
if (attempt >= attempts)
|
|
23929
|
+
throw new TypeError(`GitHub did not expose promotion dispatch ${requestedDispatchId} within ${attempts} attempts.`);
|
|
23930
|
+
await sleep(pollIntervalMs);
|
|
23931
|
+
return discover(attempt + 1);
|
|
23932
|
+
};
|
|
23933
|
+
return discover(1);
|
|
23934
|
+
};
|
|
23879
23935
|
var inspectAbsoluteMobileCiRun = async (options) => {
|
|
23880
23936
|
const requestedRunId = runId(options.runId, "GitHub run ID");
|
|
23881
23937
|
const result = await requireCommand2(options.run ?? defaultRunner, [
|
|
@@ -23885,7 +23941,7 @@ var inspectAbsoluteMobileCiRun = async (options) => {
|
|
|
23885
23941
|
requestedRunId,
|
|
23886
23942
|
...repositoryArgs(options.repository),
|
|
23887
23943
|
"--json",
|
|
23888
|
-
"conclusion,createdAt,databaseId,headBranch,headSha,jobs,startedAt,status,updatedAt,url,workflowName"
|
|
23944
|
+
"conclusion,createdAt,databaseId,displayTitle,headBranch,headSha,jobs,startedAt,status,updatedAt,url,workflowName"
|
|
23889
23945
|
], { cwd: options.projectRoot }, "GitHub workflow inspection");
|
|
23890
23946
|
return parseAbsoluteMobileCiRunStatus(JSON.parse(result.stdout), requestedRunId);
|
|
23891
23947
|
};
|
|
@@ -23898,6 +23954,7 @@ var parseAbsoluteMobileCiRunStatus = (value, requestedRunId) => {
|
|
|
23898
23954
|
return {
|
|
23899
23955
|
conclusion: optionalString(value.conclusion, "GitHub run conclusion"),
|
|
23900
23956
|
createdAt: requiredString(value.createdAt, "GitHub run createdAt"),
|
|
23957
|
+
displayTitle: requiredString(value.displayTitle, "GitHub run displayTitle"),
|
|
23901
23958
|
headBranch: requiredString(value.headBranch, "GitHub run headBranch"),
|
|
23902
23959
|
headSha: requiredString(value.headSha, "GitHub run headSha"),
|
|
23903
23960
|
jobs: value.jobs.map(parseJob),
|
|
@@ -23910,20 +23967,44 @@ var parseAbsoluteMobileCiRunStatus = (value, requestedRunId) => {
|
|
|
23910
23967
|
};
|
|
23911
23968
|
};
|
|
23912
23969
|
var parseAbsoluteMobilePromotionContext = (value) => {
|
|
23913
|
-
if (!isRecord9(value) || value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT || value.platform !== "android" && value.platform !== "ios")
|
|
23970
|
+
if (!isRecord9(value) || value.format !== 1 && value.format !== ABSOLUTE_MOBILE_CI_PROMOTION_CONTEXT_FORMAT || value.platform !== "android" && value.platform !== "ios")
|
|
23914
23971
|
throw new TypeError("Mobile promotion context is invalid.");
|
|
23915
23972
|
const { platform } = value;
|
|
23916
23973
|
const sourceArtifact = requiredString(value.sourceArtifact, "promotion source artifact");
|
|
23917
23974
|
if (sourceArtifact !== `absolute-mobile-${platform}`)
|
|
23918
23975
|
throw new TypeError("Mobile promotion source artifact is invalid.");
|
|
23919
23976
|
return {
|
|
23920
|
-
format:
|
|
23977
|
+
dispatchId: value.format === 1 ? null : dispatchId(requiredString(value.dispatchId, "promotion dispatch ID")),
|
|
23978
|
+
format: value.format,
|
|
23921
23979
|
platform,
|
|
23922
23980
|
promotionRunId: runId(requiredString(value.promotionRunId, "promotion run ID"), "promotion run ID"),
|
|
23923
23981
|
sourceArtifact,
|
|
23924
23982
|
sourceRunId: runId(requiredString(value.sourceRunId, "source run ID"), "source run ID")
|
|
23925
23983
|
};
|
|
23926
23984
|
};
|
|
23985
|
+
var parseAbsoluteMobilePromotionRunList = (value, requestedDispatchId) => {
|
|
23986
|
+
if (!Array.isArray(value))
|
|
23987
|
+
throw new TypeError("GitHub workflow run list response is invalid.");
|
|
23988
|
+
const expectedTitle = promotionRunTitle(requestedDispatchId);
|
|
23989
|
+
const matches = value.filter((candidate) => isRecord9(candidate) && candidate.displayTitle === expectedTitle);
|
|
23990
|
+
if (matches.length === 0)
|
|
23991
|
+
return null;
|
|
23992
|
+
if (matches.length !== 1)
|
|
23993
|
+
throw new TypeError("GitHub returned multiple runs for one promotion dispatch identity.");
|
|
23994
|
+
const [match] = matches;
|
|
23995
|
+
if (!isRecord9(match) || match.event !== "workflow_dispatch")
|
|
23996
|
+
throw new TypeError("GitHub promotion run identity is invalid.");
|
|
23997
|
+
const databaseId = typeof match.databaseId === "number" || typeof match.databaseId === "string" ? String(match.databaseId) : "";
|
|
23998
|
+
const requestedRunId = runId(databaseId, "GitHub promotion run ID");
|
|
23999
|
+
const url = requiredString(match.url, "GitHub promotion run URL");
|
|
24000
|
+
if (!url.endsWith(`/actions/runs/${requestedRunId}`))
|
|
24001
|
+
throw new TypeError("GitHub promotion run URL does not match its ID.");
|
|
24002
|
+
return {
|
|
24003
|
+
dispatchId: requestedDispatchId,
|
|
24004
|
+
runId: requestedRunId,
|
|
24005
|
+
url
|
|
24006
|
+
};
|
|
24007
|
+
};
|
|
23927
24008
|
var watchAbsoluteMobileCiRun = async (options) => {
|
|
23928
24009
|
const requestedRunId = runId(options.runId, "GitHub run ID");
|
|
23929
24010
|
await requireCommand2(options.run ?? defaultRunner, [
|
|
@@ -24042,6 +24123,7 @@ var renderAuditMarkdown = (audit) => `# AbsoluteJS mobile promotion audit
|
|
|
24042
24123
|
- Artifact SHA-256: \`${audit.release.sha256}\`
|
|
24043
24124
|
- Source workflow run: ${audit.github.sourceRunId}
|
|
24044
24125
|
- Promotion workflow run: ${audit.github.promotionRunId}
|
|
24126
|
+
- Dispatch correlation: ${audit.github.dispatchId ?? "legacy workflow (not recorded)"}
|
|
24045
24127
|
- Workflow: ${audit.github.workflowName}
|
|
24046
24128
|
- GitHub identity: \`${audit.provenance.repository}/${audit.provenance.workflowPath}@${audit.provenance.ref}\`
|
|
24047
24129
|
- Certification: \`${audit.certification.certificationId}\` (${audit.certification.strength})
|
|
@@ -24091,6 +24173,8 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
|
|
|
24091
24173
|
throw new TypeError("Promotion context run identity does not match.");
|
|
24092
24174
|
if (context && context.platform !== platform)
|
|
24093
24175
|
throw new TypeError("Promotion context platform does not match.");
|
|
24176
|
+
if (context?.dispatchId && status.displayTitle !== promotionRunTitle(context.dispatchId))
|
|
24177
|
+
throw new TypeError("Promotion dispatch identity does not match the GitHub run.");
|
|
24094
24178
|
const sourceRunId = options.sourceRunId ? runId(options.sourceRunId, "GitHub source run ID") : context?.sourceRunId;
|
|
24095
24179
|
if (!sourceRunId)
|
|
24096
24180
|
throw new TypeError("Promotion artifact predates source-run context; pass --source-run-id.");
|
|
@@ -24159,6 +24243,7 @@ var auditAbsoluteMobileCiPromotion = async (options) => {
|
|
|
24159
24243
|
format: ABSOLUTE_MOBILE_CI_PROMOTION_AUDIT_FORMAT,
|
|
24160
24244
|
github: {
|
|
24161
24245
|
conclusion: status.conclusion,
|
|
24246
|
+
dispatchId: context?.dispatchId ?? null,
|
|
24162
24247
|
promotionRunId,
|
|
24163
24248
|
repository: verification.identity.repository,
|
|
24164
24249
|
sourceRunId,
|
|
@@ -25061,12 +25146,38 @@ var createGithubCertificationVerification = async (args) => {
|
|
|
25061
25146
|
console.log(relative16(projectRoot, path).replaceAll("\\", "/"));
|
|
25062
25147
|
return verification;
|
|
25063
25148
|
};
|
|
25149
|
+
var auditDispatchedGithubMobilePromotion = async (options) => {
|
|
25150
|
+
const startedAt = performance.now();
|
|
25151
|
+
try {
|
|
25152
|
+
const audited = await auditAbsoluteMobileCiPromotion({
|
|
25153
|
+
outputDirectory: valueAfter(options.args, "--outdir"),
|
|
25154
|
+
projectRoot: options.projectRoot,
|
|
25155
|
+
...options.repository ? { repository: options.repository } : {},
|
|
25156
|
+
runId: options.runId
|
|
25157
|
+
});
|
|
25158
|
+
sendTelemetryEvent("mobile:ci-promotion-audit", {
|
|
25159
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
25160
|
+
platform: options.platform,
|
|
25161
|
+
provider: "github-actions",
|
|
25162
|
+
success: true
|
|
25163
|
+
});
|
|
25164
|
+
return audited;
|
|
25165
|
+
} catch (error) {
|
|
25166
|
+
sendTelemetryEvent("mobile:ci-promotion-audit", {
|
|
25167
|
+
durationMs: Math.round(performance.now() - startedAt),
|
|
25168
|
+
platform: options.platform,
|
|
25169
|
+
provider: "github-actions",
|
|
25170
|
+
success: false
|
|
25171
|
+
});
|
|
25172
|
+
throw error;
|
|
25173
|
+
}
|
|
25174
|
+
};
|
|
25064
25175
|
var dispatchGithubMobilePromotion = async (args) => {
|
|
25065
25176
|
const [platform2] = args;
|
|
25066
25177
|
if (platform2 !== "android" && platform2 !== "ios")
|
|
25067
|
-
throw new TypeError("Usage: absolute mobile ci promote <android|ios> --run-id id --certification directory-or-json [--channel name] [store options]");
|
|
25068
|
-
const
|
|
25069
|
-
if (!
|
|
25178
|
+
throw new TypeError("Usage: absolute mobile ci promote <android|ios> --run-id id --certification directory-or-json [--channel name] [store options] [--watch] [--audit]");
|
|
25179
|
+
const sourceRunId = valueAfter(args, "--run-id");
|
|
25180
|
+
if (!sourceRunId || !/^[1-9][0-9]*$/u.test(sourceRunId))
|
|
25070
25181
|
throw new TypeError("mobile ci promote requires a numeric --run-id.");
|
|
25071
25182
|
const requestedCertification = valueAfter(args, "--certification");
|
|
25072
25183
|
if (!requestedCertification || requestedCertification.startsWith("-"))
|
|
@@ -25085,6 +25196,8 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25085
25196
|
if (certificationBase64.length > 48000)
|
|
25086
25197
|
throw new TypeError("Mobile certification is too large for protected GitHub dispatch.");
|
|
25087
25198
|
const workflow = valueAfter(args, "--workflow") ?? ".github/workflows/absolute-mobile.yml";
|
|
25199
|
+
const dispatchId2 = `amp_${randomUUID().replaceAll("-", "")}`;
|
|
25200
|
+
const repository = valueAfter(args, "--repo");
|
|
25088
25201
|
const command = [
|
|
25089
25202
|
"workflow",
|
|
25090
25203
|
"run",
|
|
@@ -25094,7 +25207,9 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25094
25207
|
"-f",
|
|
25095
25208
|
`platform=${platform2}`,
|
|
25096
25209
|
"-f",
|
|
25097
|
-
`source_run_id=${
|
|
25210
|
+
`source_run_id=${sourceRunId}`,
|
|
25211
|
+
"-f",
|
|
25212
|
+
`dispatch_id=${dispatchId2}`,
|
|
25098
25213
|
"-f",
|
|
25099
25214
|
`certification_base64=${certificationBase64}`,
|
|
25100
25215
|
"-f",
|
|
@@ -25109,21 +25224,71 @@ var dispatchGithubMobilePromotion = async (args) => {
|
|
|
25109
25224
|
const ref = valueAfter(args, "--ref");
|
|
25110
25225
|
if (ref)
|
|
25111
25226
|
command.push("--ref", ref);
|
|
25227
|
+
if (repository)
|
|
25228
|
+
command.push("--repo", repository);
|
|
25112
25229
|
const child = Bun.spawn(["gh", ...command], {
|
|
25113
25230
|
cwd: projectRoot,
|
|
25114
|
-
stderr: "
|
|
25231
|
+
stderr: "pipe",
|
|
25115
25232
|
stdin: "inherit",
|
|
25116
|
-
stdout: "
|
|
25233
|
+
stdout: "pipe"
|
|
25117
25234
|
});
|
|
25118
|
-
const exitCode = await
|
|
25235
|
+
const [exitCode, stderr] = await Promise.all([
|
|
25236
|
+
child.exited,
|
|
25237
|
+
new Response(child.stderr).text(),
|
|
25238
|
+
new Response(child.stdout).text()
|
|
25239
|
+
]);
|
|
25119
25240
|
if (exitCode !== 0)
|
|
25120
|
-
throw new TypeError(`GitHub promotion dispatch exited with status ${exitCode}
|
|
25241
|
+
throw new TypeError(`GitHub promotion dispatch exited with status ${exitCode}: ${stderr.trim() || "unknown GitHub CLI error"}`);
|
|
25242
|
+
const promotion = await discoverAbsoluteMobilePromotionRun({
|
|
25243
|
+
dispatchId: dispatchId2,
|
|
25244
|
+
projectRoot,
|
|
25245
|
+
...repository ? { repository } : {},
|
|
25246
|
+
workflow
|
|
25247
|
+
});
|
|
25121
25248
|
sendTelemetryEvent("mobile:ci-promotion-dispatched", {
|
|
25249
|
+
discovery: "correlated",
|
|
25122
25250
|
platform: platform2,
|
|
25123
25251
|
provider: "github-actions",
|
|
25124
25252
|
storeTarget: platform2 === "android" ? playTrack !== "registry-only" : testflightGroup !== ""
|
|
25125
25253
|
});
|
|
25126
|
-
|
|
25254
|
+
if (!args.includes("--json")) {
|
|
25255
|
+
console.log(`Dispatched protected ${platform2} promotion ${promotion.runId} for ${certification.release.releaseId} from workflow run ${sourceRunId}.`);
|
|
25256
|
+
console.log(promotion.url);
|
|
25257
|
+
}
|
|
25258
|
+
const shouldWatch = args.includes("--watch") || args.includes("--audit");
|
|
25259
|
+
if (shouldWatch && !args.includes("--json"))
|
|
25260
|
+
console.log(`Watching GitHub run ${promotion.runId} until completion\u2026`);
|
|
25261
|
+
const status = shouldWatch ? await watchAbsoluteMobileCiRun({
|
|
25262
|
+
projectRoot,
|
|
25263
|
+
...repository ? { repository } : {},
|
|
25264
|
+
runId: promotion.runId
|
|
25265
|
+
}) : null;
|
|
25266
|
+
const audited = args.includes("--audit") ? await auditDispatchedGithubMobilePromotion({
|
|
25267
|
+
args,
|
|
25268
|
+
platform: platform2,
|
|
25269
|
+
projectRoot,
|
|
25270
|
+
...repository ? { repository } : {},
|
|
25271
|
+
runId: promotion.runId
|
|
25272
|
+
}) : null;
|
|
25273
|
+
const result = {
|
|
25274
|
+
auditDirectory: audited ? relative16(projectRoot, audited.directory).replaceAll("\\", "/") : null,
|
|
25275
|
+
dispatchId: dispatchId2,
|
|
25276
|
+
platform: platform2,
|
|
25277
|
+
releaseId: certification.release.releaseId,
|
|
25278
|
+
runId: promotion.runId,
|
|
25279
|
+
sourceRunId,
|
|
25280
|
+
status: status?.status ?? "dispatched",
|
|
25281
|
+
url: promotion.url
|
|
25282
|
+
};
|
|
25283
|
+
if (args.includes("--json"))
|
|
25284
|
+
console.log(JSON.stringify(result, null, 2));
|
|
25285
|
+
if (args.includes("--json"))
|
|
25286
|
+
return result;
|
|
25287
|
+
if (status)
|
|
25288
|
+
renderGithubMobileRunStatus(status);
|
|
25289
|
+
if (audited)
|
|
25290
|
+
renderGithubMobilePromotionAudit(audited, projectRoot);
|
|
25291
|
+
return result;
|
|
25127
25292
|
};
|
|
25128
25293
|
var githubMobileRunId = (args) => {
|
|
25129
25294
|
const value = valueAfter(args, "--run-id");
|
|
@@ -27813,7 +27978,7 @@ var runMobile = async (args) => {
|
|
|
27813
27978
|
await publishIos(args.slice(2));
|
|
27814
27979
|
return;
|
|
27815
27980
|
}
|
|
27816
|
-
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] | 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]");
|
|
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]");
|
|
27817
27982
|
};
|
|
27818
27983
|
export {
|
|
27819
27984
|
runMobile
|