Commit 9e9f02c4 authored by Hunter Loftis's avatar Hunter Loftis

yoga: flexible and powerful

parent b659a6ce
......@@ -8,10 +8,30 @@ status() {
}
protip() {
tip=$1
url=$2
echo
echo "PRO TIP: $*" | indent
echo "See https://devcenter.heroku.com/articles/nodejs-support" | indent
echo
echo "PRO TIP: $tip" | indent
echo "See ${url:-https://devcenter.heroku.com/articles/nodejs-support}" | indent
}
file_contents() {
if test -f $1; then
echo "$(cat $1)"
else
echo ""
fi
}
package_json() {
if test -f $build_dir/package.json; then
local result="$(cat $build_dir/package.json | $bp_dir/vendor/jq -r $1)"
if [ "$result" == "null" ]; then echo ""
else echo "$result"
fi
else
echo ""
fi
}
# sed -l basically makes sed replace and buffer through stdin to stdout
......@@ -29,6 +49,13 @@ cat_npm_debug_log() {
test -f $build_dir/npm-debug.log && cat $build_dir/npm-debug.log
}
tail_error_log() {
echo ""
echo " ! Build failure:"
echo ""
tail -n 500 $logfile | indent
}
export_env_dir() {
env_dir=$1
whitelist_regex=${2:-''}
......
#!/usr/bin/env bash
####### Configure environment
set -e # fail fast
set -o pipefail # don't ignore exit codes when piping output
# set -x # enable debugging
bp_version="64"
# Configure directories
build_dir=$1
cache_dir=$2
env_dir=$3
bp_dir=$(cd $(dirname $0); cd ..; pwd)
heroku_dir=$build_dir/.heroku
logfile=/tmp/node-log.txt
# Load some convenience functions like status(), echo(), and indent()
source $bp_dir/bin/common.sh
# Fix leak
status "Resetting git environment"
# Output version
status "Node.js Buildpack v$bp_version"
# Avoid GIT_DIR leak from previous build steps
unset GIT_DIR
# Output npm debug info on error
trap cat_npm_debug_log ERR
# Create directories & log file
mkdir -p $heroku_dir/node
echo "" > $logfile
# Look in package.json's engines.node field for a semver range
semver_range=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .engines.node)
# Trap errors
trap tail_error_log ERR
# Resolve node version using semver.io
node_version=$(curl --silent --get --data-urlencode "range=${semver_range}" https://semver.io/node/resolve)
# Load config vars into environment; start with defaults
export NPM_CONFIG_PRODUCTION=true
export BUILD_CLEAN=false
# Recommend using semver ranges in a safe manner
if [ "$semver_range" == "null" ]; then
protip "Specify a node version in package.json"
semver_range=""
elif [ "$semver_range" == "*" ]; then
protip "Avoid using semver ranges like '*' in engines.node"
elif [ ${semver_range:0:1} == ">" ]; then
protip "Avoid using semver ranges starting with '>' in engines.node"
if [ -d "$env_dir" ]; then
export_env_dir $env_dir
fi
# Output info about requested range and resolved node version
if [ "$semver_range" == "" ]; then
status "Defaulting to latest stable node: $node_version"
else
status "Requested node range: $semver_range"
status "Resolved node version: $node_version"
####### Determine current state
# Which version of the buildpack did we use last time?
bp_previous=$(file_contents "$cache_dir/node/bp-version")
# What's the requested semver range for node?
node_engine=$(package_json ".engines.node")
node_previous=$(file_contents "$cache_dir/node/node-version")
# What's the requested semver range for npm?
npm_engine=$(package_json ".engines.npm")
npm_previous=$(file_contents "$cache_dir/node/npm-version")
# How does this app start?
if test -f $build_dir/Procfile; then start_method="Procfile"
elif [[ $(package_json ".scripts.start") != "" ]]; then start_method="npm start"
elif [ -f $build_dir/server.js ]; then start_method="server.js"
else start_method=""
fi
# Download node from Heroku's S3 mirror of nodejs.org/dist
status "Downloading and installing node"
node_url="http://s3pository.heroku.com/node/v$node_version/node-v$node_version-linux-x64.tar.gz"
curl $node_url -s -o - | tar xzf - -C $build_dir
# What's the source-of-truth for node_modules?
if test -d $build_dir/node_modules; then modules_source="prebuilt"
elif test -f $build_dir/npm-shrinkwrap.json; then modules_source="npm-shrinkwrap.json"
elif test -f $build_dir/package.json; then modules_source="package.json"
else modules_source=""
fi
# What does our cache look like?
test -d $cache_dir/node/node_modules && modules_cached=true || modules_cached=false
####### Provide debugging info and feedback
echo ""
status "Node engine: ${node_engine:-unspecified}"
status "Npm engine: ${npm_engine:-default}"
status "Start mechanism: ${start_method:-none}"
status "node_modules source: ${modules_source:-none}"
status "node_modules cached: $modules_cached"
# Move node (and npm) into ./vendor and make them executable
mkdir -p $build_dir/vendor
mv $build_dir/node-v$node_version-linux-x64 $build_dir/vendor/node
chmod +x $build_dir/vendor/node/bin/*
PATH=$build_dir/vendor/node/bin:$PATH
echo ""
# Run subsequent node/npm commands from the build path
status "NPM_CONFIG_PRODUCTION=$NPM_CONFIG_PRODUCTION"
status "BUILD_CLEAN=$BUILD_CLEAN"
source $bp_dir/bin/warnings.sh
####### Vendor in binaries
echo ""
# Resolve non-specific node versions using semver.io
if ! [[ "$node_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
status "Resolving node version ${node_engine:-(latest stable)} via semver.io..."
node_engine=$(curl --silent --get --data-urlencode "range=${node_engine}" https://semver.io/node/resolve)
fi
# Download node from Heroku's S3 mirror of nodejs.org/dist
status "Downloading and installing node $node_engine..."
node_url="http://s3pository.heroku.com/node/v$node_engine/node-v$node_engine-linux-x64.tar.gz"
curl $node_url -s -o - | tar xzf - -C /tmp
# Move node (and npm) into .heroku/node and make them executable
mv /tmp/node-v$node_engine-linux-x64/* $heroku_dir/node
chmod +x $heroku_dir/node/bin/*
PATH=$heroku_dir/node/bin:$PATH
# Optionally bootstrap a different npm version
if [ "$npm_engine" != "" ]; then
if ! [[ "$npm_engine" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
status "Resolving npm version ${npm_engine} via semver.io..."
npm_engine=$(curl --silent --get --data-urlencode "range=${npm_engine}" https://semver.io/npm/resolve)
fi
status "Downloading and installing npm $npm_engine (replacing version `npm --version`)..."
npm install -g npm@$npm_engine >> $logfile 2>&1
fi
# Run subsequent commands from the build directory
cd $build_dir
# If node_modules directory is checked into source control then
# rebuild any native deps. Otherwise, restore from the build cache.
if test -d $build_dir/node_modules; then
status "Found existing node_modules directory; skipping cache"
status "Rebuilding any native dependencies"
npm rebuild 2>&1 | indent
elif test -d $cache_dir/node/node_modules; then
status "Restoring node_modules directory from cache"
####### Build the project's dependencies
# Did we bust the cache?
if ! $modules_cached; then
cache_busted=true
elif [ "$node_previous" != "" ] && [ "$node_engine" != "$node_previous" ]; then
status "Node version changed ($node_previous => $node_engine); invalidating cache"
cache_busted=true
elif [ "$npm_previous" != "" ] && [ "$npm_engine" != "$npm_previous" ]; then
status "Npm version changed ($npm_previous => $npm_engine); invalidating cache"
cache_busted=true
elif [ "$bp_version" != "$bp_previous" ]; then
status "Buildpack version changed (${bp_previous:-none} => $bp_version); invalidating cache"
cache_busted=true
else
cache_busted=false
fi
if [ "$modules_source" == "" ]; then
status "Skipping dependencies"
elif [ $modules_source == "prebuilt" ]; then
status "Rebuilding any native modules for this architecture"
npm rebuild >> $logfile 2>&1
elif ! $BUILD_CLEAN && ! $cache_busted; then
status "Restoring node modules from cache"
cp -r $cache_dir/node/node_modules $build_dir/
status "Pruning unused dependencies"
npm prune >> $logfile 2>&1
status "Installing any new modules"
npm install --userconfig $build_dir/.npmrc >> $logfile 2>&1
status "Pruning cached dependencies not specified in package.json"
npm prune --production 2>&1 | indent
else
status "Installing node modules"
touch $build_dir/.npmrc
npm install --userconfig $build_dir/.npmrc >> $logfile 2>&1
status "Deduping dependency tree"
npm dedupe >> $logfile 2>&1
fi
if test -f $cache_dir/node/.heroku/node-version && [ $(cat $cache_dir/node/.heroku/node-version) != "$node_version" ]; then
status "Node version changed since last build; rebuilding dependencies"
npm rebuild 2>&1 | indent
fi
####### Create a Procfile if possible
if [ "$start_method" == "npm start" ]; then
status "No Procfile found; Adding 'web: npm start' to new Procfile"
echo "web: npm start" > $build_dir/Procfile
elif [ "$start_method" == "server.js" ]; then
status "No Procfile found; Adding 'web: node server.js' to new Procfile"
echo "web: node server.js" > $build_dir/Procfile
fi
# Scope config var availability only to `npm install`
(
if [ -d "$env_dir" ]; then
status "Exporting config vars to environment"
export_env_dir $env_dir
fi
####### Create the runtime environment (profile.d)
status "Building runtime environment"
mkdir -p $build_dir/.profile.d
status "Installing dependencies"
# Make npm output to STDOUT instead of its default STDERR
npm install --userconfig $build_dir/.npmrc --production 2>&1 | indent
)
# Runtime & Multi-buildpack exports
export_path="export PATH=\"\$HOME/.heroku/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\""
export_home="export NODE_HOME=\"\$HOME/.heroku/node\""
echo $export_path > $build_dir/.profile.d/nodejs.sh
echo $export_home >> $build_dir/.profile.d/nodejs.sh
echo $export_path > $bp_dir/export
echo $export_home >> $bp_dir/export
# Persist goodies like node-version in the slug
mkdir -p $build_dir/.heroku
####### Clean up
# Save resolved node version in the slug for later reference
echo $node_version > $build_dir/.heroku/node-version
status "Cleaning up build artifacts"
# Purge node-related cached content, being careful not to purge the top-level
# cache, for the sake of heroku-buildpack-multi apps.
# Clean up after npm
rm -rf "$build_dir/.node-gyp"
rm -rf "$build_dir/.npm"
# Clear the cache
rm -rf $cache_dir/node_modules # (for apps still on the older caching strategy)
rm -rf $cache_dir/node
####### Build successful! Store results in cache
# Create the cache
mkdir -p $cache_dir/node
# If app has a node_modules directory, cache it.
echo $node_engine > $cache_dir/node/node-version
echo $npm_engine > $cache_dir/node/npm-version
echo $bp_version > $cache_dir/node/bp-version
if test -d $build_dir/node_modules; then
status "Caching node_modules directory for future builds"
status "Caching node_modules for future builds"
cp -r $build_dir/node_modules $cache_dir/node
fi
# Copy goodies to the cache
cp -r $build_dir/.heroku $cache_dir/node
status "Cleaning up node-gyp and npm artifacts"
rm -rf "$build_dir/.node-gyp"
rm -rf "$build_dir/.npm"
# If Procfile is absent, try to create one using `npm start`
if [ ! -e $build_dir/Procfile ]; then
npm_start=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .scripts.start)
# If `scripts.start` is set in package.json, or a server.js file
# is present in the app root, then create a default Procfile
if [ "$npm_start" != "null" ] || [ -f $build_dir/server.js ]; then
status "No Procfile found; Adding npm start to new Procfile"
echo "web: npm start" > $build_dir/Procfile
else
status "Procfile not found and npm start script is undefined"
protip "Create a Procfile or specify a start script in package.json"
fi
fi
####### Summary output
# Update the PATH
status "Building runtime environment"
mkdir -p $build_dir/.profile.d
echo "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" > $build_dir/.profile.d/nodejs.sh
# Post package.json to nomnom service
# Use a subshell so failures won't break the build.
(
curl \
--data @$build_dir/package.json \
--fail \
--silent \
--request POST \
--header "content-type: application/json" \
https://nomnom.heroku.com/?request_id=$REQUEST_ID \
> /dev/null
) &
# Show the final dependency tree
echo ""
npm ls
......@@ -14,118 +14,237 @@ testDetectWithoutPackageJson() {
testNoVersion() {
compile "no-version"
assertCaptured "Node engine: unspecified"
assertCaptured "PRO TIP: Specify a node version in package.json"
assertCaptured "Defaulting to latest stable node"
assertCaptured "Resolving node version (latest stable) via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess
}
testDangerousRangeStar() {
compile "dangerous-range-star"
assertCaptured "PRO TIP: Avoid using semver ranges like '*'"
assertCaptured "Requested node range: *"
assertCaptured "Resolved node version: 0.10"
testSpecificVersion() {
compile "specific-version"
assertNotCaptured "Resolving node version"
assertCaptured "Downloading and installing node 0.10.29"
assertCapturedSuccess
}
testDangerousRangeGreaterThan() {
compile "dangerous-range-greater-than"
assertCaptured "PRO TIP: Avoid using semver ranges starting with '>'"
assertCaptured "Requested node range: >"
assertCaptured "Resolved node version: 0.10."
testStableVersion() {
compile "stable-node"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess
}
testRangeWithSpace() {
compile "range-with-space"
assertCaptured "Requested node range: >= 0.8.x"
assertCaptured "Resolved node version: 0.10."
testUnstableVersion() {
compile "unstable-version"
assertCaptured "Resolving node version >0.11.0 via semver.io"
assertCaptured "Downloading and installing node 0.11"
assertCapturedSuccess
}
testStableVersion() {
compile "stable-node"
assertNotCaptured "PRO TIP: Avoid using semver"
assertNotCaptured "PRO TIP: Specify"
assertCaptured "Resolved node version"
testDangerousRangeStar() {
compile "dangerous-range-star"
assertCaptured "PRO TIP: Avoid semver ranges like '*'"
assertCaptured "Node engine: *"
assertCaptured "Resolving node version * via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess
}
testUnstableVersion() {
compile "unstable-version"
assertCaptured "Requested node range: >0.11.0"
assertCaptured "Resolved node version: 0.11."
testDangerousRangeGreaterThan() {
compile "dangerous-range-greater-than"
assertCaptured "PRO TIP: Avoid semver ranges starting with '>'"
assertCaptured "Resolving node version >0.4 via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess
}
testProfileCreated() {
compile "stable-node"
assertCaptured "Building runtime environment"
assertFile "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" ".profile.d/nodejs.sh"
testRangeWithSpace() {
compile "range-with-space"
assertCaptured "Resolving node version >= 0.8.x via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess
}
testInvalidDependency() {
compile "invalid-dependency"
assertCaptured "not in the npm registry"
assertCaptured "npm ERR! 404"
assertCapturedError 1 ""
}
testNodeModulesCached() {
cache=$(mktmpdir)
compile "caching" $cache
assertCaptured "Caching node"
assertCaptured "Caching node_modules for future builds"
assertEquals "1" "$(ls -1 $cache/ | wc -l)"
}
testBuildWithCache() {
cache=$(mktmpdir)
compile "stable-node" $cache
assertCaptured "node_modules cached: false"
assertCaptured "Caching node_modules for future builds"
assertCapturedSuccess
compile "stable-node" $cache
assertCaptured "node_modules cached: true"
assertCaptured "Restoring node modules from cache"
assertCapturedSuccess
}
testModulesCheckedIn() {
compile "modules-checked-in"
assertCaptured "Found existing node_modules directory; skipping cache"
assertCaptured "Rebuilding any native dependencies"
assertCaptured "node_modules source: prebuilt"
assertCaptured "Rebuilding any native modules for this architecture"
assertNotCaptured "Restoring node modules"
assertNotCaptured "Pruning unused dependencies"
assertNotCaptured "Installing any new modules"
assertNotCaptured "Installing node modules"
assertNotCaptured "Deduping dependency tree"
assertCapturedSuccess
}
testUserConfig() {
compile "userconfig"
assertCaptured "https://www.google.com/"
assertCaptured "www.google.com"
assertCaptured "registry error"
assertCapturedError 1 ""
}
testProcfile() {
compile "procfile-present-only"
assertCaptured "Start mechanism: Procfile"
assertNotCaptured "new Procfile"
assertCapturedSuccess
}
testProcfileAbsentNpmStartPresent() {
compile "procfile-absent-npm-start-present"
assertCaptured "No Procfile found; Adding npm start to new Procfile"
assertCaptured "Start mechanism: npm start"
assertCaptured "No Procfile found; Adding 'web: npm start' to new Procfile"
assertFile "web: npm start" "Procfile"
assertCapturedSuccess
}
testProcfileAbsentNpmStartAbsent() {
compile "procfile-absent-npm-start-absent"
assertCaptured "Create a Procfile or specify a start script in package.json"
assertCaptured "Start mechanism: none"
assertNotCaptured "new Procfile"
assertCapturedSuccess
}
testProcfileAbsentNpmStartPresent() {
compile "procfile-absent-npm-start-present"
assertCaptured "No Procfile found; Adding npm start to new Procfile"
assertFile "web: npm start" "Procfile"
testProcfileAbsentServerPresent() {
compile "procfile-absent-server-present"
assertCaptured "Start mechanism: server.js"
assertCaptured "'web: node server.js' to new Procfile"
assertFile "web: node server.js" "Procfile"
assertCapturedSuccess
}
testEnvDirNotImported() {
compile "stable-node"
assertNotCaptured "Exporting config vars to environment"
testServerPresentOnly() {
compile "server-present-only"
assertCaptured "PRO TIP: Use 'npm init'"
assertCaptured "'web: node server.js' to new Procfile"
assertFile "web: node server.js" "Procfile"
assertCapturedSuccess
}
testEnvVars() {
env_dir=$(mktmpdir)
echo "false" > $env_dir/NPM_CONFIG_PRODUCTION
compile "stable-node" "$(mktmpdir)" $env_dir
assertCaptured "NPM_CONFIG_PRODUCTION=false"
assertCapturedSuccess
}
testEnvDirExported() {
testNoEnvVars() {
env_dir=$(mktmpdir)
echo "chicken" > $env_dir/birds
echo "koi" > $env_dir/fish
compile "stable-node" "$(mktmpdir)" $env_dir
assertCaptured "Exporting config vars to environment"
assertCaptured "NPM_CONFIG_PRODUCTION=true"
assertCapturedSuccess
}
testNoDevDependencies() {
compile "dev-dependencies"
assertNotCaptured "lodash"
assertCapturedSuccess
}
testDevDependencies() {
env_dir=$(mktmpdir)
echo "false" > $env_dir/NPM_CONFIG_PRODUCTION
compile "dev-dependencies" "$(mktmpdir)" $env_dir
assertCaptured "lodash"
assertCapturedSuccess
}
testBuildClean() {
cache=$(mktmpdir)
env_dir=$(mktmpdir)
compile "build-clean-1" $cache
assertCaptured "lodash@1.0.0"
assertCapturedSuccess
compile "build-clean-2" $cache
assertCaptured "lodash@1.0.0"
assertCapturedSuccess
echo "true" > $env_dir/BUILD_CLEAN
compile "build-clean-2" $cache $env_dir
assertCaptured "lodash@1.3.1"
assertCapturedSuccess
}
testNpmrc() {
compile "dev-dependencies"
assertNotCaptured "lodash"
assertCapturedSuccess
compile "dev-dependencies-npmrc"
assertCaptured "lodash"
assertCapturedSuccess
}
testShrinkwrap() {
compile "shrinkwrap"
assertCaptured "express@4.10.4"
assertCaptured "mime-db@1.2.0"
assertCaptured "etag@1.5.1"
assertCaptured "lodash@2.4.1"
assertNotCaptured "mocha"
assertCapturedSuccess
}
testNpmVersionRange() {
compile "npm-version-range"
assertCaptured "Resolving npm version"
assertCaptured "installing npm 1.4."
assertCapturedSuccess
}
testNpmVersionSpecific() {
compile "npm-version-specific"
assertCaptured "installing npm 2.1.11"
assertNotCaptured "Resolving npm version"
assertCapturedSuccess
}
testProfileExport() {
compile "stable-node"
assertCaptured "Building runtime environment"
assertFileContains "export PATH=\"\$HOME/.heroku/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" "${compile_dir}/.profile.d/nodejs.sh"
assertFileContains "export NODE_HOME=\"\$HOME/.heroku/node\"" "${compile_dir}/.profile.d/nodejs.sh"
assertCapturedSuccess
}
testMultiExport() {
compile "stable-node"
assertFileContains "export PATH=\"\$HOME/.heroku/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\"" "${bp_dir}/export"
assertFileContains "export NODE_HOME=\"\$HOME/.heroku/node\"" "${bp_dir}/export"
assertCapturedSuccess
}
# Utils
pushd $(dirname 0) >/dev/null
......
if [ "$node_engine" == "" ]; then
protip "Specify a node version in package.json" "https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version"
elif [ "$node_engine" == "*" ]; then
protip "Avoid semver ranges like '*' in engines.node" "https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version"
elif [ ${node_engine:0:1} == ">" ]; then
protip "Avoid semver ranges starting with '>' in engines.node" "https://devcenter.heroku.com/articles/nodejs-support#specifying-a-node-js-version"
fi
if [ "$modules_source" == "prebuilt" ]; then
protip "Avoid checking node_modules into source control" "https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-"
elif [ "$modules_source" == "" ]; then
protip "Use 'npm init' and 'npm install --save' to define dependencies"
fi
if [ "$start_method" == "" ]; then
protip "Include a Procfile, package.json start script, or server.js file to start your app" "https://devcenter.heroku.com/articles/nodejs-support#runtime-behavior"
fi
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"lodash": "1.0.0"
},
"engines": {
"node": "~0.10.0"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"lodash": "^1.0.0"
},
"engines": {
"node": "~0.10.0"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
},
"engines": {
"node": "~0.10.0"
},
"devDependencies": {
"lodash": "^2.4.1"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "~0.10.0"
},
"devDependencies": {
"lodash": "^2.4.1"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository" : {
"type" : "git",
"url" : "http://github.com/example/example.git"
},
"engines": {
"node": "0.10.33",
"npm": "1.4.x"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository" : {
"type" : "git",
"url" : "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "~0.10.0",
"npm": "2.1.11"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository" : {
"type" : "git",
"url" : "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "~0.10.0"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"dependencies": {
"express": {
"version": "4.10.4",
"from": "express@4.10.4",
"resolved": "https://registry.npmjs.org/express/-/express-4.10.4.tgz",
"dependencies": {
"accepts": {
"version": "1.1.3",
"from": "accepts@~1.1.3",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.1.3.tgz",
"dependencies": {
"mime-types": {
"version": "2.0.3",
"from": "mime-types@~2.0.3",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.3.tgz",
"dependencies": {
"mime-db": {
"version": "1.2.0",
"from": "mime-db@~1.2.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.2.0.tgz"
}
}
},
"negotiator": {
"version": "0.4.9",
"from": "negotiator@0.4.9",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.4.9.tgz"
}
}
},
"content-disposition": {
"version": "0.5.0",
"from": "content-disposition@0.5.0",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.0.tgz"
},
"cookie-signature": {
"version": "1.0.5",
"from": "cookie-signature@1.0.5",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.5.tgz"
},
"debug": {
"version": "2.1.0",
"from": "debug@~2.1.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.1.0.tgz",
"dependencies": {
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
}
}
},
"depd": {
"version": "1.0.0",
"from": "depd@~1.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.0.0.tgz"
},
"escape-html": {
"version": "1.0.1",
"from": "escape-html@1.0.1",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz"
},
"etag": {
"version": "1.5.1",
"from": "etag@~1.5.1",
"resolved": "https://registry.npmjs.org/etag/-/etag-1.5.1.tgz",
"dependencies": {
"crc": {
"version": "3.2.1",
"from": "crc@3.2.1",
"resolved": "https://registry.npmjs.org/crc/-/crc-3.2.1.tgz"
}
}
},
"finalhandler": {
"version": "0.3.2",
"from": "finalhandler@0.3.2",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-0.3.2.tgz"
},
"fresh": {
"version": "0.2.4",
"from": "fresh@0.2.4",
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.4.tgz"
},
"media-typer": {
"version": "0.3.0",
"from": "media-typer@0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
},
"methods": {
"version": "1.1.0",
"from": "methods@1.1.0",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.0.tgz"
},
"on-finished": {
"version": "2.1.1",
"from": "on-finished@~2.1.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.1.1.tgz",
"dependencies": {
"ee-first": {
"version": "1.1.0",
"from": "ee-first@1.1.0",
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.0.tgz"
}
}
},
"parseurl": {
"version": "1.3.0",
"from": "parseurl@~1.3.0",
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.0.tgz"
},
"path-to-regexp": {
"version": "0.1.3",
"from": "path-to-regexp@0.1.3",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.3.tgz"
},
"proxy-addr": {
"version": "1.0.4",
"from": "proxy-addr@~1.0.4",
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-1.0.4.tgz",
"dependencies": {
"forwarded": {
"version": "0.1.0",
"from": "forwarded@~0.1.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz"
},
"ipaddr.js": {
"version": "0.1.5",
"from": "ipaddr.js@0.1.5",
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-0.1.5.tgz"
}
}
},
"qs": {
"version": "2.3.3",
"from": "qs@2.3.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-2.3.3.tgz"
},
"range-parser": {
"version": "1.0.2",
"from": "range-parser@~1.0.2",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.2.tgz"
},
"send": {
"version": "0.10.1",
"from": "send@0.10.1",
"resolved": "https://registry.npmjs.org/send/-/send-0.10.1.tgz",
"dependencies": {
"destroy": {
"version": "1.0.3",
"from": "destroy@1.0.3",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.3.tgz"
},
"mime": {
"version": "1.2.11",
"from": "mime@1.2.11",
"resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz"
},
"ms": {
"version": "0.6.2",
"from": "ms@0.6.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.6.2.tgz"
}
}
},
"serve-static": {
"version": "1.7.1",
"from": "serve-static@~1.7.1",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.7.1.tgz"
},
"type-is": {
"version": "1.5.3",
"from": "type-is@~1.5.3",
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.5.3.tgz",
"dependencies": {
"mime-types": {
"version": "2.0.3",
"from": "mime-types@~2.0.3",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.0.3.tgz",
"dependencies": {
"mime-db": {
"version": "1.2.0",
"from": "mime-db@~1.2.0",
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.2.0.tgz"
}
}
}
}
},
"vary": {
"version": "1.0.0",
"from": "vary@~1.0.0",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.0.0.tgz"
},
"cookie": {
"version": "0.1.2",
"from": "cookie@0.1.2",
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.2.tgz"
},
"merge-descriptors": {
"version": "0.0.2",
"from": "merge-descriptors@0.0.2",
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz"
},
"utils-merge": {
"version": "1.0.0",
"from": "utils-merge@1.0.0",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz"
}
}
},
"lodash": {
"version": "2.4.1",
"from": "lodash@2.4.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.1.tgz"
}
}
}
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository": {
"type": "git",
"url": "http://github.com/example/example.git"
},
"dependencies": {
"express": "4.10.4",
"lodash": "2.4.1"
},
"engines": {
"node": "~0.10.0"
},
"devDependencies": {
"mocha": "2.0.1"
}
}
A fake README, to keep npm from polluting stderr.
\ No newline at end of file
{
"name": "node-buildpack-test-app",
"version": "0.0.1",
"description": "node buildpack integration test app",
"repository" : {
"type" : "git",
"url" : "http://github.com/example/example.git"
},
"dependencies": {
"hashish": "*"
},
"engines": {
"node": "0.10.29"
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment