eas-cli 16.13.3 → 16.14.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 (33) hide show
  1. package/README.md +128 -78
  2. package/build/commands/env/create.d.ts +0 -1
  3. package/build/commands/env/create.js +1 -28
  4. package/build/commands/update/delete.js +2 -20
  5. package/build/commands/update/republish.js +2 -139
  6. package/build/commands/update/revert-update-rollout.d.ts +24 -0
  7. package/build/commands/update/revert-update-rollout.js +264 -0
  8. package/build/commands/update/roll-back-to-embedded.d.ts +0 -2
  9. package/build/commands/update/roll-back-to-embedded.js +13 -133
  10. package/build/commands/upload.d.ts +0 -1
  11. package/build/commands/upload.js +0 -1
  12. package/build/graphql/generated.d.ts +91 -109
  13. package/build/graphql/generated.js +15 -3
  14. package/build/graphql/mutations/EnvironmentVariableMutation.d.ts +0 -2
  15. package/build/graphql/mutations/EnvironmentVariableMutation.js +0 -48
  16. package/build/graphql/types/Update.js +1 -0
  17. package/build/project/publish.js +1 -0
  18. package/build/update/delete.d.ts +5 -0
  19. package/build/update/delete.js +24 -0
  20. package/build/update/queries.d.ts +13 -1
  21. package/build/update/queries.js +62 -1
  22. package/build/update/republish.d.ts +27 -0
  23. package/build/update/republish.js +242 -1
  24. package/build/update/roll-back-to-embedded.d.ts +18 -0
  25. package/build/update/roll-back-to-embedded.js +119 -0
  26. package/build/user/fetchUser.js +15 -13
  27. package/build/utils/statuspageService.js +1 -0
  28. package/oclif.manifest.json +78 -115
  29. package/package.json +3 -2
  30. package/build/commands/env/link.d.ts +0 -23
  31. package/build/commands/env/link.js +0 -128
  32. package/build/commands/env/unlink.d.ts +0 -22
  33. package/build/commands/env/unlink.js +0 -117
@@ -15,7 +15,6 @@ export default class EnvCreate extends EasCommand {
15
15
  visibility: import("@oclif/core/lib/interfaces").OptionFlag<"plaintext" | "sensitive" | "secret" | undefined>;
16
16
  name: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
17
17
  value: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
18
- link: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
19
18
  force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
19
  type: import("@oclif/core/lib/interfaces").OptionFlag<"string" | "file" | undefined>;
21
20
  };
