eas-cli 3.18.0 → 3.18.2

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 (63) hide show
  1. package/README.md +55 -55
  2. package/build/branch/actions/SelectBranch.d.ts +33 -0
  3. package/build/branch/actions/SelectBranch.js +67 -0
  4. package/build/build/local.js +1 -1
  5. package/build/build/runBuildAndSubmit.js +0 -2
  6. package/build/channel/actions/SelectChannel.d.ts +16 -0
  7. package/build/channel/actions/SelectChannel.js +42 -24
  8. package/build/channel/branch-mapping.d.ts +17 -1
  9. package/build/channel/branch-mapping.js +48 -7
  10. package/build/channel/queries.d.ts +1 -6
  11. package/build/channel/queries.js +1 -25
  12. package/build/commands/build/version/get.js +0 -1
  13. package/build/commands/build/version/sync.js +0 -1
  14. package/build/commands/channel/rollout-preview.d.ts +32 -0
  15. package/build/commands/channel/rollout-preview.js +109 -0
  16. package/build/commands/metadata/lint.js +0 -1
  17. package/build/commands/metadata/pull.js +0 -1
  18. package/build/commands/metadata/push.js +0 -1
  19. package/build/commands/submit.js +0 -1
  20. package/build/commands/update/republish.js +23 -74
  21. package/build/graphql/generated.d.ts +192 -18
  22. package/build/graphql/generated.js +8 -2
  23. package/build/graphql/queries/BranchQuery.d.ts +3 -2
  24. package/build/graphql/queries/BranchQuery.js +43 -1
  25. package/build/graphql/queries/ChannelQuery.d.ts +3 -2
  26. package/build/graphql/queries/ChannelQuery.js +19 -5
  27. package/build/graphql/queries/RuntimeQuery.d.ts +6 -0
  28. package/build/graphql/queries/RuntimeQuery.js +70 -0
  29. package/build/graphql/types/Runtime.d.ts +1 -0
  30. package/build/graphql/types/Runtime.js +11 -0
  31. package/build/graphql/types/UpdateBranch.js +3 -1
  32. package/build/graphql/types/UpdateBranchBasicInfo.d.ts +1 -0
  33. package/build/graphql/types/UpdateBranchBasicInfo.js +11 -0
  34. package/build/metadata/apple/config/reader.js +1 -1
  35. package/build/rollout/actions/CreateRollout.d.ts +23 -0
  36. package/build/rollout/actions/CreateRollout.js +153 -0
  37. package/build/rollout/actions/EditRollout.d.ts +17 -0
  38. package/build/rollout/actions/EditRollout.js +79 -0
  39. package/build/rollout/actions/EndRollout.d.ts +24 -0
  40. package/build/rollout/actions/EndRollout.js +164 -0
  41. package/build/rollout/actions/ManageRollout.d.ts +24 -0
  42. package/build/rollout/actions/ManageRollout.js +78 -0
  43. package/build/rollout/actions/NonInteractiveRollout.d.ts +18 -0
  44. package/build/rollout/actions/NonInteractiveRollout.js +46 -0
  45. package/build/rollout/actions/RolloutMainMenu.d.ts +28 -0
  46. package/build/rollout/actions/RolloutMainMenu.js +110 -0
  47. package/build/rollout/actions/SelectRollout.js +18 -8
  48. package/build/rollout/actions/SelectRuntime.d.ts +36 -0
  49. package/build/rollout/actions/SelectRuntime.js +167 -0
  50. package/build/rollout/branch-mapping.d.ts +52 -4
  51. package/build/rollout/branch-mapping.js +68 -19
  52. package/build/rollout/utils.d.ts +9 -2
  53. package/build/rollout/utils.js +66 -21
  54. package/build/update/configure.d.ts +6 -1
  55. package/build/update/configure.js +18 -8
  56. package/build/update/republish.d.ts +26 -0
  57. package/build/update/republish.js +83 -0
  58. package/build/utils/profiles.d.ts +1 -2
  59. package/build/utils/profiles.js +3 -35
  60. package/build/utils/relay.d.ts +80 -15
  61. package/build/utils/relay.js +211 -28
  62. package/oclif.manifest.json +1 -1
  63. package/package.json +2 -2
