eas-cli 2.8.0 → 3.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.
@@ -1,6 +1,5 @@
1
1
  import EasCommand from '../../commandUtils/EasCommand';
2
2
  export default class Run extends EasCommand {
3
- static hidden: boolean;
4
3
  static description: string;
5
4
  static flags: {
6
5
  offset: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
@@ -3,17 +3,21 @@ 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 assert_1 = tslib_1.__importDefault(require("assert"));
6
7
  const fs_extra_1 = require("fs-extra");
7
- const node_assert_1 = tslib_1.__importDefault(require("node:assert"));
8
+ const path_1 = tslib_1.__importDefault(require("path"));
8
9
  const queries_1 = require("../../build/queries");
9
10
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
10
11
  const pagination_1 = require("../../commandUtils/pagination");
11
12
  const generated_1 = require("../../graphql/generated");
12
13
  const BuildQuery_1 = require("../../graphql/queries/BuildQuery");
14
+ const log_1 = tslib_1.__importDefault(require("../../log"));
13
15
  const projectUtils_1 = require("../../project/projectUtils");
14
16
  const prompts_1 = require("../../prompts");
15
17
  const run_1 = require("../../run/run");
18
+ const utils_1 = require("../../run/utils");
16
19
  const download_1 = require("../../utils/download");
20
+ const paths_1 = require("../../utils/paths");
17
21
  class Run extends EasCommand_1.default {
18
22
  async runAsync() {
19
23
  const { flags: rawFlags } = await this.parse(Run);
@@ -52,7 +56,6 @@ class Run extends EasCommand_1.default {
52
56
  }
53
57
  exports.default = Run;
54
58
  _a = Run;
55
- Run.hidden = true;
56
59
  Run.description = 'run simulator/emulator builds from eas-cli';
57
60
  Run.flags = {
58
61
  latest: core_1.Flags.boolean({
@@ -118,13 +121,8 @@ async function maybeGetBuildAsync(graphqlClient, flags, projectId, paginatedQuer
118
121
  status: generated_1.BuildStatus.Finished,
119
122
  },
120
123
  queryOptions: paginatedQueryOptions,
121
- selectPromptDisabledFunction: build => {
122
- var _b, _c, _d;
123
- return build.platform === generated_1.AppPlatform.Ios
124
- ? false
125
- : (_d = !((_c = (_b = build.artifacts) === null || _b === void 0 ? void 0 : _b.applicationArchiveUrl) === null || _c === void 0 ? void 0 : _c.endsWith('.apk'))) !== null && _d !== void 0 ? _d : false;
126
- },
127
- warningMessage: 'This is not a simulator/emulator build',
124
+ selectPromptDisabledFunction: build => !(0, utils_1.isRunnableOnSimulatorOrEmulator)(build),
125
+ warningMessage: 'Artifacts for this build have expired and are no longer available, or this is not a simulator/emulator build.',
128
126
  });
129
127
  }
130
128
  else if (flags.runArchiveFlags.latest) {
@@ -141,14 +139,23 @@ async function maybeGetBuildAsync(graphqlClient, flags, projectId, paginatedQuer
141
139
  return null;
142
140
  }
143
141
  }
142
+ function getEasBuildRunCachedAppPath(projectId, buildId, platform) {
143
+ return path_1.default.join((0, paths_1.getEasBuildRunCacheDirectoryPath)(), `${projectId}_${buildId}.${platform === generated_1.AppPlatform.Ios ? 'app' : 'apk'}`);
144
+ }
144
145
  async function getPathToSimulatorBuildAppAsync(graphqlClient, projectId, flags, queryOptions) {
145
146
  var _b, _c;
146
147
  const maybeBuild = await maybeGetBuildAsync(graphqlClient, flags, projectId, queryOptions);
147
148
  if (maybeBuild) {
149
+ const cachedAppPath = getEasBuildRunCachedAppPath(projectId, maybeBuild.id, flags.selectedPlatform);
150
+ if (await (0, fs_extra_1.pathExists)(cachedAppPath)) {
151
+ log_1.default.newLine();
152
+ log_1.default.log(`Using cached app...`);
153
+ return cachedAppPath;
154
+ }
148
155
  if (!((_b = maybeBuild.artifacts) === null || _b === void 0 ? void 0 : _b.applicationArchiveUrl)) {
149
156
  throw new Error('Build does not have an application archive url');
150
157
  }
151
- return await (0, download_1.downloadAndMaybeExtractAppAsync)(maybeBuild.artifacts.applicationArchiveUrl, flags.selectedPlatform);
158
+ return await (0, download_1.downloadAndMaybeExtractAppAsync)(maybeBuild.artifacts.applicationArchiveUrl, flags.selectedPlatform, cachedAppPath);
152
159
  }
153
160
  if (flags.runArchiveFlags.url) {
154
161
  return await (0, download_1.downloadAndMaybeExtractAppAsync)(flags.runArchiveFlags.url, flags.selectedPlatform);
@@ -157,6 +164,6 @@ async function getPathToSimulatorBuildAppAsync(graphqlClient, projectId, flags,
157
164
  return await (0, download_1.extractAppFromLocalArchiveAsync)(flags.runArchiveFlags.path, flags.selectedPlatform);
158
165
  }
159
166
  // this should never fail, due to the validation in sanitizeFlagsAsync
160
- (0, node_assert_1.default)(flags.runArchiveFlags.path);
167
+ (0, assert_1.default)(flags.runArchiveFlags.path);
161
168
  return flags.runArchiveFlags.path;
162
169
  }
@@ -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,6 @@ 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");
32
30
  const utils_2 = require("../../update/utils");
33
31
  const code_signing_1 = require("../../utils/code-signing");
34
32
  const uniqBy_1 = tslib_1.__importDefault(require("../../utils/expodash/uniqBy"));
@@ -53,10 +51,10 @@ function getRequestedPlatform(platform) {
53
51
  }
54
52
  class UpdatePublish extends EasCommand_1.default {
55
53
  async runAsync() {
56
- var _b, _c, _d;
54
+ var _b, _c;
57
55
  const { flags: rawFlags } = await this.parse(UpdatePublish);
58
56
  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);
57
+ let { auto: autoFlag, platform: platformFlag, branchName, updateMessage, inputDir, skipBundler, privateKeyPath, json: jsonFlag, nonInteractive, } = this.sanitizeFlags(rawFlags);
60
58
  const { getDynamicProjectConfigAsync, loggedIn: { graphqlClient }, } = await this.getContextAsync(UpdatePublish, {
61
59
  nonInteractive,
62
60
  });
@@ -90,7 +88,7 @@ class UpdatePublish extends EasCommand_1.default {
90
88
  try {
91
89
  const branch = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
92
90
  projectId,
93
- promptTitle: `Which branch would you like to ${republish ? 'republish' : 'publish'} on?`,
91
+ promptTitle: `Which branch would you like to publish on?`,
94
92
  displayTextForListItem: updateBranch => `${updateBranch.name} ${chalk_1.default.grey(`- current update: ${(0, utils_2.formatUpdateMessage)(updateBranch.updates[0])}`)}`,
95
93
  paginatedQueryOptions,
96
94
  });
@@ -109,131 +107,62 @@ class UpdatePublish extends EasCommand_1.default {
109
107
  (0, assert_1.default)(branchName, 'Branch name must be specified.');
110
108
  }
111
109
  }
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
- }
110
+ if (!updateMessage && autoFlag) {
111
+ updateMessage = (_b = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _b === void 0 ? void 0 : _b.trim();
181
112
  }
182
- else {
183
- if (!updateMessage && autoFlag) {
184
- updateMessage = (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim();
113
+ if (!updateMessage) {
114
+ if (nonInteractive) {
115
+ throw new Error('Must supply --message or use --auto when in non-interactive mode');
185
116
  }
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
- }));
201
- }
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
- }
117
+ const validationMessage = 'publish message may not be empty.';
118
+ if (jsonFlag) {
119
+ throw new Error(validationMessage);
213
120
  }
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...');
121
+ ({ updateMessage } = await (0, prompts_1.promptAsync)({
122
+ type: 'text',
123
+ name: 'updateMessage',
124
+ message: `Provide an update message.`,
125
+ initial: (_c = (await (0, vcs_1.getVcsClient)().getLastCommitMessageAsync())) === null || _c === void 0 ? void 0 : _c.trim(),
126
+ validate: (value) => (value ? true : validationMessage),
127
+ }));
128
+ }
129
+ // build bundle and upload assets for a new publish
130
+ if (!skipBundler) {
131
+ const bundleSpinner = (0, ora_1.ora)().start('Exporting...');
217
132
  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);
133
+ await (0, publish_1.buildBundlesAsync)({ projectDir, inputDir, exp, platformFlag });
134
+ bundleSpinner.succeed('Exported bundle(s)');
231
135
  }
232
136
  catch (e) {
233
- assetSpinner.fail('Failed to upload');
137
+ bundleSpinner.fail('Export failed');
234
138
  throw e;
235
139
  }
236
140
  }
141
+ // After possibly bundling, assert that the input directory can be found.
142
+ const distRoot = await (0, publish_1.resolveInputDirectoryAsync)(inputDir, { skipBundler });
143
+ const assetSpinner = (0, ora_1.ora)().start('Uploading...');
144
+ let unsortedUpdateInfoGroups = {};
145
+ let uploadedAssetCount = 0;
146
+ let assetLimitPerUpdateGroup = 0;
147
+ try {
148
+ const collectedAssets = await (0, publish_1.collectAssetsAsync)(distRoot);
149
+ const assets = (0, publish_1.filterExportedPlatformsByFlag)(collectedAssets, platformFlag);
150
+ realizedPlatforms = Object.keys(assets);
151
+ const uploadResults = await (0, publish_1.uploadAssetsAsync)(graphqlClient, assets, projectId, (totalAssets, missingAssets) => {
152
+ assetSpinner.text = `Uploading (${totalAssets - missingAssets}/${totalAssets})`;
153
+ });
154
+ uploadedAssetCount = uploadResults.uniqueUploadedAssetCount;
155
+ assetLimitPerUpdateGroup = uploadResults.assetLimitPerUpdateGroup;
156
+ unsortedUpdateInfoGroups = await (0, publish_1.buildUnsortedUpdateInfoGroupAsync)(assets, exp);
157
+ const uploadAssetSuccessMessage = uploadedAssetCount
158
+ ? `Uploaded ${uploadedAssetCount} ${uploadedAssetCount === 1 ? 'platform' : 'platforms'}`
159
+ : `Uploaded: No changes detected`;
160
+ assetSpinner.succeed(uploadAssetSuccessMessage);
161
+ }
162
+ catch (e) {
163
+ assetSpinner.fail('Failed to upload');
164
+ throw e;
165
+ }
237
166
  const truncatedMessage = (0, utils_2.truncateString)(updateMessage, 1024);
238
167
  if (truncatedMessage !== updateMessage) {
239
168
  log_1.default.warn('Update message exceeds the allowed 1024 character limit. Truncating message...');
@@ -260,22 +189,22 @@ class UpdatePublish extends EasCommand_1.default {
260
189
  });
261
190
  }
