#!/usr/bin/env sh # Ensure this file is executable via `chmod a+x deken`, then place it # somewhere on your ${PATH}, like ~/bin. The rest of Deken will self-install # on first run into the ~/.deken/ directory. # Much of this code is pilfered from Clojure's Leiningen tool script="$0" script_args=$* ############################################################################## # variable declarations ############################################################################## export DEKEN_VERSION="0.9.2" : "${DEKEN_HOME:=${HOME}/.deken}" : "${DEKEN_GIT_BRANCH:=main}" : "${DEKEN_BASE_URL:=https://raw.githubusercontent.com/pure-data/deken/${DEKEN_GIT_BRANCH}/developer}" # allow the user to override the default python : "${PYTHON_BIN:=python3}" export DEKEN_HOME VIRTUALENV_URL="https://bootstrap.pypa.io/virtualenv/@pyversion@/virtualenv.pyz" case "$(uname -s)" in CYGWIN*) DEKEN_HOME=$(cygpath -w "${DEKEN_HOME}") ;; *) ;; esac # This needs to be defined before we call HTTP_CLIENT below if [ "${HTTP_CLIENT}" = "" ]; then if which curl >/dev/null; then if [ -n "${https_proxy}" ]; then CURL_PROXY="-x ${https_proxy}" fi HTTP_CLIENT="curl ${CURL_PROXY} -f -L -o" else HTTP_CLIENT="wget -O" fi fi if test -z "${systeminstalled}"; then # if this script resides in /usr/ and there's a deken.hy relative to it in the # share/ folder, we consider it system-installed if test -z "${0##/usr/*}" && test -e "${0%/*}/../share/deken/deken.hy"; then systeminstalled=true else systeminstalled=false fi fi if ${systeminstalled}; then DEKEN_HOME="${0%/*}/../share/deken" fi : "${DEKEN_HY:=${DEKEN_HOME}/deken.hy}" : "${DEKENHY:=${DEKEN_HOME}/virtualenv/bin/hy}" ############################################################################## # helper functions ############################################################################## error() { echo "$@" 1>&2 } countdown() { _countdown_t=$1 _countdown_t=$((_countdown_t)) while [ $_countdown_t -gt 0 ]; do printf "\r%2d" $((_countdown_t)) sleep 1 _countdown_t=$((_countdown_t-1)) done printf "\r \r" unset _countdown_t } fetch_file() { _fetch_file_dst=${1#file://} _fetch_file_src=${2#file://} if test -e "${_fetch_file_src}"; then cp -v "${_fetch_file_src}" "${_fetch_file_dst}" else ${HTTP_CLIENT} "${_fetch_file_dst}" "${_fetch_file_src}" fi unset _fetch_file_dst unset _fetch_file_src } uninstall_deken() { if ${systeminstalled}; then # on Debian we disallow uninstalling error "Uninstalling is disabled for system-provided deken!" error "Instead, use your package manager to remove deken." exit 1 fi error "I'm going to uninstall myself and my dependencies from ${DEKEN_HOME} now." error "Feel free to Ctrl-C now if you don't want to do this." countdown 5 error "Uninstalling deken." rm -rf "${DEKEN_HOME}" "$0" exit 0 } bail_install() { error "Self-installation of Deken failed." error "Please paste any errors in the bug tracker at https://github.com/pure-data/deken/issues" # remove all traces of our attempts to install. rm -rf "${DEKEN_HOME}" # bail from this script. exit 1 } bail_install_msg() { error "$@" bail_install } bail_requirements() { rm -f "${DEKEN_HOME}/requirements.txt" cat >/dev/stderr </dev/stderr </dev/null; then virtualenv --system-site-packages "${DEKEN_HOME}/virtualenv" else _install_virtualenv_pyversion=$("${PYTHON_BIN}" -c "import sys; print('%s.%s' % (sys.version_info.major, sys.version_info.minor))") _install_virtualenv_url=$(echo "${VIRTUALENV_URL}" | sed -e "s|@pyversion@|${_install_virtualenv_pyversion}|g") echo "Downloading & installing Virtualenv for ${_install_virtualenv_pyversion} using ${_install_virtualenv_url}" rm -rf "${DEKEN_HOME}/virtualenv.pyz" mkdir -p "${DEKEN_HOME}" fetch_file "${DEKEN_HOME}/virtualenv.pyz" "${_install_virtualenv_url}" && \ "${PYTHON_BIN}" "${DEKEN_HOME}/virtualenv.pyz" "${DEKEN_HOME}/virtualenv" rm -rf "${DEKEN_HOME}/virtualenv.pyz" unset _install_virtualenv_pyversion unset _install_virtualenv_url fi [ -d "${DEKEN_HOME}/virtualenv" ] || \ bail_install } install_deken() { if ${systeminstalled}; then # on Debian, we can skip installation return fi which "${PYTHON_BIN}" >/dev/null || \ bail_install_msg "Oops, no Python found! You need Python3 to run Deken: ${PYTHON_BIN} You can specify an alternative Python interpreter via the PYTHON_BIN envvar" error "This is your first time running deken on this machine." error "I'm going to install myself and my dependencies into ${DEKEN_HOME} now." error "Feel free to Ctrl-C now if you don't want to do this." countdown 3 error "Installing deken." mkdir -p "${DEKEN_HOME}" [ -e "${DEKEN_HOME}/requirements.txt" ] || (\ ( echo "Fetching Python requirements file: ${DEKEN_BASE_URL}/requirements.txt" && \ fetch_file "${DEKEN_HOME}/requirements.txt" "${DEKEN_BASE_URL}/requirements.txt" ) || bail_install) [ -e "${DEKEN_HOME}/requirements.txt" ] || bail_install [ -e "${DEKEN_HY}" ] || (\ ( echo "Fetching main hylang file: ${DEKEN_BASE_URL}/deken.hy" && \ fetch_file "${DEKEN_HY}" "${DEKEN_BASE_URL}/deken.hy" ) || bail_install) [ -e "${DEKEN_HY}" ] || bail_install [ -d "${DEKEN_HOME}/virtualenv" ] || install_virtualenv if ! [ -x "${DEKENHY}" ]; then echo "Installing deken library dependencies." "${DEKEN_HOME}/virtualenv/bin/pip" install -r "${DEKEN_HOME}/requirements.txt" || bail_requirements fi echo "${DEKEN_VERSION}" > "${DEKEN_HOME}/version.txt" } upgrade_deken() { if ${systeminstalled}; then # on Debian we disallow upgrading error "Direct upgrading is disabled for system-provided deken!" error "Instead, use your package manager to install newer versions." exit 1 fi # first upgrade this script itself echo "Upgrading ${script}." fetch_file "${DEKEN_HOME}/.upgrade-deken" "${DEKEN_BASE_URL}/deken" || ( error "Error upgrading deken"; exit 1; ) if diff -q "${DEKEN_HOME}/.upgrade-deken" "$script" >/dev/null; then # launcher script is already up-to-date rm "${DEKEN_HOME}/.upgrade-deken" else if test -w "${script}"; then cat "${DEKEN_HOME}/.upgrade-deken" > "{script}" rm "${DEKEN_HOME}/.upgrade-deken" error "The deken-installer has changed." error "Please re-run the last command" error "" error "Hint: ${script} ${script_args}" exit else rm "${DEKEN_HOME}/.upgrade-deken" error "Unable to update '${script}', proceeding anyhow..." fi fi # next upgrade our dependencies for f in requirements.txt deken.hy do echo "Fetching ${f} file: ${DEKEN_BASE_URL}/${f}" fetch_file "${DEKEN_HOME}/.upgrade-${f}" "${DEKEN_BASE_URL}/${f}" || ( error "Error upgrading ${f}"; exit 1; ) mv "${DEKEN_HOME}/.upgrade-${f}" "${DEKEN_HOME}/${f}" done # finally update the python dependencies "${DEKEN_HOME}/virtualenv/bin/pip" install -r "${DEKEN_HOME}/requirements.txt" || bail_requirements echo "${DEKEN_VERSION}" > "${DEKEN_HOME}/version.txt" echo "Successfully upgraded." } tryrun_deken() { if ${systeminstalled}; then hy3 "${DEKEN_HY}" "$@" exit $? fi # check if the 'deken' script and the actual implementation match _tryrun_deken_version=$(cat "${DEKEN_HOME}/version.txt" 2>/dev/null) if test -n "${_tryrun_deken_version}" && test "x${_tryrun_deken_version}" != "x${DEKEN_VERSION}"; then bail_upgrade "${_tryrun_deken_version}" fi unset _tryrun_deken_version if [ ! -x "${DEKENHY}" ]; then error "Unable to find '${DEKENHY}'" error "Try running '$0 install --self' or '$0 upgrade --self'" exit 1 fi if [ ! -e "${DEKEN_HY}" ]; then error "Unable to find '${DEKEN_HY}'" error "Try running '$0 install --self' or '$0 upgrade --self'" exit 1 fi "${DEKENHY}" "${DEKEN_HY}" "$@" } ############################################################################## # here starts the code ############################################################################## # catch 'uninstall --self' early, so we don't run into the "installed" checks if [ $# -eq 2 ] && [ "$1" = "uninstall" ] && [ "$2" = "--self" ]; then uninstall_deken exit fi if [ $# -eq 1 ] && [ "$1" = "--version" ]; then echo "${DEKEN_VERSION}" exit fi if [ "$(id -u)" -eq 0 ] && [ "${DEKEN_ROOT}" = "" ]; then error "WARNING: You're currently running as root; probably by accident." error "Press Control-C to abort or Enter to continue as root." error "Set DEKEN_ROOT=yes to disable this warning." read -r _ fi # make sure we are deployed [ -d "${DEKEN_HOME}" ] || install_deken # last check to make sure we can bootstrap [ -d "${DEKEN_HOME}" ] || bail_install # catch the special "upgrade" command if [ $# -eq 2 ] && [ "$2" = "--self" ]; then case "$1" in install) install_deken exit ;; update|upgrade) upgrade_deken exit ;; esac fi # run the real deken command with args passed through tryrun_deken "$@"