@limrun/api 0.48.1 → 0.48.2
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 +4 -0
- package/exec-client.d.mts +64 -0
- package/exec-client.d.mts.map +1 -1
- package/exec-client.d.ts +64 -0
- package/exec-client.d.ts.map +1 -1
- package/exec-client.js +67 -1
- package/exec-client.js.map +1 -1
- package/exec-client.mjs +67 -1
- package/exec-client.mjs.map +1 -1
- package/index.d.mts +1 -1
- package/index.d.mts.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js.map +1 -1
- package/index.mjs.map +1 -1
- package/package.json +1 -1
- package/resources/xcode-instances-helpers.d.mts +34 -1
- package/resources/xcode-instances-helpers.d.mts.map +1 -1
- package/resources/xcode-instances-helpers.d.ts +34 -1
- package/resources/xcode-instances-helpers.d.ts.map +1 -1
- package/resources/xcode-instances-helpers.js +26 -2
- package/resources/xcode-instances-helpers.js.map +1 -1
- package/resources/xcode-instances-helpers.mjs +26 -2
- package/resources/xcode-instances-helpers.mjs.map +1 -1
- package/src/exec-client.ts +124 -1
- package/src/index.ts +3 -0
- package/src/resources/xcode-instances-helpers.ts +65 -2
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
package/src/exec-client.ts
CHANGED
|
@@ -51,6 +51,28 @@ export type XcodeBuildExecRequest = {
|
|
|
51
51
|
sdk?: 'iphonesimulator' | 'iphoneos' | 'watchsimulator' | 'watchos';
|
|
52
52
|
configuration?: 'Debug' | 'Release';
|
|
53
53
|
artifactName?: string;
|
|
54
|
+
/**
|
|
55
|
+
* xcodebuild action. 'build' (default) produces one .app; with a
|
|
56
|
+
* simulator attached it is installed there. 'build-for-testing' compiles
|
|
57
|
+
* the scheme's test targets and, with a simulator attached, runs them
|
|
58
|
+
* there, streaming per-case results as 'xctest' events; the exec exits
|
|
59
|
+
* non-zero when tests fail. Requires the iphonesimulator sdk.
|
|
60
|
+
*/
|
|
61
|
+
action?: 'build' | 'build-for-testing';
|
|
62
|
+
/**
|
|
63
|
+
* Run only these tests, in xcodebuild's -only-testing format:
|
|
64
|
+
* Target[/Class[/method]]. Mutually exclusive with skipTesting; only
|
|
65
|
+
* valid with action 'build-for-testing'.
|
|
66
|
+
*/
|
|
67
|
+
onlyTesting?: string[];
|
|
68
|
+
/** Skip these tests, in xcodebuild's -skip-testing format. */
|
|
69
|
+
skipTesting?: string[];
|
|
70
|
+
/**
|
|
71
|
+
* When false, 'build-for-testing' compiles the test targets but does not
|
|
72
|
+
* run them even when a simulator is attached. Servers without support
|
|
73
|
+
* ignore the field and still run the tests.
|
|
74
|
+
*/
|
|
75
|
+
runTests?: boolean;
|
|
54
76
|
};
|
|
55
77
|
xcodegen?: {
|
|
56
78
|
spec?: string;
|
|
@@ -234,6 +256,12 @@ export type ExecOptions = {
|
|
|
234
256
|
apiUrl: string;
|
|
235
257
|
token: string;
|
|
236
258
|
log?: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
|
|
259
|
+
/**
|
|
260
|
+
* Called for each XCTest event as it streams from a build-for-testing run:
|
|
261
|
+
* one per finished test case, then the terminal summary. The same data
|
|
262
|
+
* lands accumulated on ExecResult.xctest.
|
|
263
|
+
*/
|
|
264
|
+
onXctestEvent?: (event: XctestEvent) => void;
|
|
237
265
|
};
|
|
238
266
|
|
|
239
267
|
export type ExecResult = {
|
|
@@ -252,6 +280,13 @@ export type ExecResult = {
|
|
|
252
280
|
* without a playstore request, or when the server predates the feature.
|
|
253
281
|
*/
|
|
254
282
|
playstore?: PlaystoreEvent;
|
|
283
|
+
/**
|
|
284
|
+
* XCTest results streamed by a build-for-testing run: every finished case
|
|
285
|
+
* plus the terminal summary. Absent for plain builds and servers that
|
|
286
|
+
* predate the feature. A missing summary means the run died partway; the
|
|
287
|
+
* cases already streamed still stand.
|
|
288
|
+
*/
|
|
289
|
+
xctest?: { cases: XctestCaseEvent[]; summary?: XctestSummaryEvent };
|
|
255
290
|
/**
|
|
256
291
|
* True when the client gave up waiting for the build's event stream. The
|
|
257
292
|
* exit code is fabricated in that case; the remote build may still be
|
|
@@ -268,6 +303,32 @@ export type ExecResult = {
|
|
|
268
303
|
incomplete?: { reason: 'timeout' | 'stream-lost' | 'stream-closed'; message: string };
|
|
269
304
|
};
|
|
270
305
|
|
|
306
|
+
export type XctestCaseEvent = {
|
|
307
|
+
type: 'case';
|
|
308
|
+
/**
|
|
309
|
+
* The test class as XCTest reports it: module-qualified for Swift
|
|
310
|
+
* ('AppTests.LoginTests'), bare for Objective-C.
|
|
311
|
+
*/
|
|
312
|
+
testClass: string;
|
|
313
|
+
method: string;
|
|
314
|
+
passed: boolean;
|
|
315
|
+
durationMs: number;
|
|
316
|
+
/** First failure message of a failed case. */
|
|
317
|
+
failureMessage?: string;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
export type XctestSummaryEvent = {
|
|
321
|
+
type: 'summary';
|
|
322
|
+
passed: number;
|
|
323
|
+
failed: number;
|
|
324
|
+
/** True only once every test in scope ran to completion. */
|
|
325
|
+
planFinished: boolean;
|
|
326
|
+
/** Crash or per-target failure detail when the run degraded. */
|
|
327
|
+
error?: string;
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
export type XctestEvent = XctestCaseEvent | XctestSummaryEvent;
|
|
331
|
+
|
|
271
332
|
export type AppStoreEvent = {
|
|
272
333
|
/** 'unknown' means an App Store upload event arrived but its payload was unreadable. */
|
|
273
334
|
state: 'uploading' | 'processing' | 'accepted' | 'failed' | 'unknown';
|
|
@@ -389,6 +450,13 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
389
450
|
private detached = false;
|
|
390
451
|
private appStoreEvent: AppStoreEvent | null = null;
|
|
391
452
|
private playstoreEvent: PlaystoreEvent | null = null;
|
|
453
|
+
private xctestCases: XctestCaseEvent[] = [];
|
|
454
|
+
private xctestSummary: XctestSummaryEvent | null = null;
|
|
455
|
+
// Highest server-assigned event id already dispatched. The server numbers
|
|
456
|
+
// every frame and replays the stream from the start on reconnect; skipping
|
|
457
|
+
// ids at or below this mark drops the replayed prefix for every event type
|
|
458
|
+
// at once, and stays correct if the server ever honors Last-Event-ID.
|
|
459
|
+
private lastSeenEventId = -1;
|
|
392
460
|
private readonly options: ExecOptions;
|
|
393
461
|
private readonly log: (level: 'debug' | 'info' | 'warn' | 'error', msg: string) => void;
|
|
394
462
|
|
|
@@ -555,7 +623,18 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
555
623
|
let timeoutMs = 3600 * 1000;
|
|
556
624
|
if (request.command === 'xcodebuild' && request.testflight) {
|
|
557
625
|
timeoutMs += (Math.max(0, request.testflight.waitTimeoutSeconds ?? 0) + 900) * 1000;
|
|
558
|
-
}
|
|
626
|
+
}
|
|
627
|
+
if (request.command === 'xcodebuild' && request.xcodebuild?.action === 'build-for-testing') {
|
|
628
|
+
// The test run happens after the build: products sync plus the suite,
|
|
629
|
+
// which the server caps at an hour per target. Two hours cover the
|
|
630
|
+
// common unit-plus-UI shape; the client cannot know the target count, so
|
|
631
|
+
// schemes with more targets may need a liveness-based deadline instead.
|
|
632
|
+
// Kept even with runTests: false: servers without run suppression still
|
|
633
|
+
// execute the suite, and the cap is an upper bound so a pure build
|
|
634
|
+
// finishes early regardless.
|
|
635
|
+
timeoutMs += 2 * 3600 * 1000;
|
|
636
|
+
}
|
|
637
|
+
if (request.command === 'run') {
|
|
559
638
|
timeoutMs = (Math.max(1, request.timeoutSeconds ?? 3600) + 60) * 1000;
|
|
560
639
|
}
|
|
561
640
|
let exitCode: number;
|
|
@@ -629,6 +708,14 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
629
708
|
...('additionalMetadata' in request ? request.additionalMetadata ?? {} : {}),
|
|
630
709
|
...(this.appStoreEvent ? { appstore: this.appStoreEvent } : {}),
|
|
631
710
|
...(this.playstoreEvent ? { playstore: this.playstoreEvent } : {}),
|
|
711
|
+
...(this.xctestCases.length > 0 || this.xctestSummary ?
|
|
712
|
+
{
|
|
713
|
+
xctest: {
|
|
714
|
+
cases: this.xctestCases,
|
|
715
|
+
...(this.xctestSummary ? { summary: this.xctestSummary } : {}),
|
|
716
|
+
},
|
|
717
|
+
}
|
|
718
|
+
: {}),
|
|
632
719
|
...(timedOut ? { timedOut } : {}),
|
|
633
720
|
...(incomplete ? { incomplete } : {}),
|
|
634
721
|
};
|
|
@@ -768,6 +855,15 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
768
855
|
this.streamDeadSince = 0;
|
|
769
856
|
proofOfLifeThisCycle = true;
|
|
770
857
|
lastStreamError = undefined;
|
|
858
|
+
const eventId = message.id !== undefined ? parseInt(message.id, 10) : NaN;
|
|
859
|
+
if (!Number.isNaN(eventId)) {
|
|
860
|
+
if (eventId <= this.lastSeenEventId) {
|
|
861
|
+
// A reconnect replays the stream from the start; this frame was
|
|
862
|
+
// already dispatched on an earlier connection.
|
|
863
|
+
return;
|
|
864
|
+
}
|
|
865
|
+
this.lastSeenEventId = eventId;
|
|
866
|
+
}
|
|
771
867
|
const data = typeof message.data === 'string' ? message.data : String(message.data ?? '');
|
|
772
868
|
const eventType = message.event;
|
|
773
869
|
if (eventType === 'command') {
|
|
@@ -795,6 +891,33 @@ export class ExecChildProcess implements PromiseLike<ExecResult> {
|
|
|
795
891
|
this.playstoreEvent = { state: 'unknown' };
|
|
796
892
|
this.log('warn', `SSE playstore event has invalid data: ${data}`);
|
|
797
893
|
}
|
|
894
|
+
} else if (eventType === 'xctest') {
|
|
895
|
+
let event: XctestEvent | undefined;
|
|
896
|
+
try {
|
|
897
|
+
const parsed = JSON.parse(data) as XctestEvent;
|
|
898
|
+
if (parsed?.type === 'case') {
|
|
899
|
+
this.xctestCases.push(parsed);
|
|
900
|
+
event = parsed;
|
|
901
|
+
} else if (parsed?.type === 'summary') {
|
|
902
|
+
this.xctestSummary = parsed;
|
|
903
|
+
event = parsed;
|
|
904
|
+
} else {
|
|
905
|
+
// The simulator owns the frame vocabulary and may grow it; the
|
|
906
|
+
// callback's contract is cases and summaries only.
|
|
907
|
+
this.log('debug', `SSE xctest event of unknown type ignored: ${data}`);
|
|
908
|
+
}
|
|
909
|
+
} catch {
|
|
910
|
+
this.log('warn', `SSE xctest event has invalid data: ${data}`);
|
|
911
|
+
}
|
|
912
|
+
if (event) {
|
|
913
|
+
try {
|
|
914
|
+
this.options.onXctestEvent?.(event);
|
|
915
|
+
} catch (err) {
|
|
916
|
+
// The consumer's callback, not the frame: the run must not be
|
|
917
|
+
// derailed and the log must not blame the payload.
|
|
918
|
+
this.log('warn', `onXctestEvent callback threw: ${String(err)}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
798
921
|
} else if (eventType === 'exitCode') {
|
|
799
922
|
const exitCode = parseInt(data, 10);
|
|
800
923
|
if (Number.isNaN(exitCode)) {
|
package/src/index.ts
CHANGED
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
type ExecChildProcess,
|
|
21
21
|
type ExecRequest,
|
|
22
22
|
type WebhookConfig,
|
|
23
|
+
type XctestEvent,
|
|
23
24
|
} from '../exec-client';
|
|
24
25
|
|
|
25
26
|
export type { AppStoreUploadConfig, WebhookConfig } from '../exec-client';
|
|
@@ -117,6 +118,33 @@ export type XcodeProjectConfig = {
|
|
|
117
118
|
* discovery picks the wrong bundle.
|
|
118
119
|
*/
|
|
119
120
|
artifactName?: string;
|
|
121
|
+
/**
|
|
122
|
+
* xcodebuild action. 'build' (default) produces one .app; with a simulator
|
|
123
|
+
* attached it is installed there. 'build-for-testing' compiles the scheme's
|
|
124
|
+
* test targets and, with a simulator attached, runs them there: per-case
|
|
125
|
+
* results stream through XcodeBuildOptions.onXctestEvent and accumulate on
|
|
126
|
+
* ExecResult.xctest, and the exec exits non-zero when tests fail. Requires
|
|
127
|
+
* the iphonesimulator sdk. Older limbuild servers reject the field.
|
|
128
|
+
*/
|
|
129
|
+
action?: 'build' | 'build-for-testing';
|
|
130
|
+
/**
|
|
131
|
+
* Run only these tests, in xcodebuild's -only-testing format:
|
|
132
|
+
* Target[/Class[/method]]. A bare target runs that target unfiltered;
|
|
133
|
+
* targets named by no entry are skipped. Mutually exclusive with
|
|
134
|
+
* skipTesting; only valid with action 'build-for-testing'.
|
|
135
|
+
*/
|
|
136
|
+
onlyTesting?: string[];
|
|
137
|
+
/**
|
|
138
|
+
* Skip these tests, in xcodebuild's -skip-testing format. A bare target
|
|
139
|
+
* skips that whole target.
|
|
140
|
+
*/
|
|
141
|
+
skipTesting?: string[];
|
|
142
|
+
/**
|
|
143
|
+
* When false, 'build-for-testing' compiles the test targets but does not
|
|
144
|
+
* run them even when a simulator is attached. Servers without support
|
|
145
|
+
* ignore the field and still run the tests.
|
|
146
|
+
*/
|
|
147
|
+
runTests?: boolean;
|
|
120
148
|
};
|
|
121
149
|
|
|
122
150
|
export type XcodeGenConfig = {
|
|
@@ -260,6 +288,12 @@ export type XcodeBuildOptions = {
|
|
|
260
288
|
* build. Older limbuild servers silently ignore this option.
|
|
261
289
|
*/
|
|
262
290
|
webhook?: WebhookConfig;
|
|
291
|
+
/**
|
|
292
|
+
* Called for each XCTest event streamed by a build-for-testing run: one per
|
|
293
|
+
* finished test case, then the terminal summary. The same data accumulates
|
|
294
|
+
* on ExecResult.xctest.
|
|
295
|
+
*/
|
|
296
|
+
onXctestEvent?: (event: XctestEvent) => void;
|
|
263
297
|
};
|
|
264
298
|
|
|
265
299
|
export type SimulatorInstallState =
|
|
@@ -843,6 +877,25 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
843
877
|
if (options?.reactNative?.devServerURL && settings?.configuration === 'Release') {
|
|
844
878
|
throw new Error('reactNative.devServerURL is only supported for Debug builds');
|
|
845
879
|
}
|
|
880
|
+
if (settings?.onlyTesting?.length && settings?.skipTesting?.length) {
|
|
881
|
+
throw new Error('onlyTesting and skipTesting are mutually exclusive; pass one');
|
|
882
|
+
}
|
|
883
|
+
if (
|
|
884
|
+
(settings?.onlyTesting?.length || settings?.skipTesting?.length) &&
|
|
885
|
+
settings?.action !== 'build-for-testing'
|
|
886
|
+
) {
|
|
887
|
+
throw new Error("onlyTesting/skipTesting require action: 'build-for-testing'");
|
|
888
|
+
}
|
|
889
|
+
if (settings?.runTests === false && settings?.action !== 'build-for-testing') {
|
|
890
|
+
throw new Error("runTests: false requires action: 'build-for-testing'");
|
|
891
|
+
}
|
|
892
|
+
if (
|
|
893
|
+
settings?.action === 'build-for-testing' &&
|
|
894
|
+
settings.sdk !== undefined &&
|
|
895
|
+
settings.sdk !== 'iphonesimulator'
|
|
896
|
+
) {
|
|
897
|
+
throw new Error("action: 'build-for-testing' requires the iphonesimulator sdk");
|
|
898
|
+
}
|
|
846
899
|
if (options?.buildSettings) {
|
|
847
900
|
validateBuildSettings(options.buildSettings);
|
|
848
901
|
}
|
|
@@ -862,6 +915,16 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
862
915
|
...(options?.webhook && { webhook: options.webhook }),
|
|
863
916
|
};
|
|
864
917
|
|
|
918
|
+
// One options object for both exec call sites, so a future per-exec
|
|
919
|
+
// option cannot be added to one branch and silently dropped from the
|
|
920
|
+
// other.
|
|
921
|
+
const execOptions = {
|
|
922
|
+
apiUrl,
|
|
923
|
+
token,
|
|
924
|
+
log,
|
|
925
|
+
...(options?.onXctestEvent && { onXctestEvent: options.onXctestEvent }),
|
|
926
|
+
};
|
|
927
|
+
|
|
865
928
|
if (options?.upload && 'assetName' in options.upload) {
|
|
866
929
|
const requestPromise = mintAssetUploadUrls(
|
|
867
930
|
client.assets,
|
|
@@ -875,14 +938,14 @@ export class XcodeInstances extends GeneratedXcodeInstances {
|
|
|
875
938
|
request.additionalMetadata = { signedDownloadUrl: asset.signedDownloadUrl };
|
|
876
939
|
return request;
|
|
877
940
|
});
|
|
878
|
-
return exec(requestPromise,
|
|
941
|
+
return exec(requestPromise, execOptions);
|
|
879
942
|
}
|
|
880
943
|
|
|
881
944
|
if (options?.upload && 'signedUploadUrl' in options.upload) {
|
|
882
945
|
request.signedUploadUrl = options.upload.signedUploadUrl;
|
|
883
946
|
}
|
|
884
947
|
|
|
885
|
-
return exec(request,
|
|
948
|
+
return exec(request, execOptions);
|
|
886
949
|
},
|
|
887
950
|
|
|
888
951
|
run(commandLine: string, options?: XcodeRunOptions): ExecChildProcess {
|
package/src/version.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const VERSION = '0.48.
|
|
1
|
+
export const VERSION = '0.48.2'; // x-release-please-version
|
package/version.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.48.
|
|
1
|
+
export declare const VERSION = "0.48.2";
|
|
2
2
|
//# sourceMappingURL=version.d.mts.map
|
package/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const VERSION = "0.48.
|
|
1
|
+
export declare const VERSION = "0.48.2";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/version.js
CHANGED
package/version.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const VERSION = '0.48.
|
|
1
|
+
export const VERSION = '0.48.2'; // x-release-please-version
|
|
2
2
|
//# sourceMappingURL=version.mjs.map
|