eas-cli 2.9.0 → 3.1.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 +6 -1209
  2. package/build/branch/queries.d.ts +2 -1
  3. package/build/build/ios/credentials.d.ts +3 -0
  4. package/build/build/ios/credentials.js +16 -1
  5. package/build/build/ios/graphql.d.ts +4 -1
  6. package/build/build/ios/graphql.js +2 -1
  7. package/build/build/ios/prepareJob.d.ts +2 -0
  8. package/build/build/ios/prepareJob.js +17 -1
  9. package/build/build/queries.d.ts +6 -0
  10. package/build/build/queries.js +25 -1
  11. package/build/channel/errors.d.ts +3 -0
  12. package/build/channel/errors.js +9 -0
  13. package/build/channel/queries.js +1 -1
  14. package/build/commands/branch/delete.js +1 -1
  15. package/build/commands/branch/view.js +3 -1
  16. package/build/commands/build/index.d.ts +1 -0
  17. package/build/commands/build/index.js +2 -0
  18. package/build/commands/build/resign.d.ts +55 -0
  19. package/build/commands/build/resign.js +207 -0
  20. package/build/commands/build/run.js +2 -2
  21. package/build/commands/channel/edit.js +1 -1
  22. package/build/commands/update/configure.js +15 -2
  23. package/build/commands/update/index.d.ts +1 -0
  24. package/build/commands/update/index.js +85 -139
  25. package/build/commands/update/list.js +3 -1
  26. package/build/commands/update/republish.d.ts +36 -0
  27. package/build/commands/update/republish.js +218 -0
  28. package/build/devices/queries.js +8 -4
  29. package/build/graphql/generated.d.ts +199 -5
  30. package/build/graphql/generated.js +9 -2
  31. package/build/graphql/mutations/BuildMutation.d.ts +5 -1
  32. package/build/graphql/mutations/BuildMutation.js +17 -0
  33. package/build/graphql/queries/ChannelQuery.js +2 -1
  34. package/build/graphql/types/Update.js +6 -0
  35. package/build/run/android/emulator.js +3 -3
  36. package/build/update/configure.d.ts +1 -1
  37. package/build/update/configure.js +52 -5
  38. package/build/update/getBranchNameFromChannelNameAsync.d.ts +2 -0
  39. package/build/update/getBranchNameFromChannelNameAsync.js +45 -0
  40. package/build/update/queries.js +3 -1
  41. package/build/utils/queries.d.ts +5 -1
  42. package/build/utils/queries.js +1 -1
  43. package/oclif.manifest.json +1 -1
  44. package/package.json +3 -3
@@ -19,7 +19,6 @@ const pagination_1 = require("../../commandUtils/pagination");
19
19
  const fetch_1 = tslib_1.__importDefault(require("../../fetch"));
20
20
  const generated_1 = require("../../graphql/generated");
21
21
  const PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
22
- const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
23
22
  const log_1 = tslib_1.__importStar(require("../../log"));
24
23
  const ora_1 = require("../../ora");
25
24
  const platform_1 = require("../../platform");
@@ -28,7 +27,7 @@ const publish_1 = require("../../project/publish");
28
27
  const workflow_1 = require("../../project/workflow");
29
28
  const prompts_1 = require("../../prompts");
30
29
  const configure_1 = require("../../update/configure");
31
- const queries_3 = require("../../update/queries");
30
+ const getBranchNameFromChannelNameAsync_1 = require("../../update/getBranchNameFromChannelNameAsync");
32
31
  const utils_2 = require("../../update/utils");
33
32
  const code_signing_1 = require("../../utils/code-signing");
34
33
  const uniqBy_1 = tslib_1.__importDefault(require("../../utils/expodash/uniqBy"));
@@ -53,10 +52,11 @@ function getRequestedPlatform(platform) {
53
52
  }
