eas-cli 0.56.0 → 0.57.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 (43) hide show
  1. package/README.md +58 -49
  2. package/build/branch/queries.d.ts +5 -0
  3. package/build/branch/queries.js +87 -0
  4. package/build/build/context.d.ts +1 -0
  5. package/build/build/createContext.d.ts +2 -1
  6. package/build/build/createContext.js +2 -1
  7. package/build/build/metadata.d.ts +1 -0
  8. package/build/build/metadata.js +14 -4
  9. package/build/build/runBuildAndSubmit.d.ts +1 -0
  10. package/build/build/runBuildAndSubmit.js +1 -0
  11. package/build/commandUtils/pagination.d.ts +13 -0
  12. package/build/commandUtils/pagination.js +42 -0
  13. package/build/commands/branch/list.d.ts +3 -4
  14. package/build/commands/branch/list.js +6 -69
  15. package/build/commands/branch/view.d.ts +3 -0
  16. package/build/commands/branch/view.js +15 -63
  17. package/build/commands/build/index.d.ts +1 -0
  18. package/build/commands/build/index.js +9 -0
  19. package/build/commands/update/index.d.ts +2 -2
  20. package/build/commands/update/index.js +10 -4
  21. package/build/commands/update/list.js +13 -18
  22. package/build/credentials/ios/actions/SetUpAdhocProvisioningProfile.js +2 -2
  23. package/build/devices/actions/create/action.d.ts +5 -2
  24. package/build/devices/actions/create/action.js +12 -2
  25. package/build/devices/actions/create/developerPortalMethod.d.ts +6 -0
  26. package/build/devices/actions/create/developerPortalMethod.js +90 -0
  27. package/build/devices/manager.js +4 -4
  28. package/build/graphql/generated.d.ts +184 -57
  29. package/build/graphql/generated.js +7 -1
  30. package/build/graphql/queries/BranchQuery.d.ts +2 -1
  31. package/build/graphql/queries/BranchQuery.js +26 -0
  32. package/build/graphql/queries/UpdateQuery.d.ts +6 -8
  33. package/build/graphql/queries/UpdateQuery.js +27 -23
  34. package/build/update/queries.d.ts +3 -0
  35. package/build/update/queries.js +72 -0
  36. package/build/update/utils.d.ts +14 -0
  37. package/build/update/utils.js +26 -9
  38. package/build/utils/expodash/chunk.d.ts +1 -0
  39. package/build/utils/expodash/chunk.js +16 -0
  40. package/build/utils/queries.d.ts +25 -0
  41. package/build/utils/queries.js +67 -0
  42. package/oclif.manifest.json +1 -1
  43. package/package.json +5 -4
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EasPaginatedQueryFlags = exports.getPaginatedQueryOptions = void 0;
4
+ const core_1 = require("@oclif/core");
5
+ const getPaginatedQueryOptions = (flags) => {
6
+ var _a, _b, _c;
7
+ return {
8
+ json: (_a = flags.json) !== null && _a !== void 0 ? _a : false,
9
+ offset: (_b = flags.offset) !== null && _b !== void 0 ? _b : 0,
10
+ nonInteractive: (_c = flags['non-interactive']) !== null && _c !== void 0 ? _c : false,
11
+ ...('limit' in flags && { limit: flags.limit }),
12
+ };
13
+ };
14
+ exports.getPaginatedQueryOptions = getPaginatedQueryOptions;
15
+ const parseFlagInputStringAsInteger = (input, flagName, lowerLimit, upperLimit) => {
16
+ const inputAsNumber = Number(input);
17
+ if (isNaN(inputAsNumber)) {
18
+ throw new Error(`Unable to parse ${input} as a number`);
19
+ }
20
+ if (inputAsNumber < lowerLimit || inputAsNumber > upperLimit) {
21
+ throw new Error(`--${flagName} must be between ${lowerLimit} and ${upperLimit}`);
22
+ }
23
+ return inputAsNumber;
24
+ };
25
+ exports.EasPaginatedQueryFlags = {
26
+ offset: core_1.Flags.integer({
27
+ description: 'Start queries from specified index. Use for paginating results. Defaults to 0.',
28
+ // eslint-disable-next-line async-protect/async-suffix
29
+ parse: async (input) => parseFlagInputStringAsInteger(input, 'offset', 0, Number.MAX_SAFE_INTEGER),
30
+ }),
31
+ limit: core_1.Flags.integer({
32
+ description: 'The number of query items to list at once. The default value is 50 (the maximum is 100). Using a lower value may help increase command speed.',
33
+ // eslint-disable-next-line async-protect/async-suffix
34
+ parse: async (input) => parseFlagInputStringAsInteger(input, 'limit', 1, 100),
35
+ }),
36
+ json: core_1.Flags.boolean({
37
+ description: 'Enable JSON output, non-JSON messages will be printed to stderr.',
38
+ }),
39
+ 'non-interactive': core_1.Flags.boolean({
40
+ description: 'Run the command in non-interactive mode.',
41
+ }),
42
+ };
@@ -1,12 +1,11 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
- import { UpdateBranchFragment } from '../../graphql/generated';
3
- export declare function listBranchesAsync({ projectId, }: {
4
- projectId: string;
5
- }): Promise<UpdateBranchFragment[]>;
6
2
  export default class BranchList extends EasCommand {
7
3
  static description: string;
8
4
  static flags: {
5
+ offset: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
6
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
9
7
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
9
  };
11
10
  runAsync(): Promise<void>;
12
11
  }
