#!/usr/bin/env bash # This script serves as the **Python Buildpack** compiler. # # A [buildpack](http://devcenter.heroku.m/articles/buildpacks) is an # adapter between a Python application and Heroku's runtime. # ## Usage # Compiling an app into a slug is simple: # # $ bin/compile # ## Assumptions # # This buildpack makes the following assumptions: # # - The desired Python VM is available on the base system. # - Library dependencies are available on the base system. # - Django applications # Fail fast and fail hard. set -eo pipefail # Prepend proper path for virtualenv hackery. This will be deprecated soon. export PATH=:/usr/local/bin:$PATH # Paths. BIN_DIR=$(cd $(dirname $0); pwd) # absolute path ROOT_DIR=$(dirname $BIN_DIR) BUILD_DIR=$1 CACHE_DIR=$2 # The detected application type (Python|Python/Django). NAME=$($BIN_DIR/detect $BUILD_DIR) # Where to store the Pip download cache. PIP_DOWNLOAD_CACHE=${PIP_DOWNLOAD_CACHE:-$CACHE_DIR/pip_downloads} # The directories to include in the cache. VIRTUALENV_DIRS="bin include lib" # Python versions. This will be used in the future to specify custom Pythons. PYTHON_VERSION="2.7.2" PYTHON_EXE="python2.7" # The slug compiler doesn't do a very good job of sanitizing environment variables. unset GIT_DIR # We'll need to send these statics to other scripts we `source`. export PIP_DOWNLOAD_CACHE export BUILD_DIR # Syntax sugar. indent() { RE="s/^/ /" [ $(uname) == "Darwin" ] && sed -l "$RE" || sed -u "$RE" } function virtualenv (){ python "$ROOT_DIR/vendor/virtualenv-1.7/virtualenv.py" "$@" } # ## Build Time # # Switch to the repo's context. cd $BUILD_DIR # ### Sanity Checks # # Just a little peace of mind. # Reject a Django app that appears to be packaged incorrectly. if [ "$NAME" = "Python" ]; then [ -f settings.py ] && { echo " ! Django settings must be in a package subdirectory"; exit 1; } (grep -Fiq "django" requirements.txt) && [ -f settings.py ] && { echo " ! Django app must be in a package subdirectory"; exit 1; } fi # Warn for a checked-in virtualenv. if [ -d "lib" ] || [ -d "bin" ]; then echo " ! You have a virtualenv checked in. You should ignore the appropriate paths in your repo. See http://devcenter.heroku.com/articles/gitignore for more info."; fi # Reject a conflicting checked-in virtualenv. if [ -f "lib/python2.7" ]; then echo " ! Checked-in virtualenv conflict." exit 1; fi # Copy old artifacts out of the cache. mkdir -p $CACHE_DIR for dir in $VIRTUALENV_DIRS; do cp -R $CACHE_DIR/$dir . &> /dev/null || true done # ### Virtualenv Setup # # Create the virtualenv. Rebuild if corrupt. # TODO: Bootstrap a bottled Python VM... set +e echo "-----> Preparing Python interpreter ($PYTHON_VERSION)" echo "-----> Preparing virtualenv version $(virtualenv --version)" # Try to create the virtualenv. OUT=$(virtualenv --python $PYTHON_EXE --distribute --never-download --prompt=venv . 2>&1) # If there's an error, purge and recreate. [ $? -ne 0 ] && { echo " ! Virtualenv corrupt, rebuilding." echo $OUT for dir in $VIRTUALENV_DIRS; do rm -fr $dir &> /dev/null || true done OUT=$(virtualenv --python $PYTHON_EXE --distribute --never-download --prompt=venv . ) } echo "$OUT" | indent set -e # Create set-aside .heroku folder. mkdir -p .heroku # Pylibmc support. source $BIN_DIR/steps/pylibmc # Activate the virtualenv. echo "-----> Activating virtualenv" source bin/activate # If no requirements given, assume `setup.py develop` if [ ! -f requirements.txt ]; then echo "-e ." > requirements.txt fi # Install Mercurial, if it appears to be required. if (grep -Fiq "hg+" requirements.txt) then pip install --use-mirrors mercurial | indent fi # Install dependencies with Pip. echo "-----> Installing dependencies using pip version $(pip --version | awk '{print $2}')" pip install --use-mirrors -r requirements.txt --src ./.heroku/src | indent # Do additional application hackery if applications appears to be a Django app. # Optionally, disable all Django-specific changes with `DISABLE_INJECTION` env. if [ "$NAME" = "Python/Django" ] && ! [ "$DISABLE_INJECTION" ]; then source $BIN_DIR/steps/django fi # Make Virtualenv's paths relative for portability. set +e OUT=$(virtualenv --python $PYTHON_EXE --relocatable .) [ $? -ne 0 ] && { echo " ! Error making virtualenv relocatable" echo "$OUT" | indent exit 1 } set -e # Store new artifacts in cache. for dir in $VIRTUALENV_DIRS; do rm -rf $CACHE_DIR/$dir cp -R $dir $CACHE_DIR/ done # fin.