stim 1.19.0 → 1.20.0
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/README.md +20 -3
- package/dist/{activity-BSw0x84R.mjs → activity-BrGh9EAI.mjs} +3 -3
- package/dist/{agent-device-usage-output-Bjxu_VmX.mjs → agent-device-usage-output-DVyv_hWj.mjs} +7 -7
- package/dist/android-0vv-OruW.mjs +6 -0
- package/dist/android-Brx6EKvJ.mjs +1258 -0
- package/dist/{android-BdQC6zs1.mjs → android-BsYNR81W.mjs} +103 -45
- package/dist/{android-DE5g_5gv.mjs → android-CK3fpEyZ.mjs} +2 -2
- package/dist/{android-cas-C87geAg-.mjs → android-cas-DCAuXryb.mjs} +9 -5
- package/dist/android-cas-compiler.mjs +1 -1
- package/dist/api-run.mjs +159 -8
- package/dist/{api-DIf_s86s.mjs → api-zuX3ynYN.mjs} +5 -1
- package/dist/api.d.mts +98 -4
- package/dist/api.mjs +1 -1
- package/dist/{app-install-CFI4ptUL.mjs → app-install-0LgaQRT7.mjs} +9 -8
- package/dist/artifact-CnC0mfov.mjs +807 -0
- package/dist/artifact-DClXJNWx.mjs +772 -0
- package/dist/artifact-lifecycle-DzPpBYvK.mjs +30 -0
- package/dist/{browser-web-CDhqQxhv.mjs → browser-web-CzBmhmgs.mjs} +19 -19
- package/dist/{budget-Dz-kyXLv.mjs → budget-BONq9vXY.mjs} +3101 -3062
- package/dist/build-BixRI6sB.mjs +462 -0
- package/dist/{build-facts-B87vo-Im.d.mts → build-facts-DcLv2HSd.d.mts} +1 -32
- package/dist/build-plan-CP0tckNU.mjs +553 -0
- package/dist/{cache-manifest-DiwKSyv9.mjs → cache-manifest-qA3dT2x2.mjs} +1 -1
- package/dist/cache-manifest.mjs +1 -1
- package/dist/{manifest-CX0d1IkZ.mjs → ccache-CjHQ5F8g.mjs} +13 -19
- package/dist/{chrome-BTUOo8p2.mjs → chrome-Dp2lMLBe.mjs} +1 -1
- package/dist/{cli-C9Zvgbdc.mjs → cli-B2R8wH1F.mjs} +20 -20
- package/dist/cli.mjs +1 -1
- package/dist/{client-CuUo8Nvg.mjs → client-BLy1Pw2V.mjs} +89 -307
- package/dist/collector-run.d.mts +4 -0
- package/dist/collector-run.mjs +7 -7
- package/dist/{command-output-Bt8ygHE7.mjs → command-output-BjPN99ZB.mjs} +2 -2
- package/dist/{config-DC5jEtd1.mjs → config-D0LhTY3S.mjs} +8 -12
- package/dist/{created-devices-D0s26Hx5.mjs → created-devices-HU_DAb_Q.mjs} +7 -6
- package/dist/{dependency-state-DKaS1OM9.mjs → dependency-state-B2m8eqsS.mjs} +2 -2
- package/dist/{detached-entry-pB_IPfV4.mjs → detached-entry-BXq4fIl-.mjs} +1 -1
- package/dist/{dev-client-CDY_zAmD.mjs → dev-client-ClATaJoH.mjs} +2 -2
- package/dist/{device-BHw0WlHl.mjs → device-B7lsC0K1.mjs} +8 -8
- package/dist/device-DVCwAFlX.mjs +727 -0
- package/dist/{device-capacity-BoN1jQkO.mjs → device-capacity-Ca_ke1LE.mjs} +13 -14
- package/dist/device-host-worker.mjs +38 -18
- package/dist/device-ios-CRUqPxKw.mjs +413 -0
- package/dist/{device-lease-Cu7PtQpZ.mjs → device-lease-DHOBXGgS.mjs} +135 -22
- package/dist/{device-lease-run-D38SnZtt.mjs → device-lease-run-CvDeHTkG.mjs} +4 -4
- package/dist/{device-pool-CP4rAsHx.mjs → device-pool-L2-5GxMy.mjs} +3 -3
- package/dist/{device-remote-B5GiUifk.mjs → device-remote-Bmg42063.mjs} +17 -68
- package/dist/doctor-C2MSuMMg.mjs +515 -0
- package/dist/{doctor-PRJH94v6.mjs → doctor-DPAqhVCK.mjs} +333 -744
- package/dist/{doctor-watchman-wZhyTdp4.mjs → doctor-watchman-CB4NnVZm.mjs} +13 -12
- package/dist/{simslim-D9LkJxIO.mjs → eas-build-Dw_ec2Km.mjs} +6 -152
- package/dist/{stim-installations-DbmUjsLv.mjs → eas-session-ledger-DZlC-Fq3.mjs} +57 -4
- package/dist/{error-diagnostics--v5HGOU0.mjs → error-diagnostics-Du7I_GuY.mjs} +17 -12
- package/dist/errors-DzkMYh8Q.mjs +205 -0
- package/dist/{exec-i8GDPwq9.mjs → exec-B81Qodjn.mjs} +40 -10
- package/dist/{gc-CyHMcKcn.mjs → gc-PWp34zGb.mjs} +21 -20
- package/dist/{device-ios-B2ruohE-.mjs → gradle-C2OEKCCI.mjs} +21 -650
- package/dist/{guide-dRaQJJk9.mjs → guide-Dai9lphv.mjs} +504 -117
- package/dist/{hosted-android-Bs7fytj7.mjs → hosted-android-DQ-Agsd8.mjs} +5 -5
- package/dist/{hosted-client-C5G4h_OJ.mjs → hosted-client-Io01s5By.mjs} +9 -3
- package/dist/{hosted-ios-Bik035eL.mjs → hosted-ios-DIxFnYp-.mjs} +3 -3
- package/dist/{hosted-logs-CI9Az9a4.mjs → hosted-logs-DP-ABU_M.mjs} +8 -8
- package/dist/{hosted-macos-DABm3ScO.mjs → hosted-macos-Aziw9T_9.mjs} +7 -6
- package/dist/{hosted-native-DbV2aUVw.mjs → hosted-native-BdAQi21M.mjs} +17 -14
- package/dist/{idle-shutdown-DYiMBn34.mjs → idle-shutdown-DdMqoSUB.mjs} +7 -7
- package/dist/{ios-DYUr9LGc.mjs → ios-BZ9dg2Fr.mjs} +8 -7
- package/dist/{ios-BWbHFZVz.mjs → ios-CXLIUxXJ.mjs} +180 -24
- package/dist/ios-CZuv6gRv.mjs +1112 -0
- package/dist/{ios-DCrkgXEM.mjs → ios-VGvNdsv2.mjs} +1 -1
- package/dist/{ios-device-CrW8aMb5.mjs → ios-device-B1B14xND.mjs} +1 -1
- package/dist/{ios-device-CJ_ZOGky.mjs → ios-device-BreZOpM0.mjs} +3 -3
- package/dist/ios-project-B9drt_aY.mjs +197 -0
- package/dist/{ios-state-By0XuLTQ.mjs → ios-state-CjrL2qv-.mjs} +1 -1
- package/dist/js-swap-C8icNYIY.mjs +432 -0
- package/dist/launch-DxH0THLP.mjs +745 -0
- package/dist/{react-native-ios-CSpIxexZ.mjs → launch-YrbqaIFj.mjs} +59 -612
- package/dist/{launch-verify-DCveSE6d.mjs → launch-verify-BzRdbuO1.mjs} +26 -7
- package/dist/{logs-Bao1HOMy.mjs → logs-DHaRNfg7.mjs} +11 -11
- package/dist/macos-C0iljhsN.mjs +2 -0
- package/dist/{macos-ciyBdTY5.mjs → macos-V_3Ui7g4.mjs} +42 -50
- package/dist/macos-run.mjs +1 -1
- package/dist/maintenance-run.mjs +13 -10
- package/dist/{metro-DI2AjPF_.mjs → metro-D_j9sN-B.mjs} +18 -5
- package/dist/{metro-gateway-BxpJLIwh.mjs → metro-gateway-BBJldOdi.mjs} +2 -2
- package/dist/miss-reason-BPAzZS0r.mjs +238 -0
- package/dist/{named-ports-B4iWH4E-.mjs → named-ports-C2lvoPsb.mjs} +3 -3
- package/dist/native-android-lHzaew7r.mjs +226 -0
- package/dist/native-gradle-inputs-BQnIdAlL.mjs +356 -0
- package/dist/{native-runtime-BGxsbfny.mjs → native-runtime-BRmWL7ru.mjs} +9 -8
- package/dist/native-xcode-inputs-eutS19jU.mjs +307 -0
- package/dist/native-xcode-ios-DP3cHLwu.mjs +434 -0
- package/dist/{ndjson-LGZAMsqa.mjs → ndjson-GOnbyvso.mjs} +6 -6
- package/dist/offload-worker.d.mts +41 -7
- package/dist/offload-worker.mjs +409 -74
- package/dist/{ownership-EB0ySdGK.mjs → ownership-DXzMMSx6.mjs} +2 -2
- package/dist/{placement-log-Dxe34rJm.mjs → placement-log-reFbjqEr.mjs} +1 -1
- package/dist/plan-BDk2AeY_.mjs +260 -0
- package/dist/plan-placement-D_YYlE6N.mjs +73 -0
- package/dist/{ports-ByPc0_97.mjs → ports-jzvAJXXL.mjs} +3 -3
- package/dist/{prebuild-BKJ3wJI1.mjs → prebuild-Bw44QxVh.mjs} +4 -4
- package/dist/{preview-BbSottmO.mjs → preview-BaBJ72Al.mjs} +50 -32
- package/dist/project-B8_duAzW.mjs +1067 -0
- package/dist/project-plan-BPwbLwQu.mjs +10 -0
- package/dist/{pull-request-IHNdMnG8.mjs → pull-request-vOhLNmIb.mjs} +1 -1
- package/dist/pull-requests.mjs +1 -1
- package/dist/react-native-android-chm1DMlA.mjs +921 -0
- package/dist/react-native-build-oAw6gQeW.mjs +239 -0
- package/dist/react-native-doctor-DYXoh-6I.mjs +78 -0
- package/dist/react-native-ios-SCTK_CT5.mjs +595 -0
- package/dist/{recordings-Bv8McZN4.mjs → recordings-Bk-VivUV.mjs} +1 -1
- package/dist/{reload-rAPdqeRR.mjs → reload-D34GkY78.mjs} +17 -13
- package/dist/remote-BU9UyhCJ.mjs +192 -0
- package/dist/{remote-cache-N3hAEaAL.mjs → remote-cache-Bsts6f6Y.mjs} +5 -5
- package/dist/result-BDoRk1k3.mjs +186 -0
- package/dist/{run-lAkle1-c.mjs → run-qvff78hv.mjs} +3 -3
- package/dist/{server-bare-CkIjnegq.mjs → server-bare-dWjAarki.mjs} +5 -5
- package/dist/server-command-7XIKlWtO.mjs +69 -0
- package/dist/{server-expo-n3tUi3Em.mjs → server-expo-CsYu2aKL.mjs} +107 -82
- package/dist/server-expo-DV43vep4.mjs +2 -0
- package/dist/{settings-CxgngUZk.mjs → settings-Bbztm4oV.mjs} +179 -129
- package/dist/{settings-2am7TSxp.mjs → settings-CT3E71-i.mjs} +6 -6
- package/dist/{state-lKh8shBp.d.mts → settings-vk3mJGSm.d.mts} +4 -2
- package/dist/settings.schema.json +84 -0
- package/dist/simslim-ChMfe8rg.mjs +149 -0
- package/dist/{slot-launch-HV_KtPIw.mjs → slot-launch-B9hn96wd.mjs} +6 -6
- package/dist/{stage-BgaZJVZY.mjs → stage-BNKKdDNc.mjs} +6 -3
- package/dist/start-CbQCSnXQ.mjs +2 -0
- package/dist/{start-DtxthMfI.mjs → start-CdvcTaXr.mjs} +97 -68
- package/dist/{state-Tp4uImDS.mjs → state-Boo0IMJq.mjs} +1 -1
- package/dist/{state-D2KtnALG.mjs → state-DI84A-uu.mjs} +29 -10
- package/dist/{stats-8EJ5ivFT.mjs → stats-BIIDrjA8.mjs} +5 -5
- package/dist/{status-BEeL6IKz.mjs → status-BDGHwkCs.mjs} +3 -1
- package/dist/{status-BOyp0CUl.mjs → status-DARMkc9q.mjs} +81 -293
- package/dist/status-watch-DvmI3SmN.mjs +296 -0
- package/dist/{stim-desktop-C65jk_q2.mjs → stim-desktop-8GQX2OV8.mjs} +1 -1
- package/dist/{stop-DRipAg-e.mjs → stop-CCZS-C9m.mjs} +2 -2
- package/dist/{stop-D0UIpZuh.mjs → stop-WQPQHLuy.mjs} +5 -5
- package/dist/{stop-zDlknFoz.mjs → stop-iNemHsJC.mjs} +21 -20
- package/dist/supervisor-run.d.mts +7 -2
- package/dist/supervisor-run.mjs +32 -20
- package/dist/{support-DsNaFg6D.mjs → support-CnE_SIjt.mjs} +17 -7
- package/dist/{support-CNqYkV0y.mjs → support-DCco5fut.mjs} +61 -8
- package/dist/{swiftpm-macos-aJzPAj1t.mjs → swiftpm-macos-afeNUExW.mjs} +5 -5
- package/dist/tailnet-CoJUb2tB.mjs +265 -0
- package/dist/toolchain-BIupIro1.mjs +2 -0
- package/dist/{toolchain-C3xSoyUl.mjs → toolchain-DqyDXs30.mjs} +206 -53
- package/dist/{trigger-CAhmG-4s.mjs → trigger-C0MWBi8O.mjs} +2 -2
- package/dist/trigger-SHLOWJ4O.mjs +2 -0
- package/dist/{warm-progress-pFLB2e6P.mjs → warm-progress-C3ASD6z5.mjs} +6 -6
- package/dist/{watchman-BL1IdOtq.mjs → watchman-CQl51YZu.mjs} +6 -6
- package/dist/{web-DbbrPUn0.mjs → web-B3eldHFH.mjs} +9 -13
- package/dist/web-BLXpOe6b.mjs +2 -0
- package/dist/web-run.mjs +10 -10
- package/dist/{workspace-state-CR9X86xJ.mjs → workspace-state-CSIFuuDa.mjs} +33 -33
- package/dist/{workspaces-Djb2gfi9.mjs → workspaces-D2xSHBDY.mjs} +1260 -1258
- package/dist/{worktree-BL4M1rhB.mjs → worktree-BxDqaTev.mjs} +84 -48
- package/dist/worktree-q7_OZNY0.mjs +2 -0
- package/package.json +8 -6
- package/shim/native-android.gradle +101 -0
- package/dist/android-BXc6BmEi.mjs +0 -3732
- package/dist/android-BvV1gGdN.mjs +0 -4
- package/dist/ios-ITYno9rO.mjs +0 -2241
- package/dist/macos-nIOATJmR.mjs +0 -2
- package/dist/metro-store-BAHFcJm6.mjs +0 -106
- package/dist/miss-reason-CjQCOKqq.mjs +0 -1743
- package/dist/plan-placement-BVY59qa8.mjs +0 -290
- package/dist/project-B5eQ1USs.mjs +0 -612
- package/dist/react-native-ios-BMh2i25I.mjs +0 -2
- package/dist/server-expo-DGsdUGX2.mjs +0 -2
- package/dist/start-D8NbxNBb.mjs +0 -2
- package/dist/trigger-CoalsMTV.mjs +0 -2
- package/dist/web-DFpFeZu5.mjs +0 -2
- package/dist/worktree-DV-A0LNL.mjs +0 -2
|
@@ -1,27 +1,27 @@
|
|
|
1
|
-
import { n as debugLog } from "./exec-
|
|
2
|
-
import { y as refuseRelativeStimPaths } from "./config-
|
|
1
|
+
import { n as debugLog } from "./exec-B81Qodjn.mjs";
|
|
2
|
+
import { y as refuseRelativeStimPaths } from "./config-D0LhTY3S.mjs";
|
|
3
3
|
import { readFileSync } from "node:fs";
|
|
4
4
|
import { runId } from "@stim-cli/core/state";
|
|
5
5
|
import { Command } from "commander";
|
|
6
6
|
//#region bin/cli.ts
|
|
7
7
|
const commands = /* @__PURE__ */ new Map([
|
|
8
|
-
["doctor", () => import("./doctor-
|
|
9
|
-
["worktree", () => import("./worktree-
|
|
10
|
-
["start", () => import("./start-
|
|
11
|
-
["ports", () => import("./ports-
|
|
12
|
-
["stop", () => import("./stop-
|
|
13
|
-
["ios", () => import("./ios-
|
|
14
|
-
["android", () => import("./android-
|
|
15
|
-
["web", () => import("./web-
|
|
16
|
-
["macos", () => import("./macos-
|
|
17
|
-
["reload", () => import("./reload-
|
|
18
|
-
["device", () => import("./device-
|
|
19
|
-
["logs", () => import("./logs-
|
|
20
|
-
["settings", () => import("./settings-
|
|
21
|
-
["status", () => import("./status-
|
|
22
|
-
["stats", () => import("./stats-
|
|
23
|
-
["gc", () => import("./gc-
|
|
24
|
-
["guide", () => import("./guide-
|
|
8
|
+
["doctor", () => import("./doctor-C2MSuMMg.mjs")],
|
|
9
|
+
["worktree", () => import("./worktree-q7_OZNY0.mjs")],
|
|
10
|
+
["start", () => import("./start-CbQCSnXQ.mjs")],
|
|
11
|
+
["ports", () => import("./ports-jzvAJXXL.mjs")],
|
|
12
|
+
["stop", () => import("./stop-CCZS-C9m.mjs")],
|
|
13
|
+
["ios", () => import("./ios-BZ9dg2Fr.mjs")],
|
|
14
|
+
["android", () => import("./android-0vv-OruW.mjs")],
|
|
15
|
+
["web", () => import("./web-BLXpOe6b.mjs")],
|
|
16
|
+
["macos", () => import("./macos-C0iljhsN.mjs")],
|
|
17
|
+
["reload", () => import("./reload-D34GkY78.mjs")],
|
|
18
|
+
["device", () => import("./device-B7lsC0K1.mjs")],
|
|
19
|
+
["logs", () => import("./logs-DHaRNfg7.mjs")],
|
|
20
|
+
["settings", () => import("./settings-CT3E71-i.mjs")],
|
|
21
|
+
["status", () => import("./status-DARMkc9q.mjs")],
|
|
22
|
+
["stats", () => import("./stats-BIIDrjA8.mjs")],
|
|
23
|
+
["gc", () => import("./gc-PWp34zGb.mjs")],
|
|
24
|
+
["guide", () => import("./guide-Dai9lphv.mjs")]
|
|
25
25
|
]);
|
|
26
26
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf-8"));
|
|
27
27
|
const program = new Command();
|
|
@@ -52,7 +52,7 @@ try {
|
|
|
52
52
|
}));
|
|
53
53
|
}
|
|
54
54
|
try {
|
|
55
|
-
const { triggerMaintenance } = await import("./trigger-
|
|
55
|
+
const { triggerMaintenance } = await import("./trigger-SHLOWJ4O.mjs");
|
|
56
56
|
triggerMaintenance(action.name());
|
|
57
57
|
} catch {}
|
|
58
58
|
});
|
package/dist/cli.mjs
CHANGED
|
@@ -4,6 +4,6 @@ const encode = (version) => version.split(".", 3).reduce((sum, part) => sum * 1e
|
|
|
4
4
|
if (encode(process.versions.node) < encode("22.12.0")) {
|
|
5
5
|
process.stderr.write(`STIM_NODE_UNSUPPORTED: Stim needs Node 22.12.0 or later; this is Node ${process.versions.node} at ${process.execPath}. To run Stim where a project pins an older Node, see https://stim.appandflow.com/docs/requirements#older-node-pins\n`);
|
|
6
6
|
process.exitCode = 1;
|
|
7
|
-
} else import("./cli-
|
|
7
|
+
} else import("./cli-B2R8wH1F.mjs");
|
|
8
8
|
//#endregion
|
|
9
9
|
export {};
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { n as debugLog, t as getExecutor } from "./exec-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { a as toolchainMismatches, m as COMPILATION_CACHE_UNAVAILABLE, u as readRubyVersion } from "./toolchain-C3xSoyUl.mjs";
|
|
1
|
+
import { n as debugLog, t as getExecutor } from "./exec-B81Qodjn.mjs";
|
|
2
|
+
import { _ as loadConfig$1, d as getConfigDir } from "./config-D0LhTY3S.mjs";
|
|
3
|
+
import { D as withDirLock } from "./workspace-state-CSIFuuDa.mjs";
|
|
4
|
+
import { a as markApproved, c as pinnedEndpoint$1, i as lapsedLine, l as realIo, n as findPeer$1, o as parseMachine$1, r as isLapsed, s as pendingReport, t as endpoint, u as requestedLine, x as runCancellationSignal } from "./tailnet-CoJUb2tB.mjs";
|
|
5
|
+
import { n as CCACHE_UNAVAILABLE } from "./ccache-CjHQ5F8g.mjs";
|
|
6
|
+
import { l as toolchainMismatches, m as readRubyVersion, v as COMPILATION_CACHE_UNAVAILABLE } from "./toolchain-DqyDXs30.mjs";
|
|
7
|
+
import { c as gitVisiblePaths, i as nativeGradleTransfer, l as nativeTransferManifest, m as manifestDigest, s as checkNativeTransferMembership, u as sourceManifest } from "./native-gradle-inputs-BQnIdAlL.mjs";
|
|
9
8
|
import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync, writeSync } from "node:fs";
|
|
10
|
-
import { basename, dirname, join, relative } from "node:path";
|
|
11
|
-
import { OFFLOAD_MODES, acceptsShape, automaticMachineEnabled, buildMachinesFile, buildMachinesLock, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock,
|
|
12
|
-
import { hostname } from "node:os";
|
|
9
|
+
import { basename, dirname, isAbsolute, join, relative, sep } from "node:path";
|
|
10
|
+
import { OFFLOAD_MODES, acceptsShape, automaticMachineEnabled, buildMachinesFile, buildMachinesLock, deviceHostMachinesClaims, deviceHostMachinesFile, deviceHostMachinesLock, isJsonObject, parseMachine, readBuildMachines, readDeviceHostMachines, requireAutomaticMachine, runId, saturation, settingDefinition } from "@stim-cli/core/state";
|
|
13
11
|
import { InvalidArgumentError } from "commander";
|
|
14
12
|
import { releaseClaim, tryAcquireClaim } from "@stim-cli/core/ownership-claim";
|
|
15
13
|
import { createHash } from "node:crypto";
|
|
14
|
+
import { hostname } from "node:os";
|
|
16
15
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
17
16
|
import { WebSocket } from "ws";
|
|
18
17
|
//#region src/offload/access-ticket.ts
|
|
@@ -32,95 +31,6 @@ function readHostPermissions(value) {
|
|
|
32
31
|
} : void 0;
|
|
33
32
|
}
|
|
34
33
|
//#endregion
|
|
35
|
-
//#region src/offload/tailnet.ts
|
|
36
|
-
const MAC_APP_TAILSCALE = "/Applications/Tailscale.app/Contents/MacOS/Tailscale";
|
|
37
|
-
const HELLO_TIMEOUT_MS = 1e4;
|
|
38
|
-
function isLapsed(credential) {
|
|
39
|
-
return credential.state === "pending" && !!credential.expiresAt && Date.parse(credential.expiresAt) <= Date.now();
|
|
40
|
-
}
|
|
41
|
-
function markApproved(credential) {
|
|
42
|
-
const { expiresAt: _expiresAt, ...rest } = credential;
|
|
43
|
-
return {
|
|
44
|
-
...rest,
|
|
45
|
-
state: "approved"
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
function pendingReport(credential) {
|
|
49
|
-
return {
|
|
50
|
-
state: "pending",
|
|
51
|
-
...credential.expiresAt ? { expiresAt: credential.expiresAt } : {}
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
function lapsedLine(machine, credential) {
|
|
55
|
-
return `${machine} did not approve this Mac before the request lapsed at ${credential.expiresAt}.`;
|
|
56
|
-
}
|
|
57
|
-
function requestedLine(credential) {
|
|
58
|
-
return `Requested at ${credential.requestedAt}${credential.expiresAt ? `; the request lapses at ${credential.expiresAt}` : ""}.`;
|
|
59
|
-
}
|
|
60
|
-
function tailscaleStatus() {
|
|
61
|
-
for (const binary of ["tailscale", MAC_APP_TAILSCALE]) {
|
|
62
|
-
const output = getExecutor().runFileQuiet(binary, ["status", "--json"], { timeoutMs: 5e3 });
|
|
63
|
-
if (output === null) continue;
|
|
64
|
-
try {
|
|
65
|
-
const status = JSON.parse(output);
|
|
66
|
-
return isJsonObject(status) && status.BackendState === "Running" ? status : null;
|
|
67
|
-
} catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
function hello({ url, servername, host }, auth) {
|
|
74
|
-
return new Promise((resolve) => {
|
|
75
|
-
const socket = new WebSocket(url, {
|
|
76
|
-
handshakeTimeout: HELLO_TIMEOUT_MS,
|
|
77
|
-
servername,
|
|
78
|
-
headers: { Host: host }
|
|
79
|
-
});
|
|
80
|
-
const done = (reply) => {
|
|
81
|
-
clearTimeout(timer);
|
|
82
|
-
socket.removeAllListeners();
|
|
83
|
-
socket.on("error", () => {});
|
|
84
|
-
socket.close();
|
|
85
|
-
resolve(reply);
|
|
86
|
-
};
|
|
87
|
-
const timer = setTimeout(() => done({ failed: "no reply in time" }), HELLO_TIMEOUT_MS);
|
|
88
|
-
socket.on("open", () => {
|
|
89
|
-
socket.send(JSON.stringify({
|
|
90
|
-
id: 1,
|
|
91
|
-
method: "hello",
|
|
92
|
-
params: {
|
|
93
|
-
protocol: 1,
|
|
94
|
-
client: {
|
|
95
|
-
name: "stim",
|
|
96
|
-
version: "1"
|
|
97
|
-
},
|
|
98
|
-
auth
|
|
99
|
-
}
|
|
100
|
-
}));
|
|
101
|
-
});
|
|
102
|
-
socket.on("message", (data) => {
|
|
103
|
-
let message;
|
|
104
|
-
try {
|
|
105
|
-
message = JSON.parse(String(data));
|
|
106
|
-
} catch {
|
|
107
|
-
message = null;
|
|
108
|
-
}
|
|
109
|
-
if (isJsonObject(message) && ("result" in message || "error" in message)) return done(message);
|
|
110
|
-
done({ failed: "the reply was not a hello result" });
|
|
111
|
-
});
|
|
112
|
-
socket.on("error", (error) => done({ failed: error.message }));
|
|
113
|
-
socket.on("close", () => done({ failed: "the connection closed before a reply" }));
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
const realIo = {
|
|
117
|
-
status: tailscaleStatus,
|
|
118
|
-
hello
|
|
119
|
-
};
|
|
120
|
-
function pinnedEndpoint$1(credential, status = tailscaleStatus) {
|
|
121
|
-
return pinnedEndpoint(credential, parseMachine(credential.machine) ? status() : null);
|
|
122
|
-
}
|
|
123
|
-
//#endregion
|
|
124
34
|
//#region src/device-host/machines.ts
|
|
125
35
|
function note$1(title, detail, fix = null) {
|
|
126
36
|
return {
|
|
@@ -766,159 +676,6 @@ function reportRemoteFailure(event, fields) {
|
|
|
766
676
|
} catch {}
|
|
767
677
|
}
|
|
768
678
|
//#endregion
|
|
769
|
-
//#region src/engine/native-run.ts
|
|
770
|
-
const NATIVE_RUN_LOCK = "native-run";
|
|
771
|
-
const NATIVE_RUN_WAIT_MS = 18e5;
|
|
772
|
-
const WAIT_HEARTBEAT_MS = 3e4;
|
|
773
|
-
const CANCEL_REQUEST_KEY = "nativeRunCancel";
|
|
774
|
-
function nativeRunHolder(holder) {
|
|
775
|
-
const { command, platform, slot } = holder.details;
|
|
776
|
-
return {
|
|
777
|
-
command: typeof command === "string" ? command : null,
|
|
778
|
-
platform: typeof platform === "string" ? platform : null,
|
|
779
|
-
slot: typeof slot === "string" ? slot : "default"
|
|
780
|
-
};
|
|
781
|
-
}
|
|
782
|
-
function describeNativeRunHolder(holder, now) {
|
|
783
|
-
const { command, slot } = nativeRunHolder(holder);
|
|
784
|
-
const started = Date.parse(holder.startedAt);
|
|
785
|
-
const facts = [`pid ${holder.owner.pid}`];
|
|
786
|
-
if (Number.isFinite(started)) facts.push(`running for ${formatElapsed(now - started)}`);
|
|
787
|
-
if (command === null) return `another Stim run (${facts.join(", ")})`;
|
|
788
|
-
return `\`stim ${command}${slot === "default" ? "" : ` --slot ${slot}`}\` (${facts.join(", ")})`;
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Report a wait on the native-run lock: one line as soon as a holder is seen, a line for each new
|
|
792
|
-
* holder, and a heartbeat every WAIT_HEARTBEAT_MS while the same holder keeps it.
|
|
793
|
-
*/
|
|
794
|
-
function nativeRunWaitNotice({ write, now = Date.now, describe = describeNativeRunHolder }) {
|
|
795
|
-
let claimId = null;
|
|
796
|
-
let lastAt = 0;
|
|
797
|
-
return (holder, firstLine) => {
|
|
798
|
-
const at = now();
|
|
799
|
-
if (holder.claimId !== claimId) {
|
|
800
|
-
claimId = holder.claimId;
|
|
801
|
-
lastAt = at;
|
|
802
|
-
write(firstLine ?? `waiting for ${describe(holder, at)} in this workspace to finish`);
|
|
803
|
-
return;
|
|
804
|
-
}
|
|
805
|
-
if (at - lastAt < WAIT_HEARTBEAT_MS) return;
|
|
806
|
-
lastAt = at;
|
|
807
|
-
write(`still waiting for ${describe(holder, at)}`);
|
|
808
|
-
};
|
|
809
|
-
}
|
|
810
|
-
/**
|
|
811
|
-
* What `stop` does about the run holding the native-run lock. A build is interrupted only when the stop
|
|
812
|
-
* leaves it nothing to deploy to: the whole workspace stops, the build targets the stopped slot, or the
|
|
813
|
-
* stopped slot is the workspace's only device. A build for another slot that stays is left running and
|
|
814
|
-
* the stop proceeds without the lock. Only an owner whose identity is proven live is signalled.
|
|
815
|
-
*/
|
|
816
|
-
function decideStopAction({ stopSlot, holder, ownerIdentity, deviceSlots }) {
|
|
817
|
-
if (holder.command === "macos") {
|
|
818
|
-
if (stopSlot !== void 0) return { action: "proceed" };
|
|
819
|
-
return ownerIdentity === "same" ? { action: "interrupt" } : { action: "refuse" };
|
|
820
|
-
}
|
|
821
|
-
if (holder.command !== "ios" && holder.command !== "android") return { action: "wait" };
|
|
822
|
-
if (!(stopSlot === void 0 || holder.slot === stopSlot || deviceSlots.includes(stopSlot) && deviceSlots.every((slot) => slot === stopSlot))) return { action: "proceed" };
|
|
823
|
-
return ownerIdentity === "same" ? { action: "interrupt" } : { action: "refuse" };
|
|
824
|
-
}
|
|
825
|
-
function requestNativeRunCancel(root, claimId, pid = process.pid) {
|
|
826
|
-
updateWorkspaceState(root, (state) => ({
|
|
827
|
-
...state,
|
|
828
|
-
[CANCEL_REQUEST_KEY]: {
|
|
829
|
-
claimId,
|
|
830
|
-
pid,
|
|
831
|
-
at: (/* @__PURE__ */ new Date()).toISOString()
|
|
832
|
-
}
|
|
833
|
-
}));
|
|
834
|
-
}
|
|
835
|
-
function cancelRequestFor(root, claimId) {
|
|
836
|
-
const request = readWorkspaceState$1(root)?.[CANCEL_REQUEST_KEY];
|
|
837
|
-
if (request?.claimId !== claimId || typeof request.pid !== "number") return null;
|
|
838
|
-
return { pid: request.pid };
|
|
839
|
-
}
|
|
840
|
-
function clearNativeRunCancel(root, claimId) {
|
|
841
|
-
try {
|
|
842
|
-
clearWorkspaceStateKey(root, CANCEL_REQUEST_KEY, (value) => value?.claimId === claimId);
|
|
843
|
-
} catch {}
|
|
844
|
-
}
|
|
845
|
-
let cancelled = null;
|
|
846
|
-
let cancellationSignal;
|
|
847
|
-
/** The active native run aborts this signal when it is cancelled. */
|
|
848
|
-
function runCancellationSignal() {
|
|
849
|
-
return cancellationSignal;
|
|
850
|
-
}
|
|
851
|
-
/** Why this process's native run was cancelled, or null while it was not. */
|
|
852
|
-
function runCancellation() {
|
|
853
|
-
return cancelled;
|
|
854
|
-
}
|
|
855
|
-
function cancelledFailure(platform, { code, message }) {
|
|
856
|
-
if (cancelled === null) return null;
|
|
857
|
-
return {
|
|
858
|
-
code: "STIM_CANCELLED",
|
|
859
|
-
message: `The ${platform} run was ${cancelled} before it finished.`,
|
|
860
|
-
remedy: `Run \`stim ${platform}\` again when you want the app on a device.`,
|
|
861
|
-
lines: [`it stopped at ${code ?? "an unfinished step"}${message ? `: ${message}` : ""}`]
|
|
862
|
-
};
|
|
863
|
-
}
|
|
864
|
-
function cancelOnInterrupt({ root, claim, write, exit, controller }) {
|
|
865
|
-
let signals = 0;
|
|
866
|
-
const onInterrupt = () => {
|
|
867
|
-
signals += 1;
|
|
868
|
-
if (signals > 1) {
|
|
869
|
-
exit(130);
|
|
870
|
-
return;
|
|
871
|
-
}
|
|
872
|
-
const request = cancelRequestFor(root, claim.claimId);
|
|
873
|
-
cancelled = request ? `cancelled by \`stim stop\` (pid ${request.pid})` : "cancelled by an interrupt";
|
|
874
|
-
controller.abort();
|
|
875
|
-
refuseDeclaredSpawns(`the run was ${cancelled}`);
|
|
876
|
-
const signalled = signalDeclaredSpawns("SIGINT");
|
|
877
|
-
write(signalled > 0 ? `${cancelled}; stopping the running build tool` : request ? `${cancelled}; stopping before the next build tool or install` : cancelled);
|
|
878
|
-
if (signalled === 0 && !request) exit(130);
|
|
879
|
-
};
|
|
880
|
-
process.on("SIGINT", onInterrupt);
|
|
881
|
-
return () => {
|
|
882
|
-
process.off("SIGINT", onInterrupt);
|
|
883
|
-
allowDeclaredSpawns();
|
|
884
|
-
clearNativeRunCancel(root, claim.claimId);
|
|
885
|
-
};
|
|
886
|
-
}
|
|
887
|
-
/**
|
|
888
|
-
* Run an `ios`, `android` or `macos` invocation under the workspace's native-run lock: the claim records the
|
|
889
|
-
* command, platform and slot, and a wait for another holder is reported on `write`. The first SIGINT
|
|
890
|
-
* forwards the interrupt to the running declared build tool; a run `stim stop` cancelled between build
|
|
891
|
-
* tools starts no further one; a terminal interrupt with no build tool running, or a second SIGINT, exits
|
|
892
|
-
* with 130 at once.
|
|
893
|
-
*/
|
|
894
|
-
function withNativeBuildRun(root, details, fn, { write, exit = (code) => process.exit(code) }) {
|
|
895
|
-
const notice = nativeRunWaitNotice({ write });
|
|
896
|
-
return withWorkspaceProcessLock(workspaceDir(root), NATIVE_RUN_LOCK, async (claim) => {
|
|
897
|
-
cancelled = null;
|
|
898
|
-
const controller = new AbortController();
|
|
899
|
-
cancellationSignal = controller.signal;
|
|
900
|
-
const dispose = cancelOnInterrupt({
|
|
901
|
-
root,
|
|
902
|
-
claim,
|
|
903
|
-
write,
|
|
904
|
-
exit,
|
|
905
|
-
controller
|
|
906
|
-
});
|
|
907
|
-
try {
|
|
908
|
-
return await fn(claim);
|
|
909
|
-
} finally {
|
|
910
|
-
dispose();
|
|
911
|
-
cancellationSignal = void 0;
|
|
912
|
-
}
|
|
913
|
-
}, {
|
|
914
|
-
external: true,
|
|
915
|
-
waitMs: NATIVE_RUN_WAIT_MS,
|
|
916
|
-
declareSpawns: true,
|
|
917
|
-
details: { ...details },
|
|
918
|
-
onHeld: (holder) => notice(holder)
|
|
919
|
-
});
|
|
920
|
-
}
|
|
921
|
-
//#endregion
|
|
922
679
|
//#region src/offload/client.ts
|
|
923
680
|
const CONNECT_TIMEOUT_MS = 1e4;
|
|
924
681
|
const OFFER_TIMEOUT_MS = 2e4;
|
|
@@ -1402,61 +1159,37 @@ function repoIdentity(projectRoot) {
|
|
|
1402
1159
|
lockfile: lock ? createHash("sha256").update(readFileSync(join(repoRoot, lock))).digest("hex") : null
|
|
1403
1160
|
};
|
|
1404
1161
|
}
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
repoRoot
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
"
|
|
1413
|
-
"--exclude-standard"
|
|
1414
|
-
], {
|
|
1415
|
-
untrimmed: true,
|
|
1416
|
-
timeoutMs: 12e4
|
|
1417
|
-
});
|
|
1418
|
-
const files = [];
|
|
1419
|
-
for (const path of new Set(listed.split("\0").filter(Boolean))) {
|
|
1420
|
-
const absolute = join(repoRoot, path);
|
|
1421
|
-
let stat;
|
|
1422
|
-
try {
|
|
1423
|
-
stat = lstatSync(absolute);
|
|
1424
|
-
} catch {
|
|
1425
|
-
continue;
|
|
1426
|
-
}
|
|
1427
|
-
if (stat.isSymbolicLink()) {
|
|
1428
|
-
const target = Buffer.from(readlinkSync(absolute));
|
|
1429
|
-
files.push({
|
|
1430
|
-
path,
|
|
1431
|
-
kind: "link",
|
|
1432
|
-
size: target.length,
|
|
1433
|
-
sha256: sha256(target)
|
|
1434
|
-
});
|
|
1435
|
-
} else if (stat.isFile()) {
|
|
1436
|
-
const content = readFileSync(absolute);
|
|
1437
|
-
files.push({
|
|
1438
|
-
path,
|
|
1439
|
-
kind: stat.mode & 73 ? "exec" : "file",
|
|
1440
|
-
size: content.length,
|
|
1441
|
-
sha256: sha256(content)
|
|
1442
|
-
});
|
|
1443
|
-
}
|
|
1162
|
+
const sha256 = (content) => createHash("sha256").update(content).digest("hex");
|
|
1163
|
+
/** Why the native inputs cannot be sent to a remote Mac, such as an ignored file among them, or null when they can. */
|
|
1164
|
+
function nativeTransferRefusal(repoRoot, snapshot) {
|
|
1165
|
+
try {
|
|
1166
|
+
checkNativeTransferMembership(snapshot, gitVisiblePaths(repoRoot));
|
|
1167
|
+
return null;
|
|
1168
|
+
} catch (error) {
|
|
1169
|
+
return error.message.split("\n")[0];
|
|
1444
1170
|
}
|
|
1445
|
-
return files;
|
|
1446
1171
|
}
|
|
1447
|
-
const sha256 = (content) => createHash("sha256").update(content).digest("hex");
|
|
1448
1172
|
function blobContent(repoRoot, file) {
|
|
1449
1173
|
const absolute = join(repoRoot, file.path);
|
|
1450
|
-
return file.kind === "link" ? Buffer.from(readlinkSync(absolute)) : readFileSync(absolute);
|
|
1174
|
+
return file.kind === "directory" ? Buffer.alloc(0) : file.kind === "link" ? Buffer.from(readlinkSync(absolute)) : readFileSync(absolute);
|
|
1451
1175
|
}
|
|
1452
1176
|
/** Closes every connection the choice still holds. */
|
|
1453
1177
|
function closeOffload(choice) {
|
|
1454
1178
|
choice.connection.close();
|
|
1455
1179
|
for (const each of choice.runnersUp.splice(0)) each.connection.close();
|
|
1456
1180
|
}
|
|
1181
|
+
const NATIVE_BUILD_FEATURE = {
|
|
1182
|
+
xcode: "native-xcode-build",
|
|
1183
|
+
gradle: "native-gradle-build"
|
|
1184
|
+
};
|
|
1185
|
+
const unsupportedNative = (provider) => `This worker does not support native ${provider === "xcode" ? "Xcode" : "Gradle"} builds.`;
|
|
1186
|
+
const ANDROID_PACKAGE = /^[A-Za-z_][\w]*(?:\.[A-Za-z_][\w]*)+$/;
|
|
1187
|
+
function nativeTransferInput(native) {
|
|
1188
|
+
return native?.provider === "xcode" ? native.snapshot : native?.transfer;
|
|
1189
|
+
}
|
|
1457
1190
|
/** Connects to one paired machine and asks it for an offer; the caller closes the connection it returns. */
|
|
1458
1191
|
async function probeMachine(credential, identity, { connectMs = CONNECT_TIMEOUT_MS, offerMs = OFFER_TIMEOUT_MS } = {}) {
|
|
1459
|
-
const target = pinnedEndpoint$1(credential);
|
|
1192
|
+
const target = await pinnedEndpoint$1(credential);
|
|
1460
1193
|
if (typeof target === "string") return {
|
|
1461
1194
|
credential,
|
|
1462
1195
|
failure: target
|
|
@@ -1466,10 +1199,18 @@ async function probeMachine(credential, identity, { connectMs = CONNECT_TIMEOUT_
|
|
|
1466
1199
|
credential,
|
|
1467
1200
|
failure: connection.failure
|
|
1468
1201
|
};
|
|
1202
|
+
if (identity.native && !connection.supports(NATIVE_BUILD_FEATURE[identity.native])) {
|
|
1203
|
+
connection.close();
|
|
1204
|
+
return {
|
|
1205
|
+
credential,
|
|
1206
|
+
failure: unsupportedNative(identity.native)
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
const native = identity.native === "gradle" || identity.native && connection.supports("native-xcode-toolchain") ? identity.native : void 0;
|
|
1469
1210
|
const reply = await connection.request("build.offer", {
|
|
1470
1211
|
repo: identity.repo,
|
|
1471
1212
|
...identity.lockfile ? { lockfile: identity.lockfile } : {},
|
|
1472
|
-
...identity.rubyVersion ? { rubyVersion: identity.rubyVersion } : {}
|
|
1213
|
+
...native ? { native } : identity.rubyVersion ? { rubyVersion: identity.rubyVersion } : {}
|
|
1473
1214
|
}, offerMs);
|
|
1474
1215
|
const failure = replyError(reply);
|
|
1475
1216
|
if (failure || !("result" in reply)) {
|
|
@@ -1502,7 +1243,8 @@ async function chooseBuildMachine({ projectRoot, target, mode, here, note, machi
|
|
|
1502
1243
|
const rubyVersion = readRubyVersion(projectRoot) ?? void 0;
|
|
1503
1244
|
const asked = await Promise.all(machines.map((credential) => probeMachine(credential, {
|
|
1504
1245
|
...identity,
|
|
1505
|
-
rubyVersion
|
|
1246
|
+
rubyVersion,
|
|
1247
|
+
...target.platform !== "macos" ? { native: target.native } : {}
|
|
1506
1248
|
})));
|
|
1507
1249
|
const { order, reasons, candidates } = pickOffer({
|
|
1508
1250
|
localEnabled: selected !== "auto" || automaticMachineEnabled("build", "local"),
|
|
@@ -1540,6 +1282,10 @@ async function chooseBuildMachine({ projectRoot, target, mode, here, note, machi
|
|
|
1540
1282
|
automatic: selected === "auto"
|
|
1541
1283
|
};
|
|
1542
1284
|
}
|
|
1285
|
+
function validArtifactIdentity(request, fingerprint) {
|
|
1286
|
+
if (request.platform === "macos") return true;
|
|
1287
|
+
return request.native?.provider === "gradle" ? fingerprint === null : typeof fingerprint === "string" && fingerprint.length > 0;
|
|
1288
|
+
}
|
|
1543
1289
|
const ARTIFACT_NAME = {
|
|
1544
1290
|
ios: /^[^/]+\.app$/,
|
|
1545
1291
|
android: /^[^/]+\.apk$/,
|
|
@@ -1550,12 +1296,12 @@ const ARTIFACT_NAME = {
|
|
|
1550
1296
|
* `RESUME_WINDOW_MS`, shorter than the machine's grace for a dropped connection, has passed. Returns why it
|
|
1551
1297
|
* could not.
|
|
1552
1298
|
*/
|
|
1553
|
-
async function resumeJob(credential, job, abandoned, signal) {
|
|
1299
|
+
async function resumeJob(credential, job, abandoned, signal, native) {
|
|
1554
1300
|
const deadline = Date.now() + RESUME_WINDOW_MS;
|
|
1555
1301
|
let delay = RESUME_DELAY_MS;
|
|
1556
1302
|
let last = "no attempt";
|
|
1557
1303
|
while (!abandoned()) {
|
|
1558
|
-
const target = pinnedEndpoint$1(credential);
|
|
1304
|
+
const target = await pinnedEndpoint$1(credential);
|
|
1559
1305
|
const connection = typeof target === "string" ? {
|
|
1560
1306
|
failure: target,
|
|
1561
1307
|
refused: false
|
|
@@ -1568,6 +1314,10 @@ async function resumeJob(credential, job, abandoned, signal) {
|
|
|
1568
1314
|
connection.close();
|
|
1569
1315
|
break;
|
|
1570
1316
|
}
|
|
1317
|
+
if (native && !connection.supports(NATIVE_BUILD_FEATURE[native])) {
|
|
1318
|
+
connection.close();
|
|
1319
|
+
return unsupportedNative(native);
|
|
1320
|
+
}
|
|
1571
1321
|
const early = [];
|
|
1572
1322
|
connection.onProgress((event) => early.push(event));
|
|
1573
1323
|
const cancel = () => connection.close();
|
|
@@ -1600,10 +1350,11 @@ async function resumeJob(credential, job, abandoned, signal) {
|
|
|
1600
1350
|
return `no connection to it within ${RESUME_WINDOW_MS / 6e4} min (${last})`;
|
|
1601
1351
|
}
|
|
1602
1352
|
/** Mirrors the checkout on the machine: the manifest in pages, then every blob it lacks. */
|
|
1603
|
-
async function syncSource(connection, identity, onEnter) {
|
|
1353
|
+
async function syncSource(connection, identity, onEnter, native) {
|
|
1604
1354
|
onEnter("sync");
|
|
1605
1355
|
const syncStarted = Date.now();
|
|
1606
|
-
const manifest = sourceManifest(identity.repoRoot);
|
|
1356
|
+
const manifest = native && "declaration" in native ? nativeGradleTransfer(join(identity.repoRoot, identity.project), native.declaration).files : native ? nativeTransferManifest(identity.repoRoot, native, sourceManifest(identity.repoRoot)) : sourceManifest(identity.repoRoot);
|
|
1357
|
+
if (native && "declaration" in native && manifestDigest(manifest) !== native.digest) return { failure: "The native Gradle source changed before upload." };
|
|
1607
1358
|
const bySha = new Map(manifest.map((file) => [file.sha256, file]));
|
|
1608
1359
|
const missing = [];
|
|
1609
1360
|
for (let index = 0; index < manifest.length || index === 0;) {
|
|
@@ -1647,12 +1398,20 @@ async function syncSource(connection, identity, onEnter) {
|
|
|
1647
1398
|
digest: manifestDigest(manifest)
|
|
1648
1399
|
};
|
|
1649
1400
|
}
|
|
1401
|
+
function stagedArtifactEscapes(stagingDir, artifactPath, hasContents) {
|
|
1402
|
+
if ((hasContents ? [artifactPath, join(artifactPath, "Contents")] : [artifactPath]).some((path) => lstatSync(path, { throwIfNoEntry: false })?.isSymbolicLink())) return true;
|
|
1403
|
+
if (!existsSync(artifactPath)) return false;
|
|
1404
|
+
const inside = relative(realpathSync(stagingDir), realpathSync(artifactPath));
|
|
1405
|
+
return inside === ".." || inside.startsWith(`..${sep}`) || isAbsolute(inside);
|
|
1406
|
+
}
|
|
1650
1407
|
/**
|
|
1651
1408
|
* Builds on the chosen machine and brings the `.app` or `.apk` back into `stagingDir`, verified against the
|
|
1652
1409
|
* sha256 the machine reports. The caller re-fingerprints and stores it.
|
|
1653
1410
|
*/
|
|
1654
1411
|
async function offloadBuild({ choice, expectedFingerprint, request, stagingDir, onPhase, onEnter, onRecord, note }) {
|
|
1655
1412
|
const { identity } = choice;
|
|
1413
|
+
const native = request.platform !== "macos" ? request.native : void 0;
|
|
1414
|
+
let sourceDigest = null;
|
|
1656
1415
|
const started = Date.now();
|
|
1657
1416
|
const fail = (reason) => {
|
|
1658
1417
|
closeOffload(choice);
|
|
@@ -1684,6 +1443,8 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1684
1443
|
signal?.addEventListener("abort", cancel, { once: true });
|
|
1685
1444
|
try {
|
|
1686
1445
|
throwIfCancelled();
|
|
1446
|
+
if (!validArtifactIdentity(request, expectedFingerprint)) return fail("The build request has an invalid reusable artifact identity.");
|
|
1447
|
+
if (native?.provider === "xcode" && native.snapshot.hash !== expectedFingerprint) return fail("The native source snapshot does not match the requested artifact identity.");
|
|
1687
1448
|
let job = null;
|
|
1688
1449
|
let early = [];
|
|
1689
1450
|
let resuming = false;
|
|
@@ -1691,7 +1452,7 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1691
1452
|
if (resuming || settled) return;
|
|
1692
1453
|
resuming = true;
|
|
1693
1454
|
note(`offload: the connection to ${choice.machine} dropped (${why}); reattaching to the build there`);
|
|
1694
|
-
const resumed = await resumeJob(choice.credential, job, () => settled, signal);
|
|
1455
|
+
const resumed = await resumeJob(choice.credential, job, () => settled, signal, native?.provider);
|
|
1695
1456
|
resuming = false;
|
|
1696
1457
|
if (settled) {
|
|
1697
1458
|
if (typeof resumed !== "string") resumed.connection.close();
|
|
@@ -1739,13 +1500,19 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1739
1500
|
if (moveOn(reason)) continue;
|
|
1740
1501
|
return fail(reason);
|
|
1741
1502
|
}
|
|
1742
|
-
|
|
1503
|
+
if (native && !choice.connection.supports(NATIVE_BUILD_FEATURE[native.provider])) {
|
|
1504
|
+
const reason = unsupportedNative(native.provider);
|
|
1505
|
+
if (moveOn(reason)) continue;
|
|
1506
|
+
return fail(reason);
|
|
1507
|
+
}
|
|
1508
|
+
const synced = await syncSource(choice.connection, identity, onEnter, nativeTransferInput(native));
|
|
1743
1509
|
throwIfCancelled();
|
|
1744
1510
|
if ("failure" in synced) {
|
|
1745
1511
|
if (moveOn(synced.failure)) continue;
|
|
1746
1512
|
return fail(synced.failure);
|
|
1747
1513
|
}
|
|
1748
1514
|
({syncMs, uploadedBytes} = synced);
|
|
1515
|
+
sourceDigest = synced.digest;
|
|
1749
1516
|
onPhase("sync", `${synced.files} files, uploaded ${synced.uploaded} (${mb(uploadedBytes)}) in ${seconds(syncMs)}`);
|
|
1750
1517
|
workerStarted = Date.now();
|
|
1751
1518
|
early = [];
|
|
@@ -1757,6 +1524,15 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1757
1524
|
project: identity.project,
|
|
1758
1525
|
platform: request.platform,
|
|
1759
1526
|
fingerprint: request.platform === "macos" ? synced.digest : expectedFingerprint,
|
|
1527
|
+
...native ? { native: {
|
|
1528
|
+
provider: native.provider,
|
|
1529
|
+
sourceDigest: synced.digest,
|
|
1530
|
+
...native.provider === "xcode" ? {
|
|
1531
|
+
cacheKey: native.cacheKey,
|
|
1532
|
+
arch: native.arch,
|
|
1533
|
+
parameters: native.snapshot.parameters
|
|
1534
|
+
} : { inputs: native.transfer.declaration }
|
|
1535
|
+
} } : {},
|
|
1760
1536
|
...request.platform === "macos" ? { macos: {
|
|
1761
1537
|
product: request.product,
|
|
1762
1538
|
infoPlist: request.infoPlist,
|
|
@@ -1766,6 +1542,7 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1766
1542
|
} } : {
|
|
1767
1543
|
configuration: request.platform === "ios" ? request.configuration : null,
|
|
1768
1544
|
scheme: request.platform === "ios" ? request.scheme : null,
|
|
1545
|
+
iosProjectPath: request.platform === "ios" ? request.iosProjectPath ?? null : null,
|
|
1769
1546
|
runtime: request.platform === "ios" ? request.runtime : null,
|
|
1770
1547
|
packageName: packageName(join(identity.repoRoot, identity.project)),
|
|
1771
1548
|
isExpo: request.isExpo,
|
|
@@ -1797,6 +1574,8 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1797
1574
|
connection.onProgress(null);
|
|
1798
1575
|
const workerMs = Date.now() - workerStarted;
|
|
1799
1576
|
if (result.ok !== true) return fail(`${String(result.code ?? "failed")}: ${String(result.message ?? "")}`);
|
|
1577
|
+
if (native && (result.sourceDigest !== sourceDigest || result.fingerprint !== expectedFingerprint)) return fail("The worker returned a different native source or artifact identity.");
|
|
1578
|
+
if (native?.provider === "gradle" && !ANDROID_PACKAGE.test(String(result.androidPackage ?? ""))) return fail("The worker returned no verified native APK package.");
|
|
1800
1579
|
const artifact = result.artifact;
|
|
1801
1580
|
const name = typeof artifact?.name === "string" ? artifact.name : "";
|
|
1802
1581
|
if (!ARTIFACT_NAME[request.platform].test(name) || typeof artifact.sha256 !== "string") return fail(`the machine reported no .${request.platform === "android" ? "apk" : "app"} artifact`);
|
|
@@ -1847,6 +1626,7 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1847
1626
|
throwIfCancelled();
|
|
1848
1627
|
rmSync(archive, { force: true });
|
|
1849
1628
|
const artifactPath = join(stagingDir, name);
|
|
1629
|
+
if (stagedArtifactEscapes(stagingDir, artifactPath, request.platform === "macos")) return fail(`fetch: ${machine} returned ${name} as or through a symbolic link or outside the staging directory`);
|
|
1850
1630
|
if (request.platform === "ios" && !existsSync(join(artifactPath, "Info.plist"))) return fail(`fetch: ${name} has no Info.plist`);
|
|
1851
1631
|
if (request.platform === "android" && !(lstatSync(artifactPath, { throwIfNoEntry: false })?.isFile() ?? false)) return fail(`fetch: ${name} is not a file`);
|
|
1852
1632
|
if (request.platform === "macos") {
|
|
@@ -1868,6 +1648,7 @@ async function offloadBuild({ choice, expectedFingerprint, request, stagingDir,
|
|
|
1868
1648
|
sha256: digest
|
|
1869
1649
|
} } : {},
|
|
1870
1650
|
compilationCache: request.platform === "ios" ? compilationActivity(result.compilationCache) : COMPILATION_CACHE_UNAVAILABLE,
|
|
1651
|
+
...native?.provider === "gradle" ? { androidPackage: result.androidPackage } : {},
|
|
1871
1652
|
ccache: request.platform === "android" ? ccacheActivity(result.compilationCache) : CCACHE_UNAVAILABLE,
|
|
1872
1653
|
timings: {
|
|
1873
1654
|
offerMs: choice.offerMs,
|
|
@@ -1979,9 +1760,10 @@ function offloadCheck(projectRoot, targets) {
|
|
|
1979
1760
|
};
|
|
1980
1761
|
probe.connection.close();
|
|
1981
1762
|
resolved ??= targets();
|
|
1763
|
+
const resolvedTargets = await resolved;
|
|
1982
1764
|
const problems = [];
|
|
1983
|
-
for (const target of
|
|
1984
|
-
const labeled =
|
|
1765
|
+
for (const target of resolvedTargets) for (const problem of offerProblems(probe.offer, target)) {
|
|
1766
|
+
const labeled = resolvedTargets.length > 1 && !SHARED_PROBLEMS.has(problem.code) ? {
|
|
1985
1767
|
...problem,
|
|
1986
1768
|
reason: `${PLATFORM_LABEL[target.platform]}: ${problem.reason}`
|
|
1987
1769
|
} : problem;
|
|
@@ -1994,4 +1776,4 @@ function offloadCheck(projectRoot, targets) {
|
|
|
1994
1776
|
};
|
|
1995
1777
|
}
|
|
1996
1778
|
//#endregion
|
|
1997
|
-
export {
|
|
1779
|
+
export { inspectDeviceHostMachines as C, hostingMachines as S, requireLocalBuild as _, nativeTransferRefusal as a, inspectBuildMachines as b, offloadPlacement as c, simulatorRuntime as d, setRemoteLogSink as f, parseBuildMachineOption as g, namedBuildMachine as h, closeOffload as i, placementLoad as l, buildPlacementSettings as m, buildPlacementCandidates as n, offloadBuild as o, OffloadRefusal as p, chooseBuildMachine as r, offloadCheck as s, BuildConnection as t, remotePhaseText as u, resolveBuildMachine as v, configuredMachines as x, resolveBuildPlacement as y };
|
package/dist/collector-run.d.mts
CHANGED
|
@@ -18,6 +18,10 @@ interface ExecOptions {
|
|
|
18
18
|
* `runFile` and `runFileAsync`: reject nonempty stderr even when the command exits successfully.
|
|
19
19
|
*/
|
|
20
20
|
rejectStderr?: boolean;
|
|
21
|
+
/** `runFile` and `runFileAsync`: run in its own session with all stdio ignored and return ''; the child reports through a file, so a background process it leaves behind cannot hold a pipe open. */
|
|
22
|
+
detachedSilent?: boolean;
|
|
23
|
+
/** `runFile` and `runFileAsync`: exact values replaced with `***` in every string field of a thrown error, including message, stack, stdout, stderr and output. */
|
|
24
|
+
redact?: readonly string[];
|
|
21
25
|
}
|
|
22
26
|
interface Executor {
|
|
23
27
|
run(cmd: string, opts?: ExecOptions): string;
|