In Phase 2, recreate a venv and reinstall Python packages only if necessary.

* Teach the build script how to do special vars. Factor up file reading.
* Use a static string for the PyPI JSON location, as it will soon be overrideable via an env var for testing.
This commit is contained in:
Erik Rose 2015-12-03 16:32:59 -05:00
parent 02255fa024
commit 46779da3b5
3 changed files with 72 additions and 43 deletions

View file

@ -2,7 +2,8 @@
"""Stitch together the letsencrypt-auto script.
Implement a simple templating language in which {{ some/file }} turns into the
contents of the file at ./pieces/some/file.
contents of the file at ./pieces/some/file except for certain tokens which have
other, special definitions.
"""
from os.path import dirname, join
@ -10,18 +11,37 @@ import re
from sys import argv
def le_version(build_script_dir):
"""Return the version number stamped in letsencrypt/__init__.py."""
return re.search('''^__version__ = ['"](.+)['"].*''',
file_contents(join(dirname(build_script_dir),
'letsencrypt',
'__init__.py')),
re.M).group(1)
def file_contents(path):
with open(path) as file:
return file.read()
def main():
dir = dirname(argv[0])
def replacer(match):
rel_path = match.group(1)
with open(join(dir, 'pieces', rel_path)) as replacement:
return replacement.read()
special_replacements = {
'LE_AUTO_VERSION': le_version(dir)
}
with open(join(dir, 'letsencrypt-auto.template')) as template:
result = re.sub(r'{{\s*([A-Za-z0-9_./-]+)\s*}}',
replacer,
template.read())
def replacer(match):
token = match.group(1)
if token in special_replacements:
return special_replacements[token]
else:
return file_contents(join(dir, 'pieces', token))
result = re.sub(r'{{\s*([A-Za-z0-9_./-]+)\s*}}',
replacer,
file_contents(join(dir, 'letsencrypt-auto.template')))
with open(join(dir, 'letsencrypt-auto'), 'w') as out:
out.write(result)

View file

@ -160,43 +160,49 @@ elif [ "$1" = "--no-self-upgrade" ]; then
# Phase 2: Create venv, install LE, and run.
shift 1 # the --no-self-upgrade arg
echo "Creating virtual environment..."
# TODO: Embed LE version here, and compare it against letsencrypt --version.
# If it matches, there's no need to recreate the venv.
rm -rf "$VENV_PATH"
DeterminePythonVersion
if [ "$VERBOSE" = 1 ]; then
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH
if [ -f $VENV_BIN/letsencrypt ]; then
INSTALLED_VERSION=$($VENV_BIN/letsencrypt --version 2>&1 | cut -d " " -f 2)
else
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
INSTALLED_VERSION="0.0.0"
fi
if [ "{{ LE_AUTO_VERSION }}" = $INSTALLED_VERSION ]; then
echo "Reusing old virtual environment."
else
echo "Creating virtual environment..."
rm -rf "$VENV_PATH"
DeterminePythonVersion
if [ "$VERBOSE" = 1 ]; then
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH
else
virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
fi
# Install Python dependencies with peep, then run letsencrypt.
echo "Installing Python package dependencies..."
TEMP_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'le'` # Linux || OS X
# ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > $TEMP_DIR/letsencrypt-auto-requirements.txt
# Install Python dependencies with peep, then run letsencrypt.
echo "Installing Python package dependencies..."
TEMP_DIR=`mktemp -d 2>/dev/null || mktemp -d -t 'le'` # Linux || OS X
# ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > $TEMP_DIR/letsencrypt-auto-requirements.txt
{{ letsencrypt-auto-requirements.txt }}
UNLIKELY_EOF
# ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > $TEMP_DIR/peep.py
# ---------------------------------------------------------------------------
cat << "UNLIKELY_EOF" > $TEMP_DIR/peep.py
{{ peep.py }}
UNLIKELY_EOF
# ---------------------------------------------------------------------------
set +e
PEEP_OUT=`$VENV_BIN/python $TEMP_DIR/peep.py install -r $TEMP_DIR/letsencrypt-auto-requirements.txt`
PEEP_STATUS=$?
set -e
rm -rf $TEMP_DIR
if [ "$PEEP_STATUS" = 0 ]; then
echo "Running letsencrypt..."
echo " " $SUDO $VENV_BIN/letsencrypt "$@"
$SUDO $VENV_BIN/letsencrypt "$@"
else
# Report error:
echo $PEEP_OUT
exit 1
# ---------------------------------------------------------------------------
set +e
PEEP_OUT=`$VENV_BIN/python $TEMP_DIR/peep.py install -r $TEMP_DIR/letsencrypt-auto-requirements.txt`
PEEP_STATUS=$?
set -e
rm -rf $TEMP_DIR
if [ "$PEEP_STATUS" != 0 ]; then
# Report error:
echo $PEEP_OUT
exit 1
fi
fi
echo "Running letsencrypt..."
echo " " $SUDO $VENV_BIN/letsencrypt "$@"
$SUDO $VENV_BIN/letsencrypt "$@"
else
# Phase 1: Upgrade letsencrypt-auto if neceesary, then self-invoke.
@ -215,9 +221,7 @@ else
# dependencies (curl, etc.), for better flow control, and for the option of
# future Windows compatibility.
#
# This Python script prints a path to a temp dir containing a new copy of
# letsencrypt-auto or returns non-zero. There is no $ interpolation due to
# quotes on heredoc delimiters.
# There is no $ interpolation due to quotes on heredoc delimiters.
set +e
# -------------------------------------------------------------------------
TEMP_DIR=`$LE_PYTHON - << "UNLIKELY_EOF"

View file

@ -1,3 +1,8 @@
"""Print a path to a temp dir containing a new copy of letsencrypt-auto.
On failure, return non-zero.
"""
from distutils.version import LooseVersion
from json import loads
from os import devnull
@ -67,7 +72,7 @@ class TempDir(object):
def latest_stable_version(get, package):
"""Apply a fairly safe heuristic to determine the latest stable release of
a PyPI package."""
metadata = loads(get('https://pypi.python.org/pypi/%s/json' % package))
metadata = loads(get('https://pypi.python.org/pypi/letsencrypt/json'))
# metadata['info']['version'] actually returns the latest of any kind of
# release release, contrary to https://wiki.python.org/moin/PyPIJSON.
return str(max(LooseVersion(r) for r
@ -108,7 +113,7 @@ def main():
get = HttpsGetter().get
temp = TempDir()
try:
stable_tag = 'v' + latest_stable_version(get, 'letsencrypt')
stable_tag = 'v' + latest_stable_version(get)
print dirname(verified_new_le_auto(get, stable_tag, temp))
except ExpectedError as exc:
print exc.args[0], exc.args[1]