From 0656e1b1eb5a86a0c01d35d8b46243b31a215999 Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 30 Mar 2016 20:28:46 -0700 Subject: [PATCH 1/5] package: add `ansi-escapes` and `socket.io-client` --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 6ed4ac2..537ab22 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "retry": "0.9.0", "spdy": "3.2.3", "split-array": "1.0.1", - "minimist": "1.2.0" + "minimist": "1.2.0", + "ansi-escapes": "1.3.0", + "socket.io-client": "1.4.5" }, "devDependencies": { "alpha-sort": "1.0.2", From a2b90e5376789cb3d4a16530ac3d5e80c0652fc1 Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 30 Mar 2016 20:29:44 -0700 Subject: [PATCH 2/5] package: use more accurate terminology (`url` vs `host`) --- lib/index.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 084db4e..5623c79 100644 --- a/lib/index.js +++ b/lib/index.js @@ -103,7 +103,7 @@ export default class Now extends EventEmitter { }, { retries: 3, minTimeout: 2500, onRetry: this._onRetry }); this._id = deployment.deploymentId; - this._url = deployment.url; + this._host = deployment.url; this._missing = deployment.missing || []; return this._url; @@ -165,8 +165,16 @@ export default class Now extends EventEmitter { this._agent.close(); } + get id () { + return this._id; + } + get url () { - return this._url; + return `https://${this._host}`; + } + + get host () { + return this._host; } get syncAmount () { From 54d98c6a4eacc3b4c8785ead1df734adabc3fd36 Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 30 Mar 2016 20:30:01 -0700 Subject: [PATCH 3/5] index: prevent shadowing --- lib/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/index.js b/lib/index.js index 5623c79..ac3f41b 100644 --- a/lib/index.js +++ b/lib/index.js @@ -186,10 +186,10 @@ export default class Now extends EventEmitter { return this._syncAmount; } - async _fetch (url, opts) { + async _fetch (_url, opts) { opts.headers = opts.headers || {}; opts.headers.authorization = `Bearer ${this._token}`; - return await this._agent.fetch(url, opts); + return await this._agent.fetch(_url, opts); } } From c89f0603eadde3977cb97feed7b61fdb0c69136e Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 30 Mar 2016 20:30:18 -0700 Subject: [PATCH 4/5] print logs from build --- bin/now | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/bin/now b/bin/now index 6db140e..5f53bf9 100755 --- a/bin/now +++ b/bin/now @@ -6,6 +6,7 @@ import login from '../lib/login'; import * as cfg from '../lib/cfg'; import { version } from '../../package'; import checkUpdate from '../lib/check-update'; +import Logger from '../lib/build-logger'; import bytes from 'bytes'; import chalk from 'chalk'; import minimist from 'minimist'; @@ -105,9 +106,9 @@ async function sync (token) { if (clipboard) { try { await copy(url); - console.log(`> ${chalk.cyan('Ready!')} ${chalk.bold(`https://${url}`)} (copied to clipboard) [${elapsed}]`); + console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} (copied to clipboard) [${elapsed}]`); } catch (err) { - console.log(`> ${chalk.cyan('Ready!')} ${chalk.bold(`https://${url}`)} [${elapsed}]`); + console.log(`${chalk.cyan('> Ready!')} ${chalk.bold(url)} [${elapsed}]`); } } else { console.log(`> ${url} [${elapsed}]`); @@ -117,8 +118,12 @@ async function sync (token) { const complete = () => { const elapsed_u = ms(new Date() - start_u); console.log(`> Sync complete (${bytes(now.syncAmount)}) [${elapsed_u}] `); + + // show build logs + printLogs(now.host); + + // close http2 agent now.close(); - exit(0); }; if (now.syncAmount) { @@ -148,11 +153,29 @@ async function sync (token) { }); } else { console.log('> Sync complete (cached)'); + + if (force) { + // show build logs + printLogs(now.host); + } + now.close(); - exit(0); } } +function printLogs (host) { + // log build + const logger = new Logger(host); + logger.on('error', () => { + console.log('> Connection error.'); + exit(1); + }); + logger.on('close', () => { + console.log(`${chalk.cyan('> Build completed!')}`); + exit(0); + }); +} + function handleError (err) { if (403 === err.status) { error('Authentication error. Run `now -L` or `now --login` to log-in again.'); From 8663d3f07236cda44a1beccb704956b8e5caee2d Mon Sep 17 00:00:00 2001 From: Guillermo Rauch Date: Wed, 30 Mar 2016 20:30:40 -0700 Subject: [PATCH 5/5] build-logger: connect to `io.now.sh` and render logs --- lib/build-logger.js | 94 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 lib/build-logger.js diff --git a/lib/build-logger.js b/lib/build-logger.js new file mode 100644 index 0000000..5cc5178 --- /dev/null +++ b/lib/build-logger.js @@ -0,0 +1,94 @@ +import ansi from 'ansi-escapes'; +import io from 'socket.io-client'; +import chalk from 'chalk'; +import EventEmitter from 'events'; + +export default class Logger extends EventEmitter { + + constructor (host) { + super(); + this.host = host; + this.socket = io(`https://io.now.sh?host=${host}`); + this.socket.once('error', this.onError.bind(this)); + this.socket.on('state', this.onState.bind(this)); + this.socket.on('logs', this.onLog.bind(this)); + this.socket.on('backend', this.onComplete.bind(this)); + this.lines = new Lines(10); + } + + onState (state) { + if (!state.id) { + console.error('> Deployment not found'); + this.emit('error'); + } + + if (state.backend) { + this.onComplete(); + return; + } + + if (state.logs) { + state.logs.forEach(this.onLog, this); + } + } + + onLog (log) { + if ('command' === log.type) { + if ('npm install' === log.data) { + console.log('> Building'); + } + + console.log(`${chalk.gray('>')} ▲ ${log.data}`); + this.lines.reset(); + } else if ('stderr' === log.type) { + log.data.split('\n').forEach((v) => { + if (v.length) { + console.error(chalk.red(`> ${v}`)); + } + }); + this.lines.reset(); + } else if ('stdout' === log.type) { + log.data.split('\n').forEach((v) => { + if (v.length) { + this.lines.write(`${chalk.gray('>')} ${v}`); + } + }); + } + } + + onComplete () { + this.socket.disconnect(); + this.emit('close'); + } + + onError () { + this.emit('error'); + } + +} + +class Lines { + + constructor (maxLines = 100) { + this.max = maxLines; + this.buf = []; + } + + write (str) { + const { max, buf } = this; + + if (buf.length === max) { + process.stdout.write(ansi.eraseLines(max + 1)); + buf.shift(); + buf.forEach((line) => console.log(line)); + } + + buf.push(str); + console.log(str); + } + + reset () { + this.buf = []; + } + +}