262
191
  log_1.default.withTick(`Channel: ${chalk_1.default.bold(branchName)} pointed at branch: ${chalk_1.default.bold(branchName)}`);
263
- const gitCommitHash = await (0, vcs_1.getVcsClient)().getCommitHashAsync();
192
+ const vcsClient = (0, vcs_1.getVcsClient)();
193
+ const gitCommitHash = await vcsClient.getCommitHashAsync();
194
+ const isGitWorkingTreeDirty = await vcsClient.hasUncommittedChangesAsync();
264
195
  // Sort the updates into different groups based on their platform specific runtime versions
265
196
  const updateGroups = runtimeToPlatformMapping.map(({ runtimeVersion, platforms }) => {
266
197
  const localUpdateInfoGroup = Object.fromEntries(platforms.map(platform => [
267
198
  platform,
268
199
  unsortedUpdateInfoGroups[platform],
269
200
  ]));
270
- if (republish && !oldRuntimeVersion) {
271
- throw new Error('Cannot find the runtime version of the update group that is being republished.');
272
- }
273
201
  return {
274
202
  branchId,
275
203
  updateInfoGroup: localUpdateInfoGroup,
276
- runtimeVersion: republish ? oldRuntimeVersion : runtimeVersion,
204
+ runtimeVersion,
277
205
  message: truncatedMessage,
278
206
  gitCommitHash,
207
+ isGitWorkingTreeDirty,
279
208
  awaitingCodeSigningInfo: !!codeSigningInfo,
280
209
  };
281
210
  });
