eas-cli 3.18.3 → 4.0.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.
Files changed (44) hide show
  1. package/README.md +72 -64
  2. package/build/channel/utils.d.ts +0 -10
  3. package/build/channel/utils.js +1 -60
  4. package/build/commands/channel/rollout.d.ts +14 -2
  5. package/build/commands/channel/rollout.js +106 -270
  6. package/build/commands/update/republish.js +6 -6
  7. package/build/devices/actions/create/action.d.ts +2 -1
  8. package/build/devices/actions/create/action.js +10 -1
  9. package/build/devices/actions/create/currentMachineMethod.d.ts +3 -0
  10. package/build/devices/actions/create/currentMachineMethod.js +100 -0
  11. package/build/devices/actions/create/developerPortalMethod.js +2 -1
  12. package/build/devices/actions/create/inputMethod.d.ts +0 -1
  13. package/build/devices/actions/create/inputMethod.js +6 -64
  14. package/build/devices/actions/create/utils.d.ts +10 -0
  15. package/build/devices/actions/create/utils.js +73 -0
  16. package/build/devices/manager.js +3 -3
  17. package/build/devices/utils/errors.d.ts +3 -0
  18. package/build/devices/utils/errors.js +7 -1
  19. package/build/devices/utils/formatDevice.js +1 -0
  20. package/build/graphql/generated.d.ts +5 -4
  21. package/build/graphql/generated.js +1 -0
  22. package/build/graphql/types/credentials/AppleDevice.js +1 -0
  23. package/build/rollout/actions/CreateRollout.d.ts +2 -0
  24. package/build/rollout/actions/CreateRollout.js +79 -5
  25. package/build/rollout/actions/EditRollout.js +5 -2
  26. package/build/rollout/actions/EndRollout.d.ts +6 -3
  27. package/build/rollout/actions/EndRollout.js +22 -21
  28. package/build/rollout/actions/ManageRollout.d.ts +3 -3
  29. package/build/rollout/actions/ManageRollout.js +1 -1
  30. package/build/rollout/actions/NonInteractiveRollout.d.ts +6 -4
  31. package/build/rollout/actions/NonInteractiveRollout.js +1 -1
  32. package/build/rollout/actions/RolloutMainMenu.d.ts +4 -4
  33. package/build/rollout/actions/RolloutMainMenu.js +14 -5
  34. package/build/rollout/actions/SelectRuntime.d.ts +4 -7
  35. package/build/rollout/actions/SelectRuntime.js +22 -39
  36. package/build/rollout/branch-mapping.js +2 -2
  37. package/build/rollout/utils.d.ts +1 -1
  38. package/build/rollout/utils.js +5 -5
  39. package/build/update/republish.js +6 -6
  40. package/build/utils/relay.js +3 -9
  41. package/oclif.manifest.json +1 -1
  42. package/package.json +3 -3
  43. package/build/commands/channel/rollout-preview.d.ts +0 -32
  44. package/build/commands/channel/rollout-preview.js +0 -140
@@ -3,262 +3,72 @@ var _a;
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  const tslib_1 = require("tslib");
5
5
  const core_1 = require("@oclif/core");
6
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
- const queries_1 = require("../../branch/queries");
8
- const branch_mapping_1 = require("../../channel/branch-mapping");
9
- const queries_2 = require("../../channel/queries");
10
- const utils_1 = require("../../channel/utils");
11
6
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
12
7
  const flags_1 = require("../../commandUtils/flags");
13
- const BranchQuery_1 = require("../../graphql/queries/BranchQuery");
14
- const ChannelQuery_1 = require("../../graphql/queries/ChannelQuery");
15
8
  const log_1 = tslib_1.__importDefault(require("../../log"));