@@ -31,10 +31,6 @@ class EnvCreate extends EasCommand_1.default {
31
31
  value: core_1.Flags.string({
32
32
  description: 'Text value or the variable',
33
33
  }),
34
- link: core_1.Flags.boolean({
35
- description: 'Link account-wide variable to the current project',
36
- hidden: true, // every account-wide variable is global for now so it's not user facing
37
- }),
38
34
  force: core_1.Flags.boolean({
39
35
  description: 'Overwrite existing variable',
40
36
  default: false,
@@ -56,7 +52,7 @@ class EnvCreate extends EasCommand_1.default {
56
52
  async runAsync() {
57
53
  const { args, flags } = await this.parse(EnvCreate);
58
54
  const validatedFlags = this.sanitizeFlags(flags);
59
- const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, link, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args);
55
+ const { name, value, scope, 'non-interactive': nonInteractive, environment: environments, visibility, force, type, fileName, } = await this.promptForMissingFlagsAsync(validatedFlags, args);
60
56
  const { projectId, loggedIn: { graphqlClient }, } = await this.getContextAsync(EnvCreate, {
61
57
  nonInteractive,
62
58
  });
@@ -81,18 +77,6 @@ class EnvCreate extends EasCommand_1.default {
81
77
  });
82
78
  overwrite = true;
83
79
  }
84
- if (existingVariable.scope === generated_1.EnvironmentVariableScope.Shared) {
85
- await this.promptForOverwriteAsync({
86
- nonInteractive,
87
- force,
88
- message: `Account-wide variable with ${name} name already exists on this account.`,
89
- suggestion: 'Do you want to unlink it first?',
90
- });
91
- log_1.default.withTick(`Unlinking account-wide variable ${chalk_1.default.bold(name)} on project ${chalk_1.default.bold(projectDisplayName)}.`);
92
- await (0, variableUtils_1.performForEnvironmentsAsync)(environments, async (environment) => {
93
- await EnvironmentVariableMutation_1.EnvironmentVariableMutation.unlinkSharedEnvironmentVariableAsync(graphqlClient, existingVariable.id, projectId, environment);
94
- });
95
- }
96
80
  }
97
81
  const variable = overwrite && existingVariable
98
82
  ? await EnvironmentVariableMutation_1.EnvironmentVariableMutation.updateAsync(graphqlClient, {
@@ -153,13 +137,6 @@ class EnvCreate extends EasCommand_1.default {
153
137
  throw new Error(`Could not create variable with name ${name} on account ${ownerAccount.name}`);
154
138
  }
155
139
  log_1.default.withTick(`Created a new variable ${chalk_1.default.bold(name)} on account ${chalk_1.default.bold(ownerAccount.name)}.`);
156
- if (link) {
157
- log_1.default.withTick(`Linking account-wide variable ${chalk_1.default.bold(name)} to project ${chalk_1.default.bold(projectDisplayName)}.`);
158
- await (0, variableUtils_1.performForEnvironmentsAsync)(environments, async (environment) => {
159
- await EnvironmentVariableMutation_1.EnvironmentVariableMutation.linkSharedEnvironmentVariableAsync(graphqlClient, variable.id, projectId, environment);
160
- });
161
- log_1.default.withTick(`Linked account-wide variable ${chalk_1.default.bold(name)} to project ${chalk_1.default.bold(projectDisplayName)}.`);
162
- }
163
140
  }
164
141
  }
165
142
  async promptForOverwriteAsync({ nonInteractive, force, message, suggestion, }) {
@@ -231,7 +208,6 @@ class EnvCreate extends EasCommand_1.default {
231
208
  value,
232
209
  environment: newEnvironments,
233
210
  visibility: newVisibility,
234
- link: rest.link ?? false,
235
211
  force: rest.force ?? false,
236
212
  'non-interactive': nonInteractive,
237
213
  type: newType,
@@ -240,9 +216,6 @@ class EnvCreate extends EasCommand_1.default {
240
216
  };
241
217
  }
242
218
  sanitizeFlags(flags) {
243
- if (flags.scope !== 'account' && flags.link) {
244
- throw new Error(`Unexpected argument: --link can only be used when creating account-wide variables`);
245
- }
246
219
  return {
247
220
  ...flags,
248
221
  scope: flags.scope === 'account'
@@ -2,31 +2,13 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const chalk_1 = tslib_1.__importDefault(require("chalk"));
5
- const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
6
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
7
6
  const flags_1 = require("../../commandUtils/flags");
8
- const client_1 = require("../../graphql/client");
9
- const BackgroundJobReceipt_1 = require("../../graphql/types/BackgroundJobReceipt");
10
7
  const log_1 = tslib_1.__importDefault(require("../../log"));
11
8
  const prompts_1 = require("../../prompts");
9
+ const delete_1 = require("../../update/delete");
12
10
  const json_1 = require("../../utils/json");
13
11
  const pollForBackgroundJobReceiptAsync_1 = require("../../utils/pollForBackgroundJobReceiptAsync");
14
- async function scheduleUpdateGroupDeletionAsync(graphqlClient, { group, }) {
15
- const result = await (0, client_1.withErrorHandlingAsync)(graphqlClient
16
- .mutation((0, graphql_tag_1.default) `
17
- mutation ScheduleUpdateGroupDeletion($group: ID!) {
18
- update {
19
- scheduleUpdateGroupDeletion(group: $group) {
20
- id
21
- ...BackgroundJobReceiptData
22
- }
23
- }
24
- }
25
- ${BackgroundJobReceipt_1.BackgroundJobReceiptNode}
26
- `, { group })
27
- .toPromise());
28
- return result.update.scheduleUpdateGroupDeletion;
29
- }
30
12
  class UpdateDelete extends EasCommand_1.default {
31
13
  static description = 'delete all the updates in an update group';
32
14
  static args = [
@@ -61,7 +43,7 @@ class UpdateDelete extends EasCommand_1.default {
61
43
  return;
62
44
  }
63
45
  }
64
- const receipt = await scheduleUpdateGroupDeletionAsync(graphqlClient, { group });
46
+ const receipt = await (0, delete_1.scheduleUpdateGroupDeletionAsync)(graphqlClient, { group });
65
47
  const successfulReceipt = await (0, pollForBackgroundJobReceiptAsync_1.pollForBackgroundJobReceiptAsync)(graphqlClient, receipt);
66
48
  log_1.default.debug('Deletion result', { successfulReceipt });
67
49
  if (jsonFlag) {
@@ -2,19 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const core_1 = require("@oclif/core");
5
- const queries_1 = require("../../branch/queries");
6
- const queries_2 = require("../../channel/queries");
7
5
  const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
6
  const flags_1 = require("../../commandUtils/flags");
9
- const pagination_1 = require("../../commandUtils/pagination");
10
7
  const BranchQuery_1 = require("../../graphql/queries/BranchQuery");
11
- const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
12
8
  const log_1 = tslib_1.__importDefault(require("../../log"));
13
- const prompts_1 = require("../../prompts");
14
9
  const getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync_1 = require("../../update/getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync");
15
- const queries_3 = require("../../update/queries");
16
10
  const republish_1 = require("../../update/republish");
17
- const utils_1 = require("../../update/utils");
18
11
  const code_signing_1 = require("../../utils/code-signing");
19
12
  const json_1 = require("../../utils/json");
20
13
  const defaultRepublishPlatforms = ['android', 'ios'];
@@ -79,7 +72,7 @@ class UpdateRepublish extends EasCommand_1.default {
79
72
  (0, json_1.enableJsonOutput)();
80
73
  }
81
74
  const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(exp, flags.privateKeyPath);
82
- const existingUpdates = await getOrAskUpdatesAsync(graphqlClient, projectId, flags);
75
+ const existingUpdates = await (0, republish_1.getUpdateGroupOrAskForUpdateGroupAsync)(graphqlClient, projectId, flags);
83
76
  const updatesToPublish = existingUpdates.filter(update => flags.platform.includes(update.platform));
84
77
  if (existingUpdates.length === 0) {
85
78
  throw new Error(`There are no published updates found`);
@@ -99,7 +92,7 @@ class UpdateRepublish extends EasCommand_1.default {
99
92
  }
100
93
  const arbitraryUpdate = updatesToPublish[0];
101
94
  const targetBranch = await getOrAskTargetBranchAsync(graphqlClient, projectId, flags, arbitraryUpdate);
102
- const updateMessage = await getOrAskUpdateMessageAsync(updatesToPublish, flags);
95
+ const updateMessage = await (0, republish_1.getOrAskUpdateMessageAsync)(updatesToPublish, flags);
103
96
  await (0, republish_1.republishAsync)({
104
97
  graphqlClient,
105
98
  app: { exp, projectId },
@@ -155,133 +148,3 @@ async function getOrAskTargetBranchAsync(graphqlClient, projectId, flags, arbitr
155
148
  // if neither provided, assume republish on same branch
156
149
  return { branchId: arbitraryUpdate.branchId, branchName: arbitraryUpdate.branchName };
157
150
  }
158
- /** Retrieve the update group from either the update group id, or select from branch name. */
159
- async function getOrAskUpdatesAsync(graphqlClient, projectId, flags) {
160
- if (flags.groupId) {
161
- const updateGroup = await UpdateQuery_1.UpdateQuery.viewUpdateGroupAsync(graphqlClient, {
162
- groupId: flags.groupId,
163
- });
164
- return updateGroup.map(update => ({
165
- ...update,
166
- groupId: update.group,
167
- branchId: update.branch.id,
168
- branchName: update.branch.name,
169
- }));
170
- }
171
- if (flags.nonInteractive) {
172
- throw new Error('Must supply --group when in non-interactive mode');
173
- }
174
- if (flags.branchName) {
175
- return await askUpdatesFromBranchNameAsync(graphqlClient, {
176
- ...flags,
177
- branchName: flags.branchName,
178
- projectId,
179
- });
180
- }
181
- if (flags.channelName) {
182
- return await askUpdatesFromChannelNameAsync(graphqlClient, {
183
- ...flags,
184
- channelName: flags.channelName,
185
- projectId,
186
- });
187
- }
188
- const { choice } = await (0, prompts_1.promptAsync)({
189
- type: 'select',
190
- message: 'Find update by branch or channel?',
191
- name: 'choice',
192
- choices: [
193
- { title: 'Branch', value: 'branch' },
194
- { title: 'Channel', value: 'channel' },
195
- ],
196
- });
197
- if (choice === 'channel') {
198
- const { name } = await (0, queries_2.selectChannelOnAppAsync)(graphqlClient, {
199
- projectId,
200
- selectionPromptTitle: 'Select a channel to view',
201
- paginatedQueryOptions: {
202
- json: flags.json,
203
- nonInteractive: flags.nonInteractive,
204
- offset: 0,
205
- },
206
- });
207
- return await askUpdatesFromChannelNameAsync(graphqlClient, {
208
- ...flags,
209
- channelName: name,
210
- projectId,
211
- });
212
- }
213
- else if (choice === 'branch') {
214
- const { name } = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
215
- projectId,
216
- promptTitle: 'Select branch from which to choose update',
217
- displayTextForListItem: updateBranch => ({
218
- title: updateBranch.name,
219
- }),
220
- // discard limit and offset because this query is not their intended target
221
- paginatedQueryOptions: {
222
- json: flags.json,
223
- nonInteractive: flags.nonInteractive,
224
- offset: 0,
225
- },
226
- });
227
- return await askUpdatesFromBranchNameAsync(graphqlClient, {
228
- ...flags,
229
- branchName: name,
230
- projectId,
231
- });
232
- }
233
- else {
234
- throw new Error('Must choose update via channel or branch');
235
- }
236
- }
237
- /** Ask the user which update needs to be republished by branch name, this requires interactive mode */
238
- async function askUpdatesFromBranchNameAsync(graphqlClient, { projectId, branchName, json, nonInteractive, }) {
239
- const updateGroup = await (0, queries_3.selectUpdateGroupOnBranchAsync)(graphqlClient, {
240
- projectId,
241
- branchName,
242
- paginatedQueryOptions: (0, pagination_1.getPaginatedQueryOptions)({ json, 'non-interactive': nonInteractive }),
243
- });
244
- return updateGroup.map(update => ({
245
- ...update,
246
- groupId: update.group,
247
- branchId: update.branch.id,
248
- branchName: update.branch.name,
249
- }));
250
- }
251
- /** Ask the user which update needs to be republished by channel name, this requires interactive mode */
252
- async function askUpdatesFromChannelNameAsync(graphqlClient, { projectId, channelName, json, nonInteractive, }) {
253
- const { branchName } = await (0, getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync_1.getBranchFromChannelNameAndCreateAndLinkIfNotExistsAsync)(graphqlClient, projectId, channelName);
254
- return await askUpdatesFromBranchNameAsync(graphqlClient, {
255
- projectId,
256
- branchName,
257
- json,
258
- nonInteractive,
259
- });
260
- }
261
- /** Get or ask the user for the update (group) message for the republish */
262
- async function getOrAskUpdateMessageAsync(updates, flags) {
263
- if (flags.updateMessage) {
264
- return sanitizeUpdateMessage(flags.updateMessage);
265
- }
266
- if (flags.nonInteractive || flags.json) {
267
- throw new Error('Must supply --message when in non-interactive mode');
268
- }
269
- // This command only uses a single update group to republish, meaning these values are always identical
270
- const oldGroupId = updates[0].groupId;
271
- const oldUpdateMessage = updates[0].message;
272
- const { updateMessage } = await (0, prompts_1.promptAsync)({
273
- type: 'text',
274
- name: 'updateMessage',
275
- message: 'Provide an update message.',
276
- initial: `Republish "${oldUpdateMessage}" - group: ${oldGroupId}`,
277
- validate: (value) => (value ? true : 'Update message may not be empty.'),
278
- });
279
- return sanitizeUpdateMessage(updateMessage);
280
- }
281
- function sanitizeUpdateMessage(updateMessage) {
282
- if (updateMessage !== (0, utils_1.truncateString)(updateMessage, 1024)) {
283
- log_1.default.warn('Update message exceeds the allowed 1024 character limit, truncated update message.');
284
- return (0, utils_1.truncateString)(updateMessage, 1024);
285
- }
286
- return updateMessage;
287
- }
@@ -0,0 +1,24 @@
1
+ import EasCommand from '../../commandUtils/EasCommand';
2
+ export declare function nonNullish<TValue>(value: TValue | null | undefined): value is NonNullable<TValue>;
3
+ export default class UpdateRevertUpdateRollout extends EasCommand {
4
+ static description: string;
5
+ static flags: {
6
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
+ 'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
8
+ channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
9
+ branch: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
10
+ group: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
11
+ message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
12
+ 'private-key-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
13
+ };
14
+ static contextDefinition: {
15
+ vcsClient: import("../../commandUtils/context/VcsClientContextField").default;
16
+ loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
17
+ privateProjectConfig: import("../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
18
+ };
19
+ runAsync(): Promise<void>;
20
+ private deleteRolloutUpdateGroupAndRepublishControlUpdatesAsync;
21
+ private deleteRolloutUpdateGroupAndPublishRollBackToEmbeddedAsync;
22
+ private deleteRolloutUpdateGroupAsync;
23
+ private sanitizeFlags;
24
+ }
@@ -0,0 +1,264 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.nonNullish = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const core_1 = require("@oclif/core");
6
+ const assert_1 = tslib_1.__importDefault(require("assert"));
7
+ const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
8
+ const flags_1 = require("../../commandUtils/flags");
9
+ const log_1 = tslib_1.__importStar(require("../../log"));
10
+ const projectUtils_1 = require("../../project/projectUtils");
11
+ const publish_1 = require("../../project/publish");
12
+ const prompts_1 = require("../../prompts");
13
+ const delete_1 = require("../../update/delete");
14
+ const republish_1 = require("../../update/republish");
15
+ const roll_back_to_embedded_1 = require("../../update/roll-back-to-embedded");
16
+ const code_signing_1 = require("../../utils/code-signing");
17
+ const json_1 = require("../../utils/json");
18
+ const pollForBackgroundJobReceiptAsync_1 = require("../../utils/pollForBackgroundJobReceiptAsync");
19
+ function nonNullish(value) {
20
+ return value !== null && value !== undefined;
21
+ }
22
+ exports.nonNullish = nonNullish;
23
+ class UpdateRevertUpdateRollout extends EasCommand_1.default {
24
+ static description = 'revert a rollout update for a project';
25
+ static flags = {
26
+ channel: core_1.Flags.string({
27
+ description: 'Channel name to select an update group to revert the rollout update from',
28
+ exclusive: ['branch', 'group'],
29
+ }),
30
+ branch: core_1.Flags.string({
31
+ description: 'Branch name to select an update group to revert the rollout update from',
32
+ exclusive: ['channel', 'group'],
33
+ }),
34
+ group: core_1.Flags.string({
35
+ description: 'Rollout update group ID to revert',
36
+ exclusive: ['branch', 'channel'],
37
+ }),
38
+ message: core_1.Flags.string({
39
+ char: 'm',
40
+ description: 'Short message describing the revert',
41
+ required: false,
42
+ }),
43
+ 'private-key-path': core_1.Flags.string({
44
+ description: `File containing the PEM-encoded private key corresponding to the certificate in expo-updates' configuration. Defaults to a file named "private-key.pem" in the certificate's directory. Only relevant if you are using code signing: https://docs.expo.dev/eas-update/code-signing/`,
45
+ required: false,
46
+ }),
47
+ ...flags_1.EasNonInteractiveAndJsonFlags,
48
+ };
49
+ static contextDefinition = {
50
+ ...this.ContextOptions.ProjectConfig,
51
+ ...this.ContextOptions.LoggedIn,
52
+ ...this.ContextOptions.Vcs,
53
+ };
54
+ async runAsync() {
55
+ const { flags: rawFlags } = await this.parse(UpdateRevertUpdateRollout);
56
+ const flags = this.sanitizeFlags(rawFlags);
57
+ const { privateProjectConfig: { exp, projectId, projectDir }, loggedIn: { graphqlClient }, vcsClient, } = await this.getContextAsync(UpdateRevertUpdateRollout, {
58
+ nonInteractive: flags.nonInteractive,
59
+ withServerSideEnvironment: null,
60
+ });
61
+ if (flags.json) {
62
+ (0, json_1.enableJsonOutput)();
63
+ }
64
+ const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(exp, flags.privateKeyPath);
65
+ let updateGroupToRepublish;
66
+ if (flags.groupId) {
67
+ const updateGroup = await (0, republish_1.getUpdateGroupAsync)(graphqlClient, flags.groupId);
68
+ if (!updateGroupIsRolloutUpdateGroup(updateGroup)) {
69
+ throw new Error(`The update group with ID "${flags.groupId}" is not a rollout update group.`);
70
+ }
71
+ updateGroupToRepublish = updateGroup;
72
+ }
73
+ else {
74
+ const latestUpdateGroupForEachPublishPlatform = await (0, republish_1.askUpdateGroupForEachPublishPlatformFilteringByRuntimeVersionAsync)(graphqlClient, projectId, flags);
75
+ const uniqueUpdateGroups = getUniqueUpdateGroups(Object.values(latestUpdateGroupForEachPublishPlatform).filter(nonNullish));
76
+ const rolloutUpdateGroups = uniqueUpdateGroups.filter(updateGroupIsRolloutUpdateGroup);
77
+ if (rolloutUpdateGroups.length === 0) {
78
+ throw new Error(`No rollout update groups found.`);
79
+ }
80
+ if (rolloutUpdateGroups.length === 1) {
81
+ updateGroupToRepublish = rolloutUpdateGroups[0];
82
+ }
83
+ else {
84
+ const { choice: chosenId } = await (0, prompts_1.promptAsync)({
85
+ type: 'select',
86
+ message: 'Which rollout update group would you like to revert?',
87
+ name: 'choice',
88
+ choices: rolloutUpdateGroups.map(ug => ({
89
+ title: `Rollout update group ID: ${ug[0].groupId}, Platform: ${ug[0].platform}, Rollout Percentage: ${ug[0].rolloutPercentage}`,
90
+ value: ug[0].groupId,
91
+ })),
92
+ });
93
+ if (!chosenId) {
94
+ throw new Error('No rollout update group selected.');
95
+ }
96
+ const chosenUpdateGroup = rolloutUpdateGroups.find(ug => ug[0].groupId === chosenId);
97
+ if (!chosenUpdateGroup) {
98
+ throw new Error('No rollout update group selected.');
99
+ }
100
+ updateGroupToRepublish = chosenUpdateGroup;
101
+ }
102
+ }
103
+ const rolloutUpdateGroupWithControlUpdates = updateGroupIsUpdateGroupWithControlUpdate(updateGroupToRepublish)
104
+ ? updateGroupToRepublish
105
+ : null;
106
+ if (rolloutUpdateGroupWithControlUpdates) {
107
+ await this.deleteRolloutUpdateGroupAndRepublishControlUpdatesAsync({
108
+ graphqlClient,
109
+ exp,
110
+ projectId,
111
+ rolloutUpdateGroupWithControlUpdates,
112
+ codeSigningInfo,
113
+ flags,
114
+ });
115
+ }
116
+ else {
117
+ await this.deleteRolloutUpdateGroupAndPublishRollBackToEmbeddedAsync({
118
+ graphqlClient,
119
+ vcsClient,
120
+ exp,
121
+ projectDir,
122
+ projectId,
123
+ rolloutUpdateGroup: updateGroupToRepublish,
124
+ codeSigningInfo,
125
+ flags,
126
+ });
127
+ }
128
+ }
129
+ async deleteRolloutUpdateGroupAndRepublishControlUpdatesAsync({ graphqlClient, exp, projectId, rolloutUpdateGroupWithControlUpdates, codeSigningInfo, flags, }) {
130
+ const controlUpdateGroupIdsToRepublish = Array.from(new Set(rolloutUpdateGroupWithControlUpdates.map(update => update.rolloutControlUpdate.group)));
131
+ const updateGroupsToRepublish = await Promise.all(controlUpdateGroupIdsToRepublish.map(controlUpdateGroupIdToRepublish => (0, republish_1.getUpdateGroupAsync)(graphqlClient, controlUpdateGroupIdToRepublish)));
132
+ const updateGroupOrGroupsClause = controlUpdateGroupIdsToRepublish.length > 1
133
+ ? `control update groups (IDs: ${controlUpdateGroupIdsToRepublish
134
+ .map(id => `"${id}"`)
135
+ .join(', ')})`
136
+ : `control update group (ID: "${controlUpdateGroupIdsToRepublish[0]}")`;
137
+ if (!flags.nonInteractive) {
138
+ const confirmMessage = `Are you sure you want to revert the rollout update group with ID "${rolloutUpdateGroupWithControlUpdates[0].groupId}"? This will delete the rollout update group and republish the ${updateGroupOrGroupsClause}.`;
139
+ const didConfirm = await (0, prompts_1.confirmAsync)({ message: confirmMessage });
140
+ if (!didConfirm) {
141
+ throw new Error('Aborting...');
142
+ }
143
+ }
144
+ const updateMessages = [];
145
+ for (const updateGroup of updateGroupsToRepublish) {
146
+ updateMessages.push(await (0, republish_1.getOrAskUpdateMessageAsync)(updateGroup, flags));
147
+ }
148
+ // assert all updateGroupsToRepublish have the same branch name and id
149
+ const branchNames = updateGroupsToRepublish.flatMap(updateGroup => updateGroup[0].branchName);
150
+ const branchIds = updateGroupsToRepublish.map(updateGroup => updateGroup[0].branchId);
151
+ (0, assert_1.default)(branchNames.every(name => name === branchNames[0]), 'All update groups being republished must belong to the same branch.');
152
+ (0, assert_1.default)(branchIds.every(id => id === branchIds[0]), 'All update groups being republished must belong to the same branch.');
153
+ const targetBranch = {
154
+ branchName: branchNames[0],
155
+ branchId: branchIds[0],
156
+ };
157
+ await this.deleteRolloutUpdateGroupAsync({
158
+ graphqlClient,
159
+ rolloutUpdateGroup: rolloutUpdateGroupWithControlUpdates,
160
+ });
161
+ for (let i = 0; i < updateGroupsToRepublish.length; i++) {
162
+ const updateGroupToRepublish = updateGroupsToRepublish[i];
163
+ const updateMessage = updateMessages[i];
164
+ await (0, republish_1.republishAsync)({
165
+ graphqlClient,
166
+ app: { exp, projectId },
167
+ updatesToPublish: updateGroupToRepublish,
168
+ targetBranch,
169
+ updateMessage,
170
+ codeSigningInfo,
171
+ json: flags.json,
172
+ });
173
+ }
174
+ }
175
+ async deleteRolloutUpdateGroupAndPublishRollBackToEmbeddedAsync({ graphqlClient, vcsClient, exp, projectDir, projectId, rolloutUpdateGroup, codeSigningInfo, flags, }) {
176
+ const rolloutUpdateGroupId = rolloutUpdateGroup[0].groupId;
177
+ if (!flags.nonInteractive) {
178
+ const confirmMessage = `Are you sure you want to revert the rollout update group with ID "${rolloutUpdateGroupId}"? This will delete the rollout update group and publish a new roll-back-to-embedded update (no control update to roll back to), whose behavior may not be a true revert depending on the previous state of the branch. ${(0, log_1.learnMore)('https://expo.fyi/eas-update-update-rollouts', { learnMoreMessage: 'More info' })})`;
179
+ const didConfirm = await (0, prompts_1.confirmAsync)({ message: confirmMessage });
180
+ if (!didConfirm) {
181
+ throw new Error('Aborting...');
182
+ }
183
+ }
184
+ // check that the expo-updates package version supports roll back to embedded
185
+ await (0, projectUtils_1.enforceRollBackToEmbeddedUpdateSupportAsync)(projectDir);
186
+ const updateMessage = await (0, publish_1.getUpdateMessageForCommandAsync)(vcsClient, {
187
+ updateMessageArg: flags.updateMessage,
188
+ autoFlag: false,
189
+ nonInteractive: flags.nonInteractive,
190
+ jsonFlag: flags.json,
191
+ });
192
+ await this.deleteRolloutUpdateGroupAsync({
193
+ graphqlClient,
194
+ rolloutUpdateGroup,
195
+ });
196
+ const platforms = rolloutUpdateGroup.map(update => update.platform);
197
+ const runtimeVersion = rolloutUpdateGroup[0].runtimeVersion;
198
+ const targetBranch = {
199
+ name: rolloutUpdateGroup[0].branchName,
200
+ id: rolloutUpdateGroup[0].branchId,
201
+ };
202
+ await (0, roll_back_to_embedded_1.publishRollBackToEmbeddedUpdateAsync)({
203
+ graphqlClient,
204
+ projectId,
205
+ exp,
206
+ updateMessage,
207
+ branch: targetBranch,
208
+ codeSigningInfo,
209
+ platforms,
210
+ runtimeVersion,
211
+ json: flags.json,
212
+ });
213
+ }
214
+ async deleteRolloutUpdateGroupAsync({ graphqlClient, rolloutUpdateGroup, }) {
215
+ const rolloutUpdateGroupId = rolloutUpdateGroup[0].groupId;
216
+ const updateGroupDeletionReceipt = await (0, delete_1.scheduleUpdateGroupDeletionAsync)(graphqlClient, {
217
+ group: rolloutUpdateGroupId,
218
+ });
219
+ const successfulReceipt = await (0, pollForBackgroundJobReceiptAsync_1.pollForBackgroundJobReceiptAsync)(graphqlClient, updateGroupDeletionReceipt);
220
+ log_1.default.debug('Rollout update group deletion result', { successfulReceipt });
221
+ }
222
+ sanitizeFlags(rawFlags) {
223
+ const branchName = rawFlags.branch;
224
+ const channelName = rawFlags.channel;
225
+ const groupId = rawFlags.group;
226
+ const nonInteractive = rawFlags['non-interactive'];
227
+ const privateKeyPath = rawFlags['private-key-path'];
228
+ if (nonInteractive && !groupId) {
229
+ throw new Error('Only --group can be used in non-interactive mode');
230
+ }
231
+ return {
232
+ branchName,
233
+ channelName,
234
+ groupId,
235
+ updateMessage: rawFlags.message,
236
+ privateKeyPath,
237
+ json: rawFlags.json ?? false,
238
+ nonInteractive,
239
+ };
240
+ }
241
+ }
242
+ exports.default = UpdateRevertUpdateRollout;
243
+ function getUniqueUpdateGroups(updateGroups) {
244
+ const uniqueUpdateGroups = new Map();
245
+ for (const updateGroup of updateGroups) {
246
+ const groupId = updateGroup[0].groupId;
247
+ if (!uniqueUpdateGroups.has(groupId)) {
248
+ uniqueUpdateGroups.set(groupId, updateGroup);
249
+ }
250
+ }
251
+ return Array.from(uniqueUpdateGroups.values());
252
+ }
253
+ function updateGroupIsRolloutUpdateGroup(updateGroup) {
254
+ return updateGroup.every(updateIsRolloutUpdate);
255
+ }
256
+ function updateIsRolloutUpdate(updateGroup) {
257
+ return updateGroup.rolloutPercentage !== undefined && updateGroup.rolloutPercentage !== null;
258
+ }
259
+ function updateGroupIsUpdateGroupWithControlUpdate(updateGroup) {
260
+ return updateGroup.every(updateIsRolloutWithControlUpdate);
261
+ }
262
+ function updateIsRolloutWithControlUpdate(updateGroup) {
263
+ return !!updateGroup.rolloutControlUpdate;
264
+ }
@@ -18,7 +18,5 @@ export default class UpdateRollBackToEmbedded extends EasCommand {
18
18
  getDynamicPrivateProjectConfigAsync: import("../../commandUtils/context/DynamicProjectConfigContextField").DynamicPrivateProjectConfigContextField;
19
19
  };
20
20
  runAsync(): Promise<void>;
21
- private publishRollbacksAsync;
22
- private static selectRuntimeAsync;
23
21
  private sanitizeFlags;
24
22
  }