54
53
  class UpdatePublish extends EasCommand_1.default {
55
54
  async runAsync() {
56
- var _b, _c, _d;
55
+ var _b, _c;
57
56
  const { flags: rawFlags } = await this.parse(UpdatePublish);
58
57
  const paginatedQueryOptions = (0, pagination_1.getPaginatedQueryOptions)(rawFlags);
59
- let { auto: autoFlag, platform: platformFlag, branchName, updateMessage, republish, groupId, inputDir, skipBundler, privateKeyPath, json: jsonFlag, nonInteractive, } = this.sanitizeFlags(rawFlags);
58
+ let { auto: autoFlag, platform: platformFlag, channelName, updateMessage, inputDir, skipBundler, privateKeyPath, json: jsonFlag, nonInteractive, } = this.sanitizeFlags(rawFlags);
59
+ let branchName = this.sanitizeFlags(rawFlags).branchName;
60
60
  const { getDynamicProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdatePublish, {
61
61
  nonInteractive,
62
62
  });
@@ -70,7 +70,7 @@ class UpdatePublish extends EasCommand_1.default {
70
70
  isPublicConfig: false,
71
71
  });
72
72
  await (0, statuspageService_1.maybeWarnAboutEasOutagesAsync)(graphqlClient, [generated_1.StatuspageServiceName.EasUpdate]);
73
- await (0, configure_1.ensureEASUpdatesIsConfiguredAsync)(graphqlClient, {
73
+ await (0, configure_1.ensureEASUpdateIsConfiguredAsync)(graphqlClient, {
74
74
  exp: expPossiblyWithoutEasUpdateConfigured,
75
75
  platform: getRequestedPlatform(platformFlag),
76
76
  projectDir,
@@ -79,19 +79,27 @@ class UpdatePublish extends EasCommand_1.default {
79
79
  const { exp } = await getDynamicProjectConfigAsync();
80
80
  const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(expPrivate, privateKeyPath);
81
81
  let realizedPlatforms = [];
82
+ if (channelName && branchName) {
83
+ throw new Error('Cannot specify both --channel and --branch. Specify either --channel, --branch, or --auto');
84
+ }
85
+ if (channelName) {
86
+ branchName = await (0, getBranchNameFromChannelNameAsync_1.getBranchNameFromChannelNameAsync)(graphqlClient, projectId, channelName);
87
+ }
82
88
  if (!branchName) {
83
89
  if (autoFlag) {
84
90
  branchName = await (0, utils_1.getDefaultBranchNameAsync)();
85
91
  }
86
92
  else if (nonInteractive) {
87
- throw new Error('Must supply --branch or use --auto when in non-interactive mode');
93
+ throw new Error('Must supply --channel, --branch or --auto when in non-interactive mode');
88
94
  }
89
95
  else {
90
96
  try {
91
97
  const branch = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
92
98
  projectId,
93
- promptTitle: `Which branch would you like to ${republish ? 'republish' : 'publish'} on?`,
94
- displayTextForListItem: updateBranch => `${updateBranch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_2.formatUpdateMessage)(updateBranch.updates[0])}`)}`,
99
+ promptTitle: `Which branch would you like to publish on?`,
100
+ displayTextForListItem: updateBranch => ({
101
+ title: `${updateBranch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_2.formatUpdateMessage)(updateBranch.updates[0])}`)}`,
102
+ }),
95
103
  paginatedQueryOptions,
96
104
  });
97
105
  branchName = branch.name;
@@ -109,131 +117,62 @@ class UpdatePublish extends EasCommand_1.default {
109
117
  (0, assert_1.default)(branchName, 'Branch name must be specified.');
110
118
  }
111
119
  }
112
- let unsortedUpdateInfoGroups = {};
113
- let oldMessage, oldRuntimeVersion;
114
- let uploadedAssetCount = 0;
115
- let assetLimitPerUpdateGroup = 0;
116
- if (republish) {
117
- // If we are republishing, we don't need to worry about building the bundle or uploading the assets.
118
- // Instead we get the `updateInfoGroup` from the update we wish to republish.
119
- let updatesToRepublish;
120
- if (groupId) {
121
- const updatesByGroup = await UpdateQuery_1.UpdateQuery.viewUpdateGroupAsync(graphqlClient, {
122
- groupId,
123
- });
124
- updatesToRepublish = updatesByGroup;
125
- }
126
- else {
127
- if (nonInteractive) {
128
- throw new Error('Must supply --group when in non-interactive mode');
129
- }
130
- updatesToRepublish = await (0, queries_3.selectUpdateGroupOnBranchAsync)(graphqlClient, {
131
- projectId,
132
- branchName,
133
- paginatedQueryOptions,
134
- });
135
- }
136
- const updatesToRepublishFilteredByPlatform = updatesToRepublish.filter(
137
- // Only republish to the specified platforms
138
- update => platformFlag === 'all' || update.platform === platformFlag);
139
- if (updatesToRepublishFilteredByPlatform.length === 0) {
140
- throw new Error(`There are no updates on branch "${branchName}" published for the platform(s) "${platformFlag}" with group ID "${groupId ? groupId : updatesToRepublish[0].group}". Did you mean to publish a new update instead?`);
141
- }
142
- let publicationPlatformMessage;
143
- if (platformFlag === 'all') {
144
- if (updatesToRepublishFilteredByPlatform.length < exports.defaultPublishPlatforms.length) {
145
- log_1.default.warn(`You are republishing an update that wasn't published for all platforms.`);
146
- }
147
- publicationPlatformMessage = `The republished update will appear on the same platforms it was originally published on: ${updatesToRepublishFilteredByPlatform
148
- .map(update => update.platform)
149
- .join(', ')}`;
150
- }
151
- else {
152
- publicationPlatformMessage = `The republished update will appear only on: ${platformFlag}`;
153
- }
154
- log_1.default.withTick(publicationPlatformMessage);
155
- for (const update of updatesToRepublishFilteredByPlatform) {
156
- const { manifestFragment } = update;
157
- const platform = update.platform;
158
- unsortedUpdateInfoGroups[platform] = JSON.parse(manifestFragment);
159
- }
160
- realizedPlatforms = updatesToRepublishFilteredByPlatform.map(update => update.platform);
161
- // These are the same for each member of an update group
162
- groupId = updatesToRepublishFilteredByPlatform[0].group;
163
- oldMessage = (_b = updatesToRepublishFilteredByPlatform[0].message) !== null && _b !== void 0 ? _b : '';
164
- oldRuntimeVersion = updatesToRepublishFilteredByPlatform[0].runtimeVersion;
165
- if (!updateMessage) {
166
- if (nonInteractive) {
167
- throw new Error('Must supply --message when in non-interactive mode');
168
- }
169
- const validationMessage = 'publish message may not be empty.';
170
- if (jsonFlag) {
171
- throw new Error(validationMessage);
172
- }
173
- ({ updateMessage } = await (0, prompts_1.promptAsync)({
174
- type: 'text',
175
- name: 'updateMessage',
176
- message: `Provide an update message.`,
177
- initial: `Republish "${oldMessage}" - group: ${groupId}`,
178
- validate: (value) => (value ? true : validationMessage),
179
- }));
180
- }
120
+ if (!updateMessage && autoFlag) {
121
+ updateMessage = (_b = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _b === void 0 ? void 0 : _b.trim();
181
122
  }
182
- else {
183
- if (!updateMessage && autoFlag) {
184
- updateMessage = (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim();
123
+ if (!updateMessage) {
124
+ if (nonInteractive) {
125
+ throw new Error('Must supply --message or use --auto when in non-interactive mode');
185
126
  }
186
- if (!updateMessage) {
187
- if (nonInteractive) {
188
- throw new Error('Must supply --message or use --auto when in non-interactive mode');
189
- }
190
- const validationMessage = 'publish message may not be empty.';
191
- if (jsonFlag) {
192
- throw new Error(validationMessage);
193
- }
194
- ({ updateMessage } = await (0, prompts_1.promptAsync)({
195
- type: 'text',
196
- name: 'updateMessage',
197
- message: `Provide an update message.`,
198
- initial: (_d = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _d === void 0 ? void 0 : _d.trim(),
199
- validate: (value) => (value ? true : validationMessage),
200
- }));
127
+ const validationMessage = 'publish message may not be empty.';
128
+ if (jsonFlag) {
129
+ throw new Error(validationMessage);
201
130
  }
202
- // build bundle and upload assets for a new publish
203
- if (!skipBundler) {
204
- const bundleSpinner = (0, ora_1.ora)().start('Exporting...');
205
- try {
206
- await (0, publish_1.buildBundlesAsync)({ projectDir, inputDir, exp, platformFlag });
207
- bundleSpinner.succeed('Exported bundle(s)');
208
- }
209
- catch (e) {
210
- bundleSpinner.fail('Export failed');
211
- throw e;
212
- }
213
- }
214
- // After possibly bundling, assert that the input directory can be found.
215
- const distRoot = await (0, publish_1.resolveInputDirectoryAsync)(inputDir, { skipBundler });
216
- const assetSpinner = (0, ora_1.ora)().start('Uploading...');
131
+ ({ updateMessage } = await (0, prompts_1.promptAsync)({
132
+ type: 'text',
133
+ name: 'updateMessage',
134
+ message: `Provide an update message.`,
135
+ initial: (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim(),
136
+ validate: (value) => (value ? true : validationMessage),
137
+ }));
138
+ }
139
+ // build bundle and upload assets for a new publish
140
+ if (!skipBundler) {
141
+ const bundleSpinner = (0, ora_1.ora)().start('Exporting...');
217
142
  try {
218
- const collectedAssets = await (0, publish_1.collectAssetsAsync)(distRoot);
219
- const assets = (0, publish_1.filterExportedPlatformsByFlag)(collectedAssets, platformFlag);
220
- realizedPlatforms = Object.keys(assets);
221
- const uploadResults = await (0, publish_1.uploadAssetsAsync)(graphqlClient, assets, projectId, (totalAssets, missingAssets) => {
222
- assetSpinner.text = `Uploading (${totalAssets - missingAssets}/${totalAssets})`;
223
- });
224
- uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
225
- assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
226
- unsortedUpdateInfoGroups = await (0, publish_1.buildUnsortedUpdateInfoGroupAsync)(assets, exp);
227
- const uploadAssetSuccessMessage = uploadedAssetCount
228
- ? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'platform' : 'platforms'}`
229
- : `Uploaded: No changes detected`;
230
- assetSpinner.succeed(uploadAssetSuccessMessage);
143
+ await (0, publish_1.buildBundlesAsync)({ projectDir, inputDir, exp, platformFlag });
144
+ bundleSpinner.succeed('Exported bundle(s)');
231
145
  }
232
146
  catch (e) {
233
- assetSpinner.fail('Failed to upload');
147
+ bundleSpinner.fail('Export failed');
234
148
  throw e;
235
149
  }
236
150
  }
151
+ // After possibly bundling, assert that the input directory can be found.
152
+ const distRoot = await (0, publish_1.resolveInputDirectoryAsync)(inputDir, { skipBundler });
153
+ const assetSpinner = (0, ora_1.ora)().start('Uploading...');
154
+ let unsortedUpdateInfoGroups = {};
155
+ let uploadedAssetCount = 0;
156
+ let assetLimitPerUpdateGroup = 0;
157
+ try {
158
+ const collectedAssets = await (0, publish_1.collectAssetsAsync)(distRoot);
159
+ const assets = (0, publish_1.filterExportedPlatformsByFlag)(collectedAssets, platformFlag);
160
+ realizedPlatforms = Object.keys(assets);
161
+ const uploadResults = await (0, publish_1.uploadAssetsAsync)(graphqlClient, assets, projectId, (totalAssets, missingAssets) => {
162
+ assetSpinner.text = `Uploading (${totalAssets - missingAssets}/${totalAssets})`;
163
+ });
164
+ uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
165
+ assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
166
+ unsortedUpdateInfoGroups = await (0, publish_1.buildUnsortedUpdateInfoGroupAsync)(assets, exp);
167
+ const uploadAssetSuccessMessage = uploadedAssetCount
168
+ ? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'platform' : 'platforms'}`
169
+ : `Uploaded: No changes detected`;
170
+ assetSpinner.succeed(uploadAssetSuccessMessage);
171
+ }
172
+ catch (e) {
173
+ assetSpinner.fail('Failed to upload');
174
+ throw e;
175
+ }
237
176
  const truncatedMessage = (0, utils_2.truncateString)(updateMessage, 1024);
238
177
  if (truncatedMessage !== updateMessage) {
239
178
  log_1.default.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
@@ -269,13 +208,10 @@ class UpdatePublish extends EasCommand_1.default {
269
208
  platform,
270
209
  unsortedUpdateInfoGroups[platform],
271
210
  ]));
272
- if (republish && !oldRuntimeVersion) {
273
- throw new Error('Cannot find the runtime version of the update group that is being republished.');
274
- }
275
211
  return {
276
212
  branchId,
277
213
  updateInfoGroup: localUpdateInfoGroup,
278
- runtimeVersion: republish ? oldRuntimeVersion : runtimeVersion,
214
+ runtimeVersion,
279
215
  message: truncatedMessage,
280
216
  gitCommitHash,
281
217
  isGitWorkingTreeDirty,
@@ -376,21 +312,27 @@ class UpdatePublish extends EasCommand_1.default {
376
312
  sanitizeFlags(flags) {
377
313
  var _b, _c;
378
314
  const nonInteractive = (_b = flags['non-interactive']) !== null && _b !== void 0 ? _b : false;
379
- const { auto, branch: branchName, message: updateMessage } = flags;
380
- if (nonInteractive && !auto && !(branchName && updateMessage)) {
381
- core_1.Errors.error('--auto or both --branch and --message are required when updating in non-interactive mode', { exit: 1 });
315
+ const { auto, branch: branchName, channel: channelName, message: updateMessage } = flags;
316
+ if (nonInteractive && !auto && !(branchName && channelName && updateMessage)) {
317
+ core_1.Errors.error('--auto or both --channel or --branch and --message are required when updating in non-interactive mode', { exit: 1 });
382
318
  }
383
- const groupId = flags.group;
384
- const republish = flags.republish || !!groupId; // When --group is defined, we are republishing
385
- if (nonInteractive && republish && !groupId) {
386
- core_1.Errors.error(`--group is required when updating in non-interactive mode`, { exit: 1 });
319
+ if (flags.group || flags.republish) {
320
+ // Pick the first flag set that is defined, in this specific order
321
+ const args = [
322
+ ['--group', flags.group],
323
+ ['--branch', flags.branch],
324
+ ].filter(([_, value]) => value)[0];
325
+ log_1.default.newLine();
326
+ log_1.default.warn('The --group and --republish flags are deprecated, use the republish command instead:');
327
+ log_1.default.warn(` ${chalk_1.default.bold([`eas update:republish`, ...(args !== null && args !== void 0 ? args : [])].join(' '))}`);
328
+ log_1.default.newLine();
329
+ core_1.Errors.error('--group and --republish flags are deprecated', { exit: 1 });
387
330
  }
388
331
  return {
389
332
  auto,
390
333
  branchName,
334
+ channelName,
391
335
  updateMessage,
392
- groupId,
393
- republish,
394
336
  inputDir: flags['input-dir'],
395
337
  skipBundler: flags['skip-bundler'],
396
338
  platform: flags.platform,
@@ -408,16 +350,20 @@ UpdatePublish.flags = {
408
350
  description: 'Branch to publish the update group on',
409
351
  required: false,
410
352
  }),
353
+ channel: core_1.Flags.string({
354
+ description: 'Channel that the published update should affect',
355
+ required: false,
356
+ }),
411
357
  message: core_1.Flags.string({
412
358
  description: 'A short message describing the update',
413
359
  required: false,
414
360
  }),
415
361
  republish: core_1.Flags.boolean({
416
- description: 'Republish an update group',
362
+ description: 'Republish an update group (deprecated, see republish command)',
417
363
  exclusive: ['input-dir', 'skip-bundler'],
418
364
  }),
419
365
  group: core_1.Flags.string({
420
- description: 'Update group to republish',
366
+ description: 'Update group to republish (deprecated, see republish command)',
421
367
  exclusive: ['input-dir', 'skip-bundler'],
422
368
  }),
423
369
  'input-dir': core_1.Flags.string({
@@ -39,7 +39,9 @@ class UpdateList extends EasCommand_1.default {
39
39
  const selectedBranch = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
40
40
  projectId,
41
41
  promptTitle: 'Which branch would you like to view?',
42
- displayTextForListItem: updateBranch => updateBranch.name,
42
+ displayTextForListItem: updateBranch => ({
43
+ title: updateBranch.name,
44
+ }),
43
45
  paginatedQueryOptions:
44
46
  // discard limit and offset because this query is not those flag's intended target
45
47
  {
@@ -0,0 +1,36 @@
1
+ import { Platform } from '@expo/config';
2
+ import EasCommand from '../../commandUtils/EasCommand';
3
+ declare type UpdateRepublishRawFlags = {
4
+ branch?: string;
5
+ group?: string;
6
+ message?: string;
7
+ platform: string;
8
+ 'non-interactive': boolean;
9
+ json?: boolean;
10
+ };
11
+ declare type UpdateRepublishFlags = {
12
+ branchName?: string;
13
+ groupId?: string;
14
+ updateMessage?: string;
15
+ platform: Platform[];
16
+ nonInteractive: boolean;
17
+ json: boolean;
18
+ };
19
+ export default class UpdateRepublish extends EasCommand {
20
+ static description: string;
21
+ static flags: {
22
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
23
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
24
+ branch: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
25
+ group: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
26
+ message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
27
+ platform: import("@oclif/core/lib/interfaces").OptionFlag<string>;
28
+ };
29
+ static contextDefinition: {
30
+ loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
31
+ projectConfig: import("../../commandUtils/context/ProjectConfigContextField").default;
32
+ };
33
+ runAsync(): Promise<void>;
34
+ sanitizeFlags(rawFlags: UpdateRepublishRawFlags): UpdateRepublishFlags;
35
+ }
36
+ export {};
@@ -0,0 +1,218 @@
1
+ "use strict";
2
+ var _a;
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const url_1 = require("../../build/utils/url");
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 PublishMutation_1 = require("../../graphql/mutations/PublishMutation");
11
+ const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
12
+ const log_1 = tslib_1.__importStar(require("../../log"));
13
+ const ora_1 = require("../../ora");
14
+ const projectUtils_1 = require("../../project/projectUtils");
15
+ const prompts_1 = require("../../prompts");
16
+ const queries_1 = require("../../update/queries");
17
+ const utils_1 = require("../../update/utils");
18
+ const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
19
+ const json_1 = require("../../utils/json");
20
+ const defaultRepublishPlatforms = ['android', 'ios'];
21
+ class UpdateRepublish extends EasCommand_1.default {
22
+ async runAsync() {
23
+ const { flags: rawFlags } = await this.parse(UpdateRepublish);
24
+ const flags = this.sanitizeFlags(rawFlags);
25
+ const { projectConfig: { exp, projectId }, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdateRepublish, {
26
+ nonInteractive: flags.nonInteractive,
27
+ });
28
+ if (flags.json) {
29
+ (0, json_1.enableJsonOutput)();
30
+ }
31
+ const existingUpdates = await getOrAskUpdatesAsync(graphqlClient, projectId, flags);
32
+ const updatesToPublish = existingUpdates.filter(update => flags.platform.includes(update.platform));
33
+ if (existingUpdates.length === 0) {
34
+ throw new Error(`There are no published updates found`);
35
+ }
36
+ if (updatesToPublish.length === 0) {
37
+ 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?`);
38
+ }
39
+ if (rawFlags.platform === 'all') {
40
+ log_1.default.withTick(`The republished update will appear only on: ${rawFlags.platform}`);
41
+ }
42
+ else {
43
+ const platformsFromUpdates = updatesToPublish.map(update => update.platform);
44
+ if (platformsFromUpdates.length < defaultRepublishPlatforms.length) {
45
+ log_1.default.warn(`You are republishing an update that wasn't published for all platforms.`);
46
+ }
47
+ log_1.default.withTick(`The republished update will appear on the same platforms it was originally published on: ${platformsFromUpdates.join(', ')}`);
48
+ }
49
+ // This command only republishes a single update group
50
+ // The update group properties are the same for all updates
51
+ const { branchId, branchName, runtimeVersion } = updatesToPublish[0];
52
+ const updateMessage = await getOrAskUpdateMessageAsync(updatesToPublish, flags);
53
+ // If codesigning was created for the original update, we need to add it to the republish
54
+ const shouldRepublishWithCodesigning = updatesToPublish.some(update => update.codeSigningInfo);
55
+ if (shouldRepublishWithCodesigning) {
56
+ log_1.default.withTick(`The republished update will be signed with the same codesigning as the original update.`);
57
+ }
58
+ const publishIndicator = (0, ora_1.ora)('Republishing...').start();
59
+ let updatesRepublished;
60
+ try {
61
+ updatesRepublished = await PublishMutation_1.PublishMutation.publishUpdateGroupAsync(graphqlClient, [
62
+ {
63
+ branchId,
64
+ runtimeVersion,
65
+ message: updateMessage,
66
+ updateInfoGroup: Object.fromEntries(updatesToPublish.map(update => [update.platform, JSON.parse(update.manifestFragment)])),
67
+ gitCommitHash: updatesToPublish[0].gitCommitHash,
68
+ awaitingCodeSigningInfo: shouldRepublishWithCodesigning,
69
+ },
70
+ ]);
71
+ if (shouldRepublishWithCodesigning) {
72
+ const codeSigningByPlatform = Object.fromEntries(updatesToPublish.map(update => [update.platform, update.codeSigningInfo]));
73
+ await Promise.all(updatesRepublished.map(async (update) => {
74
+ const codeSigning = codeSigningByPlatform[update.platform];
75
+ if (codeSigning) {
76
+ await PublishMutation_1.PublishMutation.setCodeSigningInfoAsync(graphqlClient, update.id, codeSigning);
77
+ }
78
+ }));
79
+ }
80
+ publishIndicator.succeed('Republished update');
81
+ }
82
+ catch (error) {
83
+ publishIndicator.fail('Failed to republish update');
84
+ throw error;
85
+ }
86
+ if (flags.json) {
87
+ return (0, json_1.printJsonOnlyOutput)(updatesRepublished);
88
+ }
89
+ const updatesRepublishedByPlatform = Object.fromEntries(updatesRepublished.map(update => [update.platform, update]));
90
+ const updateGroupUrl = (0, url_1.getUpdateGroupUrl)((await (0, projectUtils_1.getOwnerAccountForProjectIdAsync)(graphqlClient, projectId)).name, exp.slug, updatesRepublished[0].group);
91
+ log_1.default.addNewLineIfNone();
92
+ log_1.default.log((0, formatFields_1.default)([
93
+ { label: 'Branch', value: branchName },
94
+ { label: 'Runtime version', value: updatesRepublished[0].runtimeVersion },
95
+ { label: 'Platform', value: updatesRepublished.map(update => update.platform).join(', ') },
96
+ { label: 'Update Group ID', value: updatesRepublished[0].id },
97
+ ...(updatesRepublishedByPlatform.android
98
+ ? [{ label: 'Android update ID', value: updatesRepublishedByPlatform.android.id }]
99
+ : []),
100
+ ...(updatesRepublishedByPlatform.ios
101
+ ? [{ label: 'iOS update ID', value: updatesRepublishedByPlatform.ios.id }]
102
+ : []),
103
+ { label: 'Message', value: updateMessage },
104
+ { label: 'Website link', value: (0, log_1.link)(updateGroupUrl, { dim: false }) },
105
+ ]));
106
+ }
107
+ sanitizeFlags(rawFlags) {
108
+ var _b;
109
+ const branchName = rawFlags.branch;
110
+ const groupId = rawFlags.group;
111
+ if (!branchName && !groupId) {
112
+ throw new Error('Either --branch or --group must be specified');
113
+ }
114
+ const platform = rawFlags.platform === 'all' ? defaultRepublishPlatforms : [rawFlags.platform];
115
+ return {
116
+ branchName,
117
+ groupId,
118
+ platform,
119
+ updateMessage: rawFlags.message,
120
+ json: (_b = rawFlags.json) !== null && _b !== void 0 ? _b : false,
121
+ nonInteractive: rawFlags['non-interactive'],
122
+ };
123
+ }
124
+ }
125
+ exports.default = UpdateRepublish;
126
+ _a = UpdateRepublish;
127
+ UpdateRepublish.description = 'rollback to an existing update';
128
+ UpdateRepublish.flags = {
129
+ branch: core_1.Flags.string({
130
+ description: 'Branch name to select an update to republish from',
131
+ exclusive: ['group'],
132
+ }),
133
+ group: core_1.Flags.string({
134
+ description: 'Update group ID to republish',
135
+ exclusive: ['branch'],
136
+ }),
137
+ message: core_1.Flags.string({
138
+ description: 'Short message describing the republished update',
139
+ required: false,
140
+ }),
141
+ platform: core_1.Flags.enum({
142
+ char: 'p',
143
+ options: [...defaultRepublishPlatforms, 'all'],
144
+ default: 'all',
145
+ required: false,
146
+ }),
147
+ ...flags_1.EasNonInteractiveAndJsonFlags,
148
+ };
149
+ UpdateRepublish.contextDefinition = {
150
+ ..._a.ContextOptions.ProjectConfig,
151
+ ..._a.ContextOptions.LoggedIn,
152
+ };
153
+ /** Retrieve the update group from either the update group id, or select from branch name. */
154
+ async function getOrAskUpdatesAsync(graphqlClient, projectId, flags) {
155
+ if (flags.groupId) {
156
+ const updateGroups = await UpdateQuery_1.UpdateQuery.viewUpdateGroupAsync(graphqlClient, {
157
+ groupId: flags.groupId,
158
+ });
159
+ return updateGroups.map(group => ({
160
+ ...group,
161
+ groupId: group.group,
162
+ branchId: group.branch.id,
163
+ branchName: group.branch.name,
164
+ }));
165
+ }
166
+ if (flags.branchName) {
167
+ return await askUpdatesFromBranchNameAsync(graphqlClient, {
168
+ ...flags,
169
+ branchName: flags.branchName,
170
+ projectId,
171
+ });
172
+ }
173
+ throw new Error('Must supply --group or --branch');
174
+ }
175
+ /** Ask the user which update needs to be republished by branch name, this requires interactive mode */
176
+ async function askUpdatesFromBranchNameAsync(graphqlClient, { projectId, branchName, json, nonInteractive, }) {
177
+ if (nonInteractive) {
178
+ throw new Error('Must supply --group when in non-interactive mode');
179
+ }
180
+ const updateGroups = await (0, queries_1.selectUpdateGroupOnBranchAsync)(graphqlClient, {
181
+ projectId,
182
+ branchName,
183
+ paginatedQueryOptions: (0, pagination_1.getPaginatedQueryOptions)({ json, 'non-interactive': nonInteractive }),
184
+ });
185
+ return updateGroups.map(group => ({
186
+ ...group,
187
+ groupId: group.id,
188
+ branchId: group.branch.id,
189
+ branchName: group.branch.name,
190
+ }));
191
+ }
192
+ /** Get or ask the user for the update (group) message for the republish */
193
+ async function getOrAskUpdateMessageAsync(updates, flags) {
194
+ if (flags.updateMessage) {
195
+ return sanitizeUpdateMessage(flags.updateMessage);
196
+ }
197
+ if (flags.nonInteractive || flags.json) {
198
+ throw new Error('Must supply --message when in non-interactive mode');
199
+ }
200
+ // This command only uses a single update group to republish, meaning these values are always identical
201
+ const oldGroupId = updates[0].groupId;
202
+ const oldUpdateMessage = updates[0].message;
203
+ const { updateMessage } = await (0, prompts_1.promptAsync)({
204
+ type: 'text',
205
+ name: 'updateMessage',
206
+ message: 'Provide an update message.',
207
+ initial: `Republish "${oldUpdateMessage}" - group: ${oldGroupId}`,
208
+ validate: (value) => (value ? true : 'Update message may not be empty.'),
209
+ });
210
+ return sanitizeUpdateMessage(updateMessage);
211
+ }
212
+ function sanitizeUpdateMessage(updateMessage) {
213
+ if (updateMessage !== (0, utils_1.truncateString)(updateMessage, 1024)) {
214
+ log_1.default.warn('Update message exceeds the allowed 1024 character limit, truncated update message.');
215
+ return (0, utils_1.truncateString)(updateMessage, 1024);
216
+ }
217
+ return updateMessage;
218
+ }
@@ -29,9 +29,11 @@ async function selectAppleTeamOnAccountAsync(graphqlClient, { accountName, selec
29
29
  }),
30
30
  promptOptions: {
31
31
  title: selectionPromptTitle,
32
- createDisplayTextForSelectionPromptListItem: appleTeam => appleTeam.appleTeamName
33
- ? `${appleTeam.appleTeamName} (ID: ${appleTeam.appleTeamIdentifier})`
34
- : appleTeam.appleTeamIdentifier,
32
+ createDisplayTextForSelectionPromptListItem: appleTeam => ({
33
+ title: appleTeam.appleTeamName
34
+ ? `${appleTeam.appleTeamName} (ID: ${appleTeam.appleTeamIdentifier})`
35
+ : appleTeam.appleTeamIdentifier,
36
+ }),
35
37
  getIdentifierForQueryItem: appleTeam => appleTeam.id,
36
38
  },
37
39
  });
@@ -59,7 +61,9 @@ async function selectAppleDeviceOnAppleTeamAsync(graphqlClient, { accountName, a
59
61
  }),
60
62
  promptOptions: {
61
63
  title: selectionPromptTitle,
62
- createDisplayTextForSelectionPromptListItem: appleDevice => (0, DeviceUtils_1.formatDeviceLabel)(appleDevice),
64
+ createDisplayTextForSelectionPromptListItem: appleDevice => ({
65
+ title: (0, DeviceUtils_1.formatDeviceLabel)(appleDevice),
66
+ }),
63
67
  getIdentifierForQueryItem: appleTeam => appleTeam.id,
64
68
  },
65
69
  });