#!/bin/bash # Copyright 2015 The Chromium OS Authors. All rights reserved. # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. APP_YAML="app.yaml" DEFAULT_SDK_MIRROR="https://storage.googleapis.com/appengine-sdks/featured/google_appengine_1.9.19.zip" # Apps can further modify the appengine sdk by providing this shell script in # their top level directory. This is needed because the turnaround time to # submitting patches upstream to the SDK is rather large. # WARNING: Remember that this only changes the local installation of the SDK. # So, this is only useful to fix bugs that make local development hard. AE # will use a non-patched version of the SDK. # The script will be run as: # sdk_mod APPENGINE_SDK_MOD_FILE="appengine_sdk_mod" PYTHONPATH_PREFIX="" PATH_PREFIX="" PS1_PREFIX="" usage() { cat << EOF Usage: ${BASH_SOURCE} Use this script to enter an environment to develop an appengine app in. This script will: - Download the requested version of SDK if it's not already available. - Set up the environment in the new shell so that relevant SDK and project tools are available, and PYTHONPATH is setup to use these tools. You can create some files under your toplevel directory to modify the behaviour of this script for your project: - appengine_sdk_mod: A bash script that will be executed by this script as: ./fancy_project/appengine_sdk_mod This script can be used to modify the *local installation only* of the SDK. This can, for example, fixup the SDK to ease local development. For an example, see cq_stats/appengine_sdk_mod. EOF } enter_ae_shell() { local rcfile="$(mktemp)" cat >"${rcfile}" << EOF [[ -e ~/.bashrc ]] && source ~/.bashrc export PYTHONPATH="${PYTHONPATH_PREFIX}:\${PYTHONPATH}" export PATH="${PATH_PREFIX}:\${PATH}" export PS1="${PS1_PREFIX} \${PS1}" # Clear BASH_ENV so that if a subshell is launched, we don't # get sourced twice. (This file is going to dissapear after the first time it's # sourced.) unset BASH_ENV rm -f "${rcfile}" EOF info "Entering ae_shell for ${appname}..." if [[ $# -eq 0 ]]; then # Enter a shell that will survive successful completion of this script, and # will have the new environment setup for the user. exec bash --rcfile "${rcfile}" -i else # A command was given, run that command in the new shell. # bash will ignore BASH_ENV if it detects that it's launched by sshd. # Trick it! unset SSH_CLIENT unset SSH_CONNECTION unset SSH_TTY BASH_ENV=${rcfile} exec bash -c '"$@"' "$@" fi } prepare_sdk() { local -r appengine_dir="$1" local -r ae_sdk_dir="$2" local -r appname="$3" if [[ ! -d "${ae_sdk_dir}" ]]; then local temp_ae_sdk_dir="temp_ae_sdk_dir" info "Using appegine SDK mirror ${DEFAULT_SDK_MIRROR}" rm -rf "${temp_ae_sdk_dir}" mkdir -p "${temp_ae_sdk_dir}" info "Downloading appengine SDK" local sdk_zip="${temp_ae_sdk_dir}/sdk.zip" wget -c "${DEFAULT_SDK_MIRROR}" -O "${sdk_zip}" if [[ $? -ne 0 ]]; then error "Failed to download SDK from ${DEFAULT_SDK_MIRROR}" rm -rf "${temp_ae_sdk_dir}" return ${E_GENERAL} fi info "Unpacking..." unzip -q "${sdk_zip}" -d "${temp_ae_sdk_dir}" if [[ $? -ne 0 ]]; then error "Failed to unzip ${sdk_zip}." rm -rf "${temp_ae_sdk_dir}" return ${E_GENERAL} fi mv "${temp_ae_sdk_dir}/google_appengine" "${ae_sdk_dir}" rm -rf "${temp_ae_sdk_dir}" if [[ -f "${appname}/${APPENGINE_SDK_MOD_FILE}" ]]; then info "Running appengine sdk mod script from " \ "${appname}/${APPENGINE_SDK_MOD_FILE}" if ! "./${appname}/${APPENGINE_SDK_MOD_FILE}" \ "${appengine_dir}/${ae_sdk_dir}"; then return ${E_GENERAL} fi fi fi info "Using appengine SDK at ${ae_sdk_dir}" return 0 } setup_django_path() { local -r appengine_dir="$1" local -r ae_sdk_dir="$2" local -r appname="$3" if [[ ! -f "${appname}/${APP_YAML}" ]]; then return ${E_GENERAL} fi local django_version django_version="$(awk '$0 == "- name: django" { getline; print $NF }' \ "${appname}/${APP_YAML}")" if [[ -z "${django_version}" ]]; then return ${E_GENERAL} fi info "Setting django version to ${django_version}" django_dir="${ae_sdk_dir}/lib/django-${django_version}" PYTHONPATH_PREFIX="${appengine_dir}/${django_dir}:${PYTHONPATH_PREFIX}" PATH_PREFIX="${appengine_dir}/${django_dir}/django/bin:${PATH_PREFIX}" } # This sets up the chromite path so that chromite is available inside ae_shell. # Note that this is different from using chromite/scripts/wrapper.py because the # appengine apps that launched / deployed inside the ae_shell run in an # environment controlled by the AE SDK's dev_appserver.py # This ensures that chromite is available inside that environment as well. setup_chromite_path() { local -r appengine_dir="$1" # Must go deeper. local basedir base_dir="$(dirname "$(dirname "${appengine_dir}")")" PYTHONPATH_PREFIX="${base_dir}:${PYTHONPATH_PREFIX}" } main() { local -r appengine_dir="$(readlink -e "$(dirname "${BASH_SOURCE}")")" source "${appengine_dir}/common.sh" # Argument parsing. local -r appdir="$1" shift if [[ $# -gt 0 && "$1" != "--" ]]; then error "Unexpected argument: $1" usage exit ${E_GENERAL} fi # End argument parsing. local -r appname="$(basename "${appdir}")" local -r ae_sdk_dir="google_appengine_${appname}" local appname_shell="$(echo "${appname}" | tr '[:lower:]' '[:upper:]')" if [[ ! -d "${appdir}" ]]; then error "'${appdir}' is not an appengine app source directory!" usage exit ${E_GENERAL} fi info "Found appengine directory ${appengine_dir}" info "Found appengine app ${appname} at ${appdir}" pushd "${appengine_dir}" >/dev/null if ! prepare_sdk "${appengine_dir}" "${ae_sdk_dir}" "${appname}"; then exit ${E_GENERAL} fi setup_django_path "${appengine_dir}" "${ae_sdk_dir}" "${appname}" setup_chromite_path "${appengine_dir}" PYTHONPATH_PREFIX="${appengine_dir}/${ae_sdk_dir}:${PYTHONPATH_PREFIX}" PYTHONPATH="${appengine_dir}/${appname}:${PYTHONPATH}" PATH_PREFIX="${appengine_dir}/${ae_sdk_dir}:${appengine_dir}:${PATH_PREFIX}" PS1_PREFIX="AE:${appname_shell}${PS1_PREFIX}" popd >/dev/null enter_ae_shell "$@" } main "$@"