@@ -1,90 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.listBranchesAsync = void 0;
4
3
  const tslib_1 = require("tslib");
5
- const core_1 = require("@oclif/core");
6
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
8
- const graphql_1 = require("graphql");
9
- const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
4
+ const queries_1 = require("../../branch/queries");
10
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
11
- const client_1 = require("../../graphql/client");
12
- const UpdateBranch_1 = require("../../graphql/types/UpdateBranch");
13
- const log_1 = tslib_1.__importDefault(require("../../log"));
6
+ const pagination_1 = require("../../commandUtils/pagination");
14
7
  const expoConfig_1 = require("../../project/expoConfig");
15
8
  const projectUtils_1 = require("../../project/projectUtils");
16
- const utils_1 = require("../../update/utils");
17
9
  const json_1 = require("../../utils/json");
18
- const BRANCHES_LIMIT = 10000;
19
- async function listBranchesAsync({ projectId, }) {
20
- var _a, _b;
21
- const data = await (0, client_1.withErrorHandlingAsync)(client_1.graphqlClient
22
- .query((0, graphql_tag_1.default) `
23
- query BranchesByAppQuery($appId: String!, $limit: Int!) {
24
- app {
25
- byId(appId: $appId) {
26
- id
27
- updateBranches(offset: 0, limit: $limit) {
28
- id
29
- ...UpdateBranchFragment
30
- }
31
- }
32
- }
33
- }
34
- ${(0, graphql_1.print)(UpdateBranch_1.UpdateBranchFragmentNode)}
35
- `, {
36
- appId: projectId,
37
- limit: BRANCHES_LIMIT,
38
- }, { additionalTypenames: ['UpdateBranch'] })
39
- .toPromise());
40
- return (_b = (_a = data === null || data === void 0 ? void 0 : data.app) === null || _a === void 0 ? void 0 : _a.byId.updateBranches) !== null && _b !== void 0 ? _b : [];
41
- }
42
- exports.listBranchesAsync = listBranchesAsync;
43
10
  class BranchList extends EasCommand_1.default {
44
11
  async runAsync() {
45
12
  const { flags } = await this.parse(BranchList);
46
- if (flags.json) {
13
+ const options = (0, pagination_1.getPaginatedQueryOptions)(flags);
14
+ if (options.json) {
47
15
  (0, json_1.enableJsonOutput)();
48
16
  }
49
17
  const projectDir = await (0, projectUtils_1.findProjectRootAsync)();
50
18
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir);
51
19
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
52
- const branches = await listBranchesAsync({ projectId });
53
- if (flags.json) {
54
- (0, json_1.printJsonOnlyOutput)(branches);
55
- }
56
- else {
57
- const table = new cli_table3_1.default({
58
- head: ['Branch', ...utils_1.UPDATE_COLUMNS],
59
- wordWrap: true,
60
- });
61
- table.push(...branches.map(branch => {
62
- var _a, _b, _c, _d, _e;
63
- return [
64
- branch.name,
65
- (0, utils_1.formatUpdate)(branch.updates[0]),
66
- (_b = (_a = branch.updates[0]) === null || _a === void 0 ? void 0 : _a.runtimeVersion) !== null && _b !== void 0 ? _b : 'N/A',
67
- (_d = (_c = branch.updates[0]) === null || _c === void 0 ? void 0 : _c.group) !== null && _d !== void 0 ? _d : 'N/A',
68
- (0, utils_1.getPlatformsForGroup)({
69
- updates: branch.updates,
70
- group: (_e = branch.updates[0]) === null || _e === void 0 ? void 0 : _e.group,
71
- }),
72
- ];
73
- }));
74
- log_1.default.addNewLineIfNone();
75
- log_1.default.log(chalk_1.default.bold('Branches with their most recent update group:'));
76
- log_1.default.log(table.toString());
77
- if (branches.length >= BRANCHES_LIMIT) {
78
- log_1.default.warn(`Showing first ${BRANCHES_LIMIT} branches, some results might be omitted.`);
79
- }
80
- }
20
+ await (0, queries_1.listAndRenderPaginatedBranchesAsync)(projectId, options);
81
21
  }
82
22
  }
