UNPKG

eas-cli

Version:

EAS command line tool

166 lines (165 loc) 7.99 kB
"use strict"; 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;