UNPKG

@swell/cli

Version:

Swell's command line interface/utility

185 lines (183 loc) 7.81 kB
import { confirm } from '@inquirer/prompts'; import { Flags } from '@oclif/core'; import ora from 'ora'; import * as semver from 'semver'; import { readRcFile } from '../../lib/app-config.js'; import config from '../../lib/config.js'; import { selectEnvironmentId, selectLoggedInStoreId, } from '../../lib/stores.js'; import style from '../../lib/style.js'; import { RemoteAppCommand } from '../../remote-app-command.js'; export default class AppInstall extends RemoteAppCommand { static description = `Without a version specified, install the latest version of the app. Without a store specified, install the app in the Live environment of the current store.`; static examples = [ 'swell app install', 'swell app install -v 3.2.7', 'swell app install -v 1.0.0 -s my-store-id', ]; static flags = { env: Flags.string({ char: 'e', description: 'target environment to install the app, defaults to live', }), store: Flags.string({ char: 's', description: 'target store id to install the app, defaults to the current store', }), version: Flags.string({ char: 'v', description: 'version of the app to install, defaults to latest', async parse(input) { if (!semver.valid(input)) { throw new Error(`Invalid version: ${input}`); } return input; }, }), }; static orientation = { env: 'test', }; static summary = 'Install an existing app in another store environment.'; async run() { const { flags } = await this.parse(AppInstall); const { env, store, version } = flags; this.app = await this.getAppWithConfig(); const versions = await this.getVersions(version); if (versions.length === 0) { this.error(`${version ? `Version ${version} not found` : 'No versions found'} for ${style.appConfigValue(this.app.name)} app. ${version ? '' : `Create a version with the ${style.command('swell app version')} command first.`}`); } const versionToInstall = version ? versions.find((ver) => ver.version === version)?.version : versions[0].version; let storeToInstall = store; if (!storeToInstall) { const loggedInStores = config.get('stores') || []; if (loggedInStores.length > 0) { storeToInstall = await selectLoggedInStoreId('Which store do you want to install the app in?'); } if (!storeToInstall) { return; } } let envToInstall = env; if (!envToInstall) { envToInstall = await selectEnvironmentId(); } if (envToInstall === 'live') { envToInstall = undefined; } if (envToInstall && envToInstall !== 'test') { this.error(`${envToInstall} is not a valid environment.`); } if (envToInstall === 'test' && storeToInstall === this.app.client_id) { this.error(`You cannot install an app in the test environment of the same store.`); } config.ensureLoggedIn(storeToInstall); await this.api.setStoreEnv(storeToInstall, envToInstall); // check if app is already installed on store const existingInstalledApp = await this.getInstalledApp(); const existingVersion = existingInstalledApp?.id ? existingInstalledApp.version : null; // confirm install unless params were passed explicitly let extraMessage = ''; if (!store || !env) { const continueInstall = await confirm({ message: this.installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion), }); if (!continueInstall) { // we don't want to throw an error as it is a valid exit return; } } else { extraMessage = ` ${style.appConfigValue(this.app.name)} v${versionToInstall} on ${style.storeId(storeToInstall)} [${envToInstall || 'live'}]`; } const shouldContinue = existingVersion || (await this.confirmRemoveDevelopmentApp(storeToInstall)); if (!shouldContinue) { return; } const spinner = ora(); this.log(); // install or update app depending on whether it's already installed let request; if (existingInstalledApp?.id) { spinner.start(`Updating${extraMessage}`); request = async () => this.updateInstalledApp(versionToInstall, spinner); } else { spinner.start(`Installing${extraMessage}`); request = async () => this.installApp(versionToInstall, spinner); } const response = await this.handleRequestErrors(request, () => spinner.fail('Something went wrong.')); if (response?.errors) { spinner.fail(`Error installing app: ${JSON.stringify(response.errors)}`); } else { spinner.succeed(`${existingInstalledApp?.id ? 'Update' : 'Install'} complete.\n`); this.log(`Manage app settings in your dashboard at ${style.link(this.dashboardUrl({ flags: { env: envToInstall } }, storeToInstall))}.`); } } async confirmRemoveDevelopmentApp(storeToInstall) { let sourceAppId; try { const sourceApp = await readRcFile(this.swellRcPath()); sourceAppId = sourceApp?.id; } catch { // noop } const existingSourcedApps = sourceAppId ? await this.api.get({ adminPath: `/client/apps` }, { query: { source_id: sourceAppId }, }) : null; if (existingSourcedApps?.results[0]?.id) { const continueInstall = await confirm({ default: false, message: `${style.storeId(storeToInstall)} has a development version of ${style.appConfigValue(this.app.name)}. Do you want to replace it with an installed instance?`, }); if (!continueInstall) { return false; } // uninstall the existing app const spinner = ora(); spinner.start('Removing development app...'); try { await this.api.put({ adminPath: `/client/apps/${existingSourcedApps.results[0].app_id}`, }, { body: { uninstalled: true }, }); spinner.succeed('Development app removed.'); } catch (error) { spinner.fail(`Error removing development app from ${style.storeId(storeToInstall)}.`); this.logError(error.message); return false; } } return true; } installConfirmationMessage(storeToInstall, envToInstall, versionToInstall, existingVersion) { const message = []; if (existingVersion && semver.gt(versionToInstall, existingVersion)) { message.push('Updating'); } else if (existingVersion && semver.lt(versionToInstall, existingVersion)) { message.push('Rolling back'); } else { message.push('Install'); } let versionToShow = versionToInstall; versionToShow += versionToInstall === this.app.version ? ' (latest)' : ''; message.push(style.appConfigValue(this.app.name), `v${versionToShow}`, `on ${style.storeId(storeToInstall)} [${envToInstall || 'live'}]?`); return message.join(' '); } }