diff --git a/CHANGELOG.md b/CHANGELOG.md index afe52a4234..f333750a43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ This is the log of notable changes to EAS CLI and related packages. ### 🎉 New features - [eas-cli] Add an opt-in `--max-idle-time-minutes` flag to `eas simulator`; the session stops automatically after that many minutes without activity. ([#4156](https://github.com/expo/eas-cli/pull/4156) by [@szdziedzic](https://github.com/szdziedzic)) +- [eas-cli] Add experimental `--resource-class` flag to `eas simulator`. ([#4268](https://github.com/expo/eas-cli/pull/4268) by [@gwdp](https://github.com/gwdp)) - [build-tools] Add worker-side SSH session helpers (upterm relay + session create/report/close). ([#4030](https://github.com/expo/eas-cli/pull/4030) by [@gwdp](https://github.com/gwdp)) - [worker] Wire an SSH session into the build lifecycle behind the workflow ssh flag. ([#4031](https://github.com/expo/eas-cli/pull/4031) by [@gwdp](https://github.com/gwdp)) diff --git a/packages/eas-cli/graphql.schema.json b/packages/eas-cli/graphql.schema.json index 2d72f6b23c..b496d8dc55 100644 --- a/packages/eas-cli/graphql.schema.json +++ b/packages/eas-cli/graphql.schema.json @@ -14158,6 +14158,35 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "customEvent", + "description": "A single custom event by the `id` of an `AppObserveCustomEvent`. Null when the app has no\nsuch event, including when it has aged out of retention.", + "args": [ + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "OBJECT", + "name": "AppObserveCustomEvent", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "customEventCounts", "description": null, @@ -14396,6 +14425,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -14453,6 +14502,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -14618,6 +14687,35 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "event", + "description": "A single metric event by the `id` of an `AppObserveEvent`. Null when the app has no such\nevent, including when it has aged out of retention or the id is not one this API issued.\n\n`sessionEventCount` and `userEventCount` are always null here: they are aggregates over a\ntime range, and a single event does not supply one.", + "args": [ + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "type": { + "kind": "OBJECT", + "name": "AppObserveEvent", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "events", "description": null, @@ -15028,7 +15126,7 @@ }, { "name": "updates", - "description": null, + "description": "Update download activity (the Recent updates list). Not available on the free tier or legacy plans.", "args": [ { "name": "input", @@ -16394,12 +16492,28 @@ "name": "platform", "description": null, "type": { - "kind": "NON_NULL", + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform. One of platform or platforms is required.", + "type": { + "kind": "LIST", "name": null, "ofType": { - "kind": "ENUM", - "name": "AppObservePlatform", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } } }, "defaultValue": null, @@ -16659,6 +16773,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "propertyFilters", "description": null, @@ -17524,6 +17658,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -17797,6 +17951,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "severity", "description": "Restrict to fatal-only or non-fatal-only errors.", @@ -18268,6 +18442,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -18561,6 +18755,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -19312,6 +19526,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "routeName", "description": null, @@ -19705,6 +19939,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "routeNames", "description": null, @@ -20094,6 +20348,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -20470,6 +20744,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -20510,7 +20804,7 @@ }, { "name": "downloadCount", - "description": "Downloads recorded in range across platforms; null when none were recorded (and always for the embedded bundle).", + "description": "Downloads recorded in range across platforms; null when none were recorded\n(and always for the embedded bundle). Download data is paid-only: on the\nfree tier and legacy plans this is always null.", "args": [], "type": { "kind": "SCALAR", @@ -20522,7 +20816,7 @@ }, { "name": "downloads", - "description": "One entry per platform with downloads in range; empty for the embedded bundle.", + "description": "One entry per platform with downloads in range; empty for the embedded bundle and on the free tier and legacy plans.", "args": [], "type": { "kind": "NON_NULL", @@ -21499,6 +21793,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -21896,6 +22210,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "routeName", "description": null, @@ -22403,6 +22737,26 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "platforms", + "description": "Filter to these platforms. Cannot be set together with platform.", + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "AppObservePlatform", + "ofType": null + } + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "startTime", "description": null, @@ -37355,6 +37709,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "resourceClass", + "description": null, + "type": { + "kind": "ENUM", + "name": "DeviceRunSessionResourceClass", + "ofType": null + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "sdkVersion", "description": "Expo SDK version used to select an Expo Go application archive. Only supported when expoGo is\ntrue. If omitted, the current Expo Go archive for the platform is used.", @@ -42847,6 +43213,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "applicationArchiveUrl", + "description": "Direct application archive URL installed and launched for this session. Null when\nthe session uses an EAS Build or starts without an app.", + "args": [], + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "artifacts", "description": null, @@ -42871,6 +43249,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "build", + "description": "EAS Build installed and launched for this session. Null when the session uses\na direct application archive URL or starts without an app.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "Build", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "createdAt", "description": null, @@ -43724,6 +44114,29 @@ } ] }, + { + "kind": "ENUM", + "name": "DeviceRunSessionResourceClass", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "LARGE", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "MEDIUM", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "ENUM", "name": "DeviceRunSessionStatus", @@ -75023,24 +75436,16 @@ "deprecationReason": null }, { - "name": "turtleBuildId", - "description": null, - "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null - }, - "defaultValue": null, - "isDeprecated": false, - "deprecationReason": null - }, - { - "name": "turtleJobRunId", + "name": "target", "description": null, "type": { - "kind": "SCALAR", - "name": "ID", - "ofType": null + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "INPUT_OBJECT", + "name": "TurtleSshTargetInput", + "ofType": null + } }, "defaultValue": null, "isDeprecated": false, @@ -75119,6 +75524,72 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "INPUT_OBJECT", + "name": "TurtleSshTargetInput", + "description": null, + "fields": null, + "inputFields": [ + { + "name": "id", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "ID", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "type", + "description": null, + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "ENUM", + "name": "TurtleSshTargetType", + "ofType": null + } + }, + "defaultValue": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "interfaces": null, + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "ENUM", + "name": "TurtleSshTargetType", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": [ + { + "name": "BUILD", + "description": null, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "JOB_RUN", + "description": null, + "isDeprecated": false, + "deprecationReason": null + } + ], + "possibleTypes": null + }, { "kind": "ENUM", "name": "TurtleSshTransportType", @@ -94676,6 +95147,18 @@ "isDeprecated": false, "deprecationReason": null }, + { + "name": "cancelReason", + "description": "Why the server canceled this run. Null for manually canceled runs and for\nruns canceled before the reason was recorded.", + "args": [], + "type": { + "kind": "UNION", + "name": "WorkflowRunCancelReason", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, { "name": "createdAt", "description": null, @@ -95040,6 +95523,45 @@ "enumValues": null, "possibleTypes": null }, + { + "kind": "UNION", + "name": "WorkflowRunCancelReason", + "description": null, + "fields": null, + "inputFields": null, + "interfaces": null, + "enumValues": null, + "possibleTypes": [ + { + "kind": "OBJECT", + "name": "WorkflowRunCancelReasonConcurrencyGroup", + "ofType": null + } + ] + }, + { + "kind": "OBJECT", + "name": "WorkflowRunCancelReasonConcurrencyGroup", + "description": null, + "fields": [ + { + "name": "cancelingWorkflowRun", + "description": "The newer run in the same concurrency group whose creation canceled this run.\nNull if that run no longer exists.", + "args": [], + "type": { + "kind": "OBJECT", + "name": "WorkflowRun", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [], + "enumValues": null, + "possibleTypes": null + }, { "kind": "OBJECT", "name": "WorkflowRunEdge", @@ -95621,6 +96143,12 @@ "description": null, "isDeprecated": false, "deprecationReason": null + }, + { + "name": "WAITING", + "description": null, + "isDeprecated": false, + "deprecationReason": null } ], "possibleTypes": null diff --git a/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts b/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts index c665d25ee0..8a71197805 100644 --- a/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts +++ b/packages/eas-cli/src/commands/simulator/__tests__/index.test.ts @@ -6,6 +6,7 @@ import { AppPlatform, CreateDeviceRunSessionMutation, DeviceRunSessionByIdQuery, + DeviceRunSessionResourceClass, DeviceRunSessionStatus, DeviceRunSessionType, JobRunStatus, @@ -522,6 +523,35 @@ describe(Simulator, () => { ); }); + it('omits resourceClass when --resource-class is not set', async () => { + const { command } = createCommand(['--platform', 'ios', '--non-interactive']); + await command.runAsync(); + + expect(mockCreateDeviceRunSessionAsync.mock.calls[0][1]).not.toHaveProperty('resourceClass'); + }); + + it.each([ + ['large', DeviceRunSessionResourceClass.Large], + ['medium', DeviceRunSessionResourceClass.Medium], + ] as const)( + 'forwards --resource-class %s to the create mutation', + async (flag, resourceClass) => { + const { command } = createCommand([ + '--platform', + 'ios', + '--non-interactive', + '--resource-class', + flag, + ]); + await command.runAsync(); + + expect(mockCreateDeviceRunSessionAsync).toHaveBeenCalledWith( + graphqlClient, + expect.objectContaining({ resourceClass }) + ); + } + ); + it('forwards --build-id to the create mutation', async () => { const { command } = createCommand([ '--platform', diff --git a/packages/eas-cli/src/commands/simulator/index.ts b/packages/eas-cli/src/commands/simulator/index.ts index df2b80aba3..247103306d 100644 --- a/packages/eas-cli/src/commands/simulator/index.ts +++ b/packages/eas-cli/src/commands/simulator/index.ts @@ -30,6 +30,8 @@ import { } from '../../simulator/env'; import { resolveExpoGoSdkVersionAsync } from '../../simulator/expoGo'; import { + DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE, + DEVICE_RUN_SESSION_RESOURCE_CLASS_FLAG_VALUES, DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE, DEVICE_RUN_SESSION_TYPE_FLAG_VALUES, DeviceRunSessionRemoteConfig, @@ -119,6 +121,11 @@ export default class Simulator extends EasCommand { 'Stop the simulator session automatically after this many minutes without session activity. When omitted, the session has no idle timeout and runs until its maximum duration.', min: 0, }), + 'resource-class': Flags.option({ + description: 'The instance type that will be used to run this simulator session', + hidden: true, + options: Object.values(DEVICE_RUN_SESSION_RESOURCE_CLASS_FLAG_VALUES), + })(), force: Flags.boolean({ description: '[default: true] Create a new simulator session even when an existing simulator session is present in the environment.', @@ -177,6 +184,9 @@ export default class Simulator extends EasCommand { const sdkVersionFromFlag = flags['sdk-version']?.trim() || undefined; const launchArgs = flags['launch-arg']; const openUrl = flags['open-url']?.trim() || undefined; + const resourceClass = flags['resource-class'] + ? DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE[flags['resource-class']] + : undefined; if (sdkVersionFromFlag && !flags['expo-go']) { throw new EasCommandError('The --sdk-version flag can only be used with --expo-go.'); @@ -233,6 +243,7 @@ export default class Simulator extends EasCommand { ...(expoGoSdkVersion ? { expoGo: true, sdkVersion: expoGoSdkVersion } : {}), ...(launchArgs?.length ? { launchArgs } : {}), ...(openUrl ? { openUrl } : {}), + ...(resourceClass ? { resourceClass } : {}), maxRunTimeMinutes: flags['max-duration-minutes'], maxIdleTimeMinutes: flags['max-idle-time-minutes'], }); diff --git a/packages/eas-cli/src/graphql/generated.ts b/packages/eas-cli/src/graphql/generated.ts index 798ba35a71..bb936114f3 100644 --- a/packages/eas-cli/src/graphql/generated.ts +++ b/packages/eas-cli/src/graphql/generated.ts @@ -2173,6 +2173,11 @@ export type AppNotificationPreferenceInput = { export type AppObserve = { __typename?: 'AppObserve'; appVersions: Array; + /** + * A single custom event by the `id` of an `AppObserveCustomEvent`. Null when the app has no + * such event, including when it has aged out of retention. + */ + customEvent?: Maybe; customEventCounts: AppObserveCustomEventCounts; customEventList: AppObserveCustomEventListConnection; customEventNames: AppObserveCustomEventNames; @@ -2185,6 +2190,14 @@ export type AppObserve = { errorStats: AppObserveErrorStats; /** Time-bucketed exception counts split into fatal vs non-fatal, for the stacked-bar chart. */ errorTimeSeries: AppObserveErrorTimeSeries; + /** + * A single metric event by the `id` of an `AppObserveEvent`. Null when the app has no such + * event, including when it has aged out of retention or the id is not one this API issued. + * + * `sessionEventCount` and `userEventCount` are always null here: they are aggregates over a + * time range, and a single event does not supply one. + */ + event?: Maybe; events: AppObserveEventsConnection; /** Highest Expo SDK version seen in telemetry over the trailing 30 days; null when none was reported. */ latestExpoSdkVersion?: Maybe; @@ -2208,6 +2221,7 @@ export type AppObserve = { * universe used by `appVersions` headline counts. */ uniqueActiveUserCount: Scalars['Int']['output']; + /** Update download activity (the Recent updates list). Not available on the free tier or legacy plans. */ updates: AppObserveUpdatesConnection; }; @@ -2217,6 +2231,11 @@ export type AppObserveAppVersionsArgs = { }; +export type AppObserveCustomEventArgs = { + id: Scalars['ID']['input']; +}; + + export type AppObserveCustomEventCountsArgs = { input: AppObserveCustomEventCountsInput; }; @@ -2242,6 +2261,7 @@ export type AppObserveCustomEventNamesArgs = { isEmbeddedUpdate?: InputMaybe; orderBy?: InputMaybe; platform?: InputMaybe; + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2249,6 +2269,7 @@ export type AppObserveCustomEventNamesArgs = { export type AppObserveEnvironmentsArgs = { endTime?: InputMaybe; platform?: InputMaybe; + platforms?: InputMaybe>; startTime?: InputMaybe; }; @@ -2273,6 +2294,11 @@ export type AppObserveErrorTimeSeriesArgs = { }; +export type AppObserveEventArgs = { + id: Scalars['ID']['input']; +}; + + export type AppObserveEventsArgs = { after?: InputMaybe; before?: InputMaybe; @@ -2479,7 +2505,9 @@ export type AppObserveCustomEventCountsInput = { environment?: InputMaybe; eventName: Scalars['String']['input']; isEmbeddedUpdate?: InputMaybe; - platform: AppObservePlatform; + platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. One of platform or platforms is required. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2508,6 +2536,8 @@ export type AppObserveCustomEventListFilter = { eventName?: InputMaybe; isEmbeddedUpdate?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; propertyFilters?: InputMaybe>; sessionId?: InputMaybe; startTime?: InputMaybe; @@ -2615,6 +2645,8 @@ export type AppObserveErrorGroupBreakdownInput = { fingerprint: Scalars['String']['input']; isEmbeddedUpdate?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2647,6 +2679,8 @@ export type AppObserveErrorGroupsInput = { isEmbeddedUpdate?: InputMaybe; orderBy?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; /** Restrict to fatal-only or non-fatal-only errors. */ severity?: InputMaybe; /** Restrict to a capture source (e.g. global). */ @@ -2710,6 +2744,8 @@ export type AppObserveErrorStatsInput = { environment?: InputMaybe; isEmbeddedUpdate?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2746,6 +2782,8 @@ export type AppObserveErrorTimeSeriesInput = { fingerprint?: InputMaybe; isEmbeddedUpdate?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2817,6 +2855,8 @@ export type AppObserveEventsFilter = { isEmbeddedUpdate?: InputMaybe; metricName?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; routeName?: InputMaybe; sessionId?: InputMaybe; startTime?: InputMaybe; @@ -2871,6 +2911,8 @@ export type AppObserveNavigationRoutesFilter = { endTime: Scalars['DateTime']['input']; environment?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; routeNames?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2924,6 +2966,8 @@ export type AppObserveOverviewEngagementInput = { endTime: Scalars['DateTime']['input']; environment?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2971,6 +3015,8 @@ export type AppObserveOverviewStabilityInput = { endTime: Scalars['DateTime']['input']; environment?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -2978,9 +3024,13 @@ export type AppObserveOverviewUpdate = { __typename?: 'AppObserveOverviewUpdate'; /** Null for the embedded bundle. */ appUpdateId?: Maybe; - /** Downloads recorded in range across platforms; null when none were recorded (and always for the embedded bundle). */ + /** + * Downloads recorded in range across platforms; null when none were recorded + * (and always for the embedded bundle). Download data is paid-only: on the + * free tier and legacy plans this is always null. + */ downloadCount?: Maybe; - /** One entry per platform with downloads in range; empty for the embedded bundle. */ + /** One entry per platform with downloads in range; empty for the embedded bundle and on the free tier and legacy plans. */ downloads: Array; eventCount: Scalars['Int']['output']; firstSeenAt: Scalars['DateTime']['output']; @@ -3092,6 +3142,8 @@ export type AppObserveReleasesInput = { environment?: InputMaybe; metricNames?: InputMaybe>; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -3129,6 +3181,8 @@ export type AppObserveTimeSeriesInput = { isEmbeddedUpdate?: InputMaybe; metricName: Scalars['String']['input']; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; routeName?: InputMaybe; startTime: Scalars['DateTime']['input']; }; @@ -3180,6 +3234,8 @@ export type AppObserveUpdatesInput = { first?: InputMaybe; orderBy?: InputMaybe; platform?: InputMaybe; + /** Filter to these platforms. Cannot be set together with platform. */ + platforms?: InputMaybe>; startTime: Scalars['DateTime']['input']; }; @@ -5293,6 +5349,7 @@ export type CreateDeviceRunSessionInput = { */ packageVersion?: InputMaybe; platform: AppPlatform; + resourceClass?: InputMaybe; /** * Expo SDK version used to select an Expo Go application archive. Only supported when expoGo is * true. If omitted, the current Expo Go archive for the platform is used. @@ -6043,7 +6100,17 @@ export type DeploymentsMutationDeleteWorkerDeploymentByIdentifierArgs = { export type DeviceRunSession = { __typename?: 'DeviceRunSession'; app: App; + /** + * Direct application archive URL installed and launched for this session. Null when + * the session uses an EAS Build or starts without an app. + */ + applicationArchiveUrl?: Maybe; artifacts: Array; + /** + * EAS Build installed and launched for this session. Null when the session uses + * a direct application archive URL or starts without an app. + */ + build?: Maybe; createdAt: Scalars['DateTime']['output']; finishedAt?: Maybe; id: Scalars['ID']['output']; @@ -6176,6 +6243,11 @@ export type DeviceRunSessionQueryByIdArgs = { export type DeviceRunSessionRemoteConfig = AgentDeviceRunSessionRemoteConfig | AppiumRunSessionRemoteConfig | ArgentRunSessionRemoteConfig | ServeSimRunSessionRemoteConfig; +export enum DeviceRunSessionResourceClass { + Large = 'LARGE', + Medium = 'MEDIUM' +} + export enum DeviceRunSessionStatus { Errored = 'ERRORED', InProgress = 'IN_PROGRESS', @@ -10644,8 +10716,7 @@ export type TurtleSshSessionMutation = { export type TurtleSshSessionMutationCreateOrUpdateTurtleSshSessionArgs = { connectionConfig: TurtleSshConnectionConfigInput; sessionSettings: TurtleSshSessionSettingsInput; - turtleBuildId?: InputMaybe; - turtleJobRunId?: InputMaybe; + target: TurtleSshTargetInput; }; export type TurtleSshSessionSettings = { @@ -10657,6 +10728,16 @@ export type TurtleSshSessionSettingsInput = { idleTimeoutSeconds: Scalars['Int']['input']; }; +export type TurtleSshTargetInput = { + id: Scalars['ID']['input']; + type: TurtleSshTargetType; +}; + +export enum TurtleSshTargetType { + Build = 'BUILD', + JobRun = 'JOB_RUN' +} + export enum TurtleSshTransportType { UptermV1 = 'UPTERM_V1' } @@ -13277,6 +13358,11 @@ export type WorkflowRun = ActivityTimelineProjectActivity & { __typename?: 'WorkflowRun'; activityTimestamp: Scalars['DateTime']['output']; actor?: Maybe; + /** + * Why the server canceled this run. Null for manually canceled runs and for + * runs canceled before the reason was recorded. + */ + cancelReason?: Maybe; createdAt: Scalars['DateTime']['output']; durationSeconds?: Maybe; errors: Array; @@ -13303,6 +13389,17 @@ export type WorkflowRun = ActivityTimelineProjectActivity & { workflowRevision?: Maybe; }; +export type WorkflowRunCancelReason = WorkflowRunCancelReasonConcurrencyGroup; + +export type WorkflowRunCancelReasonConcurrencyGroup = { + __typename?: 'WorkflowRunCancelReasonConcurrencyGroup'; + /** + * The newer run in the same concurrency group whose creation canceled this run. + * Null if that run no longer exists. + */ + cancelingWorkflowRun?: Maybe; +}; + export type WorkflowRunEdge = { __typename?: 'WorkflowRunEdge'; cursor: Scalars['String']['output']; @@ -13392,7 +13489,8 @@ export enum WorkflowRunStatus { Failure = 'FAILURE', InProgress = 'IN_PROGRESS', New = 'NEW', - Success = 'SUCCESS' + Success = 'SUCCESS', + Waiting = 'WAITING' } export type WorkflowRunTimeRangeInput = { diff --git a/packages/eas-cli/src/simulator/__tests__/utils.test.ts b/packages/eas-cli/src/simulator/__tests__/utils.test.ts index 7530bfcce1..2167994789 100644 --- a/packages/eas-cli/src/simulator/__tests__/utils.test.ts +++ b/packages/eas-cli/src/simulator/__tests__/utils.test.ts @@ -1,5 +1,6 @@ -import { DeviceRunSessionType } from '../../graphql/generated'; +import { DeviceRunSessionResourceClass, DeviceRunSessionType } from '../../graphql/generated'; import { + DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE, DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE, formatRemoteSessionInstructions, getRemoteSessionEnvironmentVariables, @@ -37,3 +38,14 @@ describe('Appium simulator configuration', () => { expect(instructions).not.toContain('https://appium.example.test'); }); }); + +describe('simulator resource class flags', () => { + it('maps CLI values to the GraphQL enum', () => { + expect(DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE.large).toBe( + DeviceRunSessionResourceClass.Large + ); + expect(DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE.medium).toBe( + DeviceRunSessionResourceClass.Medium + ); + }); +}); diff --git a/packages/eas-cli/src/simulator/utils.ts b/packages/eas-cli/src/simulator/utils.ts index 6225cf70fa..5a424fedea 100644 --- a/packages/eas-cli/src/simulator/utils.ts +++ b/packages/eas-cli/src/simulator/utils.ts @@ -1,4 +1,8 @@ -import { DeviceRunSessionByIdQuery, DeviceRunSessionType } from '../graphql/generated'; +import { + DeviceRunSessionByIdQuery, + DeviceRunSessionResourceClass, + DeviceRunSessionType, +} from '../graphql/generated'; import { link } from '../log'; type DeviceRunSessionByIdResult = DeviceRunSessionByIdQuery['deviceRunSessions']['byId']; @@ -37,6 +41,23 @@ export function deviceRunSessionTypeToFlagValue(type: DeviceRunSessionType): str return DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[type]; } +export const DEVICE_RUN_SESSION_RESOURCE_CLASS_FLAG_VALUES: Record< + DeviceRunSessionResourceClass, + string +> = { + [DeviceRunSessionResourceClass.Large]: 'large', + [DeviceRunSessionResourceClass.Medium]: 'medium', +}; + +export const DEVICE_RUN_SESSION_RESOURCE_CLASS_BY_FLAG_VALUE = Object.fromEntries( + ( + Object.entries(DEVICE_RUN_SESSION_RESOURCE_CLASS_FLAG_VALUES) as [ + DeviceRunSessionResourceClass, + string, + ][] + ).map(([resourceClass, value]) => [value, resourceClass]) +) as Record; + export function getRemoteSessionEnvironmentVariables( remoteConfig: DeviceRunSessionRemoteConfig ): Record {