83
23
  exports.default = BranchList;
84
24
  BranchList.description = 'list all branches';
85
25
  BranchList.flags = {
86
- json: core_1.Flags.boolean({
87
- description: 'return output as JSON',
88
- default: false,
89
- }),
26
+ ...pagination_1.EasPaginatedQueryFlags,
90
27
  };
@@ -7,7 +7,10 @@ export default class BranchView extends EasCommand {
7
7
  description: string;
8
8
  }[];
9
9
  static flags: {
10
+ offset: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
11
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
10
12
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
14
  };
12
15
  runAsync(): Promise<void>;
13
16
  }
@@ -1,78 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
- const core_1 = require("@oclif/core");
5
- const chalk_1 = tslib_1.__importDefault(require("chalk"));
6
- const cli_table3_1 = tslib_1.__importDefault(require("cli-table3"));
4
+ const queries_1 = require("../../branch/queries");
7
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
- const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
9
- const log_1 = tslib_1.__importDefault(require("../../log"));
6
+ const pagination_1 = require("../../commandUtils/pagination");
10
7
  const expoConfig_1 = require("../../project/expoConfig");
11
8
  const projectUtils_1 = require("../../project/projectUtils");
12
- const prompts_1 = require("../../prompts");
13
- const utils_1 = require("../../update/utils");
14
- const groupBy_1 = tslib_1.__importDefault(require("../../utils/expodash/groupBy"));
15
- const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
9
+ const queries_2 = require("../../update/queries");
16
10
  const json_1 = require("../../utils/json");
