From ce32dce2aa01f10da8573c3a8432b175e076d06b Mon Sep 17 00:00:00 2001 From: Remy Sharp Date: Tue, 25 Oct 2016 09:39:59 +0100 Subject: [PATCH] Support `now alias` reading `config.alias` (#93) * feat: support `now alias` reading config.alias Reads the last deployment and will automatically alias to the package.json's config.alias property * chore: corrected typo on `console.time` * fix: use `root/now.alias` over `root/config.alias` --- bin/now-alias.js | 30 ++++++++- lib/index.js | 141 +++++++++---------------------------------- lib/read-metadata.js | 112 ++++++++++++++++++++++++++++++++++ 3 files changed, 171 insertions(+), 112 deletions(-) create mode 100644 lib/read-metadata.js diff --git a/bin/now-alias.js b/bin/now-alias.js index 00d2e78..b54181b 100755 --- a/bin/now-alias.js +++ b/bin/now-alias.js @@ -13,6 +13,7 @@ import login from '../lib/login' import * as cfg from '../lib/cfg' import {error} from '../lib/error' import toHost from '../lib/to-host' +import readMetaData from '../lib/read-metadata' const argv = minimist(process.argv.slice(2), { string: ['config', 'token'], @@ -89,7 +90,7 @@ const exit = code => { setTimeout(() => process.exit(code || 0), 100) } -if (argv.help || !subcommand) { +if (argv.help) { help() exit(0) } else { @@ -211,6 +212,11 @@ async function run(token) { break default: + if (argv._.length === 0) { + await realias(alias) + break + } + if (argv._.length === 2) { await alias.set(String(argv._[0]), String(argv._[1])) } else if (argv._.length >= 3) { @@ -285,3 +291,25 @@ function findAlias(alias, list) { return _alias } + +async function realias(alias) { + const path = process.cwd() + const {pkg, name} = await readMetaData(path, { + deploymentType: 'npm', // FIXME: hard coding settingsā€¦ + quiet: true // `quiet` + }) + + const pkgConfig = pkg ? pkg.now || {} : {} + const target = pkgConfig.alias + + // the user never intended to support aliases from the package + if (!target) { + help() + return exit(0) + } + + // now try to find the last deployment + const source = await alias.last(name) + + await alias.set(source.url, target) +} diff --git a/lib/index.js b/lib/index.js index 0dbb4d7..4cb308a 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,6 +1,6 @@ // Native import {homedir} from 'os' -import {basename, resolve as resolvePath} from 'path' +import {resolve as resolvePath} from 'path' import EventEmitter from 'events' // Packages @@ -11,13 +11,13 @@ import {parse as parseIni} from 'ini' import {readFile} from 'fs-promise' import resumer from 'resumer' import splitArray from 'split-array' -import {parse as parseDockerfile} from 'docker-file-parser' // Ours import {npm as getNpmFiles, docker as getDockerFiles} from './get-files' import ua from './ua' import hash from './hash' import Agent from './agent' +import readMetaData from './read-metadata' // how many concurrent HTTP/2 stream uploads const MAX_CONCURRENT = 10 @@ -47,122 +47,25 @@ export default class Now extends EventEmitter { }) { this._path = path - let pkg = {} - - let name - let description let files - if (deploymentType === 'npm') { - try { - pkg = await readFile(resolvePath(path, 'package.json')) - pkg = JSON.parse(pkg) - } catch (err) { - const e = Error(`Failed to read JSON in "${path}/package.json"`) - e.userError = true - throw e - } - - if (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start'])) { - const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' + - 'See: https://docs.npmjs.com/cli/start.') - e.userError = true - throw e - } - - if (pkg.name === null || typeof pkg.name !== 'string') { - name = basename(path) - - if (!quiet) { - console.log(`> No \`name\` in \`package.json\`, using ${chalk.bold(name)}`) - } - } else { - name = pkg.name - } - - description = pkg.description + const {pkg, name, description} = await readMetaData(path, { + deploymentType, + quiet + }) - if (this._debug) { - console.time('> [debug] Getting files') - } + if (this._debug) { + console.time('> [debug] Getting files') + } + if (deploymentType === 'npm') { files = await getNpmFiles(path, pkg, {debug: this._debug}) - - if (this._debug) { - console.timeEnd('> [debug] Getting files') - } - } else if (deploymentType === 'docker') { - let docker - try { - const dockerfile = await readFile(resolvePath(path, 'Dockerfile'), 'utf8') - docker = parseDockerfile(dockerfile, {includeComments: true}) - } catch (err) { - const e = Error(`Failed to parse "${path}/Dockerfile"`) - e.userError = true - throw e - } - - if (docker.length <= 0) { - const e = Error('No commands found in `Dockerfile`') - e.userError = true - throw e - } - - if (!docker.some(cmd => cmd.name === 'CMD')) { - const e = Error('No `CMD` found in `Dockerfile`. ' + - 'See: https://docs.docker.com/engine/reference/builder/#/cmd') - e.userError = true - throw e - } - - if (!docker.some(cmd => cmd.name === 'EXPOSE')) { - const e = Error('No `EXPOSE` found in `Dockerfile`. A port must be supplied. ' + - 'See: https://docs.docker.com/engine/reference/builder/#/expose') - e.userError = true - throw e - } - - const labels = {} - docker - .filter(cmd => cmd.name === 'LABEL') - .forEach(({args}) => { - for (const key in args) { - if (!{}.hasOwnProperty.call(args, key)) { - continue - } - - // unescape and convert into string - try { - labels[key] = JSON.parse(args[key]) - } catch (err) { - const e = Error(`Error parsing value for LABEL ${key} in \`Dockerfile\``) - e.userError = true - throw e - } - } - }) - - if (labels.name === null) { - name = basename(path) - - if (!quiet) { - console.log(`> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}`) - } - } else { - name = labels.name - } - - description = labels.description - - if (this._debug) { - console.time('> [debug] Getting files') - } - + } else { files = await getDockerFiles(path, {debug: this._debug}) + } - if (this._debug) { - console.timeEnd('> [debug] Getting files') - } + if (this._debug) { + console.timeEnd('> [debug] Getting files') } const nowProperties = pkg ? pkg.now || {} : {} @@ -423,6 +326,22 @@ export default class Now extends EventEmitter { }) } + async last(app) { + const deployments = await this.list(app) + + const last = deployments.sort((a, b) => { + return b.created - a.created + }).shift() + + if (!last) { + const e = Error(`No deployments found for "${app}"`) + e.userError = true + throw e + } + + return last + } + getNameservers(domain) { return new Promise(resolve => { let fallback = false diff --git a/lib/read-metadata.js b/lib/read-metadata.js new file mode 100644 index 0000000..c9a58eb --- /dev/null +++ b/lib/read-metadata.js @@ -0,0 +1,112 @@ +import {basename, resolve as resolvePath} from 'path' +import chalk from 'chalk' +import {readFile} from 'fs-promise' +import {parse as parseDockerfile} from 'docker-file-parser' + +export default async function (path, { + deploymentType = 'npm', + quiet = false +}) { + let pkg = {} + + let name + let description + + if (deploymentType === 'npm') { + try { + pkg = await readFile(resolvePath(path, 'package.json')) + pkg = JSON.parse(pkg) + } catch (err) { + const e = Error(`Failed to read JSON in "${path}/package.json"`) + e.userError = true + throw e + } + + if (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start'])) { + const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' + + 'See: https://docs.npmjs.com/cli/start.') + e.userError = true + throw e + } + + if (pkg.name === null || typeof pkg.name !== 'string') { + name = basename(path) + + if (!quiet) { + console.log(`> No \`name\` in \`package.json\`, using ${chalk.bold(name)}`) + } + } else { + name = pkg.name + } + + description = pkg.description + } else if (deploymentType === 'docker') { + let docker + try { + const dockerfile = await readFile(resolvePath(path, 'Dockerfile'), 'utf8') + docker = parseDockerfile(dockerfile, {includeComments: true}) + } catch (err) { + const e = Error(`Failed to parse "${path}/Dockerfile"`) + e.userError = true + throw e + } + + if (docker.length <= 0) { + const e = Error('No commands found in `Dockerfile`') + e.userError = true + throw e + } + + if (!docker.some(cmd => cmd.name === 'CMD')) { + const e = Error('No `CMD` found in `Dockerfile`. ' + + 'See: https://docs.docker.com/engine/reference/builder/#/cmd') + e.userError = true + throw e + } + + if (!docker.some(cmd => cmd.name === 'EXPOSE')) { + const e = Error('No `EXPOSE` found in `Dockerfile`. A port must be supplied. ' + + 'See: https://docs.docker.com/engine/reference/builder/#/expose') + e.userError = true + throw e + } + + const labels = {} + docker + .filter(cmd => cmd.name === 'LABEL') + .forEach(({args}) => { + for (const key in args) { + if (!{}.hasOwnProperty.call(args, key)) { + continue + } + + // unescape and convert into string + try { + labels[key] = JSON.parse(args[key]) + } catch (err) { + const e = Error(`Error parsing value for LABEL ${key} in \`Dockerfile\``) + e.userError = true + throw e + } + } + }) + + if (labels.name === null) { + name = basename(path) + + if (!quiet) { + console.log(`> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}`) + } + } else { + name = labels.name + } + + description = labels.description + } + + return { + name, + description, + pkg + } +}