#!/usr/bin/env node import Progress from 'progress'; import copy from '../lib/copy'; import { resolve } from 'path'; import login from '../lib/login'; import * as cfg from '../lib/cfg'; import { version } from '../../package'; import Logger from '../lib/build-logger'; import bytes from 'bytes'; import chalk from 'chalk'; import minimist from 'minimist'; import Now from '../lib'; import ms from 'ms'; import { handleError, error } from '../lib/error'; const argv = minimist(process.argv.slice(2), { boolean: ['help', 'version', 'debug', 'force', 'login', 'no-clipboard', 'forward-npm'], alias: { help: 'h', debug: 'd', version: 'v', force: 'f', forceSync: 'F', login: 'L', 'no-clipboard': 'C', 'forward-npm': 'N' } }); const help = () => { console.log(` ${chalk.bold('𝚫 now')} [options] ${chalk.dim('Commands:')} deploy [path] performs a deployment ${chalk.bold('(default)')} ls | list [app] list deployments rm | remove [id] remove a deployment ln | alias [id] [url] configures aliases for deployments domains [name] manages your domain names help [cmd] displays complete help for [cmd] ${chalk.dim('Options:')} -h, --help output usage information -v, --version output the version number -d, --debug debug mode [off] -f, --force force a new deployment even if nothing has changed -L, --login configure login -C, --no-clipboard do not attempt to copy URL to clipboard -N, --forward-npm Forward login information to install private NPM modules ${chalk.dim('Examples:')} ${chalk.gray('–')} Deploys the current directory ${chalk.cyan('$ now')} ${chalk.gray('–')} Deploys a custom path ${chalk.dim('`/usr/src/project`')} ${chalk.cyan('$ now /usr/src/project')} ${chalk.gray('–')} Lists all deployments with their IDs ${chalk.cyan('$ now ls')} ${chalk.gray('–')} Associates deployment ${chalk.dim('`deploymentId`')} with ${chalk.dim('`custom-domain.com`')} ${chalk.cyan('$ now alias deploymentId custom-domain.com')} ${chalk.gray('–')} Displays comprehensive help for the subcommand ${chalk.dim('`list`')} ${chalk.cyan('$ now help list')} `); }; let path = argv._[0]; if (path) { if ('/' !== path[0]) { path = resolve(process.cwd(), path); } } else { path = process.cwd(); } const exit = (code) => { // we give stdout some time to flush out // because there's a node bug where // stdout writes are asynchronous // https://github.com/nodejs/node/issues/6456 setTimeout(() => process.exit(code || 0), 100); }; // options const debug = argv.debug; const clipboard = !argv['no-clipboard']; const forwardNpm = argv['forward-npm']; const forceNew = argv.force; const forceSync = argv.forceSync; const shouldLogin = argv.login; const apiUrl = argv.url || 'https://api.zeit.co'; const isTTY = process.stdout.isTTY; const quiet = !isTTY; const config = cfg.read(); const alwaysForwardNpm = config.forwardNpm; if (argv.h || argv.help) { help(); exit(0); } else if (argv.v || argv.version) { console.log(chalk.bold('𝚫 now'), version); process.exit(0); } else if (!config.token || shouldLogin) { login(apiUrl) .then((token) => { if (shouldLogin) { console.log('> Logged in successfully. Token saved in ~/.now.json'); process.exit(0); } else { sync(token).catch((err) => { error(`Unknown error: ${err.stack}`); process.exit(1); }); } }) .catch((e) => { error(`Authentication error – ${e.message}`); process.exit(1); }); } else { sync(config.token).catch((err) => { error(`Unknown error: ${err.stack}`); process.exit(1); }); } async function sync (token) { const start = Date.now(); if (!quiet) { console.log(`> Deploying "${path}"`); } const now = new Now(apiUrl, token, { debug }); try { await now.create(path, { forceNew, forceSync, forwardNpm: alwaysForwardNpm || forwardNpm, quiet }); } catch (err) { handleError(err); process.exit(1); } const { url } = now; const elapsed = ms(new Date() - start); if (isTTY) { if (clipboard) { try { await copy(url); console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]`); } catch (err) { console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} [${elapsed}]`); } } else { console.log(`> ${url} [${elapsed}]`); } } else { process.stdout.write(url); } const start_u = new Date(); const complete = () => { if (!quiet) { const elapsed_u = ms(new Date() - start_u); console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsed_u}] `); console.log('> Initializing…'); } // close http2 agent now.close(); // show build logs printLogs(now.host); }; if (now.syncAmount) { const bar = new Progress('> Upload [:bar] :percent :etas', { width: 20, complete: '=', incomplete: '', total: now.syncAmount }); now.upload(); now.on('upload', ({ names, data }) => { const amount = data.length; if (debug) { console.log(`> [debug] Uploaded: ${names.join(' ')} (${bytes(data.length)})`); } bar.tick(amount); }); now.on('complete', complete); now.on('error', (err) => { error('Upload failed'); handleError(err); process.exit(1); }); } else { if (!quiet) { console.log(`> Initializing…`); } // close http2 agent now.close(); // show build logs printLogs(now.host); } } function printLogs (host) { // log build const logger = new Logger(host, { debug, quiet }); logger.on('error', () => { process.exit(1); }); logger.on('close', () => { if (!quiet) { console.log(`${chalk.cyan('> Deployment complete!')}`); } process.exit(0); }); }