eas-cli 18.10.0 → 18.12.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 +107 -102
- package/build/build/runBuildAndSubmit.d.ts +3 -1
- package/build/build/runBuildAndSubmit.js +12 -4
- package/build/build/utils/repository.js +8 -0
- package/build/build/validateLockfile.d.ts +1 -0
- package/build/build/validateLockfile.js +37 -0
- package/build/commandUtils/buildFlags.d.ts +1 -0
- package/build/commandUtils/buildFlags.js +18 -0
- package/build/commandUtils/convex.d.ts +1 -0
- package/build/commandUtils/convex.js +8 -2
- package/build/commands/build/dev.d.ts +1 -0
- package/build/commands/build/dev.js +12 -2
- package/build/commands/build/run.d.ts +1 -0
- package/build/commands/build/run.js +12 -3
- package/build/commands/integrations/convex/connect.d.ts +1 -0
- package/build/commands/integrations/convex/connect.js +65 -7
- package/build/commands/integrations/convex/team/invite.js +6 -1
- package/build/commands/observe/events.js +12 -24
- package/build/commands/observe/logs.d.ts +29 -0
- package/build/commands/observe/logs.js +163 -0
- package/build/commands/observe/metrics.js +11 -19
- package/build/commands/observe/versions.js +11 -19
- package/build/commands/simulator/start.js +85 -92
- package/build/commands/simulator/stop.js +1 -1
- package/build/graphql/generated.d.ts +290 -10
- package/build/graphql/generated.js +32 -3
- package/build/graphql/mutations/DeviceRunSessionMutation.d.ts +2 -2
- package/build/graphql/mutations/DeviceRunSessionMutation.js +4 -4
- package/build/graphql/queries/DeviceRunSessionQuery.js +8 -1
- package/build/graphql/queries/ObserveQuery.d.ts +21 -1
- package/build/graphql/queries/ObserveQuery.js +80 -0
- package/build/graphql/types/ConvexTeamConnection.d.ts +1 -1
- package/build/graphql/types/ConvexTeamConnection.js +1 -0
- package/build/graphql/types/Observe.d.ts +1 -0
- package/build/graphql/types/Observe.js +26 -1
- package/build/observe/fetchCustomEvents.d.ts +19 -0
- package/build/observe/fetchCustomEvents.js +21 -0
- package/build/observe/formatCustomEvents.d.ts +70 -0
- package/build/observe/formatCustomEvents.js +140 -0
- package/build/observe/formatEvents.js +5 -34
- package/build/observe/formatMetrics.js +2 -7
- package/build/observe/formatUtils.d.ts +27 -0
- package/build/observe/formatUtils.js +64 -0
- package/build/observe/formatVersions.js +2 -9
- package/build/observe/platforms.d.ts +21 -0
- package/build/observe/platforms.js +48 -0
- package/build/observe/resolveProjectContext.d.ts +22 -0
- package/build/observe/resolveProjectContext.js +21 -0
- package/build/run/ios/run.d.ts +2 -1
- package/build/run/ios/run.js +6 -2
- package/build/run/ios/simulator.d.ts +4 -1
- package/build/run/ios/simulator.js +14 -2
- package/build/run/run.d.ts +2 -1
- package/build/run/run.js +2 -2
- package/oclif.manifest.json +568 -375
- package/package.json +5 -5
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const core_1 = require("@oclif/core");
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
7
|
+
const pagination_1 = require("../../commandUtils/pagination");
|
|
8
|
+
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
9
|
+
const ObserveQuery_1 = require("../../graphql/queries/ObserveQuery");
|
|
10
|
+
const fetchCustomEvents_1 = require("../../observe/fetchCustomEvents");
|
|
11
|
+
const formatCustomEvents_1 = require("../../observe/formatCustomEvents");
|
|
12
|
+
const platforms_1 = require("../../observe/platforms");
|
|
13
|
+
const resolveProjectContext_1 = require("../../observe/resolveProjectContext");
|
|
14
|
+
const startAndEndTime_1 = require("../../observe/startAndEndTime");
|
|
15
|
+
const json_1 = require("../../utils/json");
|
|
16
|
+
const DEFAULT_EVENTS_LIMIT = 10;
|
|
17
|
+
class ObserveLogs extends EasCommand_1.default {
|
|
18
|
+
static hidden = true;
|
|
19
|
+
static description = 'display individual custom events (logs) emitted by the app, filtered by the event name in the argument. With no arguments, a list of the available event names and associated event counts is returned.';
|
|
20
|
+
static args = {
|
|
21
|
+
eventName: core_1.Args.string({
|
|
22
|
+
description: 'Custom event name to filter by',
|
|
23
|
+
required: false,
|
|
24
|
+
}),
|
|
25
|
+
};
|
|
26
|
+
static flags = {
|
|
27
|
+
platform: core_1.Flags.option({
|
|
28
|
+
description: 'Filter by platform',
|
|
29
|
+
options: platforms_1.allowedPlatformFlagValues,
|
|
30
|
+
})(),
|
|
31
|
+
after: core_1.Flags.string({
|
|
32
|
+
description: 'Cursor for pagination. Use the endCursor from a previous query to fetch the next page.',
|
|
33
|
+
}),
|
|
34
|
+
limit: (0, pagination_1.getLimitFlagWithCustomValues)({
|
|
35
|
+
defaultTo: DEFAULT_EVENTS_LIMIT,
|
|
36
|
+
limit: 100,
|
|
37
|
+
}),
|
|
38
|
+
start: core_1.Flags.string({
|
|
39
|
+
description: 'Start of time range (ISO date)',
|
|
40
|
+
exclusive: ['days'],
|
|
41
|
+
}),
|
|
42
|
+
end: core_1.Flags.string({
|
|
43
|
+
description: 'End of time range (ISO date)',
|
|
44
|
+
exclusive: ['days'],
|
|
45
|
+
}),
|
|
46
|
+
days: core_1.Flags.integer({
|
|
47
|
+
description: 'Show events from the last N days (mutually exclusive with --start/--end)',
|
|
48
|
+
min: 1,
|
|
49
|
+
exclusive: ['start', 'end'],
|
|
50
|
+
}),
|
|
51
|
+
'app-version': core_1.Flags.string({
|
|
52
|
+
description: 'Filter by app version',
|
|
53
|
+
}),
|
|
54
|
+
'update-id': core_1.Flags.string({
|
|
55
|
+
description: 'Filter by EAS update ID',
|
|
56
|
+
}),
|
|
57
|
+
'session-id': core_1.Flags.string({
|
|
58
|
+
description: 'Filter by session ID',
|
|
59
|
+
}),
|
|
60
|
+
'all-events': core_1.Flags.boolean({
|
|
61
|
+
description: 'When no event name argument is provided, list all events across all event names instead of a summary of event names + counts.',
|
|
62
|
+
default: false,
|
|
63
|
+
}),
|
|
64
|
+
'project-id': core_1.Flags.string({
|
|
65
|
+
description: 'EAS project ID (defaults to the project ID of the current directory)',
|
|
66
|
+
}),
|
|
67
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
68
|
+
};
|
|
69
|
+
static contextDefinition = {
|
|
70
|
+
...this.ContextOptions.ProjectId,
|
|
71
|
+
...this.ContextOptions.LoggedIn,
|
|
72
|
+
};
|
|
73
|
+
static loggedInOnlyContextDefinition = {
|
|
74
|
+
...this.ContextOptions.LoggedIn,
|
|
75
|
+
};
|
|
76
|
+
async runAsync() {
|
|
77
|
+
const { flags, args } = await this.parse(ObserveLogs);
|
|
78
|
+
if (args.eventName && flags['all-events']) {
|
|
79
|
+
throw new Error('--all-events cannot be combined with an event name argument. Pass an event name to filter by it, or pass --all-events to list all events across all event names.');
|
|
80
|
+
}
|
|
81
|
+
const { projectId, graphqlClient } = await (0, resolveProjectContext_1.resolveObserveCommandContextAsync)({
|
|
82
|
+
command: this,
|
|
83
|
+
commandClass: ObserveLogs,
|
|
84
|
+
loggedInOnlyContextDefinition: ObserveLogs.loggedInOnlyContextDefinition,
|
|
85
|
+
projectIdOverride: flags['project-id'],
|
|
86
|
+
nonInteractive: flags['non-interactive'],
|
|
87
|
+
});
|
|
88
|
+
if (flags.json) {
|
|
89
|
+
(0, json_1.enableJsonOutput)();
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
log_1.default.warn('EAS Observe is in preview and subject to breaking changes.');
|
|
93
|
+
}
|
|
94
|
+
const { daysBack, startTime, endTime } = (0, startAndEndTime_1.resolveTimeRange)(flags);
|
|
95
|
+
const platform = (0, platforms_1.appObservePlatformFromFlag)(flags.platform);
|
|
96
|
+
if (!args.eventName && !flags['all-events']) {
|
|
97
|
+
const { names, isTruncated } = await ObserveQuery_1.ObserveQuery.customEventNamesAsync(graphqlClient, {
|
|
98
|
+
appId: projectId,
|
|
99
|
+
startTime,
|
|
100
|
+
endTime,
|
|
101
|
+
platform,
|
|
102
|
+
});
|
|
103
|
+
if (flags.json) {
|
|
104
|
+
(0, json_1.printJsonOnlyOutput)((0, formatCustomEvents_1.buildObserveCustomEventNamesJson)(names, isTruncated));
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
log_1.default.addNewLineIfNone();
|
|
108
|
+
log_1.default.log((0, formatCustomEvents_1.buildObserveCustomEventNamesTable)(names, {
|
|
109
|
+
daysBack,
|
|
110
|
+
startTime,
|
|
111
|
+
endTime,
|
|
112
|
+
isTruncated,
|
|
113
|
+
}));
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const { events, pageInfo } = await (0, fetchCustomEvents_1.fetchObserveCustomEventsAsync)(graphqlClient, projectId, {
|
|
118
|
+
eventName: args.eventName,
|
|
119
|
+
limit: flags.limit ?? DEFAULT_EVENTS_LIMIT,
|
|
120
|
+
...(flags.after && { after: flags.after }),
|
|
121
|
+
startTime,
|
|
122
|
+
endTime,
|
|
123
|
+
platform,
|
|
124
|
+
appVersion: flags['app-version'],
|
|
125
|
+
updateId: flags['update-id'],
|
|
126
|
+
sessionId: flags['session-id'],
|
|
127
|
+
});
|
|
128
|
+
if (args.eventName && events.length === 0) {
|
|
129
|
+
const { names, isTruncated } = await ObserveQuery_1.ObserveQuery.customEventNamesAsync(graphqlClient, {
|
|
130
|
+
appId: projectId,
|
|
131
|
+
startTime,
|
|
132
|
+
endTime,
|
|
133
|
+
platform,
|
|
134
|
+
});
|
|
135
|
+
if (flags.json) {
|
|
136
|
+
(0, json_1.printJsonOnlyOutput)((0, formatCustomEvents_1.buildObserveCustomEventsEmptyWithSuggestionsJson)(args.eventName, names, isTruncated));
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
log_1.default.addNewLineIfNone();
|
|
140
|
+
log_1.default.log((0, formatCustomEvents_1.buildObserveCustomEventsEmptyWithSuggestionsTable)(args.eventName, names, {
|
|
141
|
+
daysBack,
|
|
142
|
+
startTime,
|
|
143
|
+
endTime,
|
|
144
|
+
isTruncated,
|
|
145
|
+
}));
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (flags.json) {
|
|
150
|
+
(0, json_1.printJsonOnlyOutput)((0, formatCustomEvents_1.buildObserveCustomEventsJson)(events, pageInfo));
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
log_1.default.addNewLineIfNone();
|
|
154
|
+
log_1.default.log((0, formatCustomEvents_1.buildObserveCustomEventsTable)(events, pageInfo, {
|
|
155
|
+
eventName: args.eventName,
|
|
156
|
+
daysBack,
|
|
157
|
+
startTime,
|
|
158
|
+
endTime,
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
exports.default = ObserveLogs;
|
|
@@ -4,11 +4,12 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
6
|
const flags_1 = require("../../commandUtils/flags");
|
|
7
|
-
const generated_1 = require("../../graphql/generated");
|
|
8
7
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
9
8
|
const fetchMetrics_1 = require("../../observe/fetchMetrics");
|
|
10
9
|
const formatMetrics_1 = require("../../observe/formatMetrics");
|
|
11
10
|
const metricNames_1 = require("../../observe/metricNames");
|
|
11
|
+
const platforms_1 = require("../../observe/platforms");
|
|
12
|
+
const resolveProjectContext_1 = require("../../observe/resolveProjectContext");
|
|
12
13
|
const startAndEndTime_1 = require("../../observe/startAndEndTime");
|
|
13
14
|
const json_1 = require("../../utils/json");
|
|
14
15
|
const DEFAULT_METRICS = [
|
|
@@ -35,7 +36,7 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
35
36
|
static flags = {
|
|
36
37
|
platform: core_1.Flags.option({
|
|
37
38
|
description: 'Filter by platform',
|
|
38
|
-
options:
|
|
39
|
+
options: platforms_1.allowedPlatformFlagValues,
|
|
39
40
|
})(),
|
|
40
41
|
metric: core_1.Flags.option({
|
|
41
42
|
description: 'Metric name to display (can be specified multiple times).',
|
|
@@ -74,20 +75,13 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
74
75
|
};
|
|
75
76
|
async runAsync() {
|
|
76
77
|
const { flags } = await this.parse(ObserveMetrics);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
const ctx = await this.getContextAsync(ObserveMetrics, {
|
|
86
|
-
nonInteractive: flags['non-interactive'],
|
|
87
|
-
});
|
|
88
|
-
projectId = ctx.projectId;
|
|
89
|
-
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
90
|
-
}
|
|
78
|
+
const { projectId, graphqlClient } = await (0, resolveProjectContext_1.resolveObserveCommandContextAsync)({
|
|
79
|
+
command: this,
|
|
80
|
+
commandClass: ObserveMetrics,
|
|
81
|
+
loggedInOnlyContextDefinition: ObserveMetrics.loggedInOnlyContextDefinition,
|
|
82
|
+
projectIdOverride: flags['project-id'],
|
|
83
|
+
nonInteractive: flags['non-interactive'],
|
|
84
|
+
});
|
|
91
85
|
if (flags.json) {
|
|
92
86
|
(0, json_1.enableJsonOutput)();
|
|
93
87
|
}
|
|
@@ -98,9 +92,7 @@ class ObserveMetrics extends EasCommand_1.default {
|
|
|
98
92
|
? flags.metric.map(metricNames_1.resolveMetricName)
|
|
99
93
|
: DEFAULT_METRICS;
|
|
100
94
|
const { daysBack, startTime, endTime } = (0, startAndEndTime_1.resolveTimeRange)(flags);
|
|
101
|
-
const platforms = flags.platform
|
|
102
|
-
? [flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios]
|
|
103
|
-
: [generated_1.AppPlatform.Android, generated_1.AppPlatform.Ios];
|
|
95
|
+
const platforms = (0, platforms_1.appPlatformsFromFlag)(flags.platform);
|
|
104
96
|
const { metricsMap, buildNumbersMap, updateIdsMap, totalEventCounts } = await (0, fetchMetrics_1.fetchObserveMetricsAsync)(graphqlClient, projectId, metricNames, platforms, startTime, endTime);
|
|
105
97
|
const argumentsStat = flags.stat?.length
|
|
106
98
|
? Array.from(new Set(flags.stat.map(formatMetrics_1.resolveStatKey)))
|
|
@@ -4,10 +4,11 @@ const tslib_1 = require("tslib");
|
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
5
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
6
|
const flags_1 = require("../../commandUtils/flags");
|
|
7
|
-
const generated_1 = require("../../graphql/generated");
|
|
8
7
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
9
8
|
const fetchVersions_1 = require("../../observe/fetchVersions");
|
|
10
9
|
const formatVersions_1 = require("../../observe/formatVersions");
|
|
10
|
+
const platforms_1 = require("../../observe/platforms");
|
|
11
|
+
const resolveProjectContext_1 = require("../../observe/resolveProjectContext");
|
|
11
12
|
const startAndEndTime_1 = require("../../observe/startAndEndTime");
|
|
12
13
|
const json_1 = require("../../utils/json");
|
|
13
14
|
class ObserveVersions extends EasCommand_1.default {
|
|
@@ -16,7 +17,7 @@ class ObserveVersions extends EasCommand_1.default {
|
|
|
16
17
|
static flags = {
|
|
17
18
|
platform: core_1.Flags.option({
|
|
18
19
|
description: 'Filter by platform',
|
|
19
|
-
options:
|
|
20
|
+
options: platforms_1.allowedPlatformFlagValues,
|
|
20
21
|
})(),
|
|
21
22
|
start: core_1.Flags.string({
|
|
22
23
|
description: 'Start of time range (ISO date)',
|
|
@@ -45,20 +46,13 @@ class ObserveVersions extends EasCommand_1.default {
|
|
|
45
46
|
};
|
|
46
47
|
async runAsync() {
|
|
47
48
|
const { flags } = await this.parse(ObserveVersions);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const ctx = await this.getContextAsync(ObserveVersions, {
|
|
57
|
-
nonInteractive: flags['non-interactive'],
|
|
58
|
-
});
|
|
59
|
-
projectId = ctx.projectId;
|
|
60
|
-
graphqlClient = ctx.loggedIn.graphqlClient;
|
|
61
|
-
}
|
|
49
|
+
const { projectId, graphqlClient } = await (0, resolveProjectContext_1.resolveObserveCommandContextAsync)({
|
|
50
|
+
command: this,
|
|
51
|
+
commandClass: ObserveVersions,
|
|
52
|
+
loggedInOnlyContextDefinition: ObserveVersions.loggedInOnlyContextDefinition,
|
|
53
|
+
projectIdOverride: flags['project-id'],
|
|
54
|
+
nonInteractive: flags['non-interactive'],
|
|
55
|
+
});
|
|
62
56
|
if (flags.json) {
|
|
63
57
|
(0, json_1.enableJsonOutput)();
|
|
64
58
|
}
|
|
@@ -66,9 +60,7 @@ class ObserveVersions extends EasCommand_1.default {
|
|
|
66
60
|
log_1.default.warn('EAS Observe is in preview and subject to breaking changes.');
|
|
67
61
|
}
|
|
68
62
|
const { startTime, endTime } = (0, startAndEndTime_1.resolveTimeRange)(flags);
|
|
69
|
-
const platforms = flags.platform
|
|
70
|
-
? [flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios]
|
|
71
|
-
: [generated_1.AppPlatform.Android, generated_1.AppPlatform.Ios];
|
|
63
|
+
const platforms = (0, platforms_1.appPlatformsFromFlag)(flags.platform);
|
|
72
64
|
const results = await (0, fetchVersions_1.fetchObserveVersionsAsync)(graphqlClient, projectId, platforms, startTime, endTime);
|
|
73
65
|
if (flags.json) {
|
|
74
66
|
(0, json_1.printJsonOnlyOutput)((0, formatVersions_1.buildObserveVersionsJson)(results));
|
|
@@ -51,6 +51,7 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
51
51
|
const platform = flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios;
|
|
52
52
|
const createSpinner = (0, ora_1.ora)('🚀 Creating device run session').start();
|
|
53
53
|
let deviceRunSessionId;
|
|
54
|
+
let jobRunUrl;
|
|
54
55
|
try {
|
|
55
56
|
const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.createDeviceRunSessionAsync(graphqlClient, {
|
|
56
57
|
appId: projectId,
|
|
@@ -60,33 +61,31 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
60
61
|
});
|
|
61
62
|
deviceRunSessionId = session.id;
|
|
62
63
|
const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
|
|
63
|
-
|
|
64
|
+
jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
|
|
64
65
|
createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}) ${(0, log_1.link)(jobRunUrl)}`);
|
|
65
66
|
}
|
|
66
67
|
catch (err) {
|
|
67
68
|
createSpinner.fail('Failed to create device run session');
|
|
68
69
|
throw err;
|
|
69
70
|
}
|
|
70
|
-
const checkReadiness = getReadinessCheckerForType(flags.type);
|
|
71
71
|
const pollSpinner = (0, ora_1.ora)(`⏳ Waiting for ${flags.type} daemon to start`).start();
|
|
72
72
|
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
73
|
-
let
|
|
73
|
+
let remoteConfig;
|
|
74
74
|
try {
|
|
75
75
|
while (Date.now() < deadline) {
|
|
76
76
|
const session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
|
|
77
77
|
if (session.status === generated_1.DeviceRunSessionStatus.Errored ||
|
|
78
78
|
session.status === generated_1.DeviceRunSessionStatus.Stopped) {
|
|
79
|
-
throw new Error(`Device run session ${deviceRunSessionId} ${session.status.toLowerCase()} before the ${flags.type} daemon was ready
|
|
79
|
+
throw new Error(`Device run session ${deviceRunSessionId} ${session.status.toLowerCase()} before the ${flags.type} daemon was ready. ${(0, log_1.link)(jobRunUrl)}`);
|
|
80
80
|
}
|
|
81
81
|
const jobRunStatus = session.turtleJobRun?.status;
|
|
82
82
|
if (jobRunStatus === generated_1.JobRunStatus.Errored ||
|
|
83
83
|
jobRunStatus === generated_1.JobRunStatus.Canceled ||
|
|
84
84
|
jobRunStatus === generated_1.JobRunStatus.Finished) {
|
|
85
|
-
throw new Error(`Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready
|
|
85
|
+
throw new Error(`Turtle job run for device run session ${deviceRunSessionId} ${jobRunStatus.toLowerCase()} before the ${flags.type} daemon was ready. ${(0, log_1.link)(jobRunUrl)}`);
|
|
86
86
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (result.ready) {
|
|
87
|
+
if (session.remoteConfig) {
|
|
88
|
+
remoteConfig = session.remoteConfig;
|
|
90
89
|
pollSpinner.succeed(`🎉 ${flags.type} daemon is ready`);
|
|
91
90
|
break;
|
|
92
91
|
}
|
|
@@ -94,112 +93,106 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
94
93
|
}
|
|
95
94
|
}
|
|
96
95
|
catch (err) {
|
|
97
|
-
pollSpinner.fail(`Failed while polling for ${flags.type} daemon
|
|
96
|
+
pollSpinner.fail(`Failed while polling for ${flags.type} daemon to start`);
|
|
97
|
+
await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
|
|
98
98
|
throw err;
|
|
99
99
|
}
|
|
100
|
-
if (!
|
|
100
|
+
if (!remoteConfig) {
|
|
101
101
|
pollSpinner.fail(`Timed out waiting for ${flags.type} daemon to start`);
|
|
102
|
-
|
|
102
|
+
await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
|
|
103
|
+
throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} daemon to start. ${(0, log_1.link)(jobRunUrl)}`);
|
|
103
104
|
}
|
|
104
105
|
log_1.default.newLine();
|
|
105
106
|
log_1.default.log(`🔑 Run the following in your shell to attach to ${flags.type}:`);
|
|
106
107
|
log_1.default.newLine();
|
|
107
|
-
log_1.default.log(
|
|
108
|
+
log_1.default.log(formatRemoteConfigShellSnippet(remoteConfig));
|
|
108
109
|
log_1.default.newLine();
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
exports.default = SimulatorStart;
|
|
113
|
-
function getReadinessCheckerForType(type) {
|
|
114
|
-
switch (type) {
|
|
115
|
-
case DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[generated_1.DeviceRunSessionType.AgentDevice]:
|
|
116
|
-
return checkAgentDeviceReadiness;
|
|
117
|
-
default:
|
|
118
|
-
throw new Error(`Unsupported device run session type: ${type}`);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
const AGENT_DEVICE_BASE_URL_ENV_VAR = 'AGENT_DEVICE_DAEMON_BASE_URL';
|
|
122
|
-
const AGENT_DEVICE_AUTH_TOKEN_ENV_VAR = 'AGENT_DEVICE_DAEMON_AUTH_TOKEN';
|
|
123
|
-
function checkAgentDeviceReadiness(logMessages) {
|
|
124
|
-
let baseUrl;
|
|
125
|
-
let authToken;
|
|
126
|
-
for (const msg of logMessages) {
|
|
127
|
-
baseUrl = baseUrl ?? extractExportedEnvValue(msg, AGENT_DEVICE_BASE_URL_ENV_VAR);
|
|
128
|
-
authToken = authToken ?? extractExportedEnvValue(msg, AGENT_DEVICE_AUTH_TOKEN_ENV_VAR);
|
|
129
|
-
if (baseUrl && authToken) {
|
|
130
|
-
break;
|
|
110
|
+
if (flags['non-interactive']) {
|
|
111
|
+
log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
|
|
112
|
+
return;
|
|
131
113
|
}
|
|
114
|
+
await waitForSessionEndOrInterruptAsync({
|
|
115
|
+
graphqlClient,
|
|
116
|
+
deviceRunSessionId,
|
|
117
|
+
jobRunUrl,
|
|
118
|
+
});
|
|
132
119
|
}
|
|
133
|
-
if (baseUrl && authToken) {
|
|
134
|
-
return {
|
|
135
|
-
ready: true,
|
|
136
|
-
message: [
|
|
137
|
-
`export ${AGENT_DEVICE_BASE_URL_ENV_VAR}='${baseUrl}'`,
|
|
138
|
-
`export ${AGENT_DEVICE_AUTH_TOKEN_ENV_VAR}='${authToken}'`,
|
|
139
|
-
].join('\n'),
|
|
140
|
-
};
|
|
141
|
-
}
|
|
142
|
-
return { ready: false };
|
|
143
120
|
}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
121
|
+
exports.default = SimulatorStart;
|
|
122
|
+
async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessionId, jobRunUrl, }) {
|
|
123
|
+
const spinner = (0, ora_1.ora)(`Device run session active — press Ctrl+C to stop, or run \`eas simulator:stop --id ${deviceRunSessionId}\` from another shell`).start();
|
|
124
|
+
const abortController = new AbortController();
|
|
125
|
+
const { signal } = abortController;
|
|
126
|
+
const abortPromise = new Promise(resolve => {
|
|
127
|
+
signal.addEventListener('abort', () => {
|
|
128
|
+
resolve();
|
|
129
|
+
}, { once: true });
|
|
130
|
+
});
|
|
131
|
+
const sigintHandler = () => {
|
|
132
|
+
if (signal.aborted) {
|
|
133
|
+
// Force exit on a second Ctrl+C in case cleanup is hanging. The session may still be
|
|
134
|
+
// running on EAS, so tell the user how to make sure it gets terminated.
|
|
135
|
+
spinner.fail(`Aborted before the device run session could be stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
|
|
136
|
+
process.exit(130);
|
|
150
137
|
}
|
|
151
|
-
|
|
152
|
-
|
|
138
|
+
abortController.abort();
|
|
139
|
+
};
|
|
140
|
+
process.on('SIGINT', sigintHandler);
|
|
141
|
+
try {
|
|
142
|
+
while (!signal.aborted) {
|
|
143
|
+
let session;
|
|
144
|
+
try {
|
|
145
|
+
session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
log_1.default.debug(`Failed to poll device run session: ${err instanceof Error ? err.message : String(err)}`);
|
|
149
|
+
await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
|
|
153
150
|
continue;
|
|
154
151
|
}
|
|
155
|
-
|
|
152
|
+
const jobRunStatus = session.turtleJobRun?.status;
|
|
153
|
+
if (session.status === generated_1.DeviceRunSessionStatus.Errored ||
|
|
154
|
+
jobRunStatus === generated_1.JobRunStatus.Errored) {
|
|
155
|
+
spinner.fail(`Device run session errored. ${(0, log_1.link)(jobRunUrl)}`);
|
|
156
|
+
throw new Error(`Device run session ${deviceRunSessionId} errored.`);
|
|
157
|
+
}
|
|
158
|
+
if (session.status === generated_1.DeviceRunSessionStatus.Stopped ||
|
|
159
|
+
jobRunStatus === generated_1.JobRunStatus.Canceled ||
|
|
160
|
+
jobRunStatus === generated_1.JobRunStatus.Finished) {
|
|
161
|
+
spinner.succeed(`Device run session ended. ${(0, log_1.link)(jobRunUrl)}`);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
|
|
156
165
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
return undefined;
|
|
166
|
+
spinner.text = 'Stopping device run session...';
|
|
167
|
+
const stopped = await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
|
|
168
|
+
if (stopped) {
|
|
169
|
+
spinner.succeed('Device run session stopped');
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
spinner.fail(`Could not confirm the device run session was stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
|
|
165
173
|
}
|
|
166
|
-
return await response.text();
|
|
167
174
|
}
|
|
168
|
-
|
|
169
|
-
|
|
175
|
+
finally {
|
|
176
|
+
process.removeListener('SIGINT', sigintHandler);
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
|
-
function
|
|
173
|
-
// Turtle job run logs are JSONL (bunyan-shaped), e.g.
|
|
174
|
-
// {"msg":"export FOO=\"bar\"","time":"...","logId":"..."}
|
|
175
|
-
// Fall back to the raw line if it's not JSON or doesn't have a string msg.
|
|
176
|
-
const trimmed = line.trim();
|
|
177
|
-
if (!trimmed.startsWith('{')) {
|
|
178
|
-
return line;
|
|
179
|
-
}
|
|
179
|
+
async function ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId) {
|
|
180
180
|
try {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const msg = parsed.msg;
|
|
184
|
-
if (typeof msg === 'string') {
|
|
185
|
-
return msg;
|
|
186
|
-
}
|
|
187
|
-
}
|
|
181
|
+
await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, deviceRunSessionId);
|
|
182
|
+
return true;
|
|
188
183
|
}
|
|
189
|
-
catch {
|
|
190
|
-
//
|
|
184
|
+
catch (err) {
|
|
185
|
+
// Cleanup is best-effort; surface the failure but don't mask the original error.
|
|
186
|
+
log_1.default.warn(`Failed to stop device run session ${deviceRunSessionId}: ${err instanceof Error ? err.message : String(err)}`);
|
|
187
|
+
return false;
|
|
191
188
|
}
|
|
192
|
-
return line;
|
|
193
189
|
}
|
|
194
|
-
function
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
190
|
+
function formatRemoteConfigShellSnippet(remoteConfig) {
|
|
191
|
+
switch (remoteConfig.__typename) {
|
|
192
|
+
case 'AgentDeviceRunSessionRemoteConfig':
|
|
193
|
+
return [
|
|
194
|
+
`export AGENT_DEVICE_DAEMON_BASE_URL='${remoteConfig.url}'`,
|
|
195
|
+
`export AGENT_DEVICE_DAEMON_AUTH_TOKEN='${remoteConfig.token}'`,
|
|
196
|
+
].join('\n');
|
|
200
197
|
}
|
|
201
|
-
return match[1] ?? match[2] ?? match[3];
|
|
202
|
-
}
|
|
203
|
-
function escapeRegExp(value) {
|
|
204
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
205
198
|
}
|
|
@@ -26,7 +26,7 @@ class SimulatorStop extends EasCommand_1.default {
|
|
|
26
26
|
});
|
|
27
27
|
const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flags.id}`).start();
|
|
28
28
|
try {
|
|
29
|
-
const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.
|
|
29
|
+
const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, flags.id);
|
|
30
30
|
stopSpinner.succeed(`🎉 Device run session ${session.id} is ${session.status.toLowerCase()}`);
|
|
31
31
|
}
|
|
32
32
|
catch (err) {
|