17
11
  class BranchView extends EasCommand_1.default {
18
12
  async runAsync() {
19
- let { args: { name }, flags: { json: jsonFlag }, } = await this.parse(BranchView);
20
- if (jsonFlag) {
13
+ let { args: { name: branchName }, flags, } = await this.parse(BranchView);
14
+ const options = (0, pagination_1.getPaginatedQueryOptions)(flags);
15
+ if (options.json) {
21
16
  (0, json_1.enableJsonOutput)();
22
17
  }
23
18
  const projectDir = await (0, projectUtils_1.findProjectRootAsync)();
24
19
  const exp = (0, expoConfig_1.getExpoConfig)(projectDir);
25
20
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
26
- if (!name) {
27
- const validationMessage = 'Branch name may not be empty.';
28
- if (jsonFlag) {
29
- throw new Error(validationMessage);
21
+ // provide help to a user if they ran the command with missing args
22
+ if (!branchName) {
23
+ if (options.nonInteractive) {
24
+ throw new Error('Branch name may not be empty.');
30
25
  }
31
- ({ name } = await (0, prompts_1.promptAsync)({
32
- type: 'text',
33
- name: 'name',
34
- message: 'Please enter the name of the branch to view:',
35
- validate: value => (value ? true : validationMessage),
36
- }));
37
- }
38
- const { app } = await UpdateQuery_1.UpdateQuery.viewBranchAsync({
39
- appId: projectId,
40
- name,
41
- });
42
- const UpdateBranch = app === null || app === void 0 ? void 0 : app.byId.updateBranchByName;
43
- if (!UpdateBranch) {
44
- throw new Error(`Could not find branch "${name}"`);
45
- }
46
- const updates = Object.values((0, groupBy_1.default)(UpdateBranch.updates, u => u.group)).map(group => group[0]);
47
- if (jsonFlag) {
48
- (0, json_1.printJsonOnlyOutput)({ ...UpdateBranch, updates });
49
- }
50
- else {
51
- const groupTable = new cli_table3_1.default({
52
- head: utils_1.UPDATE_COLUMNS,
53
- wordWrap: true,
54
- });
55
- for (const update of updates) {
56
- groupTable.push([
57
- (0, utils_1.formatUpdate)(update),
58
- update.runtimeVersion,
59
- update.group,
60
- (0, utils_1.getPlatformsForGroup)({
61
- updates: UpdateBranch.updates,
62
- group: update.group,
63
- }),
64
- ]);
65
- }
66
- log_1.default.addNewLineIfNone();
67
- log_1.default.log(chalk_1.default.bold('Branch:'));
68
- log_1.default.log((0, formatFields_1.default)([
69
- { label: 'Name', value: UpdateBranch.name },
70
- { label: 'ID', value: UpdateBranch.id },
71
- ]));
72
- log_1.default.addNewLineIfNone();
73
- log_1.default.log(chalk_1.default.bold('Recently published update groups:'));
74
- log_1.default.log(groupTable.toString());
26
+ ({ name: branchName } = await (0, queries_1.selectBranchFromPaginatedQueryAsync)(projectId, 'Which branch would you like to view?',
27
+ // discard limit and offset because this query is not those flag's intended target
28
+ { json: options.json, nonInteractive: options.nonInteractive, offset: 0 }));
75
29
  }
30
+ await (0, queries_2.listAndRenderUpdatesOnBranchByNameAsync)(projectId, branchName, options);
76
31
  }
77
32
  }
78
33
  exports.default = BranchView;
@@ -85,8 +40,5 @@ BranchView.args = [
85
40
  },
86
41
  ];
87
42
  BranchView.flags = {
88
- json: core_1.Flags.boolean({
89
- description: `return a json with the branch's ID name and recent update groups.`,
90
- default: false,
91
- }),
43
+ ...pagination_1.EasPaginatedQueryFlags,
92
44
  };
@@ -16,6 +16,7 @@ export default class Build extends EasCommand {
16
16
  'auto-submit': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
17
  'auto-submit-with-profile': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
18
18
  'resource-class': import("@oclif/core/lib/interfaces").OptionFlag<UserInputResourceClass>;
19
+ message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
19
20
  };
20
21
  runAsync(): Promise<void>;
21
22
  private sanitizeFlagsAsync;
@@ -55,6 +55,10 @@ class Build extends EasCommand_1.default {
55
55
  log_1.default.warnDeprecatedFlag('skip-project-configuration', 'Automatic configuration of native code is no longer optional.');
56
56
  log_1.default.newLine();
57
57
  }
58
+ const message = flags['message'];
59
+ if (message && message.length > 1024) {
60
+ core_1.Errors.error('Message cannot be longer than 1024 characters.', { exit: 1 });
61
+ }
58
62
  const profile = flags['profile'];
59
63
  return {
60
64
  requestedPlatform,
@@ -75,6 +79,7 @@ class Build extends EasCommand_1.default {
75
79
  autoSubmit: flags['auto-submit'] || flags['auto-submit-with-profile'] !== undefined,
76
80
  submitProfile: (_a = flags['auto-submit-with-profile']) !== null && _a !== void 0 ? _a : profile,
77
81
  userInputResourceClass: (_b = flags['resource-class']) !== null && _b !== void 0 ? _b : types_1.UserInputResourceClass.DEFAULT,
82
+ message,
78
83
  };
79
84
  }
80
85
  }
@@ -136,4 +141,8 @@ Build.flags = {
136
141
  hidden: true,
137
142
  description: 'The instance type that will be used to run this build [experimental]',
138
143
  }),
144
+ message: core_1.Flags.string({
145
+ char: 'm',
146
+ description: 'A short message describing the build',
147
+ }),
139
148
  };