16
- const projectUtils_1 = require("../../project/projectUtils");
17
- const prompts_1 = require("../../prompts");
18
- const json_1 = require("../../utils/json");
19
- const edit_1 = require("./edit");
20
- async function promptForRolloutPercentAsync({ promptMessage, }) {
21
- const { name: rolloutPercent } = await (0, prompts_1.promptAsync)({
22
- type: 'text',
23
- name: 'name',
24
- format: value => {
25
- return parseInt(value, 10);
26
- },
27
- message: promptMessage,
28
- initial: 0,
29
- validate: (rolloutPercent) => {
30
- const floatValue = parseFloat(rolloutPercent);
31
- return Number.isInteger(floatValue) && floatValue >= 0 && floatValue <= 100
32
- ? true
33
- : 'The rollout percentage must be an integer between 0 and 100 inclusive.';
34
- },
35
- });
36
- return rolloutPercent;
37
- }
38
- function getRolloutInfo(channel) {
39
- var _b;
40
- const { branchMapping } = (0, utils_1.getBranchMapping)(channel.branchMapping);
41
- const [newBranchId, oldBranchId] = branchMapping.data.map(d => d.branchId);
42
- const newBranch = channel.updateBranches.filter(branch => branch.id === newBranchId)[0];
43
- const oldBranch = channel.updateBranches.filter(branch => branch.id === oldBranchId)[0];
44
- if (!newBranch || !oldBranch) {
45
- throw new Error(`Branch mapping rollout is missing a branch for channel "${channel.name}".`);
46
- }
47
- const branchMappingNode = (_b = branchMapping.data[0]) === null || _b === void 0 ? void 0 : _b.branchMappingLogic;
48
- (0, branch_mapping_1.assertNodeObject)(branchMappingNode);
49
- (0, branch_mapping_1.assertNumber)(branchMappingNode.operand);
50
- const currentPercent = 100 * branchMappingNode.operand;
51
- return { newBranch, oldBranch, currentPercent };
52
- }
53
- async function startRolloutAsync(graphqlClient, { channelName, branchName, percent, projectId, displayName, currentBranchMapping, channel, nonInteractive, }) {
54
- const branch = await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
55
- appId: projectId,
56
- name: branchName,
57
- });
58
- const oldBranchId = currentBranchMapping.data[0].branchId;
59
- if (branch.id === oldBranchId) {
60
- throw new Error(`channel "${channelName}" is already pointing at branch "${branchName}". Rollouts must be done with distinct branches.`);
61
- }
62
- if (percent == null) {
63
- if (nonInteractive) {
64
- throw new Error('You must specify a percent with the --percent flag when initiating a rollout with the --non-interactive flag.');
65
- }
66
- const promptMessage = `What percent of users should be directed to the branch "${branchName}"?`;
67
- percent = await promptForRolloutPercentAsync({ promptMessage });
68
- }
69
- const newBranchMapping = {
70
- version: 0,
71
- data: [
72
- {
73
- branchId: branch.id,
74
- branchMappingLogic: {
75
- operand: percent / 100,
76
- clientKey: 'rolloutToken',
77
- branchMappingOperator: 'hash_lt',
78
- },
79
- },
80
- currentBranchMapping.data[0],
81
- ],
82
- };
83
- const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
84
- channelId: channel.id,
85
- branchMapping: JSON.stringify(newBranchMapping),
86
- });
87
- const oldBranch = channel.updateBranches.filter(branch => branch.id === oldBranchId)[0];
88
- if (!oldBranch) {
89
- throw new Error(`Branch mapping is missing its only branch for channel "${channelName}" on app "${displayName}"`);
90
- }
91
- const logMessage = `Started a rollout of branch ${chalk_1.default.bold(branchName)} on channel ${chalk_1.default.bold(channelName)}! ${chalk_1.default.bold(percent)}% of users will be directed to branch ${chalk_1.default.bold(branchName)}, ${chalk_1.default.bold(100 - percent)}% to branch ${chalk_1.default.bold(oldBranch.name)}.`;
92
- return { newChannelInfo, logMessage };
93
- }
94
- async function editRolloutAsync(graphqlClient, { channelName, percent, nonInteractive, currentBranchMapping, channel, }) {
95
- const { newBranch, oldBranch, currentPercent } = getRolloutInfo(channel);
96
- if (percent == null) {
97
- if (nonInteractive) {
98
- throw new Error('A rollout is already in progress. If you wish to modify it you must use specify the new rollout percentage with the --percent flag.');
99
- }
100
- const promptMessage = `Currently ${currentPercent}% of all users are routed to branch ${newBranch.name} and ${100 - currentPercent}% of all users are routed to branch ${oldBranch.name}. What percent of users should be directed to the branch ${newBranch.name}?`;
101
- percent = await promptForRolloutPercentAsync({ promptMessage });
102
- }
103
- const newBranchMapping = { ...currentBranchMapping };
104
- newBranchMapping.data[0].branchMappingLogic.operand = percent / 100;
105
- const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
106
- channelId: channel.id,
107
- branchMapping: JSON.stringify(newBranchMapping),
108
- });
109
- const logMessage = `Rollout of branch ${chalk_1.default.bold(newBranch.name)} on channel ${chalk_1.default.bold(channelName)} updated from ${chalk_1.default.bold(currentPercent)}% to ${chalk_1.default.bold(percent)}%. ${chalk_1.default.bold(percent)}% of users will be directed to branch ${chalk_1.default.bold(newBranch.name)}, ${chalk_1.default.bold(100 - percent)}% to branch ${chalk_1.default.bold(oldBranch.name)}.`;
110
- return { newChannelInfo, logMessage };
111
- }
112
- async function endRolloutAsync(graphqlClient, { channelName, branchName, nonInteractive, projectId, channel, }) {
113
- // end rollout
114
- const { newBranch, oldBranch, currentPercent } = getRolloutInfo(channel);
115
- let endOnNewBranch;
116
- if (branchName) {
117
- const branch = await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
118
- appId: projectId,
119
- name: branchName,
120
- });
121
- switch (branch.id) {
122
- case newBranch.id:
123
- endOnNewBranch = true;
124
- break;
125
- case oldBranch.id:
126
- endOnNewBranch = false;
127
- break;
128
- default:
129
- throw new Error(`The branch "${branchName}" specified by --branch must be one of the branches involved in the rollout: "${newBranch.name}" or "${oldBranch.name}".`);
130
- }
131
- }
132
- else {
133
- if (nonInteractive) {
134
- throw new Error('Branch name must be specified with the --branch flag when both the --end and --non-interactive flag are true.');
135
- }
136
- endOnNewBranch = await (0, prompts_1.selectAsync)('Ending the rollout will send all traffic to a single branch. Which one should that be?', [
137
- {
138
- title: `${newBranch.name} ${chalk_1.default.grey(`- current percent: ${currentPercent}%`)}`,
139
- value: true,
140
- },
141
- {
142
- title: `${oldBranch.name} ${chalk_1.default.grey(`- current percent: ${100 - currentPercent}%`)}`,
143
- value: false,
144
- },
145
- ]);
146
- }
147
- if (endOnNewBranch == null) {
148
- throw new Error('Branch to end on is undefined.');
149
- }
150
- const newBranchMapping = {
151
- version: 0,
152
- data: [
153
- {
154
- branchId: endOnNewBranch ? newBranch.id : oldBranch.id,
155
- branchMappingLogic: 'true',
156
- },
157
- ],
158
- };
159
- const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
160
- channelId: channel.id,
161
- branchMapping: JSON.stringify(newBranchMapping),
162
- });
163
- const logMessage = `Rollout on channel ${chalk_1.default.bold(channelName)} ended. All traffic is now sent to branch ${chalk_1.default.bold(endOnNewBranch ? newBranch.name : oldBranch.name)}`;
164
- return { newChannelInfo, logMessage };
165
- }
9
+ const EndRollout_1 = require("../../rollout/actions/EndRollout");
10
+ const ManageRollout_1 = require("../../rollout/actions/ManageRollout");
11
+ const NonInteractiveRollout_1 = require("../../rollout/actions/NonInteractiveRollout");
12
+ const RolloutMainMenu_1 = require("../../rollout/actions/RolloutMainMenu");
13
+ var ActionRawFlagValue;
14
+ (function (ActionRawFlagValue) {
15
+ ActionRawFlagValue["CREATE"] = "create";
16
+ ActionRawFlagValue["EDIT"] = "edit";
17
+ ActionRawFlagValue["END"] = "end";
18
+ })(ActionRawFlagValue || (ActionRawFlagValue = {}));
166
19
  class ChannelRollout extends EasCommand_1.default {
167
20
  async runAsync() {
168
- const { args: { channel: channelNameArg }, flags: { json: jsonFlag, end: endFlag, branch: branchName, percent, 'non-interactive': nonInteractive, }, } = await this.parse(ChannelRollout);
169
- const { privateProjectConfig: { projectId }, loggedIn: { graphqlClient }, } = await this.getContextAsync(ChannelRollout, {
170
- nonInteractive,
21
+ const { args, flags } = await this.parse(ChannelRollout);
22
+ const argsAndFlags = this.sanitizeArgsAndFlags({ ...flags, ...args });
23
+ const { privateProjectConfig: { exp, projectId }, loggedIn: { graphqlClient }, } = await this.getContextAsync(ChannelRollout, {
24
+ nonInteractive: argsAndFlags.nonInteractive,
171
25
  });
172
- if (jsonFlag) {
173
- (0, json_1.enableJsonOutput)();
174
- }
175
- const projectDisplayName = await (0, projectUtils_1.getDisplayNameForProjectIdAsync)(graphqlClient, projectId);
176
- let channelName = channelNameArg;
177
- if (!channelName) {
178
- const { name } = await (0, queries_2.selectChannelOnAppAsync)(graphqlClient, {
179
- projectId,
180
- selectionPromptTitle: 'Select a channel on which to perform a rollout',
181
- paginatedQueryOptions: {
182
- json: jsonFlag,
183
- nonInteractive,
184
- offset: 0,
185
- },
186
- });
187
- channelName = name;
188
- }
189
- const channel = await ChannelQuery_1.ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
190
- appId: projectId,
191
- channelName,
192
- });
193
- if (!channel) {
194
- throw new Error(`Could not find a channel named "${channelName}". Check which channels exist on this project with ${chalk_1.default.bold('eas channel:list')}.`);
195
- }
196
- const { branchMapping: currentBranchMapping, isRollout } = (0, utils_1.getBranchMapping)(channel.branchMapping);
197
- if (currentBranchMapping.data.length === 0) {
198
- throw new Error('The channel is not pointing at any branches.');
199
- }
200
- if (currentBranchMapping.data.length > 2) {
201
- throw new Error('"channel:rollout" cannot handle branch mappings with more than 2 branches.');
202
- }
203
- // This combination doesn't make sense. Throw an error explaining the options.
204
- if (isRollout && branchName && !endFlag) {
205
- throw new Error(`There is a rollout in progress. You can only either edit the rollout percent or 'end' it.`);
206
- }
207
- /**
208
- * This if/else block has three branches:
209
- * 1. The branch mapping is not a rollout, i.e. it is pointing to a single branch.
210
- * 2. The branch mapping is a rollout.
211
- * a. increase/decrease the rollout percentage.
212
- * b. end the rollout.
213
- */
214
- let rolloutMutationResult;
215
- if (!isRollout) {
216
- rolloutMutationResult = await startRolloutAsync(graphqlClient, {
217
- channelName,
218
- branchName: branchName !== null && branchName !== void 0 ? branchName : (await promptForBranchNameAsync({
219
- graphqlClient,
220
- projectId,
221
- channelName,
222
- nonInteractive,
223
- json: jsonFlag,
224
- })),
225
- percent,
226
- nonInteractive,
227
- projectId,
228
- displayName: projectDisplayName,
229
- currentBranchMapping,
230
- channel,
231
- });
232
- }
233
- else if (endFlag) {
234
- rolloutMutationResult = await endRolloutAsync(graphqlClient, {
235
- channelName,
236
- branchName,
237
- nonInteractive,
238
- projectId,
239
- channel,
240
- });
26
+ if (argsAndFlags.json) {
27
+ // TODO(quin): implement json output
28
+ throw new Error('Developer Preview doesnt support JSON output yet');
29
+ }
30
+ const app = { projectId, exp };
31
+ const ctx = {
32
+ projectId,
33
+ nonInteractive: argsAndFlags.nonInteractive,
34
+ graphqlClient,
35
+ app,
36
+ };
37
+ if (argsAndFlags.nonInteractive) {
38
+ await new NonInteractiveRollout_1.NonInteractiveRollout(argsAndFlags).runAsync(ctx);
241
39
  }
242
40
  else {
243
- rolloutMutationResult = await editRolloutAsync(graphqlClient, {
244
- channelName,
245
- percent,
246
- nonInteractive,
247
- currentBranchMapping,
248
- channel,
249
- });
250
- }
251
- if (!rolloutMutationResult) {
252
- throw new Error('rollout result is empty');
41
+ log_1.default.addNewLineIfNone();
42
+ log_1.default.warn(`✨ This command is in Developer Preview and has not been released to production yet`);
43
+ log_1.default.addNewLineIfNone();
44
+ await new RolloutMainMenu_1.RolloutMainMenu(argsAndFlags).runAsync(ctx);
253
45
  }
254
- const { newChannelInfo, logMessage } = rolloutMutationResult;
255
- if (jsonFlag) {
256
- (0, json_1.printJsonOnlyOutput)(newChannelInfo);
257
- }
258
- else {
259
- log_1.default.withTick(logMessage);
46
+ }
47
+ getAction(action) {
48
+ switch (action) {
49
+ case ActionRawFlagValue.CREATE:
50
+ return RolloutMainMenu_1.MainMenuActions.CREATE_NEW;
51
+ case ActionRawFlagValue.EDIT:
52
+ return ManageRollout_1.ManageRolloutActions.EDIT;
53
+ case ActionRawFlagValue.END:
54
+ return ManageRollout_1.ManageRolloutActions.END;
260
55
  }
261
56
  }
57
+ sanitizeArgsAndFlags(rawFlags) {
58
+ var _b;
59
+ const action = rawFlags.action;
60
+ return {
61
+ channelName: rawFlags.channel,
62
+ percent: rawFlags.percent,
63
+ outcome: rawFlags.outcome,
64
+ branchNameToRollout: rawFlags.branch,
65
+ runtimeVersion: rawFlags['runtime-version'],
66
+ privateKeyPath: (_b = rawFlags['private-key-path']) !== null && _b !== void 0 ? _b : null,
67
+ action: action ? this.getAction(action) : undefined,
68
+ nonInteractive: rawFlags['non-interactive'],
69
+ json: rawFlags.json,
70
+ };
71
+ }
262
72
  }
263
73
  exports.default = ChannelRollout;
264
74
  _a = ChannelRollout;
@@ -270,17 +80,62 @@ ChannelRollout.args = [
270
80
  },
271
81
  ];
272
82
  ChannelRollout.flags = {
273
- branch: core_1.Flags.string({
274
- description: 'branch to rollout',
83
+ action: core_1.Flags.enum({
84
+ description: 'Rollout action to perform',
85
+ options: Object.values(ActionRawFlagValue),
275
86
  required: false,
87
+ relationships: [
88
+ {
89
+ type: 'all',
90
+ flags: [
91
+ {
92
+ name: 'percent',
93
+ // eslint-disable-next-line async-protect/async-suffix
94
+ when: async (flags) => {
95
+ return (!!flags['non-interactive'] &&
96
+ (flags['action'] === ActionRawFlagValue.CREATE ||
97
+ flags['action'] === ActionRawFlagValue.EDIT));
98
+ },
99
+ },
100
+ {
101
+ name: 'outcome',
102
+ // eslint-disable-next-line async-protect/async-suffix
103
+ when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.END,
104
+ },
105
+ {
106
+ name: 'branch',
107
+ // eslint-disable-next-line async-protect/async-suffix
108
+ when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.CREATE,
109
+ },
110
+ {
111
+ name: 'runtime-version',
112
+ // eslint-disable-next-line async-protect/async-suffix
113
+ when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.CREATE,
114
+ },
115
+ ],
116
+ },
117
+ ],
276
118
  }),
277
119
  percent: core_1.Flags.integer({
278
- description: 'percent of users to send to the new branch',
120
+ description: 'Percent of users to send to the new branch. Use with --action=edit or --action=create',
279
121
  required: false,
280
122
  }),
281
- end: core_1.Flags.boolean({
282
- description: 'end the rollout',
283
- default: false,
123
+ outcome: core_1.Flags.enum({
124
+ description: 'End outcome of rollout. Use with --action=end',
125
+ options: Object.values(EndRollout_1.EndOutcome),
126
+ required: false,
127
+ }),
128
+ branch: core_1.Flags.string({
129
+ description: 'Branch to roll out. Use with --action=create',
130
+ required: false,
131
+ }),
132
+ 'runtime-version': core_1.Flags.string({
133
+ description: 'Runtime version to target. Use with --action=create',
134
+ required: false,
135
+ }),
136
+ 'private-key-path': core_1.Flags.string({
137
+ description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory.`,
138
+ required: false,
284
139
  }),
285
140
  ...flags_1.EasNonInteractiveAndJsonFlags,
286
141
  };
@@ -288,22 +143,3 @@ ChannelRollout.contextDefinition = {
288
143
  ..._a.ContextOptions.ProjectConfig,
289
144
  ..._a.ContextOptions.LoggedIn,
290
145
  };
291
- async function promptForBranchNameAsync({ graphqlClient, projectId, channelName, nonInteractive, json, }) {
292
- if (nonInteractive) {
293
- throw new Error('Must supply branch flag in non-interactive mode');
294
- }
295
- const { name: branchName } = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
296
- projectId,
297
- promptTitle: `Which branch would you like roll out on ${channelName}?`,
298
- displayTextForListItem: updateBranch => ({
299
- title: updateBranch.name,
300
- }),
301
- // discard limit and offset because this query is not their intended target
302
- paginatedQueryOptions: {
303
- json,
304
- nonInteractive,
305
- offset: 0,
306
- },
307
- });
308
- return branchName;
309
- }
@@ -36,14 +36,14 @@ class UpdateRepublish extends EasCommand_1.default {
36
36
  throw new Error(`There are no updates on branch "${existingUpdates[0].branchName}" published for the platform(s) "${rawFlags.platform}" with group ID "${flags.groupId ? flags.groupId : updatesToPublish[0].groupId}". Did you mean to publish a new update instead?`);
37
37
  }
38
38
  if (rawFlags.platform === 'all') {
39
- log_1.default.withTick(`The republished update will appear only on: ${rawFlags.platform}`);
39
+ log_1.default.withTick(`The republished update group will appear only on: ${rawFlags.platform}`);
40
40
  }
41
41
  else {
42
42
  const platformsFromUpdates = updatesToPublish.map(update => update.platform);
43
43
  if (platformsFromUpdates.length < defaultRepublishPlatforms.length) {
44
- log_1.default.warn(`You are republishing an update that wasn't published for all platforms.`);
44
+ log_1.default.warn(`You are republishing an update group that wasn't published for all platforms.`);
45
45
  }
46
- log_1.default.withTick(`The republished update will appear on the same platforms it was originally published on: ${platformsFromUpdates.join(', ')}`);
46
+ log_1.default.withTick(`The republished update group will appear on the same platforms it was originally published on: ${platformsFromUpdates.join(', ')}`);
47
47
  }
48
48
  const updateMessage = await getOrAskUpdateMessageAsync(updatesToPublish, flags);
49
49
  const arbitraryUpdate = updatesToPublish[0];
@@ -88,11 +88,11 @@ _a = UpdateRepublish;
88
88
  UpdateRepublish.description = 'roll back to an existing update';
89
89
  UpdateRepublish.flags = {
90
90
  channel: core_1.Flags.string({
91
- description: 'Channel name to select an update to republish from',
91
+ description: 'Channel name to select an update group to republish from',
92
92
  exclusive: ['branch', 'group'],
93
93
  }),
94
94
  branch: core_1.Flags.string({
95
- description: 'Branch name to select an update to republish from',
95
+ description: 'Branch name to select an update group to republish from',
96
96
  exclusive: ['channel', 'group'],
97
97
  }),
98
98
  group: core_1.Flags.string({
@@ -101,7 +101,7 @@ UpdateRepublish.flags = {
101
101
  }),
102
102
  message: core_1.Flags.string({
103
103
  char: 'm',
104
- description: 'Short message describing the republished update',
104
+ description: 'Short message describing the republished update group',
105
105
  required: false,
106
106
  }),
107
107
  platform: core_1.Flags.enum({
@@ -5,7 +5,8 @@ export declare enum RegistrationMethod {
5
5
  WEBSITE = 0,
6
6
  INPUT = 1,
7
7
  DEVELOPER_PORTAL = 2,
8
- EXIT = 3
8
+ CURRENT_MACHINE = 3,
9
+ EXIT = 4
9
10
  }
10
11
  export default class DeviceCreateAction {
11
12
  private graphqlClient;
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
5
5
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
6
  const log_1 = tslib_1.__importDefault(require("../../../log"));
7
7
  const prompts_1 = require("../../../prompts");
8
+ const currentMachineMethod_1 = require("./currentMachineMethod");
8
9
  const developerPortalMethod_1 = require("./developerPortalMethod");
9
10
  const inputMethod_1 = require("./inputMethod");
10
11
  const registrationUrlMethod_1 = require("./registrationUrlMethod");
@@ -13,7 +14,8 @@ var RegistrationMethod;
13
14
  RegistrationMethod[RegistrationMethod["WEBSITE"] = 0] = "WEBSITE";
14
15
  RegistrationMethod[RegistrationMethod["INPUT"] = 1] = "INPUT";
15
16
  RegistrationMethod[RegistrationMethod["DEVELOPER_PORTAL"] = 2] = "DEVELOPER_PORTAL";
16
- RegistrationMethod[RegistrationMethod["EXIT"] = 3] = "EXIT";
17
+ RegistrationMethod[RegistrationMethod["CURRENT_MACHINE"] = 3] = "CURRENT_MACHINE";
18
+ RegistrationMethod[RegistrationMethod["EXIT"] = 4] = "EXIT";
17
19
  })(RegistrationMethod = exports.RegistrationMethod || (exports.RegistrationMethod = {}));
18
20
  class DeviceCreateAction {
19
21
  constructor(graphqlClient, appStoreApi, account, appleTeam) {
@@ -33,6 +35,9 @@ class DeviceCreateAction {
33
35
  else if (method === RegistrationMethod.INPUT) {
34
36
  await (0, inputMethod_1.runInputMethodAsync)(this.graphqlClient, this.account.id, this.appleTeam);
35
37
  }
38
+ else if (method === RegistrationMethod.CURRENT_MACHINE) {
39
+ await (0, currentMachineMethod_1.runCurrentMachineMethodAsync)(this.graphqlClient, this.account.id, this.appleTeam);
40
+ }
36
41
  else if (method === RegistrationMethod.EXIT) {
37
42
  log_1.default.log('Bye!');
38
43
  process.exit(0);
@@ -57,6 +62,10 @@ class DeviceCreateAction {
57
62
  title: `${chalk_1.default.bold('Input')} - allows you to type in UDIDs (advanced option)`,
58
63
  value: RegistrationMethod.INPUT,
59
64
  },
65
+ {
66
+ title: `${chalk_1.default.bold('Current Machine')} - automatically sets the provisioning UDID of the current Apple Silicon machine`,
67
+ value: RegistrationMethod.CURRENT_MACHINE,
68
+ },
60
69
  { title: chalk_1.default.bold('Exit'), value: RegistrationMethod.EXIT },
61
70
  ],
62
71
  validate: (val) => Object.values(RegistrationMethod).includes(val),
@@ -0,0 +1,3 @@
1
+ import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
2
+ import { AppleTeam } from '../../../graphql/generated';
3
+ export declare function runCurrentMachineMethodAsync(graphqlClient: ExpoGraphqlClient, accountId: string, appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>): Promise<void>;
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runCurrentMachineMethodAsync = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const spawn_async_1 = tslib_1.__importDefault(require("@expo/spawn-async"));
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const os_1 = tslib_1.__importDefault(require("os"));
8
+ const AppleDeviceMutation_1 = require("../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation");
9
+ const generated_1 = require("../../../graphql/generated");
10
+ const log_1 = tslib_1.__importDefault(require("../../../log"));
11
+ const ora_1 = require("../../../ora");
12
+ const prompts_1 = require("../../../prompts");
13
+ const errors_1 = require("../../utils/errors");
14
+ const utils_1 = require("./utils");
15
+ async function runCurrentMachineMethodAsync(graphqlClient, accountId, appleTeam) {
16
+ log_1.default.newLine();
17
+ log_1.default.log(chalk_1.default.white('Checking if current machine is an Apple Silicon one.'));
18
+ if (!isMachineAppleSilicon()) {
19
+ throw new errors_1.DeviceCreateError("Current machine is not of Apple Silicon type - provisioning UDID can't be added automatically.");
20
+ }
21
+ log_1.default.log(chalk_1.default.green('Check successful.'));
22
+ await collectDataAndRegisterDeviceAsync(graphqlClient, { accountId, appleTeam });
23
+ }
24
+ exports.runCurrentMachineMethodAsync = runCurrentMachineMethodAsync;
25
+ function isMachineAppleSilicon() {
26
+ return os_1.default.cpus()[0].model.includes('Apple M') && process.platform === 'darwin';
27
+ }
28
+ async function collectDataAndRegisterDeviceAsync(graphqlClient, { accountId, appleTeam, }) {
29
+ const deviceData = await collectDeviceDataAsync(appleTeam);
30
+ if (!deviceData) {
31
+ return;
32
+ }
33
+ const { udid, deviceClass, name } = deviceData;
34
+ const spinner = (0, ora_1.ora)(`Registering Apple device on Expo`).start();
35
+ try {
36
+ await AppleDeviceMutation_1.AppleDeviceMutation.createAppleDeviceAsync(graphqlClient, {
37
+ appleTeamId: appleTeam.id,
38
+ identifier: udid,
39
+ name,
40
+ deviceClass: deviceClass !== null && deviceClass !== void 0 ? deviceClass : undefined,
41
+ }, accountId);
42
+ }
43
+ catch (err) {
44
+ spinner.fail();
45
+ throw err;
46
+ }
47
+ spinner.succeed();
48
+ }
49
+ async function collectDeviceDataAsync(appleTeam, initialValues = {}) {
50
+ log_1.default.log(chalk_1.default.white('Fetching the provisioning UDID.'));
51
+ const [udid, defaultMachineName] = await fetchCurrentMachineUDIDAsync();
52
+ log_1.default.log(chalk_1.default.green(`Fetched the provisioning UDID - ${udid}`));
53
+ const name = await (0, utils_1.promptForNameAsync)(defaultMachineName !== null && defaultMachineName !== void 0 ? defaultMachineName : initialValues.name);
54
+ const deviceClass = generated_1.AppleDeviceClass.Mac;
55
+ const deviceData = {
56
+ udid,
57
+ name,
58
+ deviceClass,
59
+ };
60
+ (0, utils_1.printDeviceData)(deviceData, appleTeam);
61
+ const registrationConfirmed = await (0, prompts_1.confirmAsync)({
62
+ message: 'Is this what you want to register?',
63
+ });
64
+ if (!registrationConfirmed) {
65
+ log_1.default.log('No worries, just try again.');
66
+ log_1.default.newLine();
67
+ return null;
68
+ }
69
+ else {
70
+ return deviceData;
71
+ }
72
+ }
73
+ async function fetchCurrentMachineUDIDAsync() {
74
+ var _a, _b;
75
+ try {
76
+ const profilerData = (await (0, spawn_async_1.default)('system_profiler', ['-json', 'SPHardwareDataType'])).stdout.trim();
77
+ if (!profilerData) {
78
+ const message = 'Failed to fetch the provisioning UDID from system profiler';
79
+ log_1.default.error(message);
80
+ throw new errors_1.DeviceCreateError(message);
81
+ }
82
+ const profilerDataJSON = JSON.parse(profilerData);
83
+ const provisioningUDID = (_a = profilerDataJSON === null || profilerDataJSON === void 0 ? void 0 : profilerDataJSON.SPHardwareDataType[0]) === null || _a === void 0 ? void 0 : _a.provisioning_UDID;
84
+ if (!provisioningUDID) {
85
+ const message = 'System profiler data did not contain the provisioning UDID';
86
+ log_1.default.error(message);
87
+ throw new errors_1.DeviceCreateError(message);
88
+ }
89
+ const defaultMachineName = (_b = profilerDataJSON === null || profilerDataJSON === void 0 ? void 0 : profilerDataJSON.SPHardwareDataType[0]) === null || _b === void 0 ? void 0 : _b.machine_name;
90
+ return [provisioningUDID, defaultMachineName];
91
+ }
92
+ catch (err) {
93
+ if (!(err instanceof errors_1.DeviceCreateError)) {
94
+ const message = `Failed to fetch the provisioning UDID of the current machine - ${err.message}`;
95
+ log_1.default.error(message);
96
+ throw new errors_1.DeviceCreateError(message);
97
+ }
98
+ throw err;
99
+ }
100
+ }
@@ -15,6 +15,7 @@ const DEVICE_IMPORT_CHUNK_SIZE = 10;
15
15
  const DEVICE_CLASS_TO_GRAPHQL_TYPE = {
16
16
  [apple_utils_1.DeviceClass.IPAD]: generated_1.AppleDeviceClass.Ipad,
17
17
  [apple_utils_1.DeviceClass.IPHONE]: generated_1.AppleDeviceClass.Iphone,
18
+ [apple_utils_1.DeviceClass.MAC]: generated_1.AppleDeviceClass.Mac,
18
19
  };
19
20
  async function runDeveloperPortalMethodAsync(graphqlClient, appStoreApi, accountId, appleTeam) {
20
21
  const appleAuthCtx = await appStoreApi.ensureAuthenticatedAsync();
@@ -61,7 +62,7 @@ async function findUnregisteredPortalDevicesAsync(graphqlClient, appleAuthCtx, a
61
62
  }, {});
62
63
  const portalDevices = await apple_utils_1.Device.getAsync((0, authenticate_1.getRequestContext)(appleAuthCtx));
63
64
  return portalDevices.filter(portalDevice => !(portalDevice.attributes.udid in expoRegisteredDevicesByUdid) &&
64
- [apple_utils_1.DeviceClass.IPAD, apple_utils_1.DeviceClass.IPHONE, apple_utils_1.DeviceClass.APPLE_TV].includes(portalDevice.attributes.deviceClass));
65
+ [apple_utils_1.DeviceClass.IPAD, apple_utils_1.DeviceClass.IPHONE, apple_utils_1.DeviceClass.APPLE_TV, apple_utils_1.DeviceClass.MAC].includes(portalDevice.attributes.deviceClass));
65
66
  }
66
67
  async function chooseDevicesToImportAsync(devices) {
67
68
  const { chosenDevices } = await (0, prompts_1.promptAsync)({
@@ -1,4 +1,3 @@
1
1
  import { ExpoGraphqlClient } from '../../../commandUtils/context/contextUtils/createGraphqlClient';
2
2
  import { AppleTeam } from '../../../graphql/generated';
3
3
  export declare function runInputMethodAsync(graphqlClient: ExpoGraphqlClient, accountId: string, appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>): Promise<void>;
4
- export declare function promptForUDIDAsync(initial?: string): Promise<string>;