@@ -0,0 +1,28 @@
1
+ import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
2
+ import { UpdateChannelBasicInfoFragment } from '../../graphql/generated';
3
+ import { NonInteractiveOptions as CreateRolloutNonInteractiveOptions } from './CreateRollout';
4
+ import { NonInteractiveOptions as EditRolloutNonInteractiveOptions } from './EditRollout';
5
+ import { NonInteractiveOptions as EndRolloutNonInteractiveOptions } from './EndRollout';
6
+ import { ManageRolloutActions } from './ManageRollout';
7
+ export declare enum MainMenuActions {
8
+ CREATE_NEW = "Create a new rollout",
9
+ MANAGE_EXISTING = "Manage an existing rollout"
10
+ }
11
+ export type RolloutActions = MainMenuActions.CREATE_NEW | ManageRolloutActions.EDIT | ManageRolloutActions.END;
12
+ /**
13
+ * Manage a rollout for the project.
14
+ */
15
+ export declare class RolloutMainMenu implements EASUpdateAction<void> {
16
+ private options;
17
+ constructor(options?: {
18
+ channelName?: string;
19
+ action?: RolloutActions;
20
+ } & Partial<EditRolloutNonInteractiveOptions> & Partial<EndRolloutNonInteractiveOptions> & Partial<CreateRolloutNonInteractiveOptions>);
21
+ runAsync(ctx: EASUpdateContext): Promise<void>;
22
+ runActionAsync(ctx: EASUpdateContext, menuAction: MainMenuActions): Promise<null>;
23
+ selectRolloutAsync(ctx: EASUpdateContext): Promise<UpdateChannelBasicInfoFragment | null>;
24
+ selectChannelAsync(ctx: EASUpdateContext, filterPredicate?: (channelInfo: UpdateChannelBasicInfoFragment) => boolean): Promise<UpdateChannelBasicInfoFragment>;
25
+ resolveChannelNameAsync(ctx: EASUpdateContext, channelName: string): Promise<UpdateChannelBasicInfoFragment>;
26
+ toMainMenuAction(action: RolloutActions): MainMenuActions;
27
+ promptMenuActionAsync(): Promise<MainMenuActions>;
28
+ }
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RolloutMainMenu = exports.MainMenuActions = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const assert_1 = tslib_1.__importDefault(require("assert"));
6
+ const SelectChannel_1 = require("../../channel/actions/SelectChannel");
7
+ const ChannelQuery_1 = require("../../graphql/queries/ChannelQuery");
8
+ const log_1 = tslib_1.__importDefault(require("../../log"));
9
+ const prompts_1 = require("../../prompts");
10
+ const branch_mapping_1 = require("../branch-mapping");
11
+ const CreateRollout_1 = require("./CreateRollout");
12
+ const ManageRollout_1 = require("./ManageRollout");
13
+ const SelectRollout_1 = require("./SelectRollout");
14
+ var MainMenuActions;
15
+ (function (MainMenuActions) {
16
+ MainMenuActions["CREATE_NEW"] = "Create a new rollout";
17
+ MainMenuActions["MANAGE_EXISTING"] = "Manage an existing rollout";
18
+ })(MainMenuActions = exports.MainMenuActions || (exports.MainMenuActions = {}));
19
+ /**
20
+ * Manage a rollout for the project.
21
+ */
22
+ class RolloutMainMenu {
23
+ constructor(options = {}) {
24
+ this.options = options;
25
+ }
26
+ async runAsync(ctx) {
27
+ const { action } = this.options;
28
+ const { nonInteractive } = ctx;
29
+ if (nonInteractive) {
30
+ throw new Error(`rollout main menu cannot be run in non-interactive mode.`);
31
+ }
32
+ const menuOption = action ? this.toMainMenuAction(action) : await this.promptMenuActionAsync();
33
+ await this.runActionAsync(ctx, menuOption);
34
+ }
35
+ async runActionAsync(ctx, menuAction) {
36
+ const { channelName } = this.options;
37
+ switch (menuAction) {
38
+ case MainMenuActions.CREATE_NEW: {
39
+ const channelInfo = channelName
40
+ ? await this.resolveChannelNameAsync(ctx, channelName)
41
+ : await this.selectChannelAsync(ctx, channelInfo => !(0, branch_mapping_1.isRollout)(channelInfo));
42
+ await new CreateRollout_1.CreateRollout(channelInfo, this.options).runAsync(ctx);
43
+ return null;
44
+ }
45
+ case MainMenuActions.MANAGE_EXISTING: {
46
+ const channelInfo = channelName
47
+ ? await this.resolveChannelNameAsync(ctx, channelName)
48
+ : await this.selectRolloutAsync(ctx);
49
+ if (!channelInfo) {
50
+ log_1.default.log('You dont have any rollouts.');
51
+ }
52
+ else {
53
+ (0, assert_1.default)(this.options.action !== MainMenuActions.CREATE_NEW, 'Invalid route for create action');
54
+ const manageAction = await new ManageRollout_1.ManageRollout(channelInfo, {
55
+ ...this.options,
56
+ action: this.options.action,
57
+ callingAction: this,
58
+ }).runAsync(ctx);
59
+ await manageAction.runAsync(ctx);
60
+ }
61
+ return null;
62
+ }
63
+ }
64
+ }
65
+ async selectRolloutAsync(ctx) {
66
+ const selectRollout = new SelectRollout_1.SelectRollout();
67
+ const channelInfo = await selectRollout.runAsync(ctx);
68
+ return channelInfo;
69
+ }
70
+ async selectChannelAsync(ctx, filterPredicate) {
71
+ const selectChannelAction = new SelectChannel_1.SelectChannel({ filterPredicate });
72
+ const channelInfo = await selectChannelAction.runAsync(ctx);
73
+ if (!channelInfo) {
74
+ throw new Error(`You dont have any channels. Create one with <TODO>`);
75
+ }
76
+ return channelInfo;
77
+ }
78
+ async resolveChannelNameAsync(ctx, channelName) {
79
+ const { graphqlClient, app } = ctx;
80
+ return await ChannelQuery_1.ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
81
+ appId: app.projectId,
82
+ channelName,
83
+ });
84
+ }
85
+ toMainMenuAction(action) {
86
+ if (action === MainMenuActions.CREATE_NEW) {
87
+ return MainMenuActions.CREATE_NEW;
88
+ }
89
+ else if (action === ManageRollout_1.ManageRolloutActions.EDIT || action === ManageRollout_1.ManageRolloutActions.END) {
90
+ return MainMenuActions.MANAGE_EXISTING;
91
+ }
92
+ else {
93
+ throw new Error(`Action not supported yet: ` + action);
94
+ }
95
+ }
96
+ async promptMenuActionAsync() {
97
+ const menuOptions = [MainMenuActions.CREATE_NEW, MainMenuActions.MANAGE_EXISTING];
98
+ const { menuOption: selectedMenuOption } = await (0, prompts_1.promptAsync)({
99
+ type: 'select',
100
+ name: 'menuOption',
101
+ message: `What would you like to do?`,
102
+ choices: menuOptions.map(menuOption => ({
103
+ value: menuOption,
104
+ title: menuOption,
105
+ })),
106
+ });
107
+ return selectedMenuOption;
108
+ }
109
+ }
110
+ exports.RolloutMainMenu = RolloutMainMenu;
@@ -2,22 +2,32 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SelectRollout = void 0;
4
4
  const SelectChannel_1 = require("../../channel/actions/SelectChannel");
