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

yoga: flexible and powerful

parent b659a6ce
...@@ -8,10 +8,30 @@ status() { ...@@ -8,10 +8,30 @@ status() {
} }
protip() { protip() {
tip=$1
url=$2
echo echo
echo "PRO TIP: $*" | indent echo "PRO TIP: $tip" | indent
echo "See https://devcenter.heroku.com/articles/nodejs-support" | indent echo "See ${url:-https://devcenter.heroku.com/articles/nodejs-support}" | indent
echo }
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 # sed -l basically makes sed replace and buffer through stdin to stdout
...@@ -29,6 +49,13 @@ cat_npm_debug_log() { ...@@ -29,6 +49,13 @@ cat_npm_debug_log() {
test -f $build_dir/npm-debug.log && cat $build_dir/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() { export_env_dir() {
env_dir=$1 env_dir=$1
whitelist_regex=${2:-''} whitelist_regex=${2:-''}
......
#!/usr/bin/env bash #!/usr/bin/env bash
####### Configure environment
set -e # fail fast set -e # fail fast
set -o pipefail # don't ignore exit codes when piping output set -o pipefail # don't ignore exit codes when piping output
# set -x # enable debugging # set -x # enable debugging
bp_version="64"
# Configure directories # Configure directories
build_dir=$1 build_dir=$1
cache_dir=$2 cache_dir=$2
env_dir=$3 env_dir=$3
bp_dir=$(cd $(dirname $0); cd ..; pwd) 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() # Load some convenience functions like status(), echo(), and indent()
source $bp_dir/bin/common.sh source $bp_dir/bin/common.sh
# Fix leak # Output version
status "Resetting git environment" status "Node.js Buildpack v$bp_version"
# Avoid GIT_DIR leak from previous build steps
unset GIT_DIR unset GIT_DIR
# Output npm debug info on error # Create directories & log file
trap cat_npm_debug_log ERR mkdir -p $heroku_dir/node
echo "" > $logfile
# Look in package.json's engines.node field for a semver range # Trap errors
semver_range=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .engines.node) trap tail_error_log ERR
# Resolve node version using semver.io # Load config vars into environment; start with defaults
node_version=$(curl --silent --get --data-urlencode "range=${semver_range}" https://semver.io/node/resolve) export NPM_CONFIG_PRODUCTION=true
export BUILD_CLEAN=false
# Recommend using semver ranges in a safe manner if [ -d "$env_dir" ]; then
if [ "$semver_range" == "null" ]; then export_env_dir $env_dir
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"
fi fi
# Output info about requested range and resolved node version ####### Determine current state
if [ "$semver_range" == "" ]; then
status "Defaulting to latest stable node: $node_version" # Which version of the buildpack did we use last time?
else bp_previous=$(file_contents "$cache_dir/node/bp-version")
status "Requested node range: $semver_range"
status "Resolved node version: $node_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 fi
# Download node from Heroku's S3 mirror of nodejs.org/dist # What's the source-of-truth for node_modules?
status "Downloading and installing node" if test -d $build_dir/node_modules; then modules_source="prebuilt"
node_url="http://s3pository.heroku.com/node/v$node_version/node-v$node_version-linux-x64.tar.gz" elif test -f $build_dir/npm-shrinkwrap.json; then modules_source="npm-shrinkwrap.json"
curl $node_url -s -o - | tar xzf - -C $build_dir elif test -f $build_dir/package.json; then modules_source="package.json"
else modules_source=""
fi
# Move node (and npm) into ./vendor and make them executable # What does our cache look like?
mkdir -p $build_dir/vendor test -d $cache_dir/node/node_modules && modules_cached=true || modules_cached=false
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
# Run subsequent node/npm commands from the build path ####### Provide debugging info and feedback
cd $build_dir
# If node_modules directory is checked into source control then echo ""
# rebuild any native deps. Otherwise, restore from the build cache. status "Node engine: ${node_engine:-unspecified}"
if test -d $build_dir/node_modules; then status "Npm engine: ${npm_engine:-default}"
status "Found existing node_modules directory; skipping cache" status "Start mechanism: ${start_method:-none}"
status "Rebuilding any native dependencies" status "node_modules source: ${modules_source:-none}"
npm rebuild 2>&1 | indent status "node_modules cached: $modules_cached"
elif test -d $cache_dir/node/node_modules; then
status "Restoring node_modules directory from cache"
cp -r $cache_dir/node/node_modules $build_dir/
status "Pruning cached dependencies not specified in package.json" echo ""
npm prune --production 2>&1 | indent
if test -f $cache_dir/node/.heroku/node-version && [ $(cat $cache_dir/node/.heroku/node-version) != "$node_version" ]; then status "NPM_CONFIG_PRODUCTION=$NPM_CONFIG_PRODUCTION"
status "Node version changed since last build; rebuilding dependencies" status "BUILD_CLEAN=$BUILD_CLEAN"
npm rebuild 2>&1 | indent
fi 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 fi
# Scope config var availability only to `npm install` # Download node from Heroku's S3 mirror of nodejs.org/dist
( status "Downloading and installing node $node_engine..."
if [ -d "$env_dir" ]; then node_url="http://s3pository.heroku.com/node/v$node_engine/node-v$node_engine-linux-x64.tar.gz"
status "Exporting config vars to environment" curl $node_url -s -o - | tar xzf - -C /tmp
export_env_dir $env_dir
# 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 fi
status "Downloading and installing npm $npm_engine (replacing version `npm --version`)..."
npm install -g npm@$npm_engine >> $logfile 2>&1
fi
status "Installing dependencies" # Run subsequent commands from the build directory
# Make npm output to STDOUT instead of its default STDERR cd $build_dir
npm install --userconfig $build_dir/.npmrc --production 2>&1 | indent
)
# Persist goodies like node-version in the slug ####### Build the project's dependencies
mkdir -p $build_dir/.heroku
# 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
# Save resolved node version in the slug for later reference if [ "$modules_source" == "" ]; then
echo $node_version > $build_dir/.heroku/node-version status "Skipping dependencies"
# Purge node-related cached content, being careful not to purge the top-level elif [ $modules_source == "prebuilt" ]; then
# cache, for the sake of heroku-buildpack-multi apps. status "Rebuilding any native modules for this architecture"
rm -rf $cache_dir/node_modules # (for apps still on the older caching strategy) npm rebuild >> $logfile 2>&1
rm -rf $cache_dir/node
mkdir -p $cache_dir/node
# If app has a node_modules directory, cache it. elif ! $BUILD_CLEAN && ! $cache_busted; then
if test -d $build_dir/node_modules; then status "Restoring node modules from cache"
status "Caching node_modules directory for future builds" cp -r $cache_dir/node/node_modules $build_dir/
cp -r $build_dir/node_modules $cache_dir/node status "Pruning unused dependencies"
npm prune >> $logfile 2>&1
status "Installing any new modules"
npm install --userconfig $build_dir/.npmrc >> $logfile 2>&1
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
####### 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 fi
# Copy goodies to the cache ####### Create the runtime environment (profile.d)
cp -r $build_dir/.heroku $cache_dir/node
status "Building runtime environment"
mkdir -p $build_dir/.profile.d
# 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
####### Clean up
status "Cleaning up build artifacts"
status "Cleaning up node-gyp and npm artifacts" # Clean up after npm
rm -rf "$build_dir/.node-gyp" rm -rf "$build_dir/.node-gyp"
rm -rf "$build_dir/.npm" rm -rf "$build_dir/.npm"
# If Procfile is absent, try to create one using `npm start` # Clear the cache
if [ ! -e $build_dir/Procfile ]; then rm -rf $cache_dir/node_modules # (for apps still on the older caching strategy)
npm_start=$(cat $build_dir/package.json | $bp_dir/vendor/jq -r .scripts.start) rm -rf $cache_dir/node
# If `scripts.start` is set in package.json, or a server.js file ####### Build successful! Store results in cache
# is present in the app root, then create a default Procfile
if [ "$npm_start" != "null" ] || [ -f $build_dir/server.js ]; then # Create the cache
status "No Procfile found; Adding npm start to new Procfile" mkdir -p $cache_dir/node
echo "web: npm start" > $build_dir/Procfile
else echo $node_engine > $cache_dir/node/node-version
status "Procfile not found and npm start script is undefined" echo $npm_engine > $cache_dir/node/npm-version
protip "Create a Procfile or specify a start script in package.json" echo $bp_version > $cache_dir/node/bp-version
fi
if test -d $build_dir/node_modules; then
status "Caching node_modules for future builds"
cp -r $build_dir/node_modules $cache_dir/node
fi fi
# Update the PATH ####### Summary output
status "Building runtime environment"
mkdir -p $build_dir/.profile.d # Show the final dependency tree
echo "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" > $build_dir/.profile.d/nodejs.sh echo ""
npm ls
# 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
) &
...@@ -14,118 +14,237 @@ testDetectWithoutPackageJson() { ...@@ -14,118 +14,237 @@ testDetectWithoutPackageJson() {
testNoVersion() { testNoVersion() {
compile "no-version" compile "no-version"
assertCaptured "Node engine: unspecified"
assertCaptured "PRO TIP: Specify a node version in package.json" 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 assertCapturedSuccess
} }
testDangerousRangeStar() { testSpecificVersion() {
compile "dangerous-range-star" compile "specific-version"
assertCaptured "PRO TIP: Avoid using semver ranges like '*'" assertNotCaptured "Resolving node version"
assertCaptured "Requested node range: *" assertCaptured "Downloading and installing node 0.10.29"
assertCaptured "Resolved node version: 0.10"
assertCapturedSuccess assertCapturedSuccess
} }
testDangerousRangeGreaterThan() { testStableVersion() {
compile "dangerous-range-greater-than" compile "stable-node"
assertCaptured "PRO TIP: Avoid using semver ranges starting with '>'" assertCaptured "Downloading and installing node 0.10"
assertCaptured "Requested node range: >"
assertCaptured "Resolved node version: 0.10."
assertCapturedSuccess assertCapturedSuccess
} }
testRangeWithSpace() { testUnstableVersion() {
compile "range-with-space" compile "unstable-version"
assertCaptured "Requested node range: >= 0.8.x" assertCaptured "Resolving node version >0.11.0 via semver.io"
assertCaptured "Resolved node version: 0.10." assertCaptured "Downloading and installing node 0.11"
assertCapturedSuccess assertCapturedSuccess
} }
testStableVersion() { testDangerousRangeStar() {
compile "stable-node" compile "dangerous-range-star"
assertNotCaptured "PRO TIP: Avoid using semver" assertCaptured "PRO TIP: Avoid semver ranges like '*'"
assertNotCaptured "PRO TIP: Specify" assertCaptured "Node engine: *"
assertCaptured "Resolved node version" assertCaptured "Resolving node version * via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess assertCapturedSuccess
} }
testUnstableVersion() { testDangerousRangeGreaterThan() {
compile "unstable-version" compile "dangerous-range-greater-than"
assertCaptured "Requested node range: >0.11.0" assertCaptured "PRO TIP: Avoid semver ranges starting with '>'"
assertCaptured "Resolved node version: 0.11." assertCaptured "Resolving node version >0.4 via semver.io"
assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess assertCapturedSuccess
} }
testProfileCreated() { testRangeWithSpace() {
compile "stable-node" compile "range-with-space"
assertCaptured "Building runtime environment" assertCaptured "Resolving node version >= 0.8.x via semver.io"
assertFile "export PATH=\"\$HOME/vendor/node/bin:\$HOME/bin:\$HOME/node_modules/.bin:\$PATH\";" ".profile.d/nodejs.sh" assertCaptured "Downloading and installing node 0.10"
assertCapturedSuccess assertCapturedSuccess
} }
testInvalidDependency() { testInvalidDependency() {
compile "invalid-dependency" compile "invalid-dependency"
assertCaptured "not in the npm registry" assertCaptured "npm ERR! 404"
assertCapturedError 1 "" assertCapturedError 1 ""
} }
testNodeModulesCached() { testNodeModulesCached() {
cache=$(mktmpdir) cache=$(mktmpdir)
compile "caching" $cache compile "caching" $cache
assertCaptured "Caching node" assertCaptured "Caching node_modules for future builds"
assertEquals "1" "$(ls -1 $cache/ | wc -l)" 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() { testModulesCheckedIn() {
compile "modules-checked-in" compile "modules-checked-in"
assertCaptured "Found existing node_modules directory; skipping cache" assertCaptured "node_modules source: prebuilt"
assertCaptured "Rebuilding any native dependencies" 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 assertCapturedSuccess
} }
testUserConfig() { testUserConfig() {
compile "userconfig" compile "userconfig"
assertCaptured "https://www.google.com/" assertCaptured "www.google.com"
assertCaptured "registry error" assertCaptured "registry error"
assertCapturedError 1 "" assertCapturedError 1 ""
} }
testProcfile() {
compile "procfile-present-only"
assertCaptured "Start mechanism: Procfile"
assertNotCaptured "new Procfile"
assertCapturedSuccess
}
testProcfileAbsentNpmStartPresent() { testProcfileAbsentNpmStartPresent() {
compile "procfile-absent-npm-start-present" 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" assertFile "web: npm start" "Procfile"
assertCapturedSuccess assertCapturedSuccess
} }
testProcfileAbsentNpmStartAbsent() { testProcfileAbsentNpmStartAbsent() {
compile "procfile-absent-npm-start-absent" 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 assertCapturedSuccess
} }
testProcfileAbsentNpmStartPresent() { testProcfileAbsentServerPresent() {
compile "procfile-absent-npm-start-present" compile "procfile-absent-server-present"
assertCaptured "No Procfile found; Adding npm start to new Procfile" assertCaptured "Start mechanism: server.js"
assertFile "web: npm start" "Procfile" assertCaptured "'web: node server.js' to new Procfile"
assertFile "web: node server.js" "Procfile"
assertCapturedSuccess assertCapturedSuccess
} }
testEnvDirNotImported() { testServerPresentOnly() {
compile "stable-node" compile "server-present-only"
assertNotCaptured "Exporting config vars to environment" 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 assertCapturedSuccess
} }
testEnvDirExported() { testNoEnvVars() {
env_dir=$(mktmpdir) env_dir=$(mktmpdir)
echo "chicken" > $env_dir/birds
echo "koi" > $env_dir/fish
compile "stable-node" "$(mktmpdir)" $env_dir compile "stable-node" "$(mktmpdir)" $env_dir
assertCaptured "Exporting config vars to environment" assertCaptured "NPM_CONFIG_PRODUCTION=true"
assertCapturedSuccess 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 # Utils
pushd $(dirname 0) >/dev/null 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