eas-cli 18.8.0 → 18.9.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 +162 -94
- package/build/build/utils/url.d.ts +6 -0
- package/build/build/utils/url.js +9 -0
- package/build/commands/build/download.d.ts +6 -2
- package/build/commands/build/download.js +129 -18
- package/build/commands/integrations/asc/connect.d.ts +19 -0
- package/build/commands/integrations/asc/connect.js +159 -0
- package/build/commands/integrations/asc/disconnect.d.ts +15 -0
- package/build/commands/integrations/asc/disconnect.js +115 -0
- package/build/commands/integrations/asc/status.d.ts +14 -0
- package/build/commands/integrations/asc/status.js +65 -0
- package/build/commands/simulator/start.d.ts +16 -0
- package/build/commands/simulator/start.js +205 -0
- package/build/commands/simulator/stop.d.ts +13 -0
- package/build/commands/simulator/stop.js +38 -0
- package/build/credentials/ios/actions/AscApiKeyUtils.d.ts +2 -1
- package/build/credentials/ios/actions/AscApiKeyUtils.js +16 -0
- package/build/credentials/ios/appstore/AppStoreApi.d.ts +3 -1
- package/build/credentials/ios/appstore/AppStoreApi.js +2 -2
- package/build/graphql/generated.d.ts +368 -0
- package/build/graphql/generated.js +23 -3
- package/build/graphql/mutations/AscAppLinkMutation.d.ts +12 -0
- package/build/graphql/mutations/AscAppLinkMutation.js +36 -0
- package/build/graphql/mutations/DeviceRunSessionMutation.d.ts +6 -0
- package/build/graphql/mutations/DeviceRunSessionMutation.js +49 -0
- package/build/graphql/queries/AscAppLinkQuery.d.ts +8 -0
- package/build/graphql/queries/AscAppLinkQuery.js +67 -0
- package/build/graphql/queries/DeviceRunSessionQuery.d.ts +5 -0
- package/build/graphql/queries/DeviceRunSessionQuery.js +28 -0
- package/build/integrations/asc/ascApiKey.d.ts +7 -0
- package/build/integrations/asc/ascApiKey.js +26 -0
- package/build/integrations/asc/utils.d.ts +21 -0
- package/build/integrations/asc/utils.js +56 -0
- package/build/utils/download.d.ts +1 -0
- package/build/utils/download.js +1 -0
- package/oclif.manifest.json +3152 -2671
- package/package.json +6 -3
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tslib_1 = require("tslib");
|
|
4
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../../commandUtils/EasCommand"));
|
|
5
|
+
const flags_1 = require("../../../commandUtils/flags");
|
|
6
|
+
const utils_1 = require("../../../integrations/asc/utils");
|
|
7
|
+
const AscAppLinkQuery_1 = require("../../../graphql/queries/AscAppLinkQuery");
|
|
8
|
+
const log_1 = tslib_1.__importDefault(require("../../../log"));
|
|
9
|
+
const ora_1 = require("../../../ora");
|
|
10
|
+
const json_1 = require("../../../utils/json");
|
|
11
|
+
class IntegrationsAscStatus extends EasCommand_1.default {
|
|
12
|
+
static description = 'show the App Store Connect app link status for the current project';
|
|
13
|
+
static flags = {
|
|
14
|
+
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
15
|
+
};
|
|
16
|
+
static contextDefinition = {
|
|
17
|
+
...this.ContextOptions.ProjectId,
|
|
18
|
+
...this.ContextOptions.LoggedIn,
|
|
19
|
+
};
|
|
20
|
+
async runAsync() {
|
|
21
|
+
const { flags } = await this.parse(IntegrationsAscStatus);
|
|
22
|
+
const { json, nonInteractive } = (0, flags_1.resolveNonInteractiveAndJsonFlags)(flags);
|
|
23
|
+
if (json) {
|
|
24
|
+
(0, json_1.enableJsonOutput)();
|
|
25
|
+
}
|
|
26
|
+
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(IntegrationsAscStatus, {
|
|
27
|
+
nonInteractive,
|
|
28
|
+
});
|
|
29
|
+
const metadata = await this.fetchStatusAsync(graphqlClient, projectId);
|
|
30
|
+
if (!metadata) {
|
|
31
|
+
if (json) {
|
|
32
|
+
(0, json_1.printJsonOnlyOutput)((0, utils_1.buildInvalidJsonOutput)('status', projectId));
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
log_1.default.addNewLineIfNone();
|
|
36
|
+
log_1.default.warn('The App Store Connect API key linked to this project has been revoked or is no longer valid.');
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (json) {
|
|
41
|
+
(0, json_1.printJsonOnlyOutput)((0, utils_1.buildJsonOutput)('status', metadata));
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
log_1.default.addNewLineIfNone();
|
|
45
|
+
log_1.default.log((0, utils_1.formatAscAppLinkStatus)(metadata));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async fetchStatusAsync(graphqlClient, projectId) {
|
|
49
|
+
const spinner = (0, ora_1.ora)('Fetching App Store Connect app link status').start();
|
|
50
|
+
try {
|
|
51
|
+
const metadata = await AscAppLinkQuery_1.AscAppLinkQuery.getAppMetadataAsync(graphqlClient, projectId);
|
|
52
|
+
spinner.succeed('Fetched App Store Connect app link status');
|
|
53
|
+
return metadata;
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
if ((0, utils_1.isAscAuthenticationError)(err)) {
|
|
57
|
+
spinner.fail('App Store Connect connection is invalid');
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
spinner.fail('Failed to fetch App Store Connect app link status');
|
|
61
|
+
throw err;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
exports.default = IntegrationsAscStatus;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class SimulatorStart extends EasCommand {
|
|
3
|
+
static hidden: boolean;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
platform: import("@oclif/core/lib/interfaces").OptionFlag<"android" | "ios", import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
type: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
9
|
+
'package-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
|
+
};
|
|
11
|
+
static contextDefinition: {
|
|
12
|
+
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
13
|
+
projectId: import("../../commandUtils/context/ProjectIdContextField").ProjectIdContextField;
|
|
14
|
+
};
|
|
15
|
+
runAsync(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
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 url_1 = require("../../build/utils/url");
|
|
6
|
+
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
7
|
+
const flags_1 = require("../../commandUtils/flags");
|
|
8
|
+
const generated_1 = require("../../graphql/generated");
|
|
9
|
+
const DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSessionMutation");
|
|
10
|
+
const DeviceRunSessionQuery_1 = require("../../graphql/queries/DeviceRunSessionQuery");
|
|
11
|
+
const log_1 = tslib_1.__importStar(require("../../log"));
|
|
12
|
+
const ora_1 = require("../../ora");
|
|
13
|
+
const promise_1 = require("../../utils/promise");
|
|
14
|
+
const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
|
|
15
|
+
const POLL_INTERVAL_MS = 5_000; // 5 seconds
|
|
16
|
+
const POLL_TIMEOUT_MS = 15 * 60 * 1_000; // 15 minutes
|
|
17
|
+
// Mapping enum → CLI flag value. Declared as Record<DeviceRunSessionType, string>
|
|
18
|
+
// so adding a new enum value in codegen fails the build until it is wired up here.
|
|
19
|
+
const DEVICE_RUN_SESSION_TYPE_FLAG_VALUES = {
|
|
20
|
+
[generated_1.DeviceRunSessionType.AgentDevice]: 'agent-device',
|
|
21
|
+
};
|
|
22
|
+
const DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE = Object.fromEntries(Object.entries(DEVICE_RUN_SESSION_TYPE_FLAG_VALUES).map(([type, value]) => [value, type]));
|
|
23
|
+
class SimulatorStart extends EasCommand_1.default {
|
|
24
|
+
static hidden = true;
|
|
25
|
+
static description = '[EXPERIMENTAL] start a remote simulator session on EAS and get the credentials to connect to it with the CLI tool of your choice';
|
|
26
|
+
static flags = {
|
|
27
|
+
platform: core_1.Flags.option({
|
|
28
|
+
description: 'Device platform',
|
|
29
|
+
options: ['android', 'ios'],
|
|
30
|
+
required: true,
|
|
31
|
+
})(),
|
|
32
|
+
type: core_1.Flags.option({
|
|
33
|
+
description: 'Type of device run session to create',
|
|
34
|
+
options: Object.values(DEVICE_RUN_SESSION_TYPE_FLAG_VALUES),
|
|
35
|
+
default: DEVICE_RUN_SESSION_TYPE_FLAG_VALUES[generated_1.DeviceRunSessionType.AgentDevice],
|
|
36
|
+
})(),
|
|
37
|
+
'package-version': core_1.Flags.string({
|
|
38
|
+
description: 'Version of the package backing the device run session (e.g. "0.1.3-alpha.3"). Defaults to "latest" when omitted.',
|
|
39
|
+
}),
|
|
40
|
+
...flags_1.EASNonInteractiveFlag,
|
|
41
|
+
};
|
|
42
|
+
static contextDefinition = {
|
|
43
|
+
...this.ContextOptions.ProjectId,
|
|
44
|
+
...this.ContextOptions.LoggedIn,
|
|
45
|
+
};
|
|
46
|
+
async runAsync() {
|
|
47
|
+
const { flags } = await this.parse(SimulatorStart);
|
|
48
|
+
const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStart, {
|
|
49
|
+
nonInteractive: flags['non-interactive'],
|
|
50
|
+
});
|
|
51
|
+
const platform = flags.platform === 'android' ? generated_1.AppPlatform.Android : generated_1.AppPlatform.Ios;
|
|
52
|
+
const createSpinner = (0, ora_1.ora)('🚀 Creating device run session').start();
|
|
53
|
+
let deviceRunSessionId;
|
|
54
|
+
try {
|
|
55
|
+
const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.createDeviceRunSessionAsync(graphqlClient, {
|
|
56
|
+
appId: projectId,
|
|
57
|
+
platform,
|
|
58
|
+
type: DEVICE_RUN_SESSION_TYPE_BY_FLAG_VALUE[flags.type],
|
|
59
|
+
packageVersion: flags['package-version'],
|
|
60
|
+
});
|
|
61
|
+
deviceRunSessionId = session.id;
|
|
62
|
+
const jobRunId = (0, nullthrows_1.default)(session.turtleJobRun?.id, 'Expected device run session to start');
|
|
63
|
+
const jobRunUrl = (0, url_1.getBareJobRunUrl)(session.app.ownerAccount.name, session.app.slug, jobRunId);
|
|
64
|
+
createSpinner.succeed(`Device run session created (id: ${deviceRunSessionId}) ${(0, log_1.link)(jobRunUrl)}`);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
createSpinner.fail('Failed to create device run session');
|
|
68
|
+
throw err;
|
|
69
|
+
}
|
|
70
|
+
const checkReadiness = getReadinessCheckerForType(flags.type);
|
|
71
|
+
const pollSpinner = (0, ora_1.ora)(`⏳ Waiting for ${flags.type} daemon to start`).start();
|
|
72
|
+
const deadline = Date.now() + POLL_TIMEOUT_MS;
|
|
73
|
+
let result = { ready: false };
|
|
74
|
+
try {
|
|
75
|
+
while (Date.now() < deadline) {
|
|
76
|
+
const session = await DeviceRunSessionQuery_1.DeviceRunSessionQuery.byIdAsync(graphqlClient, deviceRunSessionId);
|
|
77
|
+
if (session.status === generated_1.DeviceRunSessionStatus.Errored ||
|
|
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.`);
|
|
80
|
+
}
|
|
81
|
+
const jobRunStatus = session.turtleJobRun?.status;
|
|
82
|
+
if (jobRunStatus === generated_1.JobRunStatus.Errored ||
|
|
83
|
+
jobRunStatus === generated_1.JobRunStatus.Canceled ||
|
|
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.`);
|
|
86
|
+
}
|
|
87
|
+
const logMessages = await fetchLogMessagesAsync(session.turtleJobRun?.logFileUrls ?? []);
|
|
88
|
+
result = checkReadiness(logMessages);
|
|
89
|
+
if (result.ready) {
|
|
90
|
+
pollSpinner.succeed(`🎉 ${flags.type} daemon is ready`);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
await (0, promise_1.sleepAsync)(POLL_INTERVAL_MS);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (err) {
|
|
97
|
+
pollSpinner.fail(`Failed while polling for ${flags.type} daemon logs`);
|
|
98
|
+
throw err;
|
|
99
|
+
}
|
|
100
|
+
if (!result.ready) {
|
|
101
|
+
pollSpinner.fail(`Timed out waiting for ${flags.type} daemon to start`);
|
|
102
|
+
throw new Error(`Timed out after ${Math.round(POLL_TIMEOUT_MS / 1000)}s waiting for ${flags.type} daemon to start.`);
|
|
103
|
+
}
|
|
104
|
+
log_1.default.newLine();
|
|
105
|
+
log_1.default.log(`🔑 Run the following in your shell to attach to ${flags.type}:`);
|
|
106
|
+
log_1.default.newLine();
|
|
107
|
+
log_1.default.log(result.message);
|
|
108
|
+
log_1.default.newLine();
|
|
109
|
+
log_1.default.log(`When you are done, stop the session with: eas simulator:stop --id ${deviceRunSessionId}`);
|
|
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;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
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
|
+
}
|
|
144
|
+
async function fetchLogMessagesAsync(logUrls) {
|
|
145
|
+
const messages = [];
|
|
146
|
+
for (const url of logUrls) {
|
|
147
|
+
const text = await fetchLogTextAsync(url);
|
|
148
|
+
if (!text) {
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
for (const line of text.split('\n')) {
|
|
152
|
+
if (!line.trim()) {
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
messages.push(extractLogMessage(line));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return messages;
|
|
159
|
+
}
|
|
160
|
+
async function fetchLogTextAsync(url) {
|
|
161
|
+
try {
|
|
162
|
+
const response = await fetch(url);
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
return await response.text();
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function extractLogMessage(line) {
|
|
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
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
const parsed = JSON.parse(trimmed);
|
|
182
|
+
if (parsed && typeof parsed === 'object' && 'msg' in parsed) {
|
|
183
|
+
const msg = parsed.msg;
|
|
184
|
+
if (typeof msg === 'string') {
|
|
185
|
+
return msg;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// not JSON, fall through
|
|
191
|
+
}
|
|
192
|
+
return line;
|
|
193
|
+
}
|
|
194
|
+
function extractExportedEnvValue(text, varName) {
|
|
195
|
+
// Matches: export NAME=value | export NAME="value" | export NAME='value'
|
|
196
|
+
const pattern = new RegExp(`export\\s+${escapeRegExp(varName)}=(?:"([^"]*)"|'([^']*)'|(\\S+))`);
|
|
197
|
+
const match = pattern.exec(text);
|
|
198
|
+
if (!match) {
|
|
199
|
+
return undefined;
|
|
200
|
+
}
|
|
201
|
+
return match[1] ?? match[2] ?? match[3];
|
|
202
|
+
}
|
|
203
|
+
function escapeRegExp(value) {
|
|
204
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
205
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
export default class SimulatorStop extends EasCommand {
|
|
3
|
+
static hidden: boolean;
|
|
4
|
+
static description: string;
|
|
5
|
+
static flags: {
|
|
6
|
+
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
|
+
id: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
static contextDefinition: {
|
|
10
|
+
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
11
|
+
};
|
|
12
|
+
runAsync(): Promise<void>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
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 DeviceRunSessionMutation_1 = require("../../graphql/mutations/DeviceRunSessionMutation");
|
|
8
|
+
const ora_1 = require("../../ora");
|
|
9
|
+
class SimulatorStop extends EasCommand_1.default {
|
|
10
|
+
static hidden = true;
|
|
11
|
+
static description = '[EXPERIMENTAL] stop a remote simulator session on EAS by its device run session ID';
|
|
12
|
+
static flags = {
|
|
13
|
+
id: core_1.Flags.string({
|
|
14
|
+
description: 'Device run session ID',
|
|
15
|
+
required: true,
|
|
16
|
+
}),
|
|
17
|
+
...flags_1.EASNonInteractiveFlag,
|
|
18
|
+
};
|
|
19
|
+
static contextDefinition = {
|
|
20
|
+
...this.ContextOptions.LoggedIn,
|
|
21
|
+
};
|
|
22
|
+
async runAsync() {
|
|
23
|
+
const { flags } = await this.parse(SimulatorStop);
|
|
24
|
+
const { loggedIn: { graphqlClient }, } = await this.getContextAsync(SimulatorStop, {
|
|
25
|
+
nonInteractive: flags['non-interactive'],
|
|
26
|
+
});
|
|
27
|
+
const stopSpinner = (0, ora_1.ora)(`🛑 Stopping device run session ${flags.id}`).start();
|
|
28
|
+
try {
|
|
29
|
+
const session = await DeviceRunSessionMutation_1.DeviceRunSessionMutation.stopDeviceRunSessionAsync(graphqlClient, flags.id);
|
|
30
|
+
stopSpinner.succeed(`🎉 Device run session ${session.id} is ${session.status.toLowerCase()}`);
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
stopSpinner.fail(`Failed to stop device run session ${flags.id}`);
|
|
34
|
+
throw err;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
exports.default = SimulatorStop;
|
|
@@ -3,7 +3,8 @@ import { CredentialsContext } from '../../context';
|
|
|
3
3
|
import { AscApiKey } from '../appstore/Credentials.types';
|
|
4
4
|
import { AscApiKeyPath, MinimalAscApiKey } from '../credentials';
|
|
5
5
|
export declare enum AppStoreApiKeyPurpose {
|
|
6
|
-
SUBMISSION_SERVICE = "EAS Submit"
|
|
6
|
+
SUBMISSION_SERVICE = "EAS Submit",
|
|
7
|
+
ASC_APP_CONNECTION = "EAS Connect"
|
|
7
8
|
}
|
|
8
9
|
export declare function promptForAscApiKeyPathAsync(ctx: CredentialsContext): Promise<AscApiKeyPath>;
|
|
9
10
|
export declare function promptForIssuerIdAsync(): Promise<string>;
|
|
@@ -15,6 +15,7 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
|
15
15
|
const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
|
|
16
16
|
const nanoid_1 = require("nanoid");
|
|
17
17
|
const path_1 = tslib_1.__importDefault(require("path"));
|
|
18
|
+
const apple_utils_1 = require("@expo/apple-utils");
|
|
18
19
|
const AppleTeamFormatting_1 = require("./AppleTeamFormatting");
|
|
19
20
|
const log_1 = tslib_1.__importStar(require("../../../log"));
|
|
20
21
|
const prompts_1 = require("../../../prompts");
|
|
@@ -25,6 +26,7 @@ const validateAscApiKey_1 = require("../validators/validateAscApiKey");
|
|
|
25
26
|
var AppStoreApiKeyPurpose;
|
|
26
27
|
(function (AppStoreApiKeyPurpose) {
|
|
27
28
|
AppStoreApiKeyPurpose["SUBMISSION_SERVICE"] = "EAS Submit";
|
|
29
|
+
AppStoreApiKeyPurpose["ASC_APP_CONNECTION"] = "EAS Connect";
|
|
28
30
|
})(AppStoreApiKeyPurpose || (exports.AppStoreApiKeyPurpose = AppStoreApiKeyPurpose = {}));
|
|
29
31
|
async function promptForAscApiKeyPathAsync(ctx) {
|
|
30
32
|
const { keyId, keyP8Path } = await promptForKeyP8AndIdAsync();
|
|
@@ -79,11 +81,25 @@ async function provideOrGenerateAscApiKeyAsync(ctx, purpose) {
|
|
|
79
81
|
}
|
|
80
82
|
async function generateAscApiKeyAsync(ctx, purpose) {
|
|
81
83
|
await ctx.appStore.ensureAuthenticatedAsync();
|
|
84
|
+
const role = await selectRoleForGeneratedAscApiKeyAsync();
|
|
82
85
|
const ascApiKey = await ctx.appStore.createAscApiKeyAsync(ctx.analytics, {
|
|
83
86
|
nickname: getAscApiKeyName(purpose),
|
|
87
|
+
roles: [role],
|
|
84
88
|
});
|
|
85
89
|
return await getMinimalAscApiKeyAsync(ascApiKey);
|
|
86
90
|
}
|
|
91
|
+
async function selectRoleForGeneratedAscApiKeyAsync() {
|
|
92
|
+
return await (0, prompts_1.selectAsync)('Select role for the generated API key:', [
|
|
93
|
+
{
|
|
94
|
+
title: 'ADMIN (default)',
|
|
95
|
+
value: apple_utils_1.UserRole.ADMIN,
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
title: 'APP_MANAGER (least privilege for app management)',
|
|
99
|
+
value: apple_utils_1.UserRole.APP_MANAGER,
|
|
100
|
+
},
|
|
101
|
+
]);
|
|
102
|
+
}
|
|
87
103
|
function getAscApiKeyName(purpose) {
|
|
88
104
|
const nameParts = ['[Expo]', purpose, (0, nanoid_1.nanoid)(10)];
|
|
89
105
|
return nameParts.join(' ');
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ProfileType } from '@expo/app-store';
|
|
2
|
+
import { UserRole } from '@expo/apple-utils';
|
|
2
3
|
import { AscApiKey, AscApiKeyInfo, DistributionCertificate, DistributionCertificateStoreInfo, ProvisioningProfile, ProvisioningProfileStoreInfo, PushKey, PushKeyStoreInfo } from './Credentials.types';
|
|
3
4
|
import { Options as AuthenticateOptions } from './authenticate';
|
|
4
5
|
import { AuthCtx, AuthenticationMode, UserAuthCtx } from './authenticateTypes';
|
|
@@ -26,8 +27,9 @@ export default class AppStoreApi {
|
|
|
26
27
|
createOrReuseAdhocProvisioningProfileAsync(udids: string[], bundleIdentifier: string, distCertSerialNumber: string, profileType: ProfileType): Promise<ProvisioningProfile>;
|
|
27
28
|
listAscApiKeysAsync(): Promise<AscApiKeyInfo[]>;
|
|
28
29
|
getAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo | null>;
|
|
29
|
-
createAscApiKeyAsync(analytics: Analytics, { nickname }: {
|
|
30
|
+
createAscApiKeyAsync(analytics: Analytics, { nickname, roles }: {
|
|
30
31
|
nickname: string;
|
|
32
|
+
roles?: UserRole[];
|
|
31
33
|
}): Promise<AscApiKey>;
|
|
32
34
|
revokeAscApiKeyAsync(keyId: string): Promise<AscApiKeyInfo>;
|
|
33
35
|
}
|
|
@@ -94,9 +94,9 @@ class AppStoreApi {
|
|
|
94
94
|
const userCtx = await this.ensureUserAuthenticatedAsync();
|
|
95
95
|
return await (0, ascApiKey_1.getAscApiKeyAsync)(userCtx, keyId);
|
|
96
96
|
}
|
|
97
|
-
async createAscApiKeyAsync(analytics, { nickname }) {
|
|
97
|
+
async createAscApiKeyAsync(analytics, { nickname, roles }) {
|
|
98
98
|
const userCtx = await this.ensureUserAuthenticatedAsync();
|
|
99
|
-
return await (0, ascApiKey_1.createAscApiKeyAsync)(analytics, userCtx, { nickname });
|
|
99
|
+
return await (0, ascApiKey_1.createAscApiKeyAsync)(analytics, userCtx, { nickname, roles });
|
|
100
100
|
}
|
|
101
101
|
async revokeAscApiKeyAsync(keyId) {
|
|
102
102
|
const userCtx = await this.ensureUserAuthenticatedAsync();
|