eas-cli
Version:
EAS command line tool
166 lines (165 loc) • 7.99 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.EndRollout = exports.EndOutcome = void 0;
const tslib_1 = require("tslib");
const assert_1 = tslib_1.__importDefault(require("assert"));
const chalk_1 = tslib_1.__importDefault(require("chalk"));
const branch_mapping_1 = require("../../channel/branch-mapping");
const edit_1 = require("../../commands/channel/edit");
const ChannelQuery_1 = require("../../graphql/queries/ChannelQuery");
const log_1 = tslib_1.__importDefault(require("../../log"));
const prompts_1 = require("../../prompts");
const republish_1 = require("../../update/republish");
const code_signing_1 = require("../../utils/code-signing");
const formatFields_1 = tslib_1.__importDefault(require("../../utils/formatFields"));
const branch_mapping_2 = require("../branch-mapping");
const utils_1 = require("../utils");
var EndOutcome;
(function (EndOutcome) {
EndOutcome["REPUBLISH_AND_REVERT"] = "republish-and-revert";
EndOutcome["REVERT"] = "revert";
})(EndOutcome || (exports.EndOutcome = EndOutcome = {}));
function isNonInteractiveOptions(options) {
return !!options.outcome;
}
function assertNonInteractiveOptions(options) {
(0, assert_1.default)(isNonInteractiveOptions(options), '--outcome is required for ending a rollout in non-interactive mode.');
}
/**
* End an existing rollout for the project.
*/
class EndRollout {
channelInfo;
options;
constructor(channelInfo, options) {
this.channelInfo = channelInfo;
this.options = options;
}
async runAsync(ctx) {
const { nonInteractive } = ctx;
if (nonInteractive) {
assertNonInteractiveOptions(this.options);
}
const channelObject = await this.getChannelObjectAsync(ctx);
const rollout = (0, branch_mapping_2.getRollout)(channelObject);
const { rolledOutBranch } = rollout;
const rolledOutUpdateGroup = rolledOutBranch.updateGroups[0];
let outcome;
if (!rolledOutUpdateGroup) {
log_1.default.log(`⚠️ There is no update group being served on the ${rolledOutBranch.name} branch.`);
(0, assert_1.default)(this.options.outcome !== EndOutcome.REPUBLISH_AND_REVERT, `The only valid outcome for this rollout is to revert users back to the ${rollout.defaultBranch.name} branch. `);
outcome = EndOutcome.REVERT;
}
else {
outcome = this.options.outcome ?? (await this.selectOutcomeAsync(rollout));
}
const didConfirm = await this.confirmOutcomeAsync(ctx, outcome, rollout);
if (!didConfirm) {
throw new Error('Aborting...');
}
return await this.performOutcomeAsync(ctx, rollout, outcome);
}
async getChannelObjectAsync(ctx) {
const { graphqlClient, app } = ctx;
const { projectId } = app;
if (!(0, branch_mapping_2.isRollout)(this.channelInfo)) {
throw new Error(`The channel ${chalk_1.default.bold(this.channelInfo.name)} is not a rollout. To end a rollout, you must specify a channel with an ongoing rollout.`);
}
const rolloutInfo = (0, branch_mapping_2.getRolloutInfo)(this.channelInfo);
return await ChannelQuery_1.ChannelQuery.viewUpdateChannelAsync(graphqlClient, {
appId: projectId,
channelName: this.channelInfo.name,
...((0, branch_mapping_2.isConstrainedRolloutInfo)(rolloutInfo)
? { filter: { runtimeVersions: [rolloutInfo.runtimeVersion] } }
: {}),
});
}
async selectOutcomeAsync(rollout) {
const { rolledOutBranch, percentRolledOut, defaultBranch } = rollout;
const rolledOutUpdateGroup = rolledOutBranch.updateGroups[0];
const defaultUpdateGroup = defaultBranch.updateGroups[0];
const outcomes = [
{
value: EndOutcome.REPUBLISH_AND_REVERT,
title: (0, utils_1.formatBranchWithUpdateGroup)(rolledOutUpdateGroup, rolledOutBranch, percentRolledOut),
},
{
value: EndOutcome.REVERT,
title: (0, utils_1.formatBranchWithUpdateGroup)(defaultUpdateGroup, defaultBranch, 100 - percentRolledOut),
},
];
const { outcome: selectedOutcome } = await (0, prompts_1.promptAsync)({
type: 'select',
name: 'outcome',
message: `Which update group would you like to serve?`,
choices: outcomes,
});
log_1.default.newLine();
if (selectedOutcome === EndOutcome.REPUBLISH_AND_REVERT) {
log_1.default.log(`➡️ 📱 The update group you chose is served by branch ${chalk_1.default.bold(rolledOutBranch.name)}`);
}
else {
log_1.default.log(`➡️ 📱 The update group you chose is served by branch ${chalk_1.default.bold(defaultBranch.name)}`);
}
return selectedOutcome;
}
async performOutcomeAsync(ctx, rollout, outcome) {
const { graphqlClient, app } = ctx;
const { rolledOutBranch, defaultBranch } = rollout;
const rolledOutUpdateGroup = rolledOutBranch.updateGroups[0];
if (outcome === EndOutcome.REPUBLISH_AND_REVERT) {
const codeSigningInfo = await (0, code_signing_1.getCodeSigningInfoAsync)(ctx.app.exp, this.options.privateKeyPath ?? undefined);
const arbitraryUpdate = rolledOutUpdateGroup[0];
const { message: oldUpdateMessage, group: oldGroupId } = arbitraryUpdate;
const newUpdateMessage = `Republish "${oldUpdateMessage}" - group: ${oldGroupId}`;
await (0, republish_1.republishAsync)({
graphqlClient,
app,
updatesToPublish: rolledOutUpdateGroup.map(update => ({
...update,
groupId: update.group,
branchId: update.branch.id,
branchName: update.branch.name,
})),
codeSigningInfo,
targetBranch: { branchId: defaultBranch.id, branchName: defaultBranch.name },
updateMessage: newUpdateMessage,
});
}
const alwaysTrueDefaultBranchMapping = (0, branch_mapping_1.getAlwaysTrueBranchMapping)(defaultBranch.id);
const newChannelInfo = await (0, edit_1.updateChannelBranchMappingAsync)(graphqlClient, {
channelId: this.channelInfo.id,
branchMapping: JSON.stringify(alwaysTrueDefaultBranchMapping),
});
log_1.default.addNewLineIfNone();
log_1.default.log(`⬅️ Reverted all users back to branch ${chalk_1.default.bold(defaultBranch.name)}`);
log_1.default.log(`✅ Successfully ended rollout`);
return newChannelInfo;
}
async confirmOutcomeAsync(ctx, selectedOutcome, rollout) {
const { nonInteractive } = ctx;
if (nonInteractive) {
return true;
}
const { rolledOutBranch, defaultBranch } = rollout;
log_1.default.newLine();
if (selectedOutcome === EndOutcome.REPUBLISH_AND_REVERT) {
log_1.default.log(`Ending the rollout will do the following:`);
const actions = (0, formatFields_1.default)([
{
label: '1.',
value: `🔁 Republish the update group from ${chalk_1.default.bold(rolledOutBranch.name)} onto ${chalk_1.default.bold(defaultBranch.name)}`,
},
{ label: '2.', value: `⬅️ Revert all users back to ${chalk_1.default.bold(defaultBranch.name)}` },
]);
log_1.default.log(actions);
}
else {
log_1.default.log(`⬅️ Ending the rollout will revert all users back to ${chalk_1.default.bold(defaultBranch.name)}`);
}
return await (0, prompts_1.confirmAsync)({
message: `Continue?`,
});
}
}
exports.EndRollout = EndRollout;
;