UNPKG

@liara/cli

Version:

The command line interface for Liara

444 lines (438 loc) • 16.1 kB
import ora from 'ora'; import path from 'path'; import fs from 'fs-extra'; import chalk from 'chalk'; import inquirer from 'inquirer'; import { Flags } from '@oclif/core'; import Command from '../base.js'; import { getPort } from '../utils/get-port.js'; import { promptPort } from '../utils/prompt-port.js'; import { AVAILABLE_PLATFORMS } from '../constants.js'; import detectPlatform from '../utils/detect-platform.js'; import supportedVersions from '../utils/get-supported-versions.js'; import TeamNotFoundError from '../errors/team-error.js'; class Init extends Command { async run() { var _a; const { args, flags } = await this.parse(Init); try { await this.setGotConfig(flags); this.log(chalk.yellow(`This command interactively creates a basic liara.json configuration file. It includes only the essential settings; additional configurations must be added manually. šŸ“š For more details on each field and its usage, visit: https://docs.liara.ir/paas/liarajson/. Afterwards, use liara deploy to deploy your app. šŸ”‘ Press ^C at any time to quit. `)); this.spinner = ora(); if (flags.y) { try { const dirName = path.basename(process.cwd()); const platform = detectPlatform(process.cwd()); const diskConfig = []; const configs = this.setLiaraJsonConfigs(getPort(platform) || 3000, dirName, 'iran', platform, (_a = supportedVersions(platform)) === null || _a === void 0 ? void 0 : _a.defaultVersion, diskConfig); await this.createLiaraJsonFile(configs); this.exit(0); } catch (error) { this.spinner.stop(); throw error; } } const team = await this.getTeam(flags['team-id']); const projects = await this.getPlatformsInfo(); const appName = await this.promptProjectName(projects, flags.name); const buildLocation = await this.buildLocationPrompt(flags['build-location']); const platform = await this.findPlatform(projects, appName, flags.platform); const port = await this.getAppPort(platform, flags.port, projects); const version = await this.promptPlatformVersion(platform, flags.version); const disks = await this.getAppDisks(appName, projects); const diskConfigs = await this.promptDiskConfig(disks, flags.disk, flags.path); const cron = await this.promptCron(platform); const healthCheck = await this.promptHealthCheck(); const configs = this.setLiaraJsonConfigs(port, appName, buildLocation, platform, version, diskConfigs, healthCheck, cron, team); await this.createLiaraJsonFile(configs); } catch (error) { this.spinner.stop(); throw error; } } async getPlatformsInfo() { try { this.spinner.start('Loading...'); const { projects } = await this.got('v1/projects').json(); this.spinner.stop(); return projects; } catch (error) { if (error.response && error.response.statusCode === 401) { throw new Error(`Authentication failed. Please log in using the 'liara login' command. If you are using an API token for authentication, please consider updating your API token. You can still create a sample 'liara.json' file using the 'liara init -y' command. `); } throw new Error(`There was something wrong while fetching your apps, You can still use 'liara init' with its flags. Use 'liara init --help' for more details.`); } } async promptProjectName(projects, flagValue) { if (flagValue) { return flagValue; } if (projects.length == 0) { const { project } = (await inquirer.prompt({ name: 'project', type: 'input', message: 'Enter app name:', })); return project; } const { project } = (await inquirer.prompt({ name: 'project', type: 'list', message: 'Select an app:', choices: [...projects.map((project) => project.project_id)], })); return project; } async findPlatform(projects, appName, flagsValue) { if (projects.length == 0) { const platform = await this.promptPlatform(); return platform; } if (flagsValue) { return flagsValue; } const project = projects.find((project) => { return project.project_id === appName; }); if (!project) { return 'static'; } return project.type; } async getAppPort(platform, flagValue, projects) { if (flagValue) { return flagValue; } const defaultPort = getPort(platform); if (!defaultPort) { const port = await promptPort(platform); return port; } return defaultPort; } async buildLocationPrompt(flagValue) { if (flagValue) { return flagValue; } const { location } = (await inquirer.prompt({ message: 'Specify the build location: ', name: 'location', type: 'list', default: 'iran', choices: ['iran', 'germany'], })); return location; } async promptPlatformVersion(platform, flagValue) { if (flagValue) { return flagValue; } const versions = supportedVersions(platform); if (versions) { let message; if (['flask', 'django'].includes(platform)) { message = 'Select python version'; } if (platform === 'laravel') { message = 'Select php version'; } if (platform === 'next') { message = 'Select node version'; } if (!message) { message = `Selcet ${platform} version: `; } const { version } = (await inquirer.prompt({ message: message || 'Select platform version', name: 'version', type: 'list', default: versions.defaultVersion, choices: versions.allVersions, })); return version; } } async createLiaraJsonFile(configs) { try { this.spinner.start('Loading...'); await fs.writeFile(`${process.cwd()}/liara.json`, JSON.stringify(configs, null, 2)); this.spinner.succeed('liara.json is successfully created!'); } catch (error) { throw new Error('There was a problem while creating liara.json!'); } } setLiaraJsonConfigs(port, appName, buildLocation, platform, platformVersion, diskConfigs, healthCheck, cron, team) { const versionKey = this.setVersionKey(platform, platformVersion); const configs = { port, platform, app: appName, build: { location: buildLocation, }, }; if (team) { configs['team-id'] = team._id; } if (cron) { configs['cron'] = cron; } if (healthCheck) { configs['healthCheck'] = healthCheck; } if (platformVersion) { configs[platform] = { [versionKey]: platformVersion, }; } if (diskConfigs) { configs['disks'] = diskConfigs.map((config) => { return { name: config.disk, mountTo: config.path }; }); } return configs; } setVersionKey(platform, platformVersion) { if (platformVersion) { if (['flask', 'django'].includes(platform)) { return 'pythonVersion'; } if (platform == 'laravel') { return 'phpVersion'; } if (platform == 'next') { return 'nodeVersion'; } return 'version'; } } async getTeam(teamId) { try { this.spinner.start('Loading...'); const teams = await this.got(`v2/teams`).json(); this.spinner.stop(); if (teamId) { const team = teams.teams.find((team) => team._id === teamId); if (!team) { throw new TeamNotFoundError(`You don't have a team with ID '${teamId}'`); } return team; } } catch (error) { if (error instanceof TeamNotFoundError) { throw new Error(error.message); } if (error.response && error.response.statusCode === 401) { throw new Error(`Authentication failed. Please log in using the 'liara login' command. If you are using an API token for authentication, please consider updating your API token. You can still create a sample 'liara.json' file using the 'liara init -y' command. `); } } } async getAppDisks(AppName, projects) { try { if (projects.length != 0) { this.spinner.start('Loading...'); const project = projects.find((project) => { return project.project_id === AppName; }); const disks = await this.got(`v1/projects/${project === null || project === void 0 ? void 0 : project._id}/disks`).json(); this.spinner.stop(); return disks.disks; } } catch (error) { if (error.response && error.response.statusCode === 401) { throw new Error(`Authentication failed. Please log in using the 'liara login' command. If you are using an API token for authentication, please consider updating your API token. You can still create a sample 'liara.json' file using the 'liara init -y' command. `); } throw new Error(`There was something wrong while fetching your app info, You can still use 'liara init' with it's flags. Use 'liara init --help' for command details.`); } } async promptPlatform() { const { platform } = (await inquirer.prompt({ name: 'platform', type: 'list', message: 'Select a platform:', choices: [...AVAILABLE_PLATFORMS.map((platform) => platform)], })); return platform; } async promptDiskConfig(disks, diskNameFlag, diskPathFlage) { let diskConfig = []; if (diskNameFlag && diskPathFlage) { return [ { disk: diskNameFlag, path: diskPathFlage, }, ]; } const { setDisk } = (await inquirer.prompt({ message: 'Configure disks? (Default: No)', type: 'confirm', name: 'setDisk', default: false, })); if (setDisk) { if (!disks || disks.length == 0) { const { diskName } = (await inquirer.prompt({ message: 'Enter Disk name: ', name: 'diskName', type: 'input', })); const { path } = (await inquirer.prompt({ message: 'Specify the mount location: ', name: 'path', type: 'input', })); diskConfig = [{ disk: diskName, path: path }]; return diskConfig; } let shouldContinue = true; while (shouldContinue && disks.length != 0) { const { diskName } = (await inquirer.prompt({ message: 'Select a Disk: ', name: 'diskName', choices: disks, type: 'list', })); const index = disks.findIndex((disk) => disk.name === diskName); disks.splice(index, 1); const { path } = (await inquirer.prompt({ message: `Mount path for ${diskName}: `, name: 'path', type: 'input', })); diskConfig.push({ disk: diskName, path }); if (disks.length != 0) { const continueAnswer = (await inquirer.prompt({ message: 'Add another disk? (Default: No)', type: 'confirm', default: false, name: 'shouldContinue', })); shouldContinue = continueAnswer.shouldContinue; } } return diskConfig; } } async promptCron(platform) { if (['next', 'laravel', 'django', 'php', 'python', 'flask', 'go'].includes(platform)) { const { setCronAnswer } = (await inquirer.prompt({ message: 'Configure cron? (Default: No)', type: 'confirm', name: 'setCronAnswer', default: false, })); if (setCronAnswer) { const { cron } = (await inquirer.prompt({ message: 'cron: ', type: 'input', name: 'cron', })); return cron.split(',').map((value) => value.trim()); } } } async promptHealthCheck() { const { setHealtCheckAnswer } = (await inquirer.prompt({ message: 'Configure healthcheck? (Default: No)', type: 'confirm', name: 'setHealtCheckAnswer', default: false, })); if (setHealtCheckAnswer) { const healthcheckConfigs = (await inquirer.prompt([ { message: 'command: ', type: 'input', name: 'command', }, { message: 'interval(ms): ', type: 'input', name: 'interval', }, { message: 'timeout(ms): ', type: 'input', name: 'timeout', }, { message: 'retries: ', type: 'input', name: 'retries', }, { message: 'startPeriod(ms): ', type: 'input', name: 'startPeriod', }, ])); return healthcheckConfigs; } } } Init.description = 'create a liara.json file'; Init.examples = ['<%= config.bin %> <%= command.id %>']; Init.flags = { ...Command.flags, y: Flags.boolean({ char: 'y', description: 'create an example file', aliases: [], }), name: Flags.string({ char: 'n', description: 'the name of the app', }), port: Flags.integer({ char: 'p', description: 'the port your app listens to', }), platform: Flags.string({ char: 'P', description: 'the platform your app needs to run on', }), version: Flags.string({ char: 'v', description: 'the version of the platform', }), 'build-location': Flags.string({ description: "name of the build's location", aliases: ['location'], }), disk: Flags.string({ description: 'the name of the disk', char: 'd', dependsOn: ['path'], }), path: Flags.string({ description: 'the path where the disk should be mounted', dependsOn: ['disk'], }), }; export default Init;