eas-cli 20.0.0 → 20.2.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 +197 -116
- package/build/commands/simulator/exec.d.ts +12 -0
- package/build/commands/simulator/exec.js +42 -0
- package/build/commands/simulator/get.d.ts +2 -1
- package/build/commands/simulator/get.js +13 -7
- package/build/commands/simulator/start.d.ts +4 -0
- package/build/commands/simulator/start.js +70 -5
- package/build/commands/simulator/stop.d.ts +2 -1
- package/build/commands/simulator/stop.js +16 -9
- package/build/commands/update/embedded/delete.d.ts +15 -0
- package/build/commands/update/embedded/delete.js +55 -0
- package/build/commands/update/embedded/list.d.ts +19 -0
- package/build/commands/update/embedded/list.js +132 -0
- package/build/commands/update/embedded/view.d.ts +17 -0
- package/build/commands/update/embedded/view.js +75 -0
- package/build/commands/update/rollback.d.ts +11 -0
- package/build/commands/update/rollback.js +117 -14
- package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +20 -0
- package/build/credentials/ios/actions/AscApiKeyUtils.js +64 -0
- package/build/credentials/ios/actions/ConfigureProvisioningProfile.js +2 -4
- package/build/credentials/ios/actions/CreateProvisioningProfile.js +2 -4
- package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +3 -20
- package/build/credentials/ios/actions/SetUpProvisioningProfile.d.ts +10 -0
- package/build/credentials/ios/actions/SetUpProvisioningProfile.js +39 -5
- package/build/credentials/ios/appstore/resolveCredentials.d.ts +1 -0
- package/build/credentials/ios/appstore/resolveCredentials.js +1 -0
- package/build/graphql/generated.d.ts +457 -4
- package/build/graphql/generated.js +35 -1
- package/build/graphql/mutations/EmbeddedUpdateMutation.d.ts +5 -0
- package/build/graphql/mutations/EmbeddedUpdateMutation.js +14 -0
- package/build/graphql/queries/EmbeddedUpdateQuery.d.ts +18 -0
- package/build/graphql/queries/EmbeddedUpdateQuery.js +81 -0
- package/build/graphql/queries/UpdateQuery.d.ts +2 -1
- package/build/graphql/queries/UpdateQuery.js +52 -0
- package/build/simulator/env.d.ts +7 -0
- package/build/simulator/env.js +44 -0
- package/build/simulator/utils.d.ts +3 -1
- package/build/simulator/utils.js +26 -7
- package/oclif.manifest.json +945 -525
- package/package.json +5 -5
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class SimulatorExec extends EasCommand {
|
|
3
|
+
static hidden: boolean;
|
|
4
|
+
static description: string;
|
|
5
|
+
static strict: boolean;
|
|
6
|
+
static contextDefinition: {
|
|
7
|
+
projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
|
|
8
|
+
};
|
|
9
|
+
private isRunningSubprocess;
|
|
10
|
+
runAsync(): Promise<void>;
|
|
11
|
+
protected catch(err: Error): Promise<any>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
|
|
5
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
6
|
+
const env_1 = require("../../simulator/env");
|
|
7
|
+
class SimulatorExec extends EasCommand_1.default {
|
|
8
|
+
static hidden = true;
|
|
9
|
+
static description = `[EXPERIMENTAL] execute a simulator command with ${env_1.SIMULATOR_DOTENV_FILE_NAME} environment loaded`;
|
|
10
|
+
static strict = false;
|
|
11
|
+
static contextDefinition = {
|
|
12
|
+
...this.ContextOptions.ProjectDir,
|
|
13
|
+
};
|
|
14
|
+
isRunningSubprocess = false;
|
|
15
|
+
async runAsync() {
|
|
16
|
+
const rawArgv = [...this.argv];
|
|
17
|
+
// Required to avoid `Warning: Command exec did not parse its arguments. Did you forget to call 'this.parse'?`
|
|
18
|
+
await this.parse(SimulatorExec, []);
|
|
19
|
+
const [command, ...args] = rawArgv;
|
|
20
|
+
if (typeof command !== 'string' || command.length === 0) {
|
|
21
|
+
throw new Error('No command provided. Run `eas simulator:exec <command> [args...]`.');
|
|
22
|
+
}
|
|
23
|
+
const { projectDir } = await this.getContextAsync(SimulatorExec, {
|
|
24
|
+
nonInteractive: true,
|
|
25
|
+
});
|
|
26
|
+
await (0, env_1.loadSimulatorEnvAsync)(projectDir);
|
|
27
|
+
this.isRunningSubprocess = true;
|
|
28
|
+
await (0, spawn_async_1.default)(command, args, {
|
|
29
|
+
stdio: 'inherit',
|
|
30
|
+
env: process.env,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
catch(err) {
|
|
34
|
+
// Propagate wrapped command from spawnAsync rejection
|
|
35
|
+
if (this.isRunningSubprocess) {
|
|
36
|
+
process.exitCode = process.exitCode ?? err.status ?? 1;
|
|
37
|
+
return Promise.resolve();
|
|
38
|
+
}
|
|
39
|
+
return super.catch(err);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.default = SimulatorExec;
|
|
@@ -5,9 +5,10 @@ export default class SimulatorGet extends EasCommand {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
-
id: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
};
|
|
10
10
|
static contextDefinition: {
|
|
11
|
+
projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
|
|
11
12
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
12
13
|
};
|
|
13
14
|
runAsync(): Promise<void>;
|
|
@@ -9,6 +9,7 @@ const generated_1 = require("../../graphql/generated");
|
|
|
9
9
|
const DeviceRunSessionQuery_1 = require("../../graphql/queries/DeviceRunSessionQuery");
|
|
10
10
|
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
11
11
|
const ora_1 = require("../../ora");
|
|
12
|
+
const env_1 = require("../../simulator/env");
|
|
12
13
|
const utils_1 = require("../../simulator/utils");
|
|
13
14
|
const json_1 = require("../../utils/json");
|
|
14
15
|
class SimulatorGet extends EasCommand_1.default {
|
|
@@ -16,13 +17,13 @@ class SimulatorGet extends EasCommand_1.default {
|
|
|
16
17
|
static description = '[EXPERIMENTAL] get info about a remote simulator session on EAS by its device run session ID';
|
|
17
18
|
static flags = {
|
|
18
19
|
id: core_1.Flags.string({
|
|
19
|
-
description:
|
|
20
|
-
required: true,
|
|
20
|
+
description: `Device run session ID. Defaults to ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
|
|
21
21
|
}),
|
|
22
22
|
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
23
23
|
};
|
|
24
24
|
static contextDefinition = {
|
|
25
25
|
...this.ContextOptions.LoggedIn,
|
|
26
|
+
...this.ContextOptions.ProjectDir,
|
|
26
27
|
};
|
|
27
28
|
async runAsync() {
|
|
28
29
|
const { flags } = await this.parse(SimulatorGet);
|
|
@@ -30,17 +31,22 @@ class SimulatorGet extends EasCommand_1.default {
|
|
|
30
31
|
if (jsonFlag) {
|
|
31
32
|
(0, json_1.enableJsonOutput)();
|
|
32
33
|
}
|
|
33
|
-
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorGet, {
|
|
34
|
+
const { projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorGet, {
|
|
34
35
|
nonInteractive,
|
|
35
36
|
});
|
|
36
|
-
|
|
37
|
+
await (0, env_1.loadSimulatorEnvAsync)(projectDir);
|
|
38
|
+
const flagId = flags.id || process.env[env_1.EAS_SIMULATOR_SESSION_ID];
|
|
39
|
+
if (!flagId) {
|
|
40
|
+
throw new Error(`No simulator session ID provided. Pass --id, or run \`eas simulator:start\` first to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`);
|
|
41
|
+
}
|
|
42
|
+
const fetchSpinner = (0, ora_1.ora)(`Fetching device run session ${flagId}`).start();
|
|
37
43
|
let session;
|
|
38
44
|
try {
|
|
39
|
-
session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient,
|
|
45
|
+
session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, flagId);
|
|
40
46
|
fetchSpinner.succeed(`Fetched device run session ${session.id}`);
|
|
41
47
|
}
|
|
42
48
|
catch (err) {
|
|
43
|
-
fetchSpinner.fail(`Failed to fetch device run session ${
|
|
49
|
+
fetchSpinner.fail(`Failed to fetch device run session ${flagId}`);
|
|
44
50
|
throw err;
|
|
45
51
|
}
|
|
46
52
|
const jobRunUrl = session.turtleJobRun
|
|
@@ -64,7 +70,7 @@ class SimulatorGet extends EasCommand_1.default {
|
|
|
64
70
|
if (session.status === generated_1.DeviceRunSessionStatus.InProgress) {
|
|
65
71
|
log_1.default.newLine();
|
|
66
72
|
if (session.remoteConfig) {
|
|
67
|
-
log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(session.remoteConfig));
|
|
73
|
+
log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(session.remoteConfig, 'env'));
|
|
68
74
|
}
|
|
69
75
|
else {
|
|
70
76
|
log_1.default.log('⏳ Session is starting up — remote config is not available yet. Re-run this command in a moment.');
|
|
@@ -8,9 +8,13 @@ export default class SimulatorStart extends EasCommand {
|
|
|
8
8
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios", import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
type: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
10
|
'package-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
'max-duration-minutes': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
'out-config-type': import("@oclif/core/lib/interfaces").OptionFlag<"env" | "dotenv", import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
14
|
};
|
|
12
15
|
static contextDefinition: {
|
|
13
16
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
17
|
+
projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
|
|
14
18
|
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
15
19
|
};
|
|
16
20
|
runAsync(): Promise<void>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const tslib_1 = require("tslib");
|
|
4
4
|
const core_1 = require("@oclif/core");
|
|
5
|
+
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
5
6
|
const url_1 = require("../../build/utils/url");
|
|
6
7
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
7
8
|
const flags_1 = require("../../commandUtils/flags");
|
|
@@ -10,12 +11,16 @@ const DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSes
|
|
|
10
11
|
const DeviceRunSessionQuery_1 = require("../../graphql/queries/DeviceRunSessionQuery");
|
|
11
12
|
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
12
13
|
const ora_1 = require("../../ora");
|
|
14
|
+
const env_1 = require("../../simulator/env");
|
|
13
15
|
const utils_1 = require("../../simulator/utils");
|
|
14
16
|
const json_1 = require("../../utils/json");
|
|
15
17
|
const promise_1 = require("../../utils/promise");
|
|
16
|
-
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
17
18
|
const POLL_INTERVAL_MS = 5_000; // 5 seconds
|
|
18
19
|
const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // 15 minutes
|
|
20
|
+
const OUT_CONFIG_TYPE_VALUES = {
|
|
21
|
+
Env: 'env',
|
|
22
|
+
Dotenv: 'dotenv',
|
|
23
|
+
};
|
|
19
24
|
class SimulatorStart extends EasCommand_1.default {
|
|
20
25
|
static hidden = true;
|
|
21
26
|
static description = '[EXPERIMENTAL] start a remote simulator session on EAS and get instructions to connect to it';
|
|
@@ -33,10 +38,25 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
33
38
|
'package-version': core_1.Flags.string({
|
|
34
39
|
description: 'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
|
|
35
40
|
}),
|
|
41
|
+
'max-duration-minutes': core_1.Flags.integer({
|
|
42
|
+
description: 'Maximum duration of the device run session in minutes before it is automatically stopped. Only customizable on paid plans. Defaults to a value derived from the job run priority when omitted.',
|
|
43
|
+
min: 0,
|
|
44
|
+
}),
|
|
45
|
+
force: core_1.Flags.boolean({
|
|
46
|
+
description: '[default: true] Create a new device session even when an existing simulator session is present in the environment.',
|
|
47
|
+
default: true,
|
|
48
|
+
allowNo: true,
|
|
49
|
+
}),
|
|
50
|
+
'out-config-type': core_1.Flags.option({
|
|
51
|
+
description: `How to output simulator connection configuration. Use "env" to print shell exports, or "dotenv" to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
|
|
52
|
+
options: Object.values(OUT_CONFIG_TYPE_VALUES),
|
|
53
|
+
default: OUT_CONFIG_TYPE_VALUES.Dotenv,
|
|
54
|
+
})(),
|
|
36
55
|
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
37
56
|
};
|
|
38
57
|
static contextDefinition = {
|
|
39
58
|
...this.ContextOptions.ProjectId,
|
|
59
|
+
...this.ContextOptions.ProjectDir,
|
|
40
60
|
...this.ContextOptions.LoggedIn,
|
|
41
61
|
};
|
|
42
62
|
async runAsync() {
|
|
@@ -45,9 +65,20 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
45
65
|
if (jsonFlag) {
|
|
46
66
|
(0, json_1.enableJsonOutput)();
|
|
47
67
|
}
|
|
48
|
-
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStart, {
|
|
68
|
+
const { projectId, projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStart, {
|
|
49
69
|
nonInteractive,
|
|
50
70
|
});
|
|
71
|
+
await (0, env_1.loadSimulatorEnvAsync)(projectDir);
|
|
72
|
+
const existingDeviceRunSessionId = process.env[env_1.EAS_SIMULATOR_SESSION_ID];
|
|
73
|
+
if (existingDeviceRunSessionId && !flags.force) {
|
|
74
|
+
throw new Error(`Existing simulator session in environment. Use --force to create a new device session.`);
|
|
75
|
+
}
|
|
76
|
+
if (existingDeviceRunSessionId) {
|
|
77
|
+
log_1.default.warn(` Overwriting previous simulator session (id: ${existingDeviceRunSessionId}). ` +
|
|
78
|
+
`The previous remote session will continue running until stopped. ` +
|
|
79
|
+
`To stop it, run: eas simulator:stop --id ${existingDeviceRunSessionId}`);
|
|
80
|
+
log_1.default.newLine();
|
|
81
|
+
}
|
|
51
82
|
const platform = flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios;
|
|
52
83
|
const createSpinner = (0, ora_1.ora)('🚀 Creating device run session').start();
|
|
53
84
|
let deviceRunSessionId;
|
|
@@ -58,11 +89,17 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
58
89
|
platform,
|
|
59
90
|
type: utils_1.DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type],
|
|
60
91
|
packageVersion: flags['package-version'],
|
|
92
|
+
maxRunTimeMinutes: flags['max-duration-minutes'],
|
|
61
93
|
});
|
|
62
94
|
deviceRunSessionId = session.id;
|
|
63
95
|
const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
|
|
64
96
|
jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
|
|
65
|
-
|
|
97
|
+
const simulatorEnvWritten = !jsonFlag && flags['out-config-type'] === OUT_CONFIG_TYPE_VALUES.Dotenv
|
|
98
|
+
? await writeSimulatorEnvSafelyAsync(projectDir, {
|
|
99
|
+
[env_1.EAS_SIMULATOR_SESSION_ID]: deviceRunSessionId,
|
|
100
|
+
})
|
|
101
|
+
: false;
|
|
102
|
+
createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}${simulatorEnvWritten ? `, saved to ${env_1.SIMULATOR_DOTENV_FILE_NAME}` : ''}) ${(0, log_1.link)(jobRunUrl)}`);
|
|
66
103
|
}
|
|
67
104
|
catch (err) {
|
|
68
105
|
createSpinner.fail('Failed to create device run session');
|
|
@@ -102,6 +139,12 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
102
139
|
await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
|
|
103
140
|
throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} session to be ready. ${(0, log_1.link)(jobRunUrl)}`);
|
|
104
141
|
}
|
|
142
|
+
if (flags['out-config-type'] === OUT_CONFIG_TYPE_VALUES.Dotenv) {
|
|
143
|
+
await writeSimulatorEnvSafelyAsync(projectDir, {
|
|
144
|
+
...(0, utils_1.getRemoteSessionEnvironmentVariables)(remoteConfig),
|
|
145
|
+
[env_1.EAS_SIMULATOR_SESSION_ID]: deviceRunSessionId,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
105
148
|
if (jsonFlag) {
|
|
106
149
|
(0, json_1.printJsonOnlyOutput)({
|
|
107
150
|
id: deviceRunSessionId,
|
|
@@ -112,7 +155,7 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
112
155
|
return;
|
|
113
156
|
}
|
|
114
157
|
log_1.default.newLine();
|
|
115
|
-
log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(remoteConfig));
|
|
158
|
+
log_1.default.log((0, utils_1.formatRemoteSessionInstructions)(remoteConfig, flags['out-config-type']));
|
|
116
159
|
log_1.default.newLine();
|
|
117
160
|
if (nonInteractive) {
|
|
118
161
|
log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
|
|
@@ -122,11 +165,22 @@ class SimulatorStart extends EasCommand_1.default {
|
|
|
122
165
|
graphqlClient,
|
|
123
166
|
deviceRunSessionId,
|
|
124
167
|
jobRunUrl,
|
|
168
|
+
projectDir,
|
|
125
169
|
});
|
|
126
170
|
}
|
|
127
171
|
}
|
|
128
172
|
exports.default = SimulatorStart;
|
|
129
|
-
async function
|
|
173
|
+
async function writeSimulatorEnvSafelyAsync(projectDir, environmentVariables) {
|
|
174
|
+
try {
|
|
175
|
+
await (0, env_1.writeSimulatorEnvAsync)(projectDir, environmentVariables);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
catch (err) {
|
|
179
|
+
log_1.default.warn(`Failed to write simulator environment variables to ${env_1.SIMULATOR_DOTENV_FILE_NAME}: ${err instanceof Error ? err.message : String(err)}`);
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessionId, jobRunUrl, projectDir, }) {
|
|
130
184
|
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();
|
|
131
185
|
const abortController = new AbortController();
|
|
132
186
|
const { signal } = abortController;
|
|
@@ -166,6 +220,7 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
|
|
|
166
220
|
jobRunStatus === generated_1.JobRunStatus.Canceled ||
|
|
167
221
|
jobRunStatus === generated_1.JobRunStatus.Finished) {
|
|
168
222
|
spinner.succeed(`Device run session ended. ${(0, log_1.link)(jobRunUrl)}`);
|
|
223
|
+
await resetSimulatorEnvVerboseAsync(projectDir);
|
|
169
224
|
return;
|
|
170
225
|
}
|
|
171
226
|
await Promise.race([(0, promise_1.sleepAsync)(POLL_INTERVAL_MS), abortPromise]);
|
|
@@ -174,6 +229,7 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
|
|
|
174
229
|
const stopped = await ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId);
|
|
175
230
|
if (stopped) {
|
|
176
231
|
spinner.succeed('Device run session stopped');
|
|
232
|
+
await resetSimulatorEnvVerboseAsync(projectDir);
|
|
177
233
|
}
|
|
178
234
|
else {
|
|
179
235
|
spinner.fail(`Could not confirm the device run session was stopped. Run \`eas simulator:stop --id ${deviceRunSessionId}\` to terminate it and avoid unexpected charges.`);
|
|
@@ -183,6 +239,15 @@ async function waitForSessionEndOrInterruptAsync({ graphqlClient, deviceRunSessi
|
|
|
183
239
|
process.removeListener('SIGINT', sigintHandler);
|
|
184
240
|
}
|
|
185
241
|
}
|
|
242
|
+
async function resetSimulatorEnvVerboseAsync(projectDir) {
|
|
243
|
+
try {
|
|
244
|
+
await (0, env_1.resetSimulatorEnvAsync)(projectDir);
|
|
245
|
+
}
|
|
246
|
+
catch (err) {
|
|
247
|
+
log_1.default.error(`Failed to clean up ${env_1.SIMULATOR_DOTENV_FILE_NAME}`);
|
|
248
|
+
throw err;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
186
251
|
async function ensureDeviceRunSessionStoppedSafelyAsync(graphqlClient, deviceRunSessionId) {
|
|
187
252
|
try {
|
|
188
253
|
await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, deviceRunSessionId);
|
|
@@ -5,9 +5,10 @@ export default class SimulatorStop extends EasCommand {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
7
|
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
-
id: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
id: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
9
|
};
|
|
10
10
|
static contextDefinition: {
|
|
11
|
+
projectDir: import("../../commandUtils/context/ProjectDirContextField").default;
|
|
11
12
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
12
13
|
};
|
|
13
14
|
runAsync(): Promise<void>;
|
|
@@ -6,19 +6,20 @@ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasComm
|
|
|
6
6
|
const flags_1 = require("../../commandUtils/flags");
|
|
7
7
|
const DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSessionMutation");
|
|
8
8
|
const ora_1 = require("../../ora");
|
|
9
|
+
const env_1 = require("../../simulator/env");
|
|
9
10
|
const json_1 = require("../../utils/json");
|
|
10
11
|
class SimulatorStop extends EasCommand_1.default {
|
|
11
12
|
static hidden = true;
|
|
12
13
|
static description = '[EXPERIMENTAL] stop a remote simulator session on EAS by its device run session ID';
|
|
13
14
|
static flags = {
|
|
14
15
|
id: core_1.Flags.string({
|
|
15
|
-
description:
|
|
16
|
-
required: true,
|
|
16
|
+
description: `Device run session ID. Defaults to ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`,
|
|
17
17
|
}),
|
|
18
18
|
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
19
19
|
};
|
|
20
20
|
static contextDefinition = {
|
|
21
21
|
...this.ContextOptions.LoggedIn,
|
|
22
|
+
...this.ContextOptions.ProjectDir,
|
|
22
23
|
};
|
|
23
24
|
async runAsync() {
|
|
24
25
|
const { flags } = await this.parse(SimulatorStop);
|
|
@@ -26,21 +27,27 @@ class SimulatorStop extends EasCommand_1.default {
|
|
|
26
27
|
if (jsonFlag) {
|
|
27
28
|
(0, json_1.enableJsonOutput)();
|
|
28
29
|
}
|
|
29
|
-
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStop, {
|
|
30
|
+
const { projectDir, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStop, {
|
|
30
31
|
nonInteractive,
|
|
31
32
|
});
|
|
32
|
-
|
|
33
|
+
await (0, env_1.loadSimulatorEnvAsync)(projectDir);
|
|
34
|
+
const flagId = flags.id || process.env[env_1.EAS_SIMULATOR_SESSION_ID];
|
|
35
|
+
if (!flagId) {
|
|
36
|
+
throw new Error(`No simulator session ID provided. Pass --id, or run \`eas simulator:start\` first to write ${env_1.SIMULATOR_DOTENV_FILE_NAME}.`);
|
|
37
|
+
}
|
|
38
|
+
const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flagId}`).start();
|
|
39
|
+
let session;
|
|
33
40
|
try {
|
|
34
|
-
|
|
41
|
+
session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.ensureDeviceRunSessionStoppedAsync(graphqlClient, flagId);
|
|
35
42
|
stopSpinner.succeed(`🎉 Device run session ${session.id} is ${session.status.toLowerCase()}`);
|
|
36
|
-
if (jsonFlag) {
|
|
37
|
-
(0, json_1.printJsonOnlyOutput)({ id: session.id, status: session.status });
|
|
38
|
-
}
|
|
39
43
|
}
|
|
40
44
|
catch (err) {
|
|
41
|
-
stopSpinner.fail(`Failed to stop device run session ${
|
|
45
|
+
stopSpinner.fail(`Failed to stop device run session ${flagId}`);
|
|
42
46
|
throw err;
|
|
43
47
|
}
|
|
48
|
+
if (jsonFlag) {
|
|
49
|
+
(0, json_1.printJsonOnlyOutput)({ id: session.id, status: session.status });
|
|
50
|
+
}
|
|
44
51
|
}
|
|
45
52
|
}
|
|
46
53
|
exports.default = SimulatorStop;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
2
|
+
export default class UpdateEmbeddedDelete extends EasCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static flags: {
|
|
8
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
static contextDefinition: {
|
|
12
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
13
|
+
};
|
|
14
|
+
runAsync(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
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 EmbeddedUpdateMutation_1 = require("../../../graphql/mutations/EmbeddedUpdateMutation");
|
|
8
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
9
|
+
const prompts_1 = require("../../../prompts");
|
|
10
|
+
const json_1 = require("../../../utils/json");
|
|
11
|
+
class UpdateEmbeddedDelete extends EasCommand_1.default {
|
|
12
|
+
static description = 'delete an embedded update registered with EAS Update';
|
|
13
|
+
static args = {
|
|
14
|
+
id: core_1.Args.string({
|
|
15
|
+
required: true,
|
|
16
|
+
description: 'The ID of the embedded update (manifest UUID from app.manifest).',
|
|
17
|
+
}),
|
|
18
|
+
};
|
|
19
|
+
static flags = {
|
|
20
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
21
|
+
};
|
|
22
|
+
static contextDefinition = {
|
|
23
|
+
...this.ContextOptions.LoggedIn,
|
|
24
|
+
};
|
|
25
|
+
async runAsync() {
|
|
26
|
+
const { args: { id: embeddedUpdateId }, flags, } = await this.parse(UpdateEmbeddedDelete);
|
|
27
|
+
const { json: jsonFlag, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
|
|
28
|
+
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateEmbeddedDelete, { nonInteractive });
|
|
29
|
+
if (jsonFlag) {
|
|
30
|
+
(0, json_1.enableJsonOutput)();
|
|
31
|
+
}
|
|
32
|
+
if (!nonInteractive) {
|
|
33
|
+
log_1.default.log(`You are about to permanently delete embedded update: "${embeddedUpdateId}". ` +
|
|
34
|
+
`Diff patches already generated against this bundle keep serving, but new diffs ` +
|
|
35
|
+
`can't be generated until you re-upload it.`);
|
|
36
|
+
log_1.default.newLine();
|
|
37
|
+
const confirmed = await (0, prompts_1.toggleConfirmAsync)({ message: 'Are you sure you wish to proceed?' });
|
|
38
|
+
if (!confirmed) {
|
|
39
|
+
log_1.default.error(`Canceled deletion of embedded update: "${embeddedUpdateId}".`);
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
// Best-effort delete on the server: deleting an unknown id succeeds (idempotent),
|
|
44
|
+
// so we don't need a not-found branch here.
|
|
45
|
+
await EmbeddedUpdateMutation_1.EmbeddedUpdateMutation.deleteEmbeddedUpdateAsync(graphqlClient, {
|
|
46
|
+
id: embeddedUpdateId,
|
|
47
|
+
});
|
|
48
|
+
if (jsonFlag) {
|
|
49
|
+
(0, json_1.printJsonOnlyOutput)({ id: embeddedUpdateId });
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
log_1.default.withTick(`Deleted embedded update ${embeddedUpdateId}`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.default = UpdateEmbeddedDelete;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Platform } from '@expo/eas-build-job';
|
|
2
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
3
|
+
export default class UpdateEmbeddedList extends EasCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<Platform | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
'runtime-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
11
|
+
limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
|
|
12
|
+
'after-cursor': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
};
|
|
14
|
+
static contextDefinition: {
|
|
15
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
16
|
+
projectId: import("../../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
17
|
+
};
|
|
18
|
+
runAsync(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const eas_build_job_1 = require("@expo/eas-build-job");
|
|
5
|
+
const core_1 = require("@oclif/core");
|
|
6
|
+
const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
|
|
8
|
+
const flags_1 = require("../../../commandUtils/flags");
|
|
9
|
+
const pagination_1 = require("../../../commandUtils/pagination");
|
|
10
|
+
const ChannelQuery_1 = require("../../../graphql/queries/ChannelQuery");
|
|
11
|
+
const EmbeddedUpdateQuery_1 = require("../../../graphql/queries/EmbeddedUpdateQuery");
|
|
12
|
+
const AppPlatform_1 = require("../../../graphql/types/AppPlatform");
|
|
13
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
14
|
+
const prompts_1 = require("../../../prompts");
|
|
15
|
+
const date_1 = require("../../../utils/date");
|
|
16
|
+
const formatFields_1 = tslib_1.__importDefault(require("../../../utils/formatFields"));
|
|
17
|
+
const json_1 = require("../../../utils/json");
|
|
18
|
+
const DEFAULT_LIMIT = 25;
|
|
19
|
+
const MAX_LIMIT = 50;
|
|
20
|
+
const CHANNELS_LIMIT = 50;
|
|
21
|
+
class UpdateEmbeddedList extends EasCommand_1.default {
|
|
22
|
+
static description = 'list embedded updates registered with EAS Update for this project';
|
|
23
|
+
static flags = {
|
|
24
|
+
platform: core_1.Flags.option({
|
|
25
|
+
char: 'p',
|
|
26
|
+
description: 'Filter by platform',
|
|
27
|
+
options: [eas_build_job_1.Platform.IOS, eas_build_job_1.Platform.ANDROID],
|
|
28
|
+
})(),
|
|
29
|
+
'runtime-version': core_1.Flags.string({
|
|
30
|
+
description: 'Filter by runtime version',
|
|
31
|
+
}),
|
|
32
|
+
channel: core_1.Flags.string({
|
|
33
|
+
description: 'Filter by channel name (pass "all" to skip the channel prompt)',
|
|
34
|
+
}),
|
|
35
|
+
limit: (0, pagination_1.getLimitFlagWithCustomValues)({ defaultTo: DEFAULT_LIMIT, limit: MAX_LIMIT }),
|
|
36
|
+
'after-cursor': core_1.Flags.string({
|
|
37
|
+
description: 'Return items after this cursor (for pagination)',
|
|
38
|
+
}),
|
|
39
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
40
|
+
};
|
|
41
|
+
static contextDefinition = {
|
|
42
|
+
...this.ContextOptions.ProjectId,
|
|
43
|
+
...this.ContextOptions.LoggedIn,
|
|
44
|
+
};
|
|
45
|
+
async runAsync() {
|
|
46
|
+
const { flags } = await this.parse(UpdateEmbeddedList);
|
|
47
|
+
const { json: jsonFlag, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
|
|
48
|
+
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateEmbeddedList, { nonInteractive });
|
|
49
|
+
if (jsonFlag) {
|
|
50
|
+
(0, json_1.enableJsonOutput)();
|
|
51
|
+
}
|
|
52
|
+
const platform = flags.platform
|
|
53
|
+
? (0, AppPlatform_1.toAppPlatform)(flags.platform)
|
|
54
|
+
: undefined;
|
|
55
|
+
// Resolve channel filter:
|
|
56
|
+
// - `--channel <name>`: use it
|
|
57
|
+
// - `--channel all` (or no flag in non-interactive / json): no channel filter
|
|
58
|
+
// - no flag in interactive: prompt with the project's channels + "All channels"
|
|
59
|
+
let channel;
|
|
60
|
+
if (flags.channel) {
|
|
61
|
+
channel = flags.channel.toLowerCase() === 'all' ? undefined : flags.channel;
|
|
62
|
+
}
|
|
63
|
+
else if (!nonInteractive && !jsonFlag) {
|
|
64
|
+
channel = await promptForChannelAsync(graphqlClient, projectId);
|
|
65
|
+
}
|
|
66
|
+
const filter = platform || flags['runtime-version'] || channel
|
|
67
|
+
? {
|
|
68
|
+
platform,
|
|
69
|
+
runtimeVersion: flags['runtime-version'],
|
|
70
|
+
channel,
|
|
71
|
+
}
|
|
72
|
+
: undefined;
|
|
73
|
+
const limit = flags.limit ?? DEFAULT_LIMIT;
|
|
74
|
+
const connection = await EmbeddedUpdateQuery_1.EmbeddedUpdateQuery.viewPaginatedAsync(graphqlClient, {
|
|
75
|
+
appId: projectId,
|
|
76
|
+
filter,
|
|
77
|
+
first: limit,
|
|
78
|
+
after: flags['after-cursor'],
|
|
79
|
+
});
|
|
80
|
+
const embeddedUpdates = connection.edges.map(e => e.node);
|
|
81
|
+
if (jsonFlag) {
|
|
82
|
+
(0, json_1.printJsonOnlyOutput)({
|
|
83
|
+
embeddedUpdates,
|
|
84
|
+
pageInfo: connection.pageInfo,
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
if (embeddedUpdates.length === 0) {
|
|
89
|
+
log_1.default.log('No embedded updates found.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
log_1.default.addNewLineIfNone();
|
|
93
|
+
log_1.default.log(chalk_1.default.bold(`Embedded updates (${embeddedUpdates.length}${connection.pageInfo.hasNextPage ? '+' : ''}):`));
|
|
94
|
+
log_1.default.newLine();
|
|
95
|
+
log_1.default.log(embeddedUpdates.map(formatEmbeddedUpdateRow).join(`\n\n${chalk_1.default.dim('———')}\n\n`));
|
|
96
|
+
if (connection.pageInfo.hasNextPage && connection.pageInfo.endCursor) {
|
|
97
|
+
log_1.default.newLine();
|
|
98
|
+
log_1.default.log(chalk_1.default.dim(`Showing ${embeddedUpdates.length}. For the next page, run with --after-cursor ${connection.pageInfo.endCursor}`));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
exports.default = UpdateEmbeddedList;
|
|
103
|
+
// Sentinel for the "All channels" option. We can't use `undefined` here because
|
|
104
|
+
// the underlying `prompts` library substitutes a choice's index when its value
|
|
105
|
+
// is undefined, which then leaks into the GraphQL filter.
|
|
106
|
+
const ALL_CHANNELS = '__embedded_update_list__all_channels__';
|
|
107
|
+
async function promptForChannelAsync(graphqlClient, projectId) {
|
|
108
|
+
const channels = await ChannelQuery_1.ChannelQuery.viewUpdateChannelsOnAppAsync(graphqlClient, {
|
|
109
|
+
appId: projectId,
|
|
110
|
+
offset: 0,
|
|
111
|
+
limit: CHANNELS_LIMIT,
|
|
112
|
+
});
|
|
113
|
+
if (channels.length === 0) {
|
|
114
|
+
// Nothing to choose from — fall back to listing everything.
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
const selected = await (0, prompts_1.selectAsync)('Filter embedded updates by which channel?', [
|
|
118
|
+
{ title: 'All channels', value: ALL_CHANNELS },
|
|
119
|
+
...channels.map(c => ({ title: c.name, value: c.name })),
|
|
120
|
+
]);
|
|
121
|
+
return selected === ALL_CHANNELS ? undefined : selected;
|
|
122
|
+
}
|
|
123
|
+
function formatEmbeddedUpdateRow(embeddedUpdate) {
|
|
124
|
+
const createdAt = new Date(embeddedUpdate.createdAt);
|
|
125
|
+
return (0, formatFields_1.default)([
|
|
126
|
+
{ label: 'ID', value: embeddedUpdate.id },
|
|
127
|
+
{ label: 'Platform', value: embeddedUpdate.platform.toLowerCase() },
|
|
128
|
+
{ label: 'Runtime version', value: embeddedUpdate.runtimeVersion },
|
|
129
|
+
{ label: 'Channel', value: embeddedUpdate.channel },
|
|
130
|
+
{ label: 'Created', value: `${(0, date_1.fromNow)(createdAt)} ago` },
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import EasCommand from '../../../commandUtils/EasCommand';
|
|
2
|
+
import { EmbeddedUpdateFragment } from '../../../graphql/queries/EmbeddedUpdateQuery';
|
|
3
|
+
export default class UpdateEmbeddedView extends EasCommand {
|
|
4
|
+
static description: string;
|
|
5
|
+
static args: {
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
8
|
+
static flags: {
|
|
9
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
|
+
};
|
|
11
|
+
static contextDefinition: {
|
|
12
|
+
loggedIn: import("../../../commandUtils/context/LoggedInContextField").default;
|
|
13
|
+
projectId: import("../../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
14
|
+
};
|
|
15
|
+
runAsync(): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
export declare function formatEmbeddedUpdate(embeddedUpdate: EmbeddedUpdateFragment): string;
|