@@ -6,8 +6,8 @@ export declare type PublishPlatformFlag = PublishPlatform | 'all';
6
6
  export declare function ensureBranchExistsAsync({ appId, name: branchName, limit, offset, }: {
7
7
  appId: string;
8
8
  name: string;
9
- limit?: number;
10
- offset?: number;
9
+ limit: number;
10
+ offset: number;
11
11
  }): Promise<{
12
12
  id: string;
13
13
  updates: Exclude<Exclude<ViewBranchUpdatesQuery['app'], null | undefined>['byId']['updateBranchByName'], null | undefined>['updates'];
@@ -11,11 +11,13 @@ const dateformat_1 = tslib_1.__importDefault(require("dateformat"));
11
11
  const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
12
12
  const nullthrows_1 = tslib_1.__importDefault(require("nullthrows"));
13
13
  const api_1 = require("../../api");
14
+ const queries_1 = require("../../branch/queries");
14
15
  const url_1 = require("../../build/utils/url");
15
16
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
16
17
  const fetch_1 = tslib_1.__importDefault(require("../../fetch"));
17
18
  const client_1 = require("../../graphql/client");
18
19
  const PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
20
+ const BranchQuery_1 = require("../../graphql/queries/BranchQuery");
19
21
  const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
20
22
  const log_1 = tslib_1.__importStar(require("../../log"));
21
23
  const ora_1 = require("../../ora");
@@ -32,7 +34,6 @@ const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields
32
34
  const json_1 = require("../../utils/json");
33
35
  const vcs_1 = require("../../vcs");
34
36
  const create_1 = require("../branch/create");
35
- const list_1 = require("../branch/list");
36
37
  const create_2 = require("../channel/create");
37
38
  exports.defaultPublishPlatforms = ['android', 'ios'];
38
39
  async function getUpdateGroupAsync({ group, }) {
@@ -143,7 +144,11 @@ class UpdatePublish extends EasCommand_1.default {
143
144
  if (jsonFlag) {
144
145
  throw new Error(validationMessage);
145
146
  }
146
- const branches = await (0, list_1.listBranchesAsync)({ projectId });
147
+ const branches = await BranchQuery_1.BranchQuery.listBranchesAsync({
148
+ appId: projectId,
149
+ limit: queries_1.BRANCHES_LIMIT,
150
+ offset: 0,
151
+ });
147
152
  if (branches.length === 0) {
148
153
  ({ name: branchName } = await (0, prompts_1.promptAsync)({
149
154
  type: 'text',
@@ -291,6 +296,7 @@ class UpdatePublish extends EasCommand_1.default {
291
296
  appId: projectId,
292
297
  name: branchName,
293
298
  limit: 0,
299
+ offset: 0,
294
300
  });
295
301
  // Sort the updates into different groups based on their platform specific runtime versions
296
302
  const updateGroups = Object.entries(runtimeToPlatformMapping).map(([runtime, platforms]) => {
@@ -357,7 +363,7 @@ class UpdatePublish extends EasCommand_1.default {
357
363
  log_1.default.addNewLineIfNone();
358
364
  for (const runtime of new Set(Object.values(runtimeVersions))) {
359
365
  const newUpdatesForRuntimeVersion = newUpdates.filter(update => update.runtimeVersion === runtime);
360
- if (!newUpdatesForRuntimeVersion.length) {
366
+ if (newUpdatesForRuntimeVersion.length === 0) {
361
367
  throw new Error(`Publish response is missing updates with runtime ${runtime}.`);
362
368
  }
363
369
  const platforms = newUpdatesForRuntimeVersion.map(update => update.platform);
@@ -459,7 +465,7 @@ async function getUpdatesToRepublishInteractiveAsync(projectId, branchName, plat
459
465
  title: formatUpdateTitle(update),
460
466
  value: update.group,
461
467
  }));
462
- if (!updateGroups.length) {
468
+ if (updateGroups.length === 0) {
463
469
  throw new Error(`There are no updates on branch "${branchName}" published for the platform(s) ${platformFlag}. Did you mean to publish a new update instead?`);
464
470
  }
465
471
  if (updates.length > pageSize) {
@@ -11,8 +11,8 @@ const log_1 = tslib_1.__importDefault(require("../../log"));
11
11
  const expoConfig_1 = require("../../project/expoConfig");
12
12
  const projectUtils_1 = require("../../project/projectUtils");
13
13
  const prompts_1 = require("../../prompts");
14
+ const queries_1 = require("../../update/queries");
14
15
  const utils_1 = require("../../update/utils");
15
- const groupBy_1 = tslib_1.__importDefault(require("../../utils/expodash/groupBy"));
16
16
  const json_1 = require("../../utils/json");
17
17
  const vcs_1 = require("../../vcs");
18
18
  class BranchView extends EasCommand_1.default {
@@ -27,8 +27,15 @@ class BranchView extends EasCommand_1.default {
27
27
  const projectId = await (0, projectUtils_1.getProjectIdAsync)(exp);
28
28
  let updateGroupDescriptions;
29
29
  if (all) {
30
- const branchesAndUpdates = await UpdateQuery_1.UpdateQuery.viewAllAsync({ appId: projectId });
31
- updateGroupDescriptions = getUpdateGroupDescriptions(branchesAndUpdates.app.byId.updateBranches);
30
+ const branchesAndUpdates = await UpdateQuery_1.UpdateQuery.viewAllAsync({
31
+ appId: projectId,
32
+ limit: queries_1.UPDATES_LIMIT,
33
+ offset: 0,
34
+ });
35
+ updateGroupDescriptions = (0, utils_1.getUpdateGroupsWithPlatforms)(branchesAndUpdates.app.byId.updates.map(update => ({
36
+ ...update,
37
+ branch: update.branch.name,
38
+ }))).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
32
39
  }
33
40
  else {
34
41
  let branchInteractive;
@@ -50,12 +57,14 @@ class BranchView extends EasCommand_1.default {
50
57
  const branchesAndUpdates = await UpdateQuery_1.UpdateQuery.viewBranchAsync({
51
58
  appId: projectId,
52
59
  name: branch,
60
+ limit: queries_1.UPDATES_LIMIT,
61
+ offset: 0,
53
62
  });
54
63
  const UpdateBranch = (_b = branchesAndUpdates.app) === null || _b === void 0 ? void 0 : _b.byId.updateBranchByName;
55
64
  if (!UpdateBranch) {
56
65
  throw new Error(`Could not find branch "${branch}"`);
57
66
  }
58
- updateGroupDescriptions = getUpdateGroupDescriptions([UpdateBranch]);
67
+ updateGroupDescriptions = (0, utils_1.getUpdateGroupsWithPlatforms)(UpdateBranch.updates.map(update => ({ ...update, branch }))).sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
59
68
  }
60
69
  if (jsonFlag) {
61
70
  (0, json_1.printJsonOnlyOutput)(updateGroupDescriptions);
@@ -82,20 +91,6 @@ BranchView.flags = {
82
91
  default: false,
83
92
  }),
84
93
  };
85
- function getUpdateGroupDescriptions(branchesAndUpdates) {
86
- const flattenedBranchesAndUpdates = branchesAndUpdates.flatMap(branch => branch.updates.map(update => {
87
- return { branch: branch.name, ...update };
88
- }));
89
- const updateGroupDescriptions = Object.values((0, groupBy_1.default)(flattenedBranchesAndUpdates, update => update.group)).map(updateGroup => {
90
- const platforms = updateGroup
91
- .map(update => update.platform)
92
- .sort()
93
- .join(', ');
94
- return { ...updateGroup[0], platforms };
95
- });
96
- updateGroupDescriptions.sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
97
- return updateGroupDescriptions;
98
- }
99
94
  function logAsTable(updateGroupDescriptions) {
100
95
  const table = new cli_table3_1.default({
101
96
  head: ['Branch', ...utils_1.UPDATE_COLUMNS],
@@ -170,10 +170,10 @@ class SetUpAdhocProvisioningProfile {
170
170
  return selected;
171
171
  }
172
172
  async registerDevicesAsync(ctx, appleTeam) {
173
- const action = new action_1.default(this.app.account, appleTeam);
173
+ const action = new action_1.default(ctx.appStore, this.app.account, appleTeam);
174
174
  const method = await action.runAsync();
175
175
  while (true) {
176
- if (method !== action_1.RegistrationMethod.INPUT) {
176
+ if (method === action_1.RegistrationMethod.WEBSITE) {
177
177
  log_1.default.newLine();
178
178
  log_1.default.log(chalk_1.default.bold("Press any key if you've already finished device registration."));
179
179
  await (0, prompts_1.pressAnyKeyToContinueAsync)();
@@ -1,14 +1,17 @@
1
+ import AppStoreApi from '../../../credentials/ios/appstore/AppStoreApi';
1
2
  import { AppleTeam } from '../../../graphql/generated';
2
3
  import { Account } from '../../../user/Account';
3
4
  export declare enum RegistrationMethod {
4
5
  WEBSITE = 0,
5
6
  INPUT = 1,
6
- EXIT = 2
7
+ DEVELOPER_PORTAL = 2,
8
+ EXIT = 3
7
9
  }
8
10
  export default class DeviceCreateAction {
11
+ private appStoreApi;
9
12
  private account;
10
13
  private appleTeam;
11
- constructor(account: Account, appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>);
14
+ constructor(appStoreApi: AppStoreApi, account: Account, appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>);
12
15
  runAsync(): Promise<RegistrationMethod>;
13
16
  private askForRegistrationMethodAsync;
14
17
  }
@@ -5,16 +5,19 @@ 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 developerPortalMethod_1 = require("./developerPortalMethod");
8
9
  const inputMethod_1 = require("./inputMethod");
9
10
  const registrationUrlMethod_1 = require("./registrationUrlMethod");
10
11
  var RegistrationMethod;
11
12
  (function (RegistrationMethod) {
12
13
  RegistrationMethod[RegistrationMethod["WEBSITE"] = 0] = "WEBSITE";
13
14
  RegistrationMethod[RegistrationMethod["INPUT"] = 1] = "INPUT";
14
- RegistrationMethod[RegistrationMethod["EXIT"] = 2] = "EXIT";
15
+ RegistrationMethod[RegistrationMethod["DEVELOPER_PORTAL"] = 2] = "DEVELOPER_PORTAL";
16
+ RegistrationMethod[RegistrationMethod["EXIT"] = 3] = "EXIT";
15
17
  })(RegistrationMethod = exports.RegistrationMethod || (exports.RegistrationMethod = {}));
16
18
  class DeviceCreateAction {
17
- constructor(account, appleTeam) {
19
+ constructor(appStoreApi, account, appleTeam) {
20
+ this.appStoreApi = appStoreApi;
18
21
  this.account = account;
19
22
  this.appleTeam = appleTeam;
20
23
  }
@@ -23,6 +26,9 @@ class DeviceCreateAction {
23
26
  if (method === RegistrationMethod.WEBSITE) {
24
27
  await (0, registrationUrlMethod_1.runRegistrationUrlMethodAsync)(this.account.id, this.appleTeam);
25
28
  }
29
+ else if (method === RegistrationMethod.DEVELOPER_PORTAL) {
30
+ await (0, developerPortalMethod_1.runDeveloperPortalMethodAsync)(this.appStoreApi, this.account.id, this.appleTeam);
31
+ }
26
32
  else if (method === RegistrationMethod.INPUT) {
27
33
  await (0, inputMethod_1.runInputMethodAsync)(this.account.id, this.appleTeam);
28
34
  }
@@ -42,6 +48,10 @@ class DeviceCreateAction {
42
48
  title: `${chalk_1.default.bold('Website')} - generates a registration URL to be opened on your devices`,
43
49
  value: RegistrationMethod.WEBSITE,
44
50
  },
51
+ {
52
+ title: `${chalk_1.default.bold('Developer Portal')} - import devices already registered on Apple Developer Portal`,
53
+ value: RegistrationMethod.DEVELOPER_PORTAL,
54
+ },
45
55
  {
46
56
  title: `${chalk_1.default.bold('Input')} - allows you to type in UDIDs (advanced option)`,
47
57
  value: RegistrationMethod.INPUT,
@@ -0,0 +1,6 @@
1
+ /// <reference types="@expo/apple-utils/ts-declarations/expo__app-store" />
2
+ import { Device } from '@expo/apple-utils';
3
+ import AppStoreApi from '../../../credentials/ios/appstore/AppStoreApi';
4
+ import { AppleTeam } from '../../../graphql/generated';
5
+ export declare function runDeveloperPortalMethodAsync(appStoreApi: AppStoreApi, accountId: string, appleTeam: Pick<AppleTeam, 'appleTeamIdentifier' | 'appleTeamName' | 'id'>): Promise<void>;
6
+ export declare function formatDeviceLabel(device: Device): string;
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatDeviceLabel = exports.runDeveloperPortalMethodAsync = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const apple_utils_1 = require("@expo/apple-utils");
6
+ const AppleDeviceMutation_1 = require("../../../credentials/ios/api/graphql/mutations/AppleDeviceMutation");
7
+ const AppleDeviceQuery_1 = require("../../../credentials/ios/api/graphql/queries/AppleDeviceQuery");
8
+ const authenticate_1 = require("../../../credentials/ios/appstore/authenticate");
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 chunk_1 = tslib_1.__importDefault(require("../../../utils/expodash/chunk"));
14
+ const DEVICE_IMPORT_CHUNK_SIZE = 10;
15
+ const DEVICE_CLASS_TO_GRAPHQL_TYPE = {
16
+ [apple_utils_1.DeviceClass.IPAD]: generated_1.AppleDeviceClass.Ipad,
17
+ [apple_utils_1.DeviceClass.IPHONE]: generated_1.AppleDeviceClass.Iphone,
18
+ };
19
+ async function runDeveloperPortalMethodAsync(appStoreApi, accountId, appleTeam) {
20
+ const appleAuthCtx = await appStoreApi.ensureAuthenticatedAsync();
21
+ const unregisteredPortalDevices = await findUnregisteredPortalDevicesAsync(appleAuthCtx, accountId, appleTeam);
22
+ if (unregisteredPortalDevices.length === 0) {
23
+ log_1.default.log('All your devices registered on Apple Developer Portal are already imported to EAS.');
24
+ return;
25
+ }
26
+ const devicesToImport = await chooseDevicesToImportAsync(unregisteredPortalDevices);
27
+ await importDevicesAsync(accountId, appleTeam, devicesToImport);
28
+ }
29
+ exports.runDeveloperPortalMethodAsync = runDeveloperPortalMethodAsync;
30
+ async function importDevicesAsync(accountId, appleTeam, devices) {
31
+ const spinner = (0, ora_1.ora)(`Importing ${devices.length} Apple device${devices.length === 1 ? '' : 's'} to EAS`).start();
32
+ const deviceChunks = (0, chunk_1.default)(devices, DEVICE_IMPORT_CHUNK_SIZE);
33
+ try {
34
+ for (const deviceChunk of deviceChunks) {
35
+ await importDeviceChunkAsync(accountId, appleTeam, deviceChunk);
36
+ }
37
+ }
38
+ catch (err) {
39
+ spinner.fail();
40
+ throw err;
41
+ }
42
+ spinner.succeed();
43
+ }
44
+ async function importDeviceChunkAsync(accountId, appleTeam, devices) {
45
+ const promises = devices.map(device => {
46
+ var _a;
47
+ return AppleDeviceMutation_1.AppleDeviceMutation.createAppleDeviceAsync({
48
+ appleTeamId: appleTeam.id,
49
+ identifier: device.attributes.udid,
50
+ name: device.attributes.name,
51
+ deviceClass: (_a = DEVICE_CLASS_TO_GRAPHQL_TYPE[device.attributes.deviceClass]) !== null && _a !== void 0 ? _a : undefined,
52
+ }, accountId);
53
+ });
54
+ await Promise.all(promises);
55
+ }
56
+ async function findUnregisteredPortalDevicesAsync(appleAuthCtx, accountId, appleTeam) {
57
+ const expoRegisteredDevices = await AppleDeviceQuery_1.AppleDeviceQuery.getAllByAppleTeamIdentifierAsync(accountId, appleTeam.appleTeamIdentifier, { useCache: false });
58
+ const expoRegisteredDevicesByUdid = expoRegisteredDevices.reduce((acc, device) => {
59
+ acc[device.identifier] = device;
60
+ return acc;
61
+ }, {});
62
+ const portalDevices = await apple_utils_1.Device.getAllIOSProfileDevicesAsync((0, authenticate_1.getRequestContext)(appleAuthCtx));
63
+ return portalDevices.filter(portalDevice => !(portalDevice.attributes.udid in expoRegisteredDevicesByUdid) &&
64
+ [apple_utils_1.DeviceClass.IPAD, apple_utils_1.DeviceClass.IPHONE].includes(portalDevice.attributes.deviceClass));
65
+ }
66
+ async function chooseDevicesToImportAsync(devices) {
67
+ const { chosenDevices } = await (0, prompts_1.promptAsync)({
68
+ type: 'multiselect',
69
+ name: 'chosenDevices',
70
+ message: 'Which devices do you want to import to EAS?',
71
+ choices: devices.map(device => ({
72
+ value: device,
73
+ title: formatDeviceLabel(device),
74
+ selected: true,
75
+ })),
76
+ });
77
+ return chosenDevices;
78
+ }
79
+ function formatDeviceLabel(device) {
80
+ const deviceDetails = formatDeviceDetails(device);
81
+ return `${device.attributes.name} - ${device.attributes.udid})${deviceDetails !== '' ? ` - ${deviceDetails}` : ''}`;
82
+ }
83
+ exports.formatDeviceLabel = formatDeviceLabel;
84
+ function formatDeviceDetails(device) {
85
+ let details = device.attributes.deviceClass;
86
+ if (device.attributes.model) {
87
+ details = device.attributes.model;
88
+ }
89
+ return details === '' ? details : `(${details})`;
90
+ }
@@ -27,12 +27,12 @@ class DeviceManager {
27
27
  log_1.default.log(chalk_1.default.green(CREATE_COMMAND_DESCRIPTION));
28
28
  log_1.default.addNewLineIfNone();
29
29
  const account = await this.resolveAccountAsync();
30
- const { team } = await this.ctx.appStore.ensureAuthenticatedAsync();
30
+ const appleAuthCtx = await this.ctx.appStore.ensureAuthenticatedAsync();
31
31
  const appleTeam = await ensureAppleTeamExistsAsync(account.id, {
32
- appleTeamIdentifier: team.id,
33
- appleTeamName: team.name,
32
+ appleTeamIdentifier: appleAuthCtx.team.id,
33
+ appleTeamName: appleAuthCtx.team.name,
34
34
  });
35
- const action = new action_1.default(account, appleTeam);
35
+ const action = new action_1.default(this.ctx.appStore, account, appleTeam);
36
36
  await action.runAsync();
37
37
  }
38
38
  async resolveAccountAsync() {