eas-cli 3.18.2 → 4.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.
- package/README.md +76 -65
- package/build/channel/branch-mapping.d.ts +1 -0
- package/build/channel/branch-mapping.js +11 -1
- package/build/channel/print-utils.d.ts +2 -0
- package/build/channel/print-utils.js +39 -0
- package/build/channel/queries.js +3 -3
- package/build/channel/utils.d.ts +0 -11
- package/build/channel/utils.js +1 -93
- package/build/commands/channel/edit.js +6 -1
- package/build/commands/channel/rollout.d.ts +14 -2
- package/build/commands/channel/rollout.js +106 -270
- package/build/commands/update/republish.d.ts +2 -21
- package/build/commands/update/republish.js +15 -6
- package/build/devices/actions/create/action.d.ts +2 -1
- package/build/devices/actions/create/action.js +10 -1
- package/build/devices/actions/create/currentMachineMethod.d.ts +3 -0
- package/build/devices/actions/create/currentMachineMethod.js +100 -0
- package/build/devices/actions/create/developerPortalMethod.js +2 -1
- package/build/devices/actions/create/inputMethod.d.ts +0 -1
- package/build/devices/actions/create/inputMethod.js +6 -64
- package/build/devices/actions/create/utils.d.ts +10 -0
- package/build/devices/actions/create/utils.js +73 -0
- package/build/devices/manager.js +3 -3
- package/build/devices/utils/errors.d.ts +3 -0
- package/build/devices/utils/errors.js +7 -1
- package/build/devices/utils/formatDevice.js +1 -0
- package/build/graphql/generated.d.ts +5 -4
- package/build/graphql/generated.js +1 -0
- package/build/graphql/types/credentials/AppleDevice.js +1 -0
- package/build/rollout/actions/CreateRollout.d.ts +2 -0
- package/build/rollout/actions/CreateRollout.js +79 -5
- package/build/rollout/actions/EditRollout.js +5 -2
- package/build/rollout/actions/EndRollout.d.ts +6 -3
- package/build/rollout/actions/EndRollout.js +22 -21
- package/build/rollout/actions/ManageRollout.d.ts +3 -3
- package/build/rollout/actions/ManageRollout.js +1 -1
- package/build/rollout/actions/NonInteractiveRollout.d.ts +6 -4
- package/build/rollout/actions/NonInteractiveRollout.js +1 -1
- package/build/rollout/actions/RolloutMainMenu.d.ts +4 -4
- package/build/rollout/actions/RolloutMainMenu.js +14 -5
- package/build/rollout/actions/SelectRuntime.d.ts +4 -7
- package/build/rollout/actions/SelectRuntime.js +22 -39
- package/build/rollout/branch-mapping.js +2 -2
- package/build/rollout/utils.d.ts +1 -1
- package/build/rollout/utils.js +5 -5
- package/build/update/republish.d.ts +3 -1
- package/build/update/republish.js +38 -15
- package/build/utils/relay.js +3 -9
- package/oclif.manifest.json +1 -1
- package/package.json +3 -3
- package/build/commands/channel/rollout-preview.d.ts +0 -32
- package/build/commands/channel/rollout-preview.js +0 -109
package/build/channel/utils.d.ts
CHANGED
|
@@ -1,13 +1,2 @@
|
|
|
1
1
|
import { UpdateBranchObject, UpdateChannelObject } from '../graphql/queries/ChannelQuery';
|
|
2
|
-
import { BranchMapping } from './branch-mapping';
|
|
3
|
-
/**
|
|
4
|
-
* Get the branch mapping and determine whether it is a rollout.
|
|
5
|
-
* Ensure that the branch mapping is properly formatted.
|
|
6
|
-
*/
|
|
7
|
-
export declare function getBranchMapping(branchMappingString?: string): {
|
|
8
|
-
branchMapping: BranchMapping;
|
|
9
|
-
isRollout: boolean;
|
|
10
|
-
rolloutPercent?: number;
|
|
11
|
-
};
|
|
12
|
-
export declare function logChannelDetails(channel: UpdateChannelObject): void;
|
|
13
2
|
export declare function getUpdateBranch(channel: UpdateChannelObject, branchId: string): UpdateBranchObject;
|
package/build/channel/utils.js
CHANGED
|
@@ -1,98 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getUpdateBranch =
|
|
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 log_1 = tslib_1.__importDefault(require("../log"));
|
|
8
|
-
const utils_1 = require("../update/utils");
|
|
9
|
-
const branch_mapping_1 = require("./branch-mapping");
|
|
10
|
-
/**
|
|
11
|
-
* Get the branch mapping and determine whether it is a rollout.
|
|
12
|
-
* Ensure that the branch mapping is properly formatted.
|
|
13
|
-
*/
|
|
14
|
-
function getBranchMapping(branchMappingString) {
|
|
15
|
-
var _a;
|
|
16
|
-
if (!branchMappingString) {
|
|
17
|
-
throw new Error('Missing branch mapping.');
|
|
18
|
-
}
|
|
19
|
-
let branchMapping;
|
|
20
|
-
try {
|
|
21
|
-
branchMapping = JSON.parse(branchMappingString);
|
|
22
|
-
}
|
|
23
|
-
catch {
|
|
24
|
-
throw new Error(`Could not parse branchMapping string into a JSON: "${branchMappingString}"`);
|
|
25
|
-
}
|
|
26
|
-
(0, assert_1.default)(branchMapping, 'Branch Mapping must be defined.');
|
|
27
|
-
if (branchMapping.version !== 0) {
|
|
28
|
-
throw new Error('Branch mapping must be version 0.');
|
|
29
|
-
}
|
|
30
|
-
const isRollout = branchMapping.data.length === 2;
|
|
31
|
-
const branchMappingNode = (_a = branchMapping.data[0]) === null || _a === void 0 ? void 0 : _a.branchMappingLogic;
|
|
32
|
-
let rolloutPercent;
|
|
33
|
-
if ((0, branch_mapping_1.isNodeObject)(branchMappingNode)) {
|
|
34
|
-
(0, branch_mapping_1.assertNumber)(branchMappingNode.operand);
|
|
35
|
-
rolloutPercent = branchMappingNode.operand;
|
|
36
|
-
}
|
|
37
|
-
switch (branchMapping.data.length) {
|
|
38
|
-
case 0:
|
|
39
|
-
break;
|
|
40
|
-
case 1:
|
|
41
|
-
if (branchMapping.data[0].branchMappingLogic !== 'true') {
|
|
42
|
-
throw new Error('Branch mapping logic for a single branch must be "true"');
|
|
43
|
-
}
|
|
44
|
-
break;
|
|
45
|
-
case 2:
|
|
46
|
-
(0, branch_mapping_1.assertNodeObject)(branchMappingNode);
|
|
47
|
-
if (branchMappingNode.clientKey !== 'rolloutToken') {
|
|
48
|
-
throw new Error('Client key of initial branch mapping must be "rolloutToken"');
|
|
49
|
-
}
|
|
50
|
-
if (branchMappingNode.branchMappingOperator !== 'hash_lt') {
|
|
51
|
-
throw new Error('Branch mapping operator of initial branch mapping must be "hash_lt"');
|
|
52
|
-
}
|
|
53
|
-
if (rolloutPercent == null) {
|
|
54
|
-
throw new Error('Branch mapping is missing a "rolloutPercent"');
|
|
55
|
-
}
|
|
56
|
-
if (branchMapping.data[1].branchMappingLogic !== 'true') {
|
|
57
|
-
throw new Error('Branch mapping logic for a the second branch of a rollout must be "true"');
|
|
58
|
-
}
|
|
59
|
-
break;
|
|
60
|
-
default:
|
|
61
|
-
throw new Error('Branch mapping data must have length less than or equal to 2.');
|
|
62
|
-
}
|
|
63
|
-
return { branchMapping, isRollout, rolloutPercent };
|
|
64
|
-
}
|
|
65
|
-
exports.getBranchMapping = getBranchMapping;
|
|
66
|
-
function logChannelDetails(channel) {
|
|
67
|
-
const { branchMapping, isRollout, rolloutPercent } = getBranchMapping(channel.branchMapping);
|
|
68
|
-
if (branchMapping.data.length > 2) {
|
|
69
|
-
throw new Error('Branch Mapping data must have length less than or equal to 2.');
|
|
70
|
-
}
|
|
71
|
-
const rolloutBranchIds = branchMapping.data.map(data => data.branchId);
|
|
72
|
-
const branchDescription = channel.updateBranches.flatMap(branch => {
|
|
73
|
-
const updateGroupWithBranchDescriptions = (0, utils_1.getUpdateGroupDescriptionsWithBranch)(branch.updateGroups);
|
|
74
|
-
const isRolloutBranch = isRollout && rolloutBranchIds.includes(branch.id);
|
|
75
|
-
const isBaseBranch = rolloutBranchIds.length > 0 && rolloutBranchIds[0] === branch.id;
|
|
76
|
-
let rolloutPercentNumber = undefined;
|
|
77
|
-
if (isRolloutBranch) {
|
|
78
|
-
rolloutPercentNumber = isBaseBranch ? rolloutPercent * 100 : (1 - rolloutPercent) * 100;
|
|
79
|
-
}
|
|
80
|
-
return updateGroupWithBranchDescriptions.map(({ branch, ...updateGroup }) => ({
|
|
81
|
-
branch,
|
|
82
|
-
branchRolloutPercentage: rolloutPercentNumber,
|
|
83
|
-
update: updateGroup,
|
|
84
|
-
}));
|
|
85
|
-
});
|
|
86
|
-
if (branchDescription.length === 0) {
|
|
87
|
-
log_1.default.log(chalk_1.default.dim('No branches are pointed to this channel.'));
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
log_1.default.log(branchDescription
|
|
91
|
-
.map(description => (0, utils_1.formatBranch)(description))
|
|
92
|
-
.join(`\n\n${chalk_1.default.dim('———')}\n\n`));
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
exports.logChannelDetails = logChannelDetails;
|
|
3
|
+
exports.getUpdateBranch = void 0;
|
|
96
4
|
function getUpdateBranchNullable(channel, branchId) {
|
|
97
5
|
const updateBranches = channel.updateBranches;
|
|
98
6
|
const updateBranch = updateBranches.find(branch => branch.id === branchId);
|
|
@@ -8,6 +8,7 @@ const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
|
8
8
|
const graphql_1 = require("graphql");
|
|
9
9
|
const graphql_tag_1 = tslib_1.__importDefault(require("graphql-tag"));
|
|
10
10
|
const queries_1 = require("../../branch/queries");
|
|
11
|
+
const branch_mapping_1 = require("../../channel/branch-mapping");
|
|
11
12
|
const queries_2 = require("../../channel/queries");
|
|
12
13
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
13
14
|
const flags_1 = require("../../commandUtils/flags");
|
|
@@ -16,6 +17,7 @@ const BranchQuery_1 = require("../../graphql/queries/BranchQuery");
|
|
|
16
17
|
const ChannelQuery_1 = require("../../graphql/queries/ChannelQuery");
|
|
17
18
|
const UpdateChannelBasicInfo_1 = require("../../graphql/types/UpdateChannelBasicInfo");
|
|
18
19
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
20
|
+
const branch_mapping_2 = require("../../rollout/branch-mapping");
|
|
19
21
|
const json_1 = require("../../utils/json");
|
|
20
22
|
async function updateChannelBranchMappingAsync(graphqlClient, { channelId, branchMapping }) {
|
|
21
23
|
const data = await (0, client_1.withErrorHandlingAsync)(graphqlClient
|
|
@@ -57,9 +59,12 @@ class ChannelEdit extends EasCommand_1.default {
|
|
|
57
59
|
selectionPromptTitle: 'Select a channel to edit',
|
|
58
60
|
paginatedQueryOptions: { json, nonInteractive, offset: 0 },
|
|
59
61
|
});
|
|
60
|
-
if (
|
|
62
|
+
if ((0, branch_mapping_2.isRollout)(existingChannel)) {
|
|
61
63
|
throw new Error('There is a rollout in progress. Manage it with "channel:rollout" instead.');
|
|
62
64
|
}
|
|
65
|
+
else if (!(0, branch_mapping_1.hasStandardBranchMap)(existingChannel)) {
|
|
66
|
+
throw new Error('Only standard branch mappings can be edited with this command.');
|
|
67
|
+
}
|
|
63
68
|
const branch = branchFlag
|
|
64
69
|
? await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
|
|
65
70
|
appId: projectId,
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import EasCommand from '../../commandUtils/EasCommand';
|
|
2
|
+
import { EndOutcome } from '../../rollout/actions/EndRollout';
|
|
3
|
+
declare enum ActionRawFlagValue {
|
|
4
|
+
CREATE = "create",
|
|
5
|
+
EDIT = "edit",
|
|
6
|
+
END = "end"
|
|
7
|
+
}
|
|
2
8
|
export default class ChannelRollout extends EasCommand {
|
|
3
9
|
static description: string;
|
|
4
10
|
static args: {
|
|
@@ -8,13 +14,19 @@ export default class ChannelRollout extends EasCommand {
|
|
|
8
14
|
static flags: {
|
|
9
15
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
10
16
|
'non-interactive': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
|
-
|
|
17
|
+
action: import("@oclif/core/lib/interfaces").OptionFlag<ActionRawFlagValue | undefined>;
|
|
12
18
|
percent: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined>;
|
|
13
|
-
|
|
19
|
+
outcome: import("@oclif/core/lib/interfaces").OptionFlag<EndOutcome | undefined>;
|
|
20
|
+
branch: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
21
|
+
'runtime-version': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
22
|
+
'private-key-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
14
23
|
};
|
|
15
24
|
static contextDefinition: {
|
|
16
25
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
17
26
|
privateProjectConfig: import("../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
18
27
|
};
|
|
19
28
|
runAsync(): Promise<void>;
|
|
29
|
+
private getAction;
|
|
30
|
+
private sanitizeArgsAndFlags;
|
|
20
31
|
}
|
|
32
|
+
export {};
|
|
@@ -3,262 +3,72 @@ 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 chalk_1 = tslib_1.__importDefault(require("chalk"));
|
|
7
|
-
const queries_1 = require("../../branch/queries");
|
|
8
|
-
const branch_mapping_1 = require("../../channel/branch-mapping");
|
|
9
|
-
const queries_2 = require("../../channel/queries");
|
|
10
|
-
const utils_1 = require("../../channel/utils");
|
|
11
6
|
const EasCommand_1 = tslib_1.__importDefault(require("../../commandUtils/EasCommand"));
|
|
12
7
|
const flags_1 = require("../../commandUtils/flags");
|
|
13
|
-
const BranchQuery_1 = require("../../graphql/queries/BranchQuery");
|
|
14
|
-
const ChannelQuery_1 = require("../../graphql/queries/ChannelQuery");
|
|
15
8
|
const log_1 = tslib_1.__importDefault(require("../../log"));
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
},
|
|
27
|
-
message: promptMessage,
|
|
28
|
-
initial: 0,
|
|
29
|
-
validate: (rolloutPercent) => {
|
|
30
|
-
const floatValue = parseFloat(rolloutPercent);
|
|
31
|
-
return Number.isInteger(floatValue) && floatValue >= 0 && floatValue <= 100
|
|
32
|
-
? true
|
|
33
|
-
: 'The rollout percentage must be an integer between 0 and 100 inclusive.';
|
|
34
|
-
},
|
|
35
|
-
});
|
|
36
|
-
return rolloutPercent;
|
|
37
|
-
}
|
|
38
|
-
function getRolloutInfo(channel) {
|
|
39
|
-
var _b;
|
|
40
|
-
const { branchMapping } = (0, utils_1.getBranchMapping)(channel.branchMapping);
|
|
41
|
-
const [newBranchId, oldBranchId] = branchMapping.data.map(d => d.branchId);
|
|
42
|
-
const newBranch = channel.updateBranches.filter(branch => branch.id === newBranchId)[0];
|
|
43
|
-
const oldBranch = channel.updateBranches.filter(branch => branch.id === oldBranchId)[0];
|
|
44
|
-
if (!newBranch || !oldBranch) {
|
|
45
|
-
throw new Error(`Branch mapping rollout is missing a branch for channel "${channel.name}".`);
|
|
46
|
-
}
|
|
47
|
-
const branchMappingNode = (_b = branchMapping.data[0]) === null || _b === void 0 ? void 0 : _b.branchMappingLogic;
|
|
48
|
-
(0, branch_mapping_1.assertNodeObject)(branchMappingNode);
|
|
49
|
-
(0, branch_mapping_1.assertNumber)(branchMappingNode.operand);
|
|
50
|
-
const currentPercent = 100 * branchMappingNode.operand;
|
|
51
|
-
return { newBranch, oldBranch, currentPercent };
|
|
52
|
-
}
|
|
53
|
-
async function startRolloutAsync(graphqlClient, { channelName, branchName, percent, projectId, displayName, currentBranchMapping, channel, nonInteractive, }) {
|
|
54
|
-
const branch = await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
|
|
55
|
-
appId: projectId,
|
|
56
|
-
name: branchName,
|
|
57
|
-
});
|
|
58
|
-
const oldBranchId = currentBranchMapping.data[0].branchId;
|
|
59
|
-
if (branch.id === oldBranchId) {
|
|
60
|
-
throw new Error(`channel "${channelName}" is already pointing at branch "${branchName}". Rollouts must be done with distinct branches.`);
|
|
61
|
-
}
|
|
62
|
-
if (percent == null) {
|
|
63
|
-
if (nonInteractive) {
|
|
64
|
-
throw new Error('You must specify a percent with the --percent flag when initiating a rollout with the --non-interactive flag.');
|
|
65
|
-
}
|
|
66
|
-
const promptMessage = `What percent of users should be directed to the branch "${branchName}"?`;
|
|
67
|
-
percent = await promptForRolloutPercentAsync({ promptMessage });
|
|
68
|
-
}
|
|
69
|
-
const newBranchMapping = {
|
|
70
|
-
version: 0,
|
|
71
|
-
data: [
|
|
72
|
-
{
|
|
73
|
-
branchId: branch.id,
|
|
74
|
-
branchMappingLogic: {
|
|
75
|
-
operand: percent / 100,
|
|
76
|
-
clientKey: 'rolloutToken',
|
|
77
|
-
branchMappingOperator: 'hash_lt',
|
|
78
|
-
},
|
|
79
|
-
},
|
|
80
|
-
currentBranchMapping.data[0],
|
|
81
|
-
],
|
|
82
|
-
};
|
|
83
|
-
const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
|
|
84
|
-
channelId: channel.id,
|
|
85
|
-
branchMapping: JSON.stringify(newBranchMapping),
|
|
86
|
-
});
|
|
87
|
-
const oldBranch = channel.updateBranches.filter(branch => branch.id === oldBranchId)[0];
|
|
88
|
-
if (!oldBranch) {
|
|
89
|
-
throw new Error(`Branch mapping is missing its only branch for channel "${channelName}" on app "${displayName}"`);
|
|
90
|
-
}
|
|
91
|
-
const logMessage = `Started a rollout of branch ${chalk_1.default.bold(branchName)} on channel ${chalk_1.default.bold(channelName)}! ${chalk_1.default.bold(percent)}% of users will be directed to branch ${chalk_1.default.bold(branchName)}, ${chalk_1.default.bold(100 - percent)}% to branch ${chalk_1.default.bold(oldBranch.name)}.`;
|
|
92
|
-
return { newChannelInfo, logMessage };
|
|
93
|
-
}
|
|
94
|
-
async function editRolloutAsync(graphqlClient, { channelName, percent, nonInteractive, currentBranchMapping, channel, }) {
|
|
95
|
-
const { newBranch, oldBranch, currentPercent } = getRolloutInfo(channel);
|
|
96
|
-
if (percent == null) {
|
|
97
|
-
if (nonInteractive) {
|
|
98
|
-
throw new Error('A rollout is already in progress. If you wish to modify it you must use specify the new rollout percentage with the --percent flag.');
|
|
99
|
-
}
|
|
100
|
-
const promptMessage = `Currently ${currentPercent}% of all users are routed to branch ${newBranch.name} and ${100 - currentPercent}% of all users are routed to branch ${oldBranch.name}. What percent of users should be directed to the branch ${newBranch.name}?`;
|
|
101
|
-
percent = await promptForRolloutPercentAsync({ promptMessage });
|
|
102
|
-
}
|
|
103
|
-
const newBranchMapping = { ...currentBranchMapping };
|
|
104
|
-
newBranchMapping.data[0].branchMappingLogic.operand = percent / 100;
|
|
105
|
-
const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
|
|
106
|
-
channelId: channel.id,
|
|
107
|
-
branchMapping: JSON.stringify(newBranchMapping),
|
|
108
|
-
});
|
|
109
|
-
const logMessage = `Rollout of branch ${chalk_1.default.bold(newBranch.name)} on channel ${chalk_1.default.bold(channelName)} updated from ${chalk_1.default.bold(currentPercent)}% to ${chalk_1.default.bold(percent)}%. ${chalk_1.default.bold(percent)}% of users will be directed to branch ${chalk_1.default.bold(newBranch.name)}, ${chalk_1.default.bold(100 - percent)}% to branch ${chalk_1.default.bold(oldBranch.name)}.`;
|
|
110
|
-
return { newChannelInfo, logMessage };
|
|
111
|
-
}
|
|
112
|
-
async function endRolloutAsync(graphqlClient, { channelName, branchName, nonInteractive, projectId, channel, }) {
|
|
113
|
-
// end rollout
|
|
114
|
-
const { newBranch, oldBranch, currentPercent } = getRolloutInfo(channel);
|
|
115
|
-
let endOnNewBranch;
|
|
116
|
-
if (branchName) {
|
|
117
|
-
const branch = await BranchQuery_1.BranchQuery.getBranchByNameAsync(graphqlClient, {
|
|
118
|
-
appId: projectId,
|
|
119
|
-
name: branchName,
|
|
120
|
-
});
|
|
121
|
-
switch (branch.id) {
|
|
122
|
-
case newBranch.id:
|
|
123
|
-
endOnNewBranch = true;
|
|
124
|
-
break;
|
|
125
|
-
case oldBranch.id:
|
|
126
|
-
endOnNewBranch = false;
|
|
127
|
-
break;
|
|
128
|
-
default:
|
|
129
|
-
throw new Error(`The branch "${branchName}" specified by --branch must be one of the branches involved in the rollout: "${newBranch.name}" or "${oldBranch.name}".`);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
if (nonInteractive) {
|
|
134
|
-
throw new Error('Branch name must be specified with the --branch flag when both the --end and --non-interactive flag are true.');
|
|
135
|
-
}
|
|
136
|
-
endOnNewBranch = await (0, prompts_1.selectAsync)('Ending the rollout will send all traffic to a single branch. Which one should that be?', [
|
|
137
|
-
{
|
|
138
|
-
title: `${newBranch.name} ${chalk_1.default.grey(`- current percent: ${currentPercent}%`)}`,
|
|
139
|
-
value: true,
|
|
140
|
-
},
|
|
141
|
-
{
|
|
142
|
-
title: `${oldBranch.name} ${chalk_1.default.grey(`- current percent: ${100 - currentPercent}%`)}`,
|
|
143
|
-
value: false,
|
|
144
|
-
},
|
|
145
|
-
]);
|
|
146
|
-
}
|
|
147
|
-
if (endOnNewBranch == null) {
|
|
148
|
-
throw new Error('Branch to end on is undefined.');
|
|
149
|
-
}
|
|
150
|
-
const newBranchMapping = {
|
|
151
|
-
version: 0,
|
|
152
|
-
data: [
|
|
153
|
-
{
|
|
154
|
-
branchId: endOnNewBranch ? newBranch.id : oldBranch.id,
|
|
155
|
-
branchMappingLogic: 'true',
|
|
156
|
-
},
|
|
157
|
-
],
|
|
158
|
-
};
|
|
159
|
-
const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
|
|
160
|
-
channelId: channel.id,
|
|
161
|
-
branchMapping: JSON.stringify(newBranchMapping),
|
|
162
|
-
});
|
|
163
|
-
const logMessage = `Rollout on channel ${chalk_1.default.bold(channelName)} ended. All traffic is now sent to branch ${chalk_1.default.bold(endOnNewBranch ? newBranch.name : oldBranch.name)}`;
|
|
164
|
-
return { newChannelInfo, logMessage };
|
|
165
|
-
}
|
|
9
|
+
const EndRollout_1 = require("../../rollout/actions/EndRollout");
|
|
10
|
+
const ManageRollout_1 = require("../../rollout/actions/ManageRollout");
|
|
11
|
+
const NonInteractiveRollout_1 = require("../../rollout/actions/NonInteractiveRollout");
|
|
12
|
+
const RolloutMainMenu_1 = require("../../rollout/actions/RolloutMainMenu");
|
|
13
|
+
var ActionRawFlagValue;
|
|
14
|
+
(function (ActionRawFlagValue) {
|
|
15
|
+
ActionRawFlagValue["CREATE"] = "create";
|
|
16
|
+
ActionRawFlagValue["EDIT"] = "edit";
|
|
17
|
+
ActionRawFlagValue["END"] = "end";
|
|
18
|
+
})(ActionRawFlagValue || (ActionRawFlagValue = {}));
|
|
166
19
|
class ChannelRollout extends EasCommand_1.default {
|
|
167
20
|
async runAsync() {
|
|
168
|
-
const { args
|
|
169
|
-
const
|
|
170
|
-
|
|
21
|
+
const { args, flags } = await this.parse(ChannelRollout);
|
|
22
|
+
const argsAndFlags = this.sanitizeArgsAndFlags({ ...flags, ...args });
|
|
23
|
+
const { privateProjectConfig: { exp, projectId }, loggedIn: { graphqlClient }, } = await this.getContextAsync(ChannelRollout, {
|
|
24
|
+
nonInteractive: argsAndFlags.nonInteractive,
|
|
171
25
|
});
|
|
172
|
-
if (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
channelName = name;
|
|
188
|
-
}
|
|
189
|
-
const channel = await ChannelQuery_1.ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
|
|
190
|
-
appId: projectId,
|
|
191
|
-
channelName,
|
|
192
|
-
});
|
|
193
|
-
if (!channel) {
|
|
194
|
-
throw new Error(`Could not find a channel named "${channelName}". Check which channels exist on this project with ${chalk_1.default.bold('eas channel:list')}.`);
|
|
195
|
-
}
|
|
196
|
-
const { branchMapping: currentBranchMapping, isRollout } = (0, utils_1.getBranchMapping)(channel.branchMapping);
|
|
197
|
-
if (currentBranchMapping.data.length === 0) {
|
|
198
|
-
throw new Error('The channel is not pointing at any branches.');
|
|
199
|
-
}
|
|
200
|
-
if (currentBranchMapping.data.length > 2) {
|
|
201
|
-
throw new Error('"channel:rollout" cannot handle branch mappings with more than 2 branches.');
|
|
202
|
-
}
|
|
203
|
-
// This combination doesn't make sense. Throw an error explaining the options.
|
|
204
|
-
if (isRollout && branchName && !endFlag) {
|
|
205
|
-
throw new Error(`There is a rollout in progress. You can only either edit the rollout percent or 'end' it.`);
|
|
206
|
-
}
|
|
207
|
-
/**
|
|
208
|
-
* This if/else block has three branches:
|
|
209
|
-
* 1. The branch mapping is not a rollout, i.e. it is pointing to a single branch.
|
|
210
|
-
* 2. The branch mapping is a rollout.
|
|
211
|
-
* a. increase/decrease the rollout percentage.
|
|
212
|
-
* b. end the rollout.
|
|
213
|
-
*/
|
|
214
|
-
let rolloutMutationResult;
|
|
215
|
-
if (!isRollout) {
|
|
216
|
-
rolloutMutationResult = await startRolloutAsync(graphqlClient, {
|
|
217
|
-
channelName,
|
|
218
|
-
branchName: branchName !== null && branchName !== void 0 ? branchName : (await promptForBranchNameAsync({
|
|
219
|
-
graphqlClient,
|
|
220
|
-
projectId,
|
|
221
|
-
channelName,
|
|
222
|
-
nonInteractive,
|
|
223
|
-
json: jsonFlag,
|
|
224
|
-
})),
|
|
225
|
-
percent,
|
|
226
|
-
nonInteractive,
|
|
227
|
-
projectId,
|
|
228
|
-
displayName: projectDisplayName,
|
|
229
|
-
currentBranchMapping,
|
|
230
|
-
channel,
|
|
231
|
-
});
|
|
232
|
-
}
|
|
233
|
-
else if (endFlag) {
|
|
234
|
-
rolloutMutationResult = await endRolloutAsync(graphqlClient, {
|
|
235
|
-
channelName,
|
|
236
|
-
branchName,
|
|
237
|
-
nonInteractive,
|
|
238
|
-
projectId,
|
|
239
|
-
channel,
|
|
240
|
-
});
|
|
26
|
+
if (argsAndFlags.json) {
|
|
27
|
+
// TODO(quin): implement json output
|
|
28
|
+
throw new Error('Developer Preview doesnt support JSON output yet');
|
|
29
|
+
}
|
|
30
|
+
const app = { projectId, exp };
|
|
31
|
+
const ctx = {
|
|
32
|
+
projectId,
|
|
33
|
+
nonInteractive: argsAndFlags.nonInteractive,
|
|
34
|
+
graphqlClient,
|
|
35
|
+
app,
|
|
36
|
+
};
|
|
37
|
+
if (argsAndFlags.nonInteractive) {
|
|
38
|
+
await new NonInteractiveRollout_1.NonInteractiveRollout(argsAndFlags).runAsync(ctx);
|
|
241
39
|
}
|
|
242
40
|
else {
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
currentBranchMapping,
|
|
248
|
-
channel,
|
|
249
|
-
});
|
|
250
|
-
}
|
|
251
|
-
if (!rolloutMutationResult) {
|
|
252
|
-
throw new Error('rollout result is empty');
|
|
41
|
+
log_1.default.addNewLineIfNone();
|
|
42
|
+
log_1.default.warn(`✨ This command is in Developer Preview and has not been released to production yet`);
|
|
43
|
+
log_1.default.addNewLineIfNone();
|
|
44
|
+
await new RolloutMainMenu_1.RolloutMainMenu(argsAndFlags).runAsync(ctx);
|
|
253
45
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
46
|
+
}
|
|
47
|
+
getAction(action) {
|
|
48
|
+
switch (action) {
|
|
49
|
+
case ActionRawFlagValue.CREATE:
|
|
50
|
+
return RolloutMainMenu_1.MainMenuActions.CREATE_NEW;
|
|
51
|
+
case ActionRawFlagValue.EDIT:
|
|
52
|
+
return ManageRollout_1.ManageRolloutActions.EDIT;
|
|
53
|
+
case ActionRawFlagValue.END:
|
|
54
|
+
return ManageRollout_1.ManageRolloutActions.END;
|
|
260
55
|
}
|
|
261
56
|
}
|
|
57
|
+
sanitizeArgsAndFlags(rawFlags) {
|
|
58
|
+
var _b;
|
|
59
|
+
const action = rawFlags.action;
|
|
60
|
+
return {
|
|
61
|
+
channelName: rawFlags.channel,
|
|
62
|
+
percent: rawFlags.percent,
|
|
63
|
+
outcome: rawFlags.outcome,
|
|
64
|
+
branchNameToRollout: rawFlags.branch,
|
|
65
|
+
runtimeVersion: rawFlags['runtime-version'],
|
|
66
|
+
privateKeyPath: (_b = rawFlags['private-key-path']) !== null && _b !== void 0 ? _b : null,
|
|
67
|
+
action: action ? this.getAction(action) : undefined,
|
|
68
|
+
nonInteractive: rawFlags['non-interactive'],
|
|
69
|
+
json: rawFlags.json,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
262
72
|
}
|
|
263
73
|
exports.default = ChannelRollout;
|
|
264
74
|
_a = ChannelRollout;
|
|
@@ -270,17 +80,62 @@ ChannelRollout.args = [
|
|
|
270
80
|
},
|
|
271
81
|
];
|
|
272
82
|
ChannelRollout.flags = {
|
|
273
|
-
|
|
274
|
-
description: '
|
|
83
|
+
action: core_1.Flags.enum({
|
|
84
|
+
description: 'Rollout action to perform',
|
|
85
|
+
options: Object.values(ActionRawFlagValue),
|
|
275
86
|
required: false,
|
|
87
|
+
relationships: [
|
|
88
|
+
{
|
|
89
|
+
type: 'all',
|
|
90
|
+
flags: [
|
|
91
|
+
{
|
|
92
|
+
name: 'percent',
|
|
93
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
94
|
+
when: async (flags) => {
|
|
95
|
+
return (!!flags['non-interactive'] &&
|
|
96
|
+
(flags['action'] === ActionRawFlagValue.CREATE ||
|
|
97
|
+
flags['action'] === ActionRawFlagValue.EDIT));
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
name: 'outcome',
|
|
102
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
103
|
+
when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.END,
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'branch',
|
|
107
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
108
|
+
when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.CREATE,
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
name: 'runtime-version',
|
|
112
|
+
// eslint-disable-next-line async-protect/async-suffix
|
|
113
|
+
when: async (flags) => !!flags['non-interactive'] && flags['action'] === ActionRawFlagValue.CREATE,
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
},
|
|
117
|
+
],
|
|
276
118
|
}),
|
|
277
119
|
percent: core_1.Flags.integer({
|
|
278
|
-
description: '
|
|
120
|
+
description: 'Percent of users to send to the new branch. Use with --action=edit or --action=create',
|
|
279
121
|
required: false,
|
|
280
122
|
}),
|
|
281
|
-
|
|
282
|
-
description: '
|
|
283
|
-
|
|
123
|
+
outcome: core_1.Flags.enum({
|
|
124
|
+
description: 'End outcome of rollout. Use with --action=end',
|
|
125
|
+
options: Object.values(EndRollout_1.EndOutcome),
|
|
126
|
+
required: false,
|
|
127
|
+
}),
|
|
128
|
+
branch: core_1.Flags.string({
|
|
129
|
+
description: 'Branch to roll out. Use with --action=create',
|
|
130
|
+
required: false,
|
|
131
|
+
}),
|
|
132
|
+
'runtime-version': core_1.Flags.string({
|
|
133
|
+
description: 'Runtime version to target. Use with --action=create',
|
|
134
|
+
required: false,
|
|
135
|
+
}),
|
|
136
|
+
'private-key-path': core_1.Flags.string({
|
|
137
|
+
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.`,
|
|
138
|
+
required: false,
|
|
284
139
|
}),
|
|
285
140
|
...flags_1.EasNonInteractiveAndJsonFlags,
|
|
286
141
|
};
|
|
@@ -288,22 +143,3 @@ ChannelRollout.contextDefinition = {
|
|
|
288
143
|
..._a.ContextOptions.ProjectConfig,
|
|
289
144
|
..._a.ContextOptions.LoggedIn,
|
|
290
145
|
};
|
|
291
|
-
async function promptForBranchNameAsync({ graphqlClient, projectId, channelName, nonInteractive, json, }) {
|
|
292
|
-
if (nonInteractive) {
|
|
293
|
-
throw new Error('Must supply branch flag in non-interactive mode');
|
|
294
|
-
}
|
|
295
|
-
const { name: branchName } = await (0, queries_1.selectBranchOnAppAsync)(graphqlClient, {
|
|
296
|
-
projectId,
|
|
297
|
-
promptTitle: `Which branch would you like roll out on ${channelName}?`,
|
|
298
|
-
displayTextForListItem: updateBranch => ({
|
|
299
|
-
title: updateBranch.name,
|
|
300
|
-
}),
|
|
301
|
-
// discard limit and offset because this query is not their intended target
|
|
302
|
-
paginatedQueryOptions: {
|
|
303
|
-
json,
|
|
304
|
-
nonInteractive,
|
|
305
|
-
offset: 0,
|
|
306
|
-
},
|
|
307
|
-
});
|
|
308
|
-
return branchName;
|
|
309
|
-
}
|
|
@@ -1,23 +1,4 @@
|
|
|
1
|
-
import { Platform } from '@expo/config';
|
|
2
1
|
import EasCommand from '../../commandUtils/EasCommand';
|
|
3
|
-
type UpdateRepublishRawFlags = {
|
|
4
|
-
branch?: string;
|
|
5
|
-
channel?: string;
|
|
6
|
-
group?: string;
|
|
7
|
-
message?: string;
|
|
8
|
-
platform: string;
|
|
9
|
-
'non-interactive': boolean;
|
|
10
|
-
json?: boolean;
|
|
11
|
-
};
|
|
12
|
-
type UpdateRepublishFlags = {
|
|
13
|
-
branchName?: string;
|
|
14
|
-
channelName?: string;
|
|
15
|
-
groupId?: string;
|
|
16
|
-
updateMessage?: string;
|
|
17
|
-
platform: Platform[];
|
|
18
|
-
nonInteractive: boolean;
|
|
19
|
-
json: boolean;
|
|
20
|
-
};
|
|
21
2
|
export default class UpdateRepublish extends EasCommand {
|
|
22
3
|
static description: string;
|
|
23
4
|
static flags: {
|
|
@@ -28,12 +9,12 @@ export default class UpdateRepublish extends EasCommand {
|
|
|
28
9
|
group: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
29
10
|
message: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
30
11
|
platform: import("@oclif/core/lib/interfaces").OptionFlag<string>;
|
|
12
|
+
'private-key-path': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined>;
|
|
31
13
|
};
|
|
32
14
|
static contextDefinition: {
|
|
33
15
|
loggedIn: import("../../commandUtils/context/LoggedInContextField").default;
|
|
34
16
|
privateProjectConfig: import("../../commandUtils/context/PrivateProjectConfigContextField").PrivateProjectConfigContextField;
|
|
35
17
|
};
|
|
36
18
|
runAsync(): Promise<void>;
|
|
37
|
-
sanitizeFlags
|
|
19
|
+
private sanitizeFlags;
|
|
38
20
|
}
|
|
39
|
-
export {};
|