5
- const branch_mapping_1 = require("../../channel/branch-mapping");
6
- const branch_mapping_2 = require("../branch-mapping");
5
+ const ora_1 = require("../../ora");
6
+ const branch_mapping_1 = require("../branch-mapping");
7
7
  /**
8
8
  * Select an existing rollout for the project.
9
9
  */
10
10
  class SelectRollout {
11
11
  async runAsync(ctx) {
12
- const rolloutFilterPredicate = (channelInfo) => {
13
- const branchMapping = (0, branch_mapping_1.getBranchMapping)(channelInfo.branchMapping);
14
- return (0, branch_mapping_2.isRollout)(branchMapping);
12
+ let assetSpinner = null;
13
+ const afterEachFilterQuery = (_externalQueryParams, totalNodesFetched, _dataset, willFetchAgain) => {
14
+ if (willFetchAgain && !assetSpinner) {
15
+ assetSpinner = (0, ora_1.ora)().start('Fetching channels...');
16
+ }
17
+ if (assetSpinner) {
18
+ assetSpinner.text = `Fetched ${totalNodesFetched} channels`;
19
+ }
15
20
  };
16
- const selectChannel = new SelectChannel_1.SelectChannel({
21
+ const selectChannelAction = new SelectChannel_1.SelectChannel({
17
22
  printedType: 'rollout',
18
- filterPredicate: rolloutFilterPredicate,
23
+ filterPredicate: branch_mapping_1.isRollout,
24
+ afterEachFilterQuery,
19
25
  });
20
- return await selectChannel.runAsync(ctx);
26
+ const channelInfo = await selectChannelAction.runAsync(ctx);
27
+ if (assetSpinner) {
28
+ assetSpinner.succeed(`Fetched all channels`);
29
+ }
30
+ return channelInfo;
21
31
  }
22
32
  }
23
33
  exports.SelectRollout = SelectRollout;
@@ -0,0 +1,36 @@
1
+ import { ExpoGraphqlClient } from '../../commandUtils/context/contextUtils/createGraphqlClient';
2
+ import { EASUpdateAction, EASUpdateContext } from '../../eas-update/utils';
3
+ import { RuntimeFragment, UpdateBranchBasicInfoFragment } from '../../graphql/generated';
4
+ import { Connection } from '../../utils/relay';
5
+ /**
6
+ * Select a runtime from a branch
7
+ */
8
+ export declare class SelectRuntime implements EASUpdateAction<string> {
9
+ private branchInfo;
10
+ private options;
11
+ private printedType;
12
+ constructor(branchInfo: UpdateBranchBasicInfoFragment, options?: {
13
+ anotherBranchToIntersectRuntimesBy?: UpdateBranchBasicInfoFragment;
14
+ });
15
+ warnNoRuntime(): void;
16
+ formatCantFindRuntime(): string;
17
+ runAsync(ctx: EASUpdateContext): Promise<string>;
18
+ getNewestRuntimeAsync(graphqlClient: ExpoGraphqlClient, { appId, branchName, anotherBranchIdToIntersectRuntimesBy, }: {
19
+ appId: string;
20
+ branchName: string;
21
+ anotherBranchIdToIntersectRuntimesBy?: string;
22
+ }): Promise<Connection<RuntimeFragment> | null>;
23
+ displayLatestUpdateGroupAsync({ graphqlClient, appId, branchName, runtime, }: {
24
+ graphqlClient: ExpoGraphqlClient;
25
+ appId: string;
26
+ branchName: string;
27
+ runtime: RuntimeFragment;
28
+ }): Promise<string>;
29
+ selectRuntimesAsync(graphqlClient: ExpoGraphqlClient, { appId, branchName, anotherBranchIdToIntersectRuntimesBy, batchSize, }: {
30
+ appId: string;
31
+ branchName: string;
32
+ anotherBranchIdToIntersectRuntimesBy?: string;
33
+ batchSize?: number;
34
+ }): Promise<RuntimeFragment | null>;
35
+ promptForRuntimeAsync(): Promise<string>;
36
+ }
@@ -0,0 +1,167 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SelectRuntime = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const assert_1 = tslib_1.__importDefault(require("assert"));
6
+ const chalk_1 = tslib_1.__importDefault(require("chalk"));
7
+ const utils_1 = require("../../eas-update/utils");
8
+ const RuntimeQuery_1 = require("../../graphql/queries/RuntimeQuery");
9
+ const UpdateQuery_1 = require("../../graphql/queries/UpdateQuery");
10
+ const log_1 = tslib_1.__importStar(require("../../log"));
11
+ const prompts_1 = require("../../prompts");
12
+ const relay_1 = require("../../utils/relay");
13
+ const utils_2 = require("../utils");
14
+ function beginSentence(phrase) {
15
+ if (typeof phrase !== 'string' || phrase.length === 0) {
16
+ return phrase; // Return the input without any modification if it's not a string or empty
17
+ }
18
+ return phrase.charAt(0).toUpperCase() + phrase.slice(1);
19
+ }
20
+ /**
21
+ * Select a runtime from a branch
22
+ */
23
+ class SelectRuntime {
24
+ constructor(branchInfo, options = {}) {
25
+ this.branchInfo = branchInfo;
26
+ this.options = options;
27
+ this.printedType = options.anotherBranchToIntersectRuntimesBy
28
+ ? `compatible runtime`
29
+ : `runtime`;
30
+ }
31
+ warnNoRuntime() {
32
+ if (this.options.anotherBranchToIntersectRuntimesBy) {
33
+ const intersectBranchName = this.options.anotherBranchToIntersectRuntimesBy.name;
34
+ log_1.default.warn(`⚠️ Branches ${this.branchInfo.name} and ${intersectBranchName} dont have any updates with the same runtime.`);
35
+ // TODO(quin): write a learn more
36
+ log_1.default.warn(`Your updates could be misconfigured. ${(0, log_1.learnMore)('https://expo.fyi/todo')}`);
37
+ }
38
+ else {
39
+ // no runtime on branch means no updates published on branch
40
+ log_1.default.warn(`⚠️ There are no updates published on branch ${this.branchInfo.name}.`);
41
+ }
42
+ }
43
+ formatCantFindRuntime() {
44
+ return `🕵️ Not finding the update you were looking for? ${(0, log_1.learnMore)('https://expo.fyi/todo')}`;
45
+ }
46
+ async runAsync(ctx) {
47
+ var _a, _b;
48
+ const { nonInteractive, graphqlClient, app } = ctx;
49
+ const { projectId } = app;
50
+ if (nonInteractive) {
51
+ throw new utils_1.NonInteractiveError(`runtime selection cannot be run in non-interactive mode.`);
52
+ }
53
+ const newestRuntimeConnection = await this.getNewestRuntimeAsync(graphqlClient, {
54
+ appId: projectId,
55
+ branchName: this.branchInfo.name,
56
+ anotherBranchIdToIntersectRuntimesBy: (_a = this.options.anotherBranchToIntersectRuntimesBy) === null || _a === void 0 ? void 0 : _a.id,
57
+ });
58
+ if (!newestRuntimeConnection) {
59
+ log_1.default.addNewLineIfNone();
60
+ this.warnNoRuntime();
61
+ return await this.promptForRuntimeAsync();
62
+ }
63
+ const moreThanOneRuntime = newestRuntimeConnection.edges.length > 1;
64
+ log_1.default.log(`✅ ${beginSentence(this.printedType)}${moreThanOneRuntime ? 's' : ''} detected`);
65
+ if (!moreThanOneRuntime) {
66
+ const runtime = newestRuntimeConnection.edges[0].node;
67
+ const formattedRuntimeWithGroup = await this.displayLatestUpdateGroupAsync({
68
+ graphqlClient,
69
+ appId: projectId,
70
+ branchName: this.branchInfo.name,
71
+ runtime,
72
+ });
73
+ log_1.default.addNewLineIfNone();
74
+ log_1.default.log(formattedRuntimeWithGroup);
75
+ log_1.default.addNewLineIfNone();
76
+ const useRuntime = await (0, prompts_1.confirmAsync)({
77
+ message: `Target ${this.printedType} ${chalk_1.default.bold(runtime.version)}?`,
78
+ });
79
+ if (useRuntime) {
80
+ return runtime.version;
81
+ }
82
+ else {
83
+ log_1.default.newLine();
84
+ log_1.default.warn(this.formatCantFindRuntime());
85
+ return await this.promptForRuntimeAsync();
86
+ }
87
+ }
88
+ log_1.default.log(this.formatCantFindRuntime());
89
+ const selectedRuntime = await this.selectRuntimesAsync(graphqlClient, {
90
+ appId: projectId,
91
+ branchName: this.branchInfo.name,
92
+ anotherBranchIdToIntersectRuntimesBy: (_b = this.options.anotherBranchToIntersectRuntimesBy) === null || _b === void 0 ? void 0 : _b.id,
93
+ });
94
+ if (!selectedRuntime) {
95
+ throw new Error(`No ${this.printedType} selected`);
96
+ }
97
+ return selectedRuntime.version;
98
+ }
99
+ async getNewestRuntimeAsync(graphqlClient, { appId, branchName, anotherBranchIdToIntersectRuntimesBy, }) {
100
+ const connection = await RuntimeQuery_1.RuntimeQuery.getRuntimesOnBranchAsync(graphqlClient, {
101
+ appId,
102
+ name: branchName,
103
+ first: 1,
104
+ filter: {
105
+ branchId: anotherBranchIdToIntersectRuntimesBy,
106
+ },
107
+ });
108
+ const { edges } = connection;
109
+ if (edges.length === 0) {
110
+ return null;
111
+ }
112
+ return connection;
113
+ }
114
+ async displayLatestUpdateGroupAsync({ graphqlClient, appId, branchName, runtime, }) {
115
+ const updateGroups = await UpdateQuery_1.UpdateQuery.viewUpdateGroupsOnBranchAsync(graphqlClient, {
116
+ appId,
117
+ branchName,
118
+ limit: 1,
119
+ offset: 0,
120
+ filter: {
121
+ runtimeVersions: [runtime.version],
122
+ },
123
+ });
124
+ (0, assert_1.default)(updateGroups.length < 2, `Expected at most one update group. Received: ${JSON.stringify(updateGroups)}`);
125
+ return (0, utils_2.formatRuntimeWithUpdateGroup)(updateGroups[0], runtime);
126
+ }
127
+ async selectRuntimesAsync(graphqlClient, { appId, branchName, anotherBranchIdToIntersectRuntimesBy, batchSize = 5, }) {
128
+ const queryAsync = async (queryParams) => {
129
+ return await RuntimeQuery_1.RuntimeQuery.getRuntimesOnBranchAsync(graphqlClient, {
130
+ appId,
131
+ name: branchName,
132
+ first: queryParams.first,
133
+ after: queryParams.after,
134
+ last: queryParams.last,
135
+ before: queryParams.before,
136
+ filter: {
137
+ branchId: anotherBranchIdToIntersectRuntimesBy,
138
+ },
139
+ });
140
+ };
141
+ const getTitleAsync = async (runtime) => {
142
+ return await this.displayLatestUpdateGroupAsync({
143
+ graphqlClient,
144
+ appId,
145
+ branchName,
146
+ runtime,
147
+ });
148
+ };
149
+ return await (0, relay_1.selectPaginatedAsync)({
150
+ queryAsync,
151
+ getTitleAsync,
152
+ printedType: 'target runtime',
153
+ pageSize: batchSize,
154
+ });
155
+ }
156
+ async promptForRuntimeAsync() {
157
+ log_1.default.log(`You can input a runtime manually then publish an update later.`);
158
+ const { runtimeVersion } = await (0, prompts_1.promptAsync)({
159
+ type: 'text',
160
+ name: 'runtimeVersion',
161
+ message: 'Input a runtime version:',
162
+ initial: '1.0.0',
163
+ });
164
+ return runtimeVersion;
165
+ }
166
+ }
167
+ exports.SelectRuntime = SelectRuntime;
@@ -1,4 +1,4 @@
1
- import { BranchMapping } from '../channel/branch-mapping';
1
+ import { BranchMapping, BranchMappingAlwaysTrue } from '../channel/branch-mapping';
2
2
  import { UpdateChannelBasicInfoFragment } from '../graphql/generated';
3
3
  import { UpdateBranchObject, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
4
4
  export type Rollout = LegacyRollout | ConstrainedRollout;
@@ -18,12 +18,52 @@ type LegacyRolloutInfo = {
18
18
  percentRolledOut: number;
19
19
  defaultBranchId: string;
20
20
  };
21
+ export type RolloutBranchMapping = LegacyRolloutBranchMapping | ConstrainedRolloutBranchMapping;
22
+ type RolloutNode = {
23
+ clientKey: 'rolloutToken';
24
+ branchMappingOperator: 'hash_lt';
25
+ operand: number;
26
+ };
27
+ type RuntimeVersionNode = {
28
+ clientKey: 'runtimeVersion';
29
+ branchMappingOperator: '==';
30
+ operand: string;
31
+ };
32
+ type RtvConstrainedRolloutNode = ['and', RolloutNode, RuntimeVersionNode] | ['and', RuntimeVersionNode, RolloutNode];
33
+ export type LegacyRolloutBranchMapping = {
34
+ version: number;
35
+ data: [
36
+ {
37
+ branchId: string;
38
+ branchMappingLogic: RolloutNode;
39
+ },
40
+ {
41
+ branchId: string;
42
+ branchMappingLogic: BranchMappingAlwaysTrue;
43
+ }
44
+ ];
45
+ };
46
+ export type ConstrainedRolloutBranchMapping = {
47
+ version: number;
48
+ data: [
49
+ {
50
+ branchId: string;
51
+ branchMappingLogic: RtvConstrainedRolloutNode;
52
+ },
53
+ {
54
+ branchId: string;
55
+ branchMappingLogic: BranchMappingAlwaysTrue;
56
+ }
57
+ ];
58
+ };
21
59
  export declare function isLegacyRolloutInfo(rollout: RolloutInfo): rollout is LegacyRolloutInfo;
22
60
  export declare function isConstrainedRolloutInfo(rollout: RolloutInfo): rollout is ConstrainedRolloutInfo;
23
61
  export declare function isConstrainedRollout(rollout: Rollout): rollout is ConstrainedRollout;
24
62
  export declare function getRolloutInfo(basicChannelInfo: UpdateChannelBasicInfoFragment): RolloutInfo;
25
- export declare function getRolloutInfoFromBranchMapping(branchMapping: BranchMapping): RolloutInfo;
63
+ export declare function getRolloutInfoFromBranchMapping(branchMapping: RolloutBranchMapping): RolloutInfo;
26
64
  export declare function getRollout(channel: UpdateChannelObject): Rollout;
65
+ export declare function composeRollout(rolloutInfo: RolloutInfo, defaultBranch: UpdateBranchObject, rolledOutBranch: UpdateBranchObject): Rollout;
66
+ export declare function getRolloutBranchMapping(branchMappingString: string): RolloutBranchMapping;
27
67
  /**
28
68
  * Detect if a branch mapping is a rollout.
29
69
  *
@@ -75,6 +115,14 @@ export declare function getRollout(channel: UpdateChannelObject): Rollout;
75
115
  ],
76
116
  }
77
117
  */
78
- export declare function isRollout(branchMapping: BranchMapping): boolean;
79
- export declare function editRolloutBranchMapping(branchMapping: BranchMapping, percent: number): BranchMapping;
118
+ export declare function isRolloutBranchMapping(branchMapping: BranchMapping): branchMapping is RolloutBranchMapping;
119
+ export declare function isRollout(channelInfo: UpdateChannelBasicInfoFragment): boolean;
120
+ export declare function createRolloutBranchMapping({ defaultBranchId, rolloutBranchId, percent, runtimeVersion, }: {
121
+ defaultBranchId: string;
122
+ rolloutBranchId: string;
123
+ percent: number;
124
+ runtimeVersion: string;
125
+ }): ConstrainedRolloutBranchMapping;
126
+ export declare function editRolloutBranchMapping(branchMapping: RolloutBranchMapping, percent: number): RolloutBranchMapping;
127
+ export declare function assertRolloutBranchMapping(branchMapping: BranchMapping): asserts branchMapping is RolloutBranchMapping;
80
128
  export {};
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.editRolloutBranchMapping = exports.isRollout = exports.getRollout = exports.getRolloutInfoFromBranchMapping = exports.getRolloutInfo = exports.isConstrainedRollout = exports.isConstrainedRolloutInfo = exports.isLegacyRolloutInfo = void 0;
3
+ exports.assertRolloutBranchMapping = exports.editRolloutBranchMapping = exports.createRolloutBranchMapping = exports.isRollout = exports.isRolloutBranchMapping = exports.getRolloutBranchMapping = exports.composeRollout = exports.getRollout = exports.getRolloutInfoFromBranchMapping = exports.getRolloutInfo = exports.isConstrainedRollout = exports.isConstrainedRolloutInfo = exports.isLegacyRolloutInfo = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const assert_1 = tslib_1.__importDefault(require("assert"));
6
6
  const branch_mapping_1 = require("../channel/branch-mapping");
@@ -18,13 +18,11 @@ function isConstrainedRollout(rollout) {
18
18
  }
19
19
  exports.isConstrainedRollout = isConstrainedRollout;
20
20
  function getRolloutInfo(basicChannelInfo) {
21
- const branchMapping = (0, branch_mapping_1.getBranchMapping)(basicChannelInfo.branchMapping);
22
- assertRollout(branchMapping);
23
- return getRolloutInfoFromBranchMapping(branchMapping);
21
+ const rolloutBranchMapping = getRolloutBranchMapping(basicChannelInfo.branchMapping);
22
+ return getRolloutInfoFromBranchMapping(rolloutBranchMapping);
24
23
  }
25
24
  exports.getRolloutInfo = getRolloutInfo;
26
25
  function getRolloutInfoFromBranchMapping(branchMapping) {
27
- assertRollout(branchMapping);
28
26
  const rolledOutBranchId = branchMapping.data[0].branchId;
29
27
  const defaultBranchId = branchMapping.data[1].branchId;
30
28
  if (isRtvConstrainedRollout(branchMapping)) {
@@ -62,20 +60,35 @@ function getRolloutInfoFromBranchMapping(branchMapping) {
62
60
  }
63
61
  exports.getRolloutInfoFromBranchMapping = getRolloutInfoFromBranchMapping;
64
62
  function getRollout(channel) {
65
- const branchMapping = (0, branch_mapping_1.getBranchMapping)(channel.branchMapping);
66
- assertRollout(branchMapping);
67
- const rolledOutBranchId = branchMapping.data[0].branchId;
63
+ const rolloutBranchMapping = getRolloutBranchMapping(channel.branchMapping);
64
+ const rolledOutBranchId = rolloutBranchMapping.data[0].branchId;
68
65
  const rolledOutBranch = (0, utils_1.getUpdateBranch)(channel, rolledOutBranchId);
69
- const defaultBranchId = branchMapping.data[1].branchId;
66
+ const defaultBranchId = rolloutBranchMapping.data[1].branchId;
70
67
  const defaultBranch = (0, utils_1.getUpdateBranch)(channel, defaultBranchId);
71
68
  const rolloutInfo = getRolloutInfo(channel);
69
+ return composeRollout(rolloutInfo, defaultBranch, rolledOutBranch);
70
+ }
71
+ exports.getRollout = getRollout;
72
+ function composeRollout(rolloutInfo, defaultBranch, rolledOutBranch) {
73
+ if (rolloutInfo.defaultBranchId !== defaultBranch.id) {
74
+ throw new branch_mapping_1.BranchMappingValidationError(`Default branch id must match. Received: ${JSON.stringify(rolloutInfo)} ${defaultBranch.id}`);
75
+ }
76
+ if (rolloutInfo.rolledOutBranchId !== rolledOutBranch.id) {
77
+ throw new branch_mapping_1.BranchMappingValidationError(`Rolled out branch id must match. Received: ${JSON.stringify(rolloutInfo)} ${rolledOutBranch.id}`);
78
+ }
72
79
  return {
73
80
  ...rolloutInfo,
74
81
  rolledOutBranch,
75
82
  defaultBranch,
76
83
  };
77
84
  }
78
- exports.getRollout = getRollout;
85
+ exports.composeRollout = composeRollout;
86
+ function getRolloutBranchMapping(branchMappingString) {
87
+ const branchMapping = (0, branch_mapping_1.getBranchMapping)(branchMappingString);
88
+ assertRolloutBranchMapping(branchMapping);
89
+ return branchMapping;
90
+ }
91
+ exports.getRolloutBranchMapping = getRolloutBranchMapping;
79
92
  /**
80
93
  * Detect if a branch mapping is a rollout.
81
94
  *
@@ -127,13 +140,43 @@ exports.getRollout = getRollout;
127
140
  ],
128
141
  }
129
142
  */
130
- function isRollout(branchMapping) {
143
+ function isRolloutBranchMapping(branchMapping) {
131
144
  return isUnconstrainedRollout(branchMapping) || isRtvConstrainedRollout(branchMapping);
132
145
  }
146
+ exports.isRolloutBranchMapping = isRolloutBranchMapping;
147
+ function isRollout(channelInfo) {
148
+ const branchMapping = (0, branch_mapping_1.getBranchMapping)(channelInfo.branchMapping);
149
+ return isRolloutBranchMapping(branchMapping);
150
+ }
133
151
  exports.isRollout = isRollout;
152
+ function createRolloutBranchMapping({ defaultBranchId, rolloutBranchId, percent, runtimeVersion, }) {
153
+ assertPercent(percent);
154
+ return {
155
+ version: 0,
156
+ data: [
157
+ {
158
+ branchId: rolloutBranchId,
159
+ branchMappingLogic: [
160
+ 'and',
161
+ {
162
+ operand: runtimeVersion,
163
+ clientKey: 'runtimeVersion',
164
+ branchMappingOperator: '==',
165
+ },
166
+ {
167
+ operand: percent / 100,
168
+ clientKey: 'rolloutToken',
169
+ branchMappingOperator: 'hash_lt',
170
+ },
171
+ ],
172
+ },
173
+ { branchId: defaultBranchId, branchMappingLogic: (0, branch_mapping_1.alwaysTrue)() },
174
+ ],
175
+ };
176
+ }
177
+ exports.createRolloutBranchMapping = createRolloutBranchMapping;
134
178
  function editRolloutBranchMapping(branchMapping, percent) {
135
- (0, assert_1.default)(Number.isInteger(percent) && percent >= 0 && percent <= 100, 'The rollout percentage must be an integer between 0 and 100 inclusive.');
136
- assertRollout(branchMapping);
179
+ assertPercent(percent);
137
180
  if (isRtvConstrainedRollout(branchMapping)) {
138
181
  return editRtvConstrainedRollout(branchMapping, percent);
139
182
  }
@@ -145,24 +188,18 @@ exports.editRolloutBranchMapping = editRolloutBranchMapping;
145
188
  function editRtvConstrainedRollout(branchMapping, percent) {
146
189
  const newBranchMapping = { ...branchMapping };
147
190
  const statementNode = newBranchMapping.data[0].branchMappingLogic;
148
- (0, branch_mapping_1.assertStatement)(statementNode);
149
191
  const nodesFromStatement = (0, branch_mapping_1.getNodesFromStatement)(statementNode);
150
192
  const rolloutNode = nodesFromStatement.find(isRolloutNode);
151
193
  (0, assert_1.default)(rolloutNode, 'Rollout node must be defined.');
152
- (0, branch_mapping_1.assertNodeObject)(rolloutNode);
153
194
  rolloutNode.operand = percent / 100;
154
195
  return newBranchMapping;
155
196
  }
156
197
  function editLegacyRollout(branchMapping, percent) {
157
198
  const newBranchMapping = { ...branchMapping };
158
199
  const rolloutNode = newBranchMapping.data[0].branchMappingLogic;
159
- (0, branch_mapping_1.assertNodeObject)(rolloutNode);
160
200
  rolloutNode.operand = percent / 100;
161
201
  return newBranchMapping;
162
202
  }
163
- function assertRollout(branchMapping) {
164
- (0, assert_1.default)(isRollout(branchMapping), 'Branch mapping node must be a rollout. Received: ' + JSON.stringify(branchMapping));
165
- }
166
203
  function isRtvConstrainedRollout(branchMapping) {
167
204
  if (branchMapping.data.length !== 2) {
168
205
  return false;
@@ -209,3 +246,15 @@ function isRolloutNode(node) {
209
246
  }
210
247
  return node.clientKey === 'rolloutToken' && node.branchMappingOperator === 'hash_lt';
211
248
  }
249
+ function assertRolloutBranchMapping(branchMapping) {
250
+ if (!isRolloutBranchMapping(branchMapping)) {
251
+ throw new branch_mapping_1.BranchMappingValidationError('Branch mapping node must be a rollout. Received: ' + JSON.stringify(branchMapping));
252
+ }
253
+ }
254
+ exports.assertRolloutBranchMapping = assertRolloutBranchMapping;
255
+ function assertPercent(percent) {
256
+ const isPercent = Number.isInteger(percent) && percent >= 0 && percent <= 100;
257
+ if (!isPercent) {
258
+ throw new branch_mapping_1.BranchMappingValidationError(`The percentage must be an integer between 0 and 100 inclusive. Received: ${percent}`);
259
+ }
260
+ }
@@ -1,3 +1,10 @@
1
- import { UpdateChannelObject } from '../graphql/queries/ChannelQuery';
1
+ import { RuntimeFragment, UpdateFragment } from '../graphql/generated';
2
+ import { UpdateBranchObject, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
3
+ import { Rollout } from './branch-mapping';
2
4
  export declare function printRollout(channel: UpdateChannelObject): void;
3
- export declare function printBranch(channel: UpdateChannelObject): void;
5
+ export declare function displayRolloutDetails(channelName: string, rollout: Rollout): void;
6
+ export declare function formatBranchWithUpdateGroup(maybeUpdateGroup: UpdateFragment[] | undefined | null, branch: UpdateBranchObject, percentRolledOut: number): string;
7
+ export declare function formatRuntimeWithUpdateGroup(maybeUpdateGroup: UpdateFragment[] | undefined | null, runtime: RuntimeFragment): string;
8
+ export declare function promptForRolloutPercentAsync({ promptMessage, }: {
9
+ promptMessage: string;
10
+ }): Promise<number>;