@@ -348,7 +277,14 @@ class UpdatePublish extends EasCommand_1.default {
348
277
  : []),
349
278
  ...(newIosUpdate ? [{ label: 'iOS update ID', value: newIosUpdate.id }] : []),
350
279
  { label: 'Message', value: truncatedMessage },
351
- ...(gitCommitHash ? [{ label: 'Commit', value: gitCommitHash }] : []),
280
+ ...(gitCommitHash
281
+ ? [
282
+ {
283
+ label: 'Commit',
284
+ value: `${gitCommitHash}${isGitWorkingTreeDirty ? '*' : ''}`,
285
+ },
286
+ ]
287
+ : []),
352
288
  { label: 'Website link', value: updateGroupLink },
353
289
  ]));
354
290
  log_1.default.addNewLineIfNone();
@@ -370,17 +306,22 @@ class UpdatePublish extends EasCommand_1.default {
370
306
  if (nonInteractive && !auto && !(branchName && updateMessage)) {
371
307
  core_1.Errors.error('--auto or both --branch and --message are required when updating in non-interactive mode', { exit: 1 });
372
308
  }
373
- const groupId = flags.group;
374
- const republish = flags.republish || !!groupId; // When --group is defined, we are republishing
375
- if (nonInteractive && republish && !groupId) {
376
- core_1.Errors.error(`--group is required when updating in non-interactive mode`, { exit: 1 });
309
+ if (flags.group || flags.republish) {
310
+ // Pick the first flag set that is defined, in this specific order
311
+ const args = [
312
+ ['--group', flags.group],
313
+ ['--branch', flags.branch],
314
+ ].filter(([_, value]) => value)[0];
315
+ log_1.default.newLine();
316
+ log_1.default.warn('The --group and --republish flags are deprecated, use the republish command instead:');
317
+ log_1.default.warn(` ${chalk_1.default.bold([`eas update:republish`, ...(args !== null && args !== void 0 ? args : [])].join(' '))}`);
318
+ log_1.default.newLine();
319
+ core_1.Errors.error('--group and --republish flags are deprecated', { exit: 1 });
377
320
  }
378
321
  return {
379
322
  auto,
380
323
  branchName,
381
324
  updateMessage,
382
- groupId,
383
- republish,
384
325
  inputDir: flags['input-dir'],
385
326
  skipBundler: flags['skip-bundler'],
386
327
  platform: flags.platform,
@@ -403,11 +344,11 @@ UpdatePublish.flags = {
403
344
  required: false,
404
345
  }),
405
346
  republish: core_1.Flags.boolean({
406
- description: 'Republish an update group',
347
+ description: 'Republish an update group (deprecated, see republish command)',
407
348
  exclusive: ['input-dir', 'skip-bundler'],
408
349
  }),
409
350
  group: core_1.Flags.string({
410
- description: 'Update group to republish',
351
+ description: 'Update group to republish (deprecated, see republish command)',
411
352
  exclusive: ['input-dir', 'skip-bundler'],
412
353
  }),
413
354
  'input-dir': core_1.Flags.string({
@@ -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
+ }