import * as chalk from 'chalk'; import {command, flag, help, namespace} from 'oo-cli'; import {appContext} from '../../lib/AppContext'; import {PublishCommand} from '../directory/Publish'; import {BaseBuildCommand} from './BaseBuildCommand'; import {TerminalConfirm} from '../../lib/TerminalConfirm'; import * as semver from 'semver'; import * as fs from 'fs'; import * as path from 'path'; import {Rivendell} from '../../lib/Rivendell'; import AppReview = Rivendell.AppReview; import {formatError} from '../../lib/formatError'; import {die} from '../../lib/die'; @namespace('app') export class PrepareCommand extends BaseBuildCommand { @flag @help('Automatically publish if the upload is successful') public publish!: boolean; @flag('use-previous-app-env-values') @help('Use app env values (.env file) from the previous version of the app. Any values in local .env file will be ignored') public usePreviousAppEnvValues: boolean = false; @flag('bump-dev-version') @help('Bump dev version before building. Allows quickly testing dev versions') public bumpDevVersion!: boolean; @command @help('Validate, package, upload and build an app to prepare for publishing') public async prepare() { try { const context = appContext(); if (!context.appId || !context.version) { console.log(chalk.gray('App id or version missing in the manifest\n')); return; } const requiresReview = await this.requiresReview(context.version); if (requiresReview) { const isInReview = await this.isInReview(context.appId, context.version); const reviewMessage = isInReview ? chalk.gray( 'The app version is already in review. This will update the review and the app can\'t be published until it is approved. ' + `${chalk.yellow('Are you sure?')}` ) : chalk.gray( 'You are uploading a release version of the app. ' + 'This will start a review process and the app can\'t be published until it is approved. ' + `${chalk.yellow('Are you sure?')}` ); const reviewConfirmation = await TerminalConfirm.ask(reviewMessage, this.noPrompt, true); if (!reviewConfirmation) { return; } } if (context.version && this.bumpDevVersion) { if (this.isDevVersion(context.version)) { const bumpedVersion = semver.parse(context.version)?.inc('prerelease', 'dev').format(); if (!bumpedVersion) { console.log(chalk.red(`Cannot parse app version: ${context.version}`)); return; } console.log(chalk.gray(`Bumping dev version to ${bumpedVersion}`)); this.updateVersionInAppManifest(context.version, bumpedVersion); context.version = bumpedVersion; } else { console.log(chalk.yellow('Not a dev version. Ignoring --bump-dev-version flag')); } } await this.run(true, true, this.usePreviousAppEnvValues); await this.reviewAppVersion(context.appId as string, context.version as string); if (this.publish) { const cmd = new PublishCommand(); cmd.noProgress = this.noProgress; cmd.noPompt = this.noPrompt; cmd.appVersion = `${context.appId}@${context.version}`; await cmd.publish(); } else { if (requiresReview) { const appVersion = `${context.appId}@${context.version}`; const appReview = await Rivendell.getReviewUrl(appVersion); const reviewUrl = (appReview as AppReview).url; console.log(chalk.gray(`\nYour app has been sent to review. Your code will be available in a private repository on GitHub, ` + `and you will receive an invitation from GitHub by email for access.` + `It is important you accept this invitation in order to receive communications about your app from the review team.`)); console.log(chalk.gray(`\nOnce the review is approved, your app can be deployed to app directory.\n`)); console.log(chalk.gray(`You can run ${chalk.cyan(`ocp review open ${appVersion}`)} to open the review URL at any time.`)); console.log(chalk.gray(`The URL for your repo is ${chalk.cyan(`${reviewUrl}`)}\n`)); } else { console.log(chalk.gray( `\nUse ${chalk.cyan( `ocp directory publish ${context.appId}@${context.version}` )} to publish and release your app\n` )); } } // explicitly exit because we're using terminal kit process.exit(0); } catch (e: any) { die(formatError(e)); } } private updateVersionInAppManifest(oldVersion: string, newVersion: string) { const file = path.join(process.cwd(), 'app.yml'); fs.writeFileSync(file, fs.readFileSync(file, 'utf8').replace(new RegExp(`(version\\s*:\\s*)${oldVersion}`), `$1${newVersion}`)); } }