lim 0.13.1 → 0.14.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.
@@ -0,0 +1,70 @@
1
+ import type { RbeStatus, XcodeClient } from '@limrun/api';
2
+ /**
3
+ * Deterministic, side-effect-light helpers behind `lim xcode rbe`, extracted
4
+ * from the command so they can be unit-tested without the oclif lifecycle.
5
+ */
6
+ export type Sleep = (ms: number) => Promise<void>;
7
+ export declare const defaultSleep: Sleep;
8
+ export declare function isTransientError(err: unknown): boolean;
9
+ /**
10
+ * Retries `fn` on transient gateway errors. Non-transient errors propagate
11
+ * immediately. After exhausting `attempts`, throws the last error.
12
+ */
13
+ export declare function retryTransient<T>(fn: () => Promise<T>, opts?: {
14
+ sleep?: Sleep;
15
+ log?: (msg: string) => void;
16
+ attempts?: number;
17
+ }): Promise<T>;
18
+ /**
19
+ * Polls `getRbe` until the stack is `running` (with a usable frontend port and
20
+ * Xcode version), starting from the `initial` status returned by `startRbe`.
21
+ * Each poll is wrapped in retryTransient so a transient blip mid-startup does
22
+ * not abort. Throws when the stack ends in `failed`, stays `starting` past
23
+ * `maxAttempts`, or reports `running` without the fields the caller needs.
24
+ */
25
+ export declare function waitForRbeRunning(client: Pick<XcodeClient, 'getRbe'>, initial: RbeStatus, opts?: {
26
+ sleep?: Sleep;
27
+ maxAttempts?: number;
28
+ }): Promise<Required<Pick<RbeStatus, 'frontendPort' | 'xcodeVersion'>> & RbeStatus>;
29
+ /**
30
+ * Builds the argv for the detached child that holds the tunnel: re-invokes this
31
+ * same CLI as `xcode rbe --serve --id <id> --port <port> [--api-key <key>]`.
32
+ * `scriptPath` is the CLI entry (process.argv[1]).
33
+ */
34
+ export declare function buildServeChildArgs(opts: {
35
+ scriptPath: string;
36
+ id: string;
37
+ port: number;
38
+ apiKey?: string;
39
+ }): string[];
40
+ /**
41
+ * A running background tunnel's coordinates, persisted to `.limrun/rbe.pid` so
42
+ * `lim xcode rbe --stop` can find and stop it (adb prints the PID and forgets
43
+ * it; we keep just enough to offer a discoverable stop). Lives under `.limrun/`,
44
+ * which is self-gitignored.
45
+ */
46
+ export type RbePidInfo = {
47
+ pid: number;
48
+ instanceId: string;
49
+ port: number;
50
+ };
51
+ export declare function rbePidFilePath(workspaceRoot: string): string;
52
+ export declare function writeRbePidFile(workspaceRoot: string, info: RbePidInfo): void;
53
+ export declare function readRbePidFile(workspaceRoot: string): RbePidInfo | null;
54
+ export declare function clearRbePidFile(workspaceRoot: string): void;
55
+ /** Whether `pid` is a live process (treats EPERM — owned by another user — as alive). */
56
+ export declare function isProcessAlive(pid: number): boolean;
57
+ /**
58
+ * Resolves when `port` on `host` is bindable; rejects with a friendly message
59
+ * when it is already in use, or with the raw error otherwise. Frees the port
60
+ * immediately (the probe listener is closed before resolving).
61
+ */
62
+ export declare function assertLocalPortFree(port: number, host?: string): Promise<void>;
63
+ /**
64
+ * Resolves true if a TCP connection to `host:port` is accepted (something is
65
+ * listening), false otherwise (connection refused, error, or no answer within
66
+ * `timeoutMs`). The timeout only bites when a connect neither completes nor is
67
+ * refused (a dropped SYN); on loopback a closed port refuses instantly.
68
+ */
69
+ export declare function probePortOpen(port: number, host?: string, timeoutMs?: number): Promise<boolean>;
70
+ //# sourceMappingURL=rbe-session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbe-session.d.ts","sourceRoot":"","sources":["../../src/lib/rbe-session.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE1D;;;GAGG;AAEH,MAAM,MAAM,KAAK,GAAG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;AAClD,eAAO,MAAM,YAAY,EAAE,KAAiE,CAAC;AAc7F,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAGtD;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAAC,CAAC,EACpC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAO,GAC3E,OAAO,CAAC,CAAC,CAAC,CAoBZ;AAED;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,EACnC,OAAO,EAAE,SAAS,EAClB,IAAI,GAAE;IAAE,KAAK,CAAC,EAAE,KAAK,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,GACjD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,GAAG,cAAc,CAAC,CAAC,GAAG,SAAS,CAAC,CAYjF;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,EAAE,CAoBX;AAED;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE5D;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAE7E;AAED,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAUvE;AAED,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAM3D;AAED,yFAAyF;AACzF,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAOnD;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAezF;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAc,EAAE,SAAS,SAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAajG"}
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.defaultSleep = void 0;
7
+ exports.isTransientError = isTransientError;
8
+ exports.retryTransient = retryTransient;
9
+ exports.waitForRbeRunning = waitForRbeRunning;
10
+ exports.buildServeChildArgs = buildServeChildArgs;
11
+ exports.rbePidFilePath = rbePidFilePath;
12
+ exports.writeRbePidFile = writeRbePidFile;
13
+ exports.readRbePidFile = readRbePidFile;
14
+ exports.clearRbePidFile = clearRbePidFile;
15
+ exports.isProcessAlive = isProcessAlive;
16
+ exports.assertLocalPortFree = assertLocalPortFree;
17
+ exports.probePortOpen = probePortOpen;
18
+ const fs_1 = __importDefault(require("fs"));
19
+ const net_1 = __importDefault(require("net"));
20
+ const path_1 = __importDefault(require("path"));
21
+ const defaultSleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
22
+ exports.defaultSleep = defaultSleep;
23
+ /**
24
+ * Matches the transient gateway / dropped-connection errors that occur right
25
+ * after an instance is created, when its proxy path is not fully serving yet.
26
+ *
27
+ * The HTTP part anchors on the exact `failed: <code>` shape `directInstanceHttpError`
28
+ * produces (`${operation} failed: ${status}...`), so a bare 502/503/504 buried in
29
+ * a response body or an instance id does NOT count as transient. Fetch-thrown
30
+ * network errors carry their own names and are matched directly. A 404 never
31
+ * reaches here — `readRbeResponse` maps it to RbeUnsupportedError first.
32
+ */
33
+ const TRANSIENT = /failed: (?:502|503|504)\b|\bEOF\b|ECONNRESET|ECONNREFUSED|socket hang up|fetch failed/i;
34
+ function isTransientError(err) {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ return TRANSIENT.test(message);
37
+ }
38
+ /**
39
+ * Retries `fn` on transient gateway errors. Non-transient errors propagate
40
+ * immediately. After exhausting `attempts`, throws the last error.
41
+ */
42
+ async function retryTransient(fn, opts = {}) {
43
+ const sleep = opts.sleep ?? exports.defaultSleep;
44
+ const attempts = opts.attempts ?? 5;
45
+ let lastErr;
46
+ for (let attempt = 1; attempt <= attempts; attempt++) {
47
+ try {
48
+ return await fn();
49
+ }
50
+ catch (err) {
51
+ if (!isTransientError(err)) {
52
+ throw err;
53
+ }
54
+ lastErr = err;
55
+ if (attempt < attempts) {
56
+ const message = err instanceof Error ? err.message : String(err);
57
+ opts.log?.(`Instance not serving yet (${message.trim()}); retrying...`);
58
+ await sleep(2000 * attempt);
59
+ }
60
+ }
61
+ }
62
+ throw lastErr;
63
+ }
64
+ /**
65
+ * Polls `getRbe` until the stack is `running` (with a usable frontend port and
66
+ * Xcode version), starting from the `initial` status returned by `startRbe`.
67
+ * Each poll is wrapped in retryTransient so a transient blip mid-startup does
68
+ * not abort. Throws when the stack ends in `failed`, stays `starting` past
69
+ * `maxAttempts`, or reports `running` without the fields the caller needs.
70
+ */
71
+ async function waitForRbeRunning(client, initial, opts = {}) {
72
+ const sleep = opts.sleep ?? exports.defaultSleep;
73
+ const maxAttempts = opts.maxAttempts ?? 15;
74
+ let status = initial;
75
+ for (let attempt = 0; status.state === 'starting' && attempt < maxAttempts; attempt++) {
76
+ await sleep(2000);
77
+ status = await retryTransient(() => client.getRbe(), { sleep });
78
+ }
79
+ if (status.state !== 'running' || !status.frontendPort || !status.xcodeVersion) {
80
+ throw new Error(`Remote-execution stack failed to start: ${status.error ?? `state is ${status.state}`}`);
81
+ }
82
+ return status;
83
+ }
84
+ /**
85
+ * Builds the argv for the detached child that holds the tunnel: re-invokes this
86
+ * same CLI as `xcode rbe --serve --id <id> --port <port> [--api-key <key>]`.
87
+ * `scriptPath` is the CLI entry (process.argv[1]).
88
+ */
89
+ function buildServeChildArgs(opts) {
90
+ // --no-create: the child must never own instance creation. The parent already
91
+ // resolved/created the instance and started RBE on it; if that instance has
92
+ // vanished by the time the child resolves it, the child should fail cleanly
93
+ // rather than spin up a stray instance the parent never started a stack on.
94
+ const args = [
95
+ opts.scriptPath,
96
+ 'xcode',
97
+ 'rbe',
98
+ '--serve',
99
+ '--no-create',
100
+ '--id',
101
+ opts.id,
102
+ '--port',
103
+ String(opts.port),
104
+ ];
105
+ if (opts.apiKey) {
106
+ args.push('--api-key', opts.apiKey);
107
+ }
108
+ return args;
109
+ }
110
+ function rbePidFilePath(workspaceRoot) {
111
+ return path_1.default.join(workspaceRoot, '.limrun', 'rbe.pid');
112
+ }
113
+ function writeRbePidFile(workspaceRoot, info) {
114
+ fs_1.default.writeFileSync(rbePidFilePath(workspaceRoot), JSON.stringify(info));
115
+ }
116
+ function readRbePidFile(workspaceRoot) {
117
+ try {
118
+ const parsed = JSON.parse(fs_1.default.readFileSync(rbePidFilePath(workspaceRoot), 'utf8'));
119
+ if (parsed && typeof parsed.pid === 'number') {
120
+ return parsed;
121
+ }
122
+ return null;
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
128
+ function clearRbePidFile(workspaceRoot) {
129
+ try {
130
+ fs_1.default.unlinkSync(rbePidFilePath(workspaceRoot));
131
+ }
132
+ catch {
133
+ // already gone
134
+ }
135
+ }
136
+ /** Whether `pid` is a live process (treats EPERM — owned by another user — as alive). */
137
+ function isProcessAlive(pid) {
138
+ try {
139
+ process.kill(pid, 0);
140
+ return true;
141
+ }
142
+ catch (err) {
143
+ return err.code === 'EPERM';
144
+ }
145
+ }
146
+ /**
147
+ * Resolves when `port` on `host` is bindable; rejects with a friendly message
148
+ * when it is already in use, or with the raw error otherwise. Frees the port
149
+ * immediately (the probe listener is closed before resolving).
150
+ */
151
+ async function assertLocalPortFree(port, host = '127.0.0.1') {
152
+ await new Promise((resolve, reject) => {
153
+ const probe = net_1.default.createServer();
154
+ probe.once('error', (err) => {
155
+ if (err.code === 'EADDRINUSE') {
156
+ reject(new Error(`Local port ${port} is already in use. Pass --port to choose another.`));
157
+ }
158
+ else {
159
+ reject(err);
160
+ }
161
+ });
162
+ probe.once('listening', () => {
163
+ probe.close(() => resolve());
164
+ });
165
+ probe.listen(port, host);
166
+ });
167
+ }
168
+ /**
169
+ * Resolves true if a TCP connection to `host:port` is accepted (something is
170
+ * listening), false otherwise (connection refused, error, or no answer within
171
+ * `timeoutMs`). The timeout only bites when a connect neither completes nor is
172
+ * refused (a dropped SYN); on loopback a closed port refuses instantly.
173
+ */
174
+ function probePortOpen(port, host = '127.0.0.1', timeoutMs = 500) {
175
+ return new Promise((resolve) => {
176
+ const socket = new net_1.default.Socket();
177
+ const done = (open) => {
178
+ socket.destroy();
179
+ resolve(open);
180
+ };
181
+ socket.setTimeout(timeoutMs);
182
+ socket.once('connect', () => done(true));
183
+ socket.once('timeout', () => done(false));
184
+ socket.once('error', () => done(false));
185
+ socket.connect(port, host);
186
+ });
187
+ }
188
+ //# sourceMappingURL=rbe-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbe-session.js","sourceRoot":"","sources":["../../src/lib/rbe-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Generates the .limrun/ workspace companion for `lim xcode rbe`: a Bazel
3
+ * package pinning the remote fleet's Xcode version and an rc fragment with the
4
+ * remote-execution flags under the `limrun` config. The caller learns the
5
+ * fleet's version key from the instance's RBE status, so the generated config
6
+ * always matches the fleet without any user action; rerunning the command
7
+ * after a fleet Xcode upgrade regenerates the pin.
8
+ */
9
+ export declare const LIMRUN_DIR = ".limrun";
10
+ export declare const TRY_IMPORT_LINE = "try-import %workspace%/.limrun/bazelrc";
11
+ /**
12
+ * Finds the Bazel workspace root by walking up from `startDir` to the first
13
+ * ancestor containing a MODULE.bazel / WORKSPACE / WORKSPACE.bazel, mirroring
14
+ * how Bazel itself locates the workspace when run from a subdirectory. Returns
15
+ * null when no workspace is found up to the filesystem root. The generated
16
+ * `.limrun/` and the `try-import` must live at this root, since `%workspace%`
17
+ * in bazelrc resolves here regardless of the directory the build is run from.
18
+ */
19
+ export declare function findBazelWorkspaceRoot(startDir: string): string | null;
20
+ /**
21
+ * Reads the workspace's pinned Bazel major version from `.bazelversion`, or
22
+ * null when the file is absent or its first line has no leading integer.
23
+ *
24
+ * Used to decide whether the generated BUILD must `load` the Xcode rules from
25
+ * apple_support: in Bazel 9 they are no longer native globals and must be
26
+ * loaded, while in Bazel 8 they ARE native globals and the apple_support rule
27
+ * impls `fail()` on the unmigrated Bazel, so loading them there breaks
28
+ * analysis. The generator runs in the workspace on the client, so the file is
29
+ * the authoritative signal for the Bazel that bazelisk will launch.
30
+ */
31
+ export declare function detectBazelMajorVersion(workspaceDir: string): number | null;
32
+ /**
33
+ * Whether to treat the workspace as Bazel 9+ for RBE config: true when the
34
+ * detected major version is >= 9, OR unknown (no `.bazelversion` means bazelisk
35
+ * runs the latest release, which is 9+). This single predicate decides both
36
+ * emitting the apple_support Xcode-rule loads and surfacing the SHA256 digest
37
+ * hint, so the two stay in lockstep.
38
+ */
39
+ export declare function isBazel9OrLater(bazelMajor: number | null): boolean;
40
+ /**
41
+ * Renders the generated Bazel package pinning the Xcode version to the fleet's.
42
+ *
43
+ * remoteVersionKey is the fleet's `xcodebuild -version` in major.minor.patch.build
44
+ * form (e.g. 26.4.0.17E192).
45
+ *
46
+ * Uses Bazel's remote/local `xcode_config` split with BOTH sets pointing at the
47
+ * SAME fleet pin (rather than a single `default=/versions=` bucket, which
48
+ * resolves with availability UNKNOWN and leaves Apple/Swift actions eligible for
49
+ * local execution). With local == remote, `--xcode_version` resolves as
50
+ * "mutually available" (BOTH), which declares up front that this build uses only
51
+ * the fleet's Xcode AND keeps apple_support from emitting its remote-only
52
+ * "...specified, but it is not available locally..." DEBUG notice (that notice
53
+ * fires only when the pinned version is in `remote_versions` but not
54
+ * `local_versions`).
55
+ *
56
+ * We intentionally do NOT name the client's own local Xcode: under
57
+ * `--config=limrun` every action runs remotely (`--spawn_strategy=remote` +
58
+ * `--noremote_local_fallback`), so a local DEVELOPER_DIR is never resolved.
59
+ * Declaring a distinct local version would only reintroduce that DEBUG notice on
60
+ * a client whose Xcode differs from the fleet's. (The fleet pin is used, not
61
+ * Bazel's `@local_config_xcode//:host_available_xcodes`: that repo is not
62
+ * visible from the main module under bzlmod and is never generated off-darwin.)
63
+ *
64
+ * When `emitLoads` is true (Bazel 9+), the Xcode rules are loaded from
65
+ * apple_support; on Bazel 8 they are native globals and MUST NOT be loaded
66
+ * (the apple_support rule impls fail on the unmigrated Bazel).
67
+ */
68
+ export declare function renderXcodeConfigBuild(remoteVersionKey: string, emitLoads: boolean): string;
69
+ /**
70
+ * Renders the rc fragment with the remote-execution flags under
71
+ * --config=limrun.
72
+ *
73
+ * - `--xcode_version` pins the fleet's version: without it, a mac client
74
+ * lacking that exact version has no mutual version and silently falls back
75
+ * to its LOCAL default, shipping the wrong version to the remote worker via
76
+ * XCODE_VERSION_OVERRIDE (the worker then rejects it).
77
+ * - `--strategy=SwiftCompile=remote` / `--strategy=Genrule=remote` override
78
+ * mnemonic-specific strategies a workspace may pin (rules_swift defaults
79
+ * SwiftCompile to a local persistent `worker`; repos often pin Genrule to
80
+ * `standalone`). Those run locally and break RBE: a local Swift worker can't
81
+ * run on a Linux client at all, and on a mac it would demand the fleet's
82
+ * Xcode locally. --spawn_strategy=remote does not override per-mnemonic
83
+ * pins, so these explicit overrides are required.
84
+ * - PATH includes /usr/sbin:/sbin so genrules that probe `sysctl` (e.g.
85
+ * `hw.logicalcpu` for `make -j`) resolve it on the worker.
86
+ * - `--extra_execution_platforms` is emitted ONLY for non-mac clients: a Linux
87
+ * host has no auto-detected darwin execution platform, so the Apple/Swift
88
+ * toolchain (exec_compatible_with macos) needs one registered to route
89
+ * actions to the mac RBE pool. On a mac it is HARMFUL: it makes bazel run
90
+ * exec-config actions on the local host instead of the remote worker, which
91
+ * then demand a local Xcode.
92
+ */
93
+ export declare function renderLimrunBazelrc(port: number, versionKey: string, isMacClient: boolean): string;
94
+ /**
95
+ * Idempotently ensures the workspace .bazelrc try-imports the generated
96
+ * fragment. Creates .bazelrc when missing. Returns true when the file changed.
97
+ */
98
+ export declare function ensureTryImport(workspaceDir: string): boolean;
99
+ export type RbeWorkspaceFiles = {
100
+ buildFile: string;
101
+ bazelrcFragment: string;
102
+ bazelrcUpdated: boolean;
103
+ };
104
+ /**
105
+ * Writes .limrun/{BUILD,bazelrc,.gitignore} into the workspace and wires the
106
+ * try-import. The .gitignore containing "*" makes the directory self-ignoring
107
+ * so nothing else in the user's repo needs to change.
108
+ */
109
+ export declare function writeRbeWorkspaceFiles(workspaceDir: string, xcodeVersionKey: string, port: number, isMacClient?: boolean, bazelMajor?: number | null): RbeWorkspaceFiles;
110
+ //# sourceMappingURL=rbe-workspace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbe-workspace.d.ts","sourceRoot":"","sources":["../../src/lib/rbe-workspace.ts"],"names":[],"mappings":"AAGA;;;;;;;GAOG;AAEH,eAAO,MAAM,UAAU,YAAY,CAAC;AACpC,eAAO,MAAM,eAAe,2CAA2C,CAAC;AAKxE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAYtE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,uBAAuB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS3E;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAElE;AA6CD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,MAAM,CAmC3F;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,OAAO,GAAG,MAAM,CAgBlG;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAiB7D;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,OAAO,CAAC;CACzB,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,MAAM,EACpB,eAAe,EAAE,MAAM,EACvB,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,OAAuC,EACpD,UAAU,GAAE,MAAM,GAAG,IAA4C,GAChE,iBAAiB,CAcnB"}
@@ -0,0 +1,263 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.TRY_IMPORT_LINE = exports.LIMRUN_DIR = void 0;
7
+ exports.findBazelWorkspaceRoot = findBazelWorkspaceRoot;
8
+ exports.detectBazelMajorVersion = detectBazelMajorVersion;
9
+ exports.isBazel9OrLater = isBazel9OrLater;
10
+ exports.renderXcodeConfigBuild = renderXcodeConfigBuild;
11
+ exports.renderLimrunBazelrc = renderLimrunBazelrc;
12
+ exports.ensureTryImport = ensureTryImport;
13
+ exports.writeRbeWorkspaceFiles = writeRbeWorkspaceFiles;
14
+ const fs_1 = __importDefault(require("fs"));
15
+ const path_1 = __importDefault(require("path"));
16
+ /**
17
+ * Generates the .limrun/ workspace companion for `lim xcode rbe`: a Bazel
18
+ * package pinning the remote fleet's Xcode version and an rc fragment with the
19
+ * remote-execution flags under the `limrun` config. The caller learns the
20
+ * fleet's version key from the instance's RBE status, so the generated config
21
+ * always matches the fleet without any user action; rerunning the command
22
+ * after a fleet Xcode upgrade regenerates the pin.
23
+ */
24
+ exports.LIMRUN_DIR = '.limrun';
25
+ exports.TRY_IMPORT_LINE = 'try-import %workspace%/.limrun/bazelrc';
26
+ const TRY_IMPORT_COMMENT = '# Added by lim xcode rbe: loads the generated remote-execution config.';
27
+ const WORKSPACE_MARKERS = ['MODULE.bazel', 'WORKSPACE', 'WORKSPACE.bazel'];
28
+ /**
29
+ * Finds the Bazel workspace root by walking up from `startDir` to the first
30
+ * ancestor containing a MODULE.bazel / WORKSPACE / WORKSPACE.bazel, mirroring
31
+ * how Bazel itself locates the workspace when run from a subdirectory. Returns
32
+ * null when no workspace is found up to the filesystem root. The generated
33
+ * `.limrun/` and the `try-import` must live at this root, since `%workspace%`
34
+ * in bazelrc resolves here regardless of the directory the build is run from.
35
+ */
36
+ function findBazelWorkspaceRoot(startDir) {
37
+ let dir = path_1.default.resolve(startDir);
38
+ for (;;) {
39
+ if (WORKSPACE_MARKERS.some((m) => fs_1.default.existsSync(path_1.default.join(dir, m)))) {
40
+ return dir;
41
+ }
42
+ const parent = path_1.default.dirname(dir);
43
+ if (parent === dir) {
44
+ return null;
45
+ }
46
+ dir = parent;
47
+ }
48
+ }
49
+ /**
50
+ * Reads the workspace's pinned Bazel major version from `.bazelversion`, or
51
+ * null when the file is absent or its first line has no leading integer.
52
+ *
53
+ * Used to decide whether the generated BUILD must `load` the Xcode rules from
54
+ * apple_support: in Bazel 9 they are no longer native globals and must be
55
+ * loaded, while in Bazel 8 they ARE native globals and the apple_support rule
56
+ * impls `fail()` on the unmigrated Bazel, so loading them there breaks
57
+ * analysis. The generator runs in the workspace on the client, so the file is
58
+ * the authoritative signal for the Bazel that bazelisk will launch.
59
+ */
60
+ function detectBazelMajorVersion(workspaceDir) {
61
+ try {
62
+ const raw = fs_1.default.readFileSync(path_1.default.join(workspaceDir, '.bazelversion'), 'utf8');
63
+ const firstLine = (raw.split('\n', 1)[0] ?? '').trim();
64
+ const match = firstLine.match(/^(\d+)/);
65
+ return match ? Number(match[1]) : null;
66
+ }
67
+ catch {
68
+ return null;
69
+ }
70
+ }
71
+ /**
72
+ * Whether to treat the workspace as Bazel 9+ for RBE config: true when the
73
+ * detected major version is >= 9, OR unknown (no `.bazelversion` means bazelisk
74
+ * runs the latest release, which is 9+). This single predicate decides both
75
+ * emitting the apple_support Xcode-rule loads and surfacing the SHA256 digest
76
+ * hint, so the two stay in lockstep.
77
+ */
78
+ function isBazel9OrLater(bazelMajor) {
79
+ return bazelMajor === null || bazelMajor >= 9;
80
+ }
81
+ /** Major.minor short alias (e.g. "26.4") used for the SDK default and --xcode_version. */
82
+ function shortVersion(versionKey) {
83
+ const parts = versionKey.split('.');
84
+ if (parts.length < 3) {
85
+ throw new Error(`unexpected Xcode version key from the instance: ${versionKey}`);
86
+ }
87
+ return `${parts[0]}.${parts[1]}`;
88
+ }
89
+ /** Renders one xcode_version rule from a major.minor.patch.build version key. */
90
+ function renderXcodeVersionRule(name, versionKey) {
91
+ // shortVersion validates the key shape (major.minor.patch[.build]) and yields
92
+ // the major.minor used for both the SDK defaults and the short alias.
93
+ const sdk = shortVersion(versionKey);
94
+ const parts = versionKey.split('.');
95
+ const fullAlias = `${parts[0]}.${parts[1]}.${parts[2]}`;
96
+ return `xcode_version(
97
+ name = "${name}",
98
+ aliases = [
99
+ "${sdk}",
100
+ "${fullAlias}",
101
+ ],
102
+ default_ios_sdk_version = "${sdk}",
103
+ default_macos_sdk_version = "${sdk}",
104
+ default_tvos_sdk_version = "${sdk}",
105
+ default_watchos_sdk_version = "${sdk}",
106
+ version = "${versionKey}",
107
+ )`;
108
+ }
109
+ /**
110
+ * Renders an `available_xcodes` set with a single member that is also its
111
+ * mandatory default. Both sets the BUILD file emits (remote, local) are
112
+ * single-version sets of this shape, pointing at the same fleet pin.
113
+ */
114
+ function renderAvailableXcodes(name, target) {
115
+ return `available_xcodes(
116
+ name = "${name}",
117
+ default = "${target}",
118
+ versions = ["${target}"],
119
+ )`;
120
+ }
121
+ /**
122
+ * Renders the generated Bazel package pinning the Xcode version to the fleet's.
123
+ *
124
+ * remoteVersionKey is the fleet's `xcodebuild -version` in major.minor.patch.build
125
+ * form (e.g. 26.4.0.17E192).
126
+ *
127
+ * Uses Bazel's remote/local `xcode_config` split with BOTH sets pointing at the
128
+ * SAME fleet pin (rather than a single `default=/versions=` bucket, which
129
+ * resolves with availability UNKNOWN and leaves Apple/Swift actions eligible for
130
+ * local execution). With local == remote, `--xcode_version` resolves as
131
+ * "mutually available" (BOTH), which declares up front that this build uses only
132
+ * the fleet's Xcode AND keeps apple_support from emitting its remote-only
133
+ * "...specified, but it is not available locally..." DEBUG notice (that notice
134
+ * fires only when the pinned version is in `remote_versions` but not
135
+ * `local_versions`).
136
+ *
137
+ * We intentionally do NOT name the client's own local Xcode: under
138
+ * `--config=limrun` every action runs remotely (`--spawn_strategy=remote` +
139
+ * `--noremote_local_fallback`), so a local DEVELOPER_DIR is never resolved.
140
+ * Declaring a distinct local version would only reintroduce that DEBUG notice on
141
+ * a client whose Xcode differs from the fleet's. (The fleet pin is used, not
142
+ * Bazel's `@local_config_xcode//:host_available_xcodes`: that repo is not
143
+ * visible from the main module under bzlmod and is never generated off-darwin.)
144
+ *
145
+ * When `emitLoads` is true (Bazel 9+), the Xcode rules are loaded from
146
+ * apple_support; on Bazel 8 they are native globals and MUST NOT be loaded
147
+ * (the apple_support rule impls fail on the unmigrated Bazel).
148
+ */
149
+ function renderXcodeConfigBuild(remoteVersionKey, emitLoads) {
150
+ // Bazel 9 migrated xcode_version/available_xcodes/xcode_config out of native
151
+ // globals into apple_support; they must be loaded there. The repo_name
152
+ // @build_bazel_apple_support is the apple_support module convention.
153
+ const loads = emitLoads ?
154
+ `load("@build_bazel_apple_support//xcode:xcode_version.bzl", "xcode_version")
155
+ load("@build_bazel_apple_support//xcode:available_xcodes.bzl", "available_xcodes")
156
+ load("@build_bazel_apple_support//xcode:xcode_config.bzl", "xcode_config")
157
+
158
+ `
159
+ : '';
160
+ const remoteRule = renderXcodeVersionRule('remote_xcode', remoteVersionKey);
161
+ return `# Generated by lim xcode rbe. Do not edit; rerun the command to refresh.
162
+ #
163
+ # Pins the Xcode version Bazel uses to the limrun fleet's Xcode, independent of
164
+ # any Xcode installed on this machine. Both the remote and local sets point at
165
+ # the SAME pin so --xcode_version resolves as mutually available (no
166
+ # apple_support remote-only DEBUG notice); under --config=limrun all actions run
167
+ # remotely, so a local DEVELOPER_DIR is never resolved. Selected via
168
+ # .limrun/bazelrc (--config=limrun).
169
+ ${loads}${remoteRule}
170
+
171
+ # Both sets point at the single fleet pin.
172
+ ${renderAvailableXcodes('remote_xcodes', ':remote_xcode')}
173
+
174
+ ${renderAvailableXcodes('local_xcodes', ':remote_xcode')}
175
+
176
+ xcode_config(
177
+ name = "remote_xcode_config",
178
+ remote_versions = ":remote_xcodes",
179
+ local_versions = ":local_xcodes",
180
+ )
181
+ `;
182
+ }
183
+ /**
184
+ * Renders the rc fragment with the remote-execution flags under
185
+ * --config=limrun.
186
+ *
187
+ * - `--xcode_version` pins the fleet's version: without it, a mac client
188
+ * lacking that exact version has no mutual version and silently falls back
189
+ * to its LOCAL default, shipping the wrong version to the remote worker via
190
+ * XCODE_VERSION_OVERRIDE (the worker then rejects it).
191
+ * - `--strategy=SwiftCompile=remote` / `--strategy=Genrule=remote` override
192
+ * mnemonic-specific strategies a workspace may pin (rules_swift defaults
193
+ * SwiftCompile to a local persistent `worker`; repos often pin Genrule to
194
+ * `standalone`). Those run locally and break RBE: a local Swift worker can't
195
+ * run on a Linux client at all, and on a mac it would demand the fleet's
196
+ * Xcode locally. --spawn_strategy=remote does not override per-mnemonic
197
+ * pins, so these explicit overrides are required.
198
+ * - PATH includes /usr/sbin:/sbin so genrules that probe `sysctl` (e.g.
199
+ * `hw.logicalcpu` for `make -j`) resolve it on the worker.
200
+ * - `--extra_execution_platforms` is emitted ONLY for non-mac clients: a Linux
201
+ * host has no auto-detected darwin execution platform, so the Apple/Swift
202
+ * toolchain (exec_compatible_with macos) needs one registered to route
203
+ * actions to the mac RBE pool. On a mac it is HARMFUL: it makes bazel run
204
+ * exec-config actions on the local host instead of the remote worker, which
205
+ * then demand a local Xcode.
206
+ */
207
+ function renderLimrunBazelrc(port, versionKey, isMacClient) {
208
+ const execPlatform = isMacClient ? '' : ('build:limrun --extra_execution_platforms=@build_bazel_apple_support//platforms:darwin_arm64\n');
209
+ return `# Generated by lim xcode rbe. Do not edit; rerun the command to refresh.
210
+ build:limrun --remote_executor=grpc://127.0.0.1:${port}
211
+ build:limrun --remote_default_exec_properties=OSFamily=Darwin
212
+ build:limrun --spawn_strategy=remote
213
+ build:limrun --noremote_local_fallback
214
+ build:limrun --strategy=SwiftCompile=remote
215
+ build:limrun --strategy=Genrule=remote
216
+ build:limrun --xcode_version_config=//.limrun:remote_xcode_config
217
+ build:limrun --xcode_version=${shortVersion(versionKey)}
218
+ ${execPlatform}build:limrun --action_env=PATH=/usr/bin:/bin:/usr/sbin:/sbin
219
+ `;
220
+ }
221
+ /**
222
+ * Idempotently ensures the workspace .bazelrc try-imports the generated
223
+ * fragment. Creates .bazelrc when missing. Returns true when the file changed.
224
+ */
225
+ function ensureTryImport(workspaceDir) {
226
+ const bazelrcPath = path_1.default.join(workspaceDir, '.bazelrc');
227
+ let current = '';
228
+ if (fs_1.default.existsSync(bazelrcPath)) {
229
+ current = fs_1.default.readFileSync(bazelrcPath, 'utf8');
230
+ // Match the try-import on a line basis (exact, uncommented) rather than a
231
+ // raw substring, so a commented-out occurrence (e.g. `# try-import ...`)
232
+ // doesn't make us skip wiring the active import.
233
+ const alreadyWired = current.split('\n').some((line) => line.trim() === exports.TRY_IMPORT_LINE);
234
+ if (alreadyWired) {
235
+ return false;
236
+ }
237
+ }
238
+ const block = `${TRY_IMPORT_COMMENT}\n${exports.TRY_IMPORT_LINE}\n`;
239
+ const next = current === '' ? block : `${current.replace(/\n*$/, '\n\n')}${block}`;
240
+ fs_1.default.writeFileSync(bazelrcPath, next);
241
+ return true;
242
+ }
243
+ /**
244
+ * Writes .limrun/{BUILD,bazelrc,.gitignore} into the workspace and wires the
245
+ * try-import. The .gitignore containing "*" makes the directory self-ignoring
246
+ * so nothing else in the user's repo needs to change.
247
+ */
248
+ function writeRbeWorkspaceFiles(workspaceDir, xcodeVersionKey, port, isMacClient = process.platform === 'darwin', bazelMajor = detectBazelMajorVersion(workspaceDir)) {
249
+ const dir = path_1.default.join(workspaceDir, exports.LIMRUN_DIR);
250
+ fs_1.default.mkdirSync(dir, { recursive: true });
251
+ const buildFile = path_1.default.join(dir, 'BUILD');
252
+ const bazelrcFragment = path_1.default.join(dir, 'bazelrc');
253
+ // Load the Xcode rules from apple_support on Bazel 9+, where they are no
254
+ // longer native globals. On a known Bazel 8 workspace they ARE native (and
255
+ // loading would fail), so omit the loads.
256
+ const emitLoads = isBazel9OrLater(bazelMajor);
257
+ fs_1.default.writeFileSync(buildFile, renderXcodeConfigBuild(xcodeVersionKey, emitLoads));
258
+ fs_1.default.writeFileSync(bazelrcFragment, renderLimrunBazelrc(port, xcodeVersionKey, isMacClient));
259
+ fs_1.default.writeFileSync(path_1.default.join(dir, '.gitignore'), '*\n');
260
+ const bazelrcUpdated = ensureTryImport(workspaceDir);
261
+ return { buildFile, bazelrcFragment, bazelrcUpdated };
262
+ }
263
+ //# sourceMappingURL=rbe-workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rbe-workspace.js","sourceRoot":"","sources":["../../src/lib/rbe-workspace.ts"],"names":[],"mappings":"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"}