UNPKG

ionic

Version:

A tool for creating and developing Ionic Framework mobile apps.

166 lines (163 loc) • 7.29 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const cli_framework_1 = require("@ionic/cli-framework"); const format_1 = require("@ionic/cli-framework/utils/format"); const utils_process_1 = require("@ionic/utils-process"); const chalk = require("chalk"); const Debug = require("debug"); const guards_1 = require("../../guards"); const color_1 = require("../../lib/color"); const command_1 = require("../../lib/command"); const errors_1 = require("../../lib/errors"); const debug = Debug('ionic:commands:deploy:build'); class BuildCommand extends command_1.Command { async getMetadata() { const dashUrl = this.env.config.getDashUrl(); return { name: 'build', type: 'project', summary: 'Create a deploy build on Appflow', description: ` This command creates a deploy build on Ionic Appflow. While the build is running, it prints the remote build log to the terminal. Customizing the build: - The ${color_1.input('--environment')} and ${color_1.input('--channel')} options can be used to customize the groups of values exposed to the build. Apart from ${color_1.input('--commit')}, every option can be specified using the full name setup within the Appflow Dashboard[^dashboard]. `, footnotes: [ { id: 'dashboard', url: dashUrl, }, ], exampleCommands: [ '', '--environment="My Custom Environment Name"', '--commit=2345cd3305a1cf94de34e93b73a932f25baac77c', '--channel="Master"', '--channel="Master" --channel="My Custom Channel"', ], options: [ { name: 'environment', summary: 'The group of environment variables exposed to your build', type: String, spec: { value: 'name' }, }, { name: 'channel', summary: 'The channel you want to auto deploy the build to. This can be repeated multiple times if multiple channels need to be specified.', type: String, spec: { value: 'name' }, }, { name: 'commit', summary: 'Commit (defaults to HEAD)', type: String, groups: ["advanced" /* ADVANCED */], spec: { value: 'sha1' }, }, ], }; } async run(inputs, options) { if (!this.project) { throw new errors_1.FatalException(`Cannot run ${color_1.input('ionic deploy build')} outside a project directory.`); } const token = this.env.session.getUserToken(); const appflowId = await this.project.requireAppflowId(); if (!options.commit) { options.commit = (await this.env.shell.output('git', ['rev-parse', 'HEAD'], { cwd: this.project.directory })).trim(); debug(`Commit hash: ${color_1.strong(options.commit)}`); } let build = await this.createDeployBuild(appflowId, token, options); const buildId = build.job_id; const details = format_1.columnar([ ['App ID', color_1.strong(appflowId)], ['Build ID', color_1.strong(buildId.toString())], ['Commit', color_1.strong(`${build.commit.sha.substring(0, 6)} ${build.commit.note}`)], ['Environment', build.environment_name ? color_1.strong(build.environment_name) : color_1.weak('not set')], ['Channels', build.pending_channels.length ? build.pending_channels.map(v => color_1.strong(`"${v}"`)).join(', ') : color_1.weak('not set')], ], { vsep: ':' }); this.env.log.ok(`Build created\n` + details + '\n\n'); build = await this.tailBuildLog(appflowId, buildId, token); if (build.state !== 'success') { throw new Error('Build failed'); } } async createDeployBuild(appflowId, token, options) { const { req } = await this.env.client.make('POST', `/apps/${appflowId}/deploys/verbose_post`); let channels = []; if (options.channel) { if (typeof (options.channel) === 'string') { channels.push(String(options.channel)); } else if (typeof (options.channel) === 'object') { channels = channels.concat(options.channel); } } req.set('Authorization', `Bearer ${token}`).send({ commit_sha: options.commit, environment_name: options.environment, channel_names: channels ? channels : undefined, }); try { const res = await this.env.client.do(req); return res.data; } catch (e) { if (guards_1.isSuperAgentError(e)) { if (e.response.status === 401) { this.env.log.error('Try logging out and back in again.'); } const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; throw new errors_1.FatalException(`Unable to create build: ` + apiErrorMessage); } else { throw e; } } } async tailBuildLog(appflowId, buildId, token) { let build; let start = 0; const ws = this.env.log.createWriteStream(cli_framework_1.LOGGER_LEVELS.INFO, false); let isCreatedMessage = false; while (!(build && (build.state === 'success' || build.state === 'failed'))) { await utils_process_1.sleep(5000); build = await this.getDeployBuild(appflowId, buildId, token); if (build && build.state === 'created' && !isCreatedMessage) { ws.write(chalk.yellow('Concurrency limit reached: build will start as soon as other builds finish.')); isCreatedMessage = true; } const trace = build.job.trace; if (trace.length > start) { ws.write(trace.substring(start)); start = trace.length; } } ws.end(); return build; } async getDeployBuild(appflowId, buildId, token) { const { req } = await this.env.client.make('GET', `/apps/${appflowId}/deploys/${buildId}`); req.set('Authorization', `Bearer ${token}`).send(); try { const res = await this.env.client.do(req); return res.data; } catch (e) { if (guards_1.isSuperAgentError(e)) { if (e.response.status === 401) { this.env.log.error('Try logging out and back in again.'); } const apiErrorMessage = (e.response.body.error && e.response.body.error.message) ? e.response.body.error.message : 'Api Error'; throw new errors_1.FatalException(`Unable to get build ${buildId}: ` + apiErrorMessage); } else { throw e; } } } } exports.BuildCommand = BuildCommand;