Browse Source

add now.json support

master
Guillermo Rauch 8 years ago
parent
commit
83326fd8c4
  1. 5
      bin/now-alias.js
  2. 4
      bin/now-deploy.js
  3. 29
      lib/get-files.js
  4. 15
      lib/index.js
  5. 63
      lib/read-metadata.js
  6. 1
      test/_fixtures/now-json-docker/Dockerfile
  7. 1
      test/_fixtures/now-json-docker/a.js
  8. 1
      test/_fixtures/now-json-docker/b.js
  9. 6
      test/_fixtures/now-json-docker/now.json
  10. 3
      test/_fixtures/now-json-throws/now.json
  11. 9
      test/_fixtures/now-json-throws/package.json
  12. 1
      test/_fixtures/now-json/a.js
  13. 1
      test/_fixtures/now-json/b.js
  14. 5
      test/_fixtures/now-json/now.json
  15. 9
      test/_fixtures/now-json/package.json
  16. 47
      test/index.js

5
bin/now-alias.js

@ -296,13 +296,12 @@ function findAlias(alias, list) {
async function realias(alias) { async function realias(alias) {
const path = process.cwd() const path = process.cwd()
const {pkg, name} = await readMetaData(path, { const {nowConfig, name} = await readMetaData(path, {
deploymentType: 'npm', // hard coding settings… deploymentType: 'npm', // hard coding settings…
quiet: true // `quiet` quiet: true // `quiet`
}) })
const pkgConfig = pkg ? pkg.now || {} : {} const target = nowConfig && nowConfig.alias
const target = pkgConfig.alias
// the user never intended to support aliases from the package // the user never intended to support aliases from the package
if (!target) { if (!target) {

4
bin/now-deploy.js

@ -363,7 +363,7 @@ async function sync(token) {
} }
} }
const {pkg: {now: pkgConfig = {}} = {}} = await readMetaData(path, { const {nowConfig} = await readMetaData(path, {
deploymentType, deploymentType,
deploymentName, deploymentName,
isStatic, isStatic,
@ -373,7 +373,7 @@ async function sync(token) {
const now = new Now(apiUrl, token, {debug}) const now = new Now(apiUrl, token, {debug})
// Merge `now.env` from package.json with `-e` arguments. // Merge `now.env` from package.json with `-e` arguments.
const pkgEnv = pkgConfig.env const pkgEnv = nowConfig && nowConfig.env
const envs = [ const envs = [
...Object.keys(pkgEnv || {}).map(k => `${k}=${pkgEnv[k]}`), ...Object.keys(pkgEnv || {}).map(k => `${k}=${pkgEnv[k]}`),
...[].concat(argv.env || []) ...[].concat(argv.env || [])

29
lib/get-files.js

@ -24,11 +24,15 @@ const IGNORED = require('./ignored')
* @return {Array} comprehensive list of paths to sync * @return {Array} comprehensive list of paths to sync
*/ */
async function npm(path, pkg, { async function npm(path, pkg, nowConfig = null, {
limit = null, limit = null,
hasNowJson = false,
debug = false debug = false
} = {}) { } = {}) {
const whitelist = pkg.now && pkg.now.files ? pkg.now.files : pkg.files const whitelist = (nowConfig && nowConfig.files) || pkg.files
// the package.json `files` whitelist still
// honors ignores: https://docs.npmjs.com/files/package.json#files
const search_ = whitelist || ['.'] const search_ = whitelist || ['.']
// convert all filenames into absolute paths // convert all filenames into absolute paths
const search = Array.prototype.concat.apply([], (await Promise.all(search_.map(file => glob(file, {cwd: path, absolute: true, dot: true}))))) const search = Array.prototype.concat.apply([], (await Promise.all(search_.map(file => glob(file, {cwd: path, absolute: true, dot: true})))))
@ -92,6 +96,10 @@ async function npm(path, pkg, {
// source: https://docs.npmjs.com/files/package.json#files // source: https://docs.npmjs.com/files/package.json#files
files.push(asAbsolute('package.json', path)) files.push(asAbsolute('package.json', path))
if (hasNowJson) {
files.push(asAbsolute('now.json', path))
}
// get files // get files
return unique(files) return unique(files)
} }
@ -125,12 +133,17 @@ const asAbsolute = function (path, parent) {
* @return {Array} comprehensive list of paths to sync * @return {Array} comprehensive list of paths to sync
*/ */
async function docker(path, { async function docker(path, nowConfig = null, {
limit = null, limit = null,
hasNowJson = false,
debug = false debug = false
} = {}) { } = {}) {
const whitelist = nowConfig && nowConfig.files
// base search path // base search path
const search_ = ['.'] // the now.json `files` whitelist still
// honors ignores: https://docs.npmjs.com/files/package.json#files
const search_ = whitelist || ['.']
// convert all filenames into absolute paths // convert all filenames into absolute paths
const search = search_.map(file => asAbsolute(file, path)) const search = search_.map(file => asAbsolute(file, path))
@ -176,6 +189,10 @@ async function docker(path, {
// source: https://docs.npmjs.com/files/package.json#files // source: https://docs.npmjs.com/files/package.json#files
files.push(asAbsolute('Dockerfile', path)) files.push(asAbsolute('Dockerfile', path))
if (hasNowJson) {
files.push(asAbsolute('now.json', path))
}
// get files // get files
return unique(files) return unique(files)
} }
@ -206,7 +223,7 @@ const glob = async function (pattern, options) {
* @return {Array} of {String}s of full paths * @return {Array} of {String}s of full paths
*/ */
const explode = async function (paths, {accepts, debug}) { async function explode(paths, {accepts, debug}) {
const list = async file => { const list = async file => {
let path = file let path = file
let s let s
@ -262,7 +279,7 @@ const explode = async function (paths, {accepts, debug}) {
const maybeRead = async function (path, default_ = '') { const maybeRead = async function (path, default_ = '') {
try { try {
return (await readFile(path, 'utf8')) return await readFile(path, 'utf8')
} catch (err) { } catch (err) {
return default_ return default_
} }

15
lib/index.js

@ -53,30 +53,31 @@ module.exports = class Now extends EventEmitter {
let files let files
const {pkg, name, description} = await readMetaData(path, { const meta = await readMetaData(path, {
deploymentType, deploymentType,
deploymentName, deploymentName,
quiet, quiet,
isStatic isStatic
}) })
const {pkg, name, description, nowConfig, hasNowJson} = meta
deploymentType = meta.deploymentType
if (this._debug) { if (this._debug) {
console.time('> [debug] Getting files') console.time('> [debug] Getting files')
} }
const opts = {debug: this._debug, hasNowJson}
if (deploymentType === 'npm') { if (deploymentType === 'npm') {
files = await getNpmFiles(path, pkg, {debug: this._debug}) files = await getNpmFiles(path, pkg, nowConfig, opts)
} else { } else {
files = await getDockerFiles(path, {debug: this._debug}) files = await getDockerFiles(path, nowConfig, opts)
} }
if (this._debug) { if (this._debug) {
console.timeEnd('> [debug] Getting files') console.timeEnd('> [debug] Getting files')
} }
const nowProperties = pkg ? pkg.now || {} : {} forwardNpm = forwardNpm || (nowConfig && nowConfig.forwardNpm)
forwardNpm = forwardNpm || nowProperties.forwardNpm
// Read .npmrc // Read .npmrc
let npmrc = {} let npmrc = {}
@ -118,7 +119,7 @@ module.exports = class Now extends EventEmitter {
this._files = hashes this._files = hashes
const engines = nowProperties.engines || pkg.engines const engines = (nowConfig && nowConfig.engines) || pkg.engines
const deployment = await this.retry(async bail => { const deployment = await this.retry(async bail => {
if (this._debug) { if (this._debug) {

63
lib/read-metadata.js

@ -12,24 +12,51 @@ const listPackage = {
} }
} }
module.exports = async function (path, { module.exports = readMetaData
async function readMetaData(path, {
deploymentType = 'npm', deploymentType = 'npm',
deploymentName, deploymentName,
quiet = false, quiet = false,
strict = true,
isStatic = false isStatic = false
}) { }) {
let pkg = {} let pkg = {}
let nowConfig = null
let hasNowJson = false
let name let name
let description let description
try {
nowConfig = JSON.parse(await readFile(resolvePath(path, 'now.json')))
hasNowJson = true
} catch (err) {
// if the file doesn't exist then that's fine; any other error bubbles up
if (err.code !== 'ENOENT') {
const e = Error(`Failed to read JSON in "${path}/now.json"`)
e.userError = true
throw e
}
}
if (hasNowJson) {
// user can specify the type of deployment explicitly in the `now.json` file
// when both a package.json and Dockerfile exist
if (nowConfig.type) {
deploymentType = nowConfig.type
}
if (nowConfig.name) {
deploymentName = nowConfig.name
}
}
if (deploymentType === 'npm') { if (deploymentType === 'npm') {
if (isStatic) { if (isStatic) {
pkg = listPackage pkg = listPackage
} else { } else {
try { try {
pkg = await readFile(resolvePath(path, 'package.json')) pkg = JSON.parse(await readFile(resolvePath(path, 'package.json')))
pkg = JSON.parse(pkg)
} catch (err) { } catch (err) {
const e = Error(`Failed to read JSON in "${path}/package.json"`) const e = Error(`Failed to read JSON in "${path}/package.json"`)
e.userError = true e.userError = true
@ -37,7 +64,7 @@ module.exports = async function (path, {
} }
} }
if (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start'])) { if (strict && (!pkg.scripts || (!pkg.scripts.start && !pkg.scripts['now-start']))) {
const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' + const e = Error('Missing `start` (or `now-start`) script in `package.json`. ' +
'See: https://docs.npmjs.com/cli/start.') 'See: https://docs.npmjs.com/cli/start.')
e.userError = true e.userError = true
@ -68,7 +95,7 @@ module.exports = async function (path, {
throw e throw e
} }
if (docker.length <= 0) { if (strict && docker.length <= 0) {
const e = Error('No commands found in `Dockerfile`') const e = Error('No commands found in `Dockerfile`')
e.userError = true e.userError = true
throw e throw e
@ -101,21 +128,43 @@ module.exports = async function (path, {
name = basename(path) name = basename(path)
if (!quiet) { if (!quiet) {
console.log(`> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}`) if (hasNowJson) {
console.log(`> No \`name\` LABEL in \`Dockerfile\` or \`name\` field in \`now.json\`, using ${chalk.bold(name)}`)
} else {
console.log(`> No \`name\` LABEL in \`Dockerfile\`, using ${chalk.bold(name)}`)
}
} }
} }
} }
description = labels.description description = labels.description
} else {
throw new TypeError(`Unsupported "deploymentType": ${deploymentType}`)
} }
if (deploymentName) { if (deploymentName) {
name = deploymentName name = deploymentName
} }
if (pkg.now) {
// if the project has both a `now.json` and `now` Object in the `package.json`
// file, then fail hard and let the user know that they need to pick one or the
// other
if (hasNowJson) {
const e = new Error('You have a `now` configuration field inside `package.json`, but configuration is also present in `now.json`! Please ensure there\'s a single source of configuration by removing one')
e.userError = true
throw e
} else {
nowConfig = pkg.now
}
}
return { return {
name, name,
description, description,
pkg deploymentType,
pkg,
nowConfig,
hasNowJson
} }
} }

1
test/_fixtures/now-json-docker/Dockerfile

@ -0,0 +1 @@
CMD echo 'world'

1
test/_fixtures/now-json-docker/a.js

@ -0,0 +1 @@
// should not be included

1
test/_fixtures/now-json-docker/b.js

@ -0,0 +1 @@
// should be included

6
test/_fixtures/now-json-docker/now.json

@ -0,0 +1,6 @@
{
"type": "docker",
"files": [
"b.js"
]
}

3
test/_fixtures/now-json-throws/now.json

@ -0,0 +1,3 @@
{
"alias": "bar.com"
}

9
test/_fixtures/now-json-throws/package.json

@ -0,0 +1,9 @@
{
"name": "woot",
"version": "0.0.1",
"description": "",
"dependencies": {},
"now": {
"alias": "foo.com"
}
}

1
test/_fixtures/now-json/a.js

@ -0,0 +1 @@
// should not be included

1
test/_fixtures/now-json/b.js

@ -0,0 +1 @@
// should be included

5
test/_fixtures/now-json/now.json

@ -0,0 +1,5 @@
{
"files": [
"b.js"
]
}

9
test/_fixtures/now-json/package.json

@ -0,0 +1,9 @@
{
"name": "woot",
"version": "0.0.1",
"description": "",
"dependencies": {},
"files": [
"a.js"
]
}

47
test/index.js

@ -4,25 +4,20 @@ const {join, resolve} = require('path')
// Packages // Packages
const test = require('ava') const test = require('ava')
const {asc: alpha} = require('alpha-sort') const {asc: alpha} = require('alpha-sort')
const {readFile} = require('fs-promise')
// Ours // Ours
const {npm: getNpmFiles_, docker: getDockerFiles} = require('../lib/get-files')
const hash = require('../lib/hash') const hash = require('../lib/hash')
const readMetadata = require('../lib/read-metadata')
const {npm: getNpmFiles_, docker: getDockerFiles} = require('../lib/get-files')
const prefix = join(__dirname, '_fixtures') + '/' const prefix = join(__dirname, '_fixtures') + '/'
const base = path => path.replace(prefix, '') const base = path => path.replace(prefix, '')
const fixture = name => resolve(`./test/_fixtures/${name}`) const fixture = name => resolve(`./test/_fixtures/${name}`)
const readJSON = async file => {
const data = await readFile(file)
return JSON.parse(data)
}
// overload to force debugging // overload to force debugging
const getNpmFiles = async dir => { const getNpmFiles = async dir => {
const pkg = await readJSON(resolve(dir, 'package.json')) const {pkg, nowConfig, hasNowJson} = await readMetadata(dir, {quiet: true, strict: false})
return getNpmFiles_(dir, pkg) return getNpmFiles_(dir, pkg, nowConfig, {hasNowJson})
} }
test('`files`', async t => { test('`files`', async t => {
@ -170,3 +165,37 @@ test('prefix regression', async t => {
t.is(base(files[0]), 'prefix-regression/package.json') t.is(base(files[0]), 'prefix-regression/package.json')
t.is(base(files[1]), 'prefix-regression/woot.js') t.is(base(files[1]), 'prefix-regression/woot.js')
}) })
test('support `now.json` files with package.json', async t => {
let files = await getNpmFiles(fixture('now-json'))
files = files.sort(alpha)
t.is(files.length, 3)
t.is(base(files[0]), 'now-json/b.js')
t.is(base(files[1]), 'now-json/now.json')
t.is(base(files[2]), 'now-json/package.json')
})
test('support `now.json` files with Dockerfile', async t => {
const f = fixture('now-json-docker')
const {deploymentType, nowConfig, hasNowJson} = await readMetadata(f, {quiet: true, strict: false})
t.is(deploymentType, 'docker')
let files = await getDockerFiles(f, nowConfig, {hasNowJson})
files = files.sort(alpha)
t.is(files.length, 3)
t.is(base(files[0]), 'now-json-docker/Dockerfile')
t.is(base(files[1]), 'now-json-docker/b.js')
t.is(base(files[2]), 'now-json-docker/now.json')
})
test('throws when both `now.json` and `package.json:now` exist', async t => {
let e
try {
await readMetadata(fixture('now-json-throws'), {quiet: true, strict: false})
} catch (err) {
e = err
}
t.is(e.name, 'Error')
t.is(e.userError, true)
t.pass(/please ensure there's a single source of configuration/i.test(e.message))
})

Loading…
Cancel
Save