Cleanup venv scripts (#8629)
Fixes https://github.com/certbot/certbot/issues/8387. * update _venv_common.py * delete venv.py scripts * rename venv script * update relevant venv3 references * remove set_python_envvars
This commit is contained in:
@@ -8,5 +8,4 @@
|
||||
.git
|
||||
.tox
|
||||
venv
|
||||
venv3
|
||||
docs
|
||||
|
||||
2
.envrc
2
.envrc
@@ -3,7 +3,7 @@
|
||||
# activated and then deactivated when you cd elsewhere. Developers have to have
|
||||
# direnv set up and run `direnv allow` to allow this file to execute on their
|
||||
# system. You can find more information at https://direnv.net/.
|
||||
. venv3/bin/activate
|
||||
. venv/bin/activate
|
||||
# direnv doesn't support modifying PS1 so we unset it to squelch the error
|
||||
# it'll otherwise print about this being done in the activate script. See
|
||||
# https://github.com/direnv/direnv/wiki/PS1. If you would like your shell
|
||||
|
||||
@@ -15,6 +15,6 @@ RUN apt-get update && \
|
||||
/tmp/* \
|
||||
/var/tmp/*
|
||||
|
||||
RUN VENV_NAME="../venv3" python3 tools/venv3.py
|
||||
RUN VENV_NAME="../venv" python3 tools/venv.py
|
||||
|
||||
ENV PATH /opt/certbot/venv3/bin:$PATH
|
||||
ENV PATH /opt/certbot/venv/bin:$PATH
|
||||
|
||||
@@ -8,11 +8,11 @@ RUN apt-get update && \
|
||||
WORKDIR /opt/certbot/src
|
||||
|
||||
# We copy all contents of the build directory to allow us to easily use
|
||||
# things like tools/venv3.py which expects all of our packages to be available.
|
||||
# things like tools/venv.py which expects all of our packages to be available.
|
||||
COPY . .
|
||||
|
||||
RUN tools/venv3.py
|
||||
ENV PATH /opt/certbot/src/venv3/bin:$PATH
|
||||
RUN tools/venv.py
|
||||
ENV PATH /opt/certbot/src/venv/bin:$PATH
|
||||
|
||||
# install in editable mode (-e) to save space: it's not possible to
|
||||
# "rm -rf /opt/certbot/src" (it's stays in the underlaying image);
|
||||
|
||||
@@ -56,18 +56,18 @@ Set up the Python virtual environment that will host your Certbot local instance
|
||||
.. code-block:: shell
|
||||
|
||||
cd certbot
|
||||
python tools/venv3.py
|
||||
python tools/venv.py
|
||||
|
||||
.. note:: You may need to repeat this when
|
||||
Certbot's dependencies change or when a new plugin is introduced.
|
||||
|
||||
You can now run the copy of Certbot from git either by executing
|
||||
``venv3/bin/certbot``, or by activating the virtual environment. You can do the
|
||||
``venv/bin/certbot``, or by activating the virtual environment. You can do the
|
||||
latter by running:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
source venv3/bin/activate
|
||||
source venv/bin/activate
|
||||
|
||||
After running this command, ``certbot`` and development tools like ``ipdb``,
|
||||
``ipython``, ``pytest``, and ``tox`` are available in the shell where you ran
|
||||
@@ -169,7 +169,7 @@ To do so you need:
|
||||
- Docker installed, and a user with access to the Docker client,
|
||||
- an available `local copy`_ of Certbot.
|
||||
|
||||
The virtual environment set up with `python tools/venv3.py` contains two CLI tools
|
||||
The virtual environment set up with `python tools/venv.py` contains two CLI tools
|
||||
that can be used once the virtual environment is activated:
|
||||
|
||||
.. code-block:: shell
|
||||
@@ -197,8 +197,8 @@ using an HTTP-01 challenge on a machine with Python 3:
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
python tools/venv3.py
|
||||
source venv3/bin/activate
|
||||
python tools/venv.py
|
||||
source venv/bin/activate
|
||||
run_acme_server &
|
||||
certbot_test certonly --standalone -d test.example.com
|
||||
# To stop Pebble, launch `fg` to get back the background job, then press CTRL+C
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
#!/bin/sh
|
||||
# This is a simple script that can be sourced to set Python environment
|
||||
# variables for use in Certbot's letstest test farm tests.
|
||||
|
||||
# Some distros like Fedora may only have an executable named python3 installed.
|
||||
if command -v python; then
|
||||
PYTHON_NAME="python"
|
||||
VENV_SCRIPT="tools/venv.py"
|
||||
VENV_PATH="venv"
|
||||
else
|
||||
# We could check for "python2" here, however, the addition of "python3"
|
||||
# only systems is what necessitated this change so checking for "python2"
|
||||
# isn't necessary.
|
||||
PYTHON_NAME="python3"
|
||||
VENV_PATH="venv3"
|
||||
VENV_SCRIPT="tools/venv3.py"
|
||||
fi
|
||||
@@ -64,7 +64,7 @@ if [ $? -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
tools/venv3.py -e acme[dev] -e certbot[dev,docs] -e certbot-apache -e certbot-ci
|
||||
tools/venv.py -e acme[dev] -e certbot[dev,docs] -e certbot-apache -e certbot-ci
|
||||
PEBBLE_LOGS="acme_server.log"
|
||||
PEBBLE_URL="https://localhost:14000/dir"
|
||||
# We configure Pebble to use port 80 for http-01 validation rather than an
|
||||
@@ -73,7 +73,7 @@ PEBBLE_URL="https://localhost:14000/dir"
|
||||
# and closer to the default configuration on various OSes.
|
||||
# 2) As of writing this, Certbot's Apache plugin requires there to be an
|
||||
# existing virtual host for the port used for http-01 validation.
|
||||
venv3/bin/run_acme_server --http-01-port 80 > "${PEBBLE_LOGS}" 2>&1 &
|
||||
venv/bin/run_acme_server --http-01-port 80 > "${PEBBLE_LOGS}" 2>&1 &
|
||||
|
||||
DumpPebbleLogs() {
|
||||
if [ -f "${PEBBLE_LOGS}" ] ; then
|
||||
@@ -96,7 +96,7 @@ if ! curl --insecure "${PEBBLE_URL}" 2>/dev/null; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo "venv3/bin/certbot" -v --debug --text --agree-tos --no-verify-ssl \
|
||||
sudo "venv/bin/certbot" -v --debug --text --agree-tos --no-verify-ssl \
|
||||
--renew-by-default --redirect --register-unsafely-without-email \
|
||||
--domain "${PUBLIC_HOSTNAME}" --server "${PEBBLE_URL}"
|
||||
if [ $? -ne 0 ] ; then
|
||||
@@ -113,7 +113,7 @@ elif [ "$OS_TYPE" = "centos" ]; then
|
||||
fi
|
||||
OPENSSL_VERSION=$(strings "$MOD_SSL_LOCATION" | egrep -o -m1 '^OpenSSL ([0-9]\.[^ ]+) ' | tail -c +9)
|
||||
APACHE_VERSION=$(sudo $APACHE_NAME -v | egrep -o 'Apache/([0-9]\.[^ ]+)' | tail -c +8)
|
||||
"venv3/bin/python" tests/letstest/scripts/test_openssl_version.py "$OPENSSL_VERSION" "$APACHE_VERSION"
|
||||
"venv/bin/python" tests/letstest/scripts/test_openssl_version.py "$OPENSSL_VERSION" "$APACHE_VERSION"
|
||||
if [ $? -ne 0 ] ; then
|
||||
FAIL=1
|
||||
fi
|
||||
@@ -121,7 +121,7 @@ fi
|
||||
|
||||
if [ "$OS_TYPE" = "ubuntu" ] ; then
|
||||
export SERVER="${PEBBLE_URL}"
|
||||
"venv3/bin/tox" -e apacheconftest
|
||||
"venv/bin/tox" -e apacheconftest
|
||||
else
|
||||
echo Not running hackish apache tests on $OS_TYPE
|
||||
fi
|
||||
|
||||
@@ -33,10 +33,11 @@ if ! ./letsencrypt-auto -v --debug --version --no-self-upgrade 2>&1 | tail -n1 |
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# This script sets the environment variables PYTHON_NAME, VENV_PATH, and
|
||||
# VENV_SCRIPT based on the version of Python available on the system. For
|
||||
# instance, Fedora uses Python 3 and Python 2 is not installed.
|
||||
. tests/letstest/scripts/set_python_envvars.sh
|
||||
if command -v python; then
|
||||
PYTHON_NAME="python"
|
||||
else
|
||||
PYTHON_NAME="python3"
|
||||
fi
|
||||
|
||||
# Now that python and openssl have been installed, we can set up a fake server
|
||||
# to provide a new version of letsencrypt-auto. First, we start the server and
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
cd letsencrypt
|
||||
|
||||
BOOTSTRAP_SCRIPT="tests/letstest/scripts/bootstrap_os_packages.sh"
|
||||
VENV_PATH=venv3
|
||||
VENV_PATH=venv
|
||||
|
||||
# install OS packages
|
||||
sudo $BOOTSTRAP_SCRIPT
|
||||
@@ -18,7 +18,7 @@ python3 tools/strip_hashes.py letsencrypt-auto-source/pieces/dependency-requirem
|
||||
# marker that'd normally prevent it from being installed, and this package is
|
||||
# not needed for any OS tested here.
|
||||
sed -i '/enum34/d' requirements.txt
|
||||
CERTBOT_PIP_NO_BINARY=:all: tools/venv3.py --requirement requirements.txt
|
||||
CERTBOT_PIP_NO_BINARY=:all: tools/venv.py --requirement requirements.txt
|
||||
. "$VENV_PATH/bin/activate"
|
||||
# pytest is needed to run tests on some of our packages so we install a pinned version here.
|
||||
tools/pip_install.py pytest
|
||||
|
||||
@@ -9,9 +9,9 @@ LE_AUTO="$REPO_ROOT/letsencrypt-auto-source/letsencrypt-auto"
|
||||
LE_AUTO="$LE_AUTO --debug --no-self-upgrade --non-interactive"
|
||||
MODULES="acme certbot certbot-apache certbot-nginx"
|
||||
PIP_INSTALL="tools/pip_install.py"
|
||||
VENV_NAME=venv3
|
||||
VENV_NAME=venv
|
||||
BOOTSTRAP_SCRIPT="$REPO_ROOT/tests/letstest/scripts/bootstrap_os_packages.sh"
|
||||
VENV_SCRIPT="tools/venv3.py"
|
||||
VENV_SCRIPT="tools/venv.py"
|
||||
|
||||
sudo $BOOTSTRAP_SCRIPT
|
||||
|
||||
|
||||
@@ -1,244 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
"""Aids in creating a developer virtual environment for Certbot.
|
||||
|
||||
When this module is run as a script, it takes the arguments that should
|
||||
be passed to pip to install the Certbot packages as command line
|
||||
arguments. The virtual environment will be created with the name "venv"
|
||||
in the current working directory and will use the default version of
|
||||
Python for the virtualenv executable in your PATH. You can change the
|
||||
name of the virtual environment by setting the environment variable
|
||||
VENV_NAME.
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
REQUIREMENTS = [
|
||||
'-e acme[dev]',
|
||||
'-e certbot[dev,docs]',
|
||||
'-e certbot-apache',
|
||||
'-e certbot-dns-cloudflare',
|
||||
'-e certbot-dns-cloudxns',
|
||||
'-e certbot-dns-digitalocean',
|
||||
'-e certbot-dns-dnsimple',
|
||||
'-e certbot-dns-dnsmadeeasy',
|
||||
'-e certbot-dns-gehirn',
|
||||
'-e certbot-dns-google',
|
||||
'-e certbot-dns-linode',
|
||||
'-e certbot-dns-luadns',
|
||||
'-e certbot-dns-nsone',
|
||||
'-e certbot-dns-ovh',
|
||||
'-e certbot-dns-rfc2136',
|
||||
'-e certbot-dns-route53',
|
||||
'-e certbot-dns-sakuracloud',
|
||||
'-e certbot-nginx',
|
||||
'-e certbot-compatibility-test',
|
||||
'-e certbot-ci',
|
||||
]
|
||||
|
||||
VERSION_PATTERN = re.compile(r'^(\d+)\.(\d+).*$')
|
||||
|
||||
|
||||
class PythonExecutableNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def find_python_executable(python_major):
|
||||
# type: (int) -> str
|
||||
"""
|
||||
Find the relevant python executable that is of the given python major version.
|
||||
Will test, in decreasing priority order:
|
||||
|
||||
* the current Python interpreter
|
||||
* 'pythonX' executable in PATH (with X the given major version) if available
|
||||
* 'python' executable in PATH if available
|
||||
* Windows Python launcher 'py' executable in PATH if available
|
||||
|
||||
Incompatible python versions for Certbot will be evicted (e.g. Python 3
|
||||
versions less than 3.6).
|
||||
|
||||
:param int python_major: the Python major version to target (2 or 3)
|
||||
:rtype: str
|
||||
:return: the relevant python executable path
|
||||
:raise RuntimeError: if no relevant python executable path could be found
|
||||
"""
|
||||
python_executable_path = None
|
||||
|
||||
# First try, current python executable
|
||||
if _check_version('{0}.{1}.{2}'.format(
|
||||
sys.version_info[0], sys.version_info[1], sys.version_info[2]), python_major):
|
||||
return sys.executable
|
||||
|
||||
# Second try, with python executables in path
|
||||
versions_to_test = ['2.7', '2', ''] if python_major == 2 else ['3', '']
|
||||
for one_version in versions_to_test:
|
||||
try:
|
||||
one_python = 'python{0}'.format(one_version)
|
||||
output = subprocess.check_output([one_python, '--version'],
|
||||
universal_newlines=True, stderr=subprocess.STDOUT)
|
||||
if _check_version(output.strip().split()[1], python_major):
|
||||
return subprocess.check_output([one_python, '-c',
|
||||
'import sys; sys.stdout.write(sys.executable);'],
|
||||
universal_newlines=True)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
# Last try, with Windows Python launcher
|
||||
try:
|
||||
env_arg = '-{0}'.format(python_major)
|
||||
output_version = subprocess.check_output(['py', env_arg, '--version'],
|
||||
universal_newlines=True, stderr=subprocess.STDOUT)
|
||||
if _check_version(output_version.strip().split()[1], python_major):
|
||||
return subprocess.check_output(['py', env_arg, '-c',
|
||||
'import sys; sys.stdout.write(sys.executable);'],
|
||||
universal_newlines=True)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
if not python_executable_path:
|
||||
raise RuntimeError('Error, no compatible Python {0} executable for Certbot could be found.'
|
||||
.format(python_major))
|
||||
|
||||
|
||||
def _check_version(version_str, major_version):
|
||||
search = VERSION_PATTERN.search(version_str)
|
||||
|
||||
if not search:
|
||||
return False
|
||||
|
||||
version = (int(search.group(1)), int(search.group(2)))
|
||||
|
||||
minimal_version_supported = (2, 7)
|
||||
if major_version == 3:
|
||||
minimal_version_supported = (3, 6)
|
||||
|
||||
if version >= minimal_version_supported:
|
||||
return True
|
||||
|
||||
print('Incompatible python version for Certbot found: {0}'.format(version_str))
|
||||
return False
|
||||
|
||||
|
||||
def subprocess_with_print(cmd, env=None, shell=False):
|
||||
if env is None:
|
||||
env = os.environ
|
||||
print('+ {0}'.format(subprocess.list2cmdline(cmd)) if isinstance(cmd, list) else cmd)
|
||||
subprocess.check_call(cmd, env=env, shell=shell)
|
||||
|
||||
|
||||
def subprocess_output_with_print(cmd, env=None, shell=False):
|
||||
if env is None:
|
||||
env = os.environ
|
||||
print('+ {0}'.format(subprocess.list2cmdline(cmd)) if isinstance(cmd, list) else cmd)
|
||||
return subprocess.check_output(cmd, env=env, shell=shell)
|
||||
|
||||
|
||||
def get_venv_python_path(venv_path):
|
||||
python_linux = os.path.join(venv_path, 'bin/python')
|
||||
if os.path.isfile(python_linux):
|
||||
return os.path.abspath(python_linux)
|
||||
python_windows = os.path.join(venv_path, 'Scripts\\python.exe')
|
||||
if os.path.isfile(python_windows):
|
||||
return os.path.abspath(python_windows)
|
||||
|
||||
raise ValueError((
|
||||
'Error, could not find python executable in venv path {0}: is it a valid venv ?'
|
||||
.format(venv_path)))
|
||||
|
||||
|
||||
def prepare_venv_path(venv_name):
|
||||
"""Determines the venv path and prepares it for use.
|
||||
|
||||
This function cleans up any Python eggs in the current working directory
|
||||
and ensures the venv path is available for use. The path used is the
|
||||
VENV_NAME environment variable if it is set and venv_name otherwise. If
|
||||
there is already a directory at the desired path, the existing directory is
|
||||
renamed by appending a timestamp to the directory name.
|
||||
|
||||
:param str venv_name: The name or path at where the virtual
|
||||
environment should be created if VENV_NAME isn't set.
|
||||
|
||||
:returns: path where the virtual environment should be created
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
for path in glob.glob('*.egg-info'):
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
env_venv_name = os.environ.get('VENV_NAME')
|
||||
if env_venv_name:
|
||||
print('Creating venv at {0}'
|
||||
' as specified in VENV_NAME'.format(env_venv_name))
|
||||
venv_name = env_venv_name
|
||||
|
||||
if os.path.isdir(venv_name):
|
||||
os.rename(venv_name, '{0}.{1}.bak'.format(venv_name, int(time.time())))
|
||||
|
||||
return venv_name
|
||||
|
||||
|
||||
def install_packages(venv_name, pip_args):
|
||||
"""Installs packages in the given venv.
|
||||
|
||||
:param str venv_name: The name or path at where the virtual
|
||||
environment should be created.
|
||||
:param pip_args: Command line arguments that should be given to
|
||||
pip to install packages
|
||||
:type pip_args: `list` of `str`
|
||||
|
||||
"""
|
||||
# Using the python executable from venv, we ensure to execute following commands in this venv.
|
||||
py_venv = get_venv_python_path(venv_name)
|
||||
subprocess_with_print([py_venv, os.path.abspath('tools/pipstrap.py')])
|
||||
# We only use this value during pip install because:
|
||||
# 1) We're really only adding it for installing cryptography, which happens here, and
|
||||
# 2) There are issues with calling it along with VIRTUALENV_NO_DOWNLOAD, which applies at the
|
||||
# steps above, not during pip install.
|
||||
env_pip_no_binary = os.environ.get('CERTBOT_PIP_NO_BINARY')
|
||||
if env_pip_no_binary:
|
||||
# Check OpenSSL version. If it's too low, don't apply the env variable.
|
||||
openssl_version_string = str(subprocess_output_with_print(['openssl', 'version']))
|
||||
matches = re.findall(r'OpenSSL ([^ ]+) ', openssl_version_string)
|
||||
if not matches:
|
||||
print('Could not find OpenSSL version, not setting PIP_NO_BINARY.')
|
||||
else:
|
||||
openssl_version = matches[0]
|
||||
|
||||
if LooseVersion(openssl_version) >= LooseVersion('1.0.2'):
|
||||
print('Setting PIP_NO_BINARY to {0}'
|
||||
' as specified in CERTBOT_PIP_NO_BINARY'.format(env_pip_no_binary))
|
||||
os.environ['PIP_NO_BINARY'] = env_pip_no_binary
|
||||
else:
|
||||
print('Not setting PIP_NO_BINARY, as OpenSSL version is too old.')
|
||||
command = [py_venv, os.path.abspath('tools/pip_install.py')]
|
||||
command.extend(pip_args)
|
||||
subprocess_with_print(command)
|
||||
if 'PIP_NO_BINARY' in os.environ:
|
||||
del os.environ['PIP_NO_BINARY']
|
||||
|
||||
if os.path.isdir(os.path.join(venv_name, 'bin')):
|
||||
# Linux/OSX specific
|
||||
print('-------------------------------------------------------------------')
|
||||
print('Please run the following command to activate developer environment:')
|
||||
print('source {0}/bin/activate'.format(venv_name))
|
||||
print('-------------------------------------------------------------------')
|
||||
elif os.path.isdir(os.path.join(venv_name, 'Scripts')):
|
||||
# Windows specific
|
||||
print('---------------------------------------------------------------------------')
|
||||
print('Please run one of the following commands to activate developer environment:')
|
||||
print('{0}\\Scripts\\activate.bat (for Batch)'.format(venv_name))
|
||||
print('.\\{0}\\Scripts\\Activate.ps1 (for Powershell)'.format(venv_name))
|
||||
print('---------------------------------------------------------------------------')
|
||||
else:
|
||||
raise ValueError('Error, directory {0} is not a valid venv.'.format(venv_name))
|
||||
259
tools/venv.py
259
tools/venv.py
@@ -1,36 +1,261 @@
|
||||
#!/usr/bin/env python
|
||||
#!/usr/bin/env python3
|
||||
# Developer virtualenv setup for Certbot client
|
||||
import os
|
||||
import sys
|
||||
"""Aids in creating a developer virtual environment for Certbot.
|
||||
|
||||
import _venv_common
|
||||
When this module is run as a script, it takes the arguments that should
|
||||
be passed to pip to install the Certbot packages as command line
|
||||
arguments. If no arguments are provided, all Certbot packages and their
|
||||
development dependencies are installed. The virtual environment will be
|
||||
created with the name "venv" in the current working directory. You can
|
||||
change the name of the virtual environment by setting the environment
|
||||
variable VENV_NAME.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
from distutils.version import LooseVersion
|
||||
import glob
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
|
||||
REQUIREMENTS = [
|
||||
'-e acme[dev]',
|
||||
'-e certbot[dev,dev3,docs]',
|
||||
'-e certbot-apache',
|
||||
'-e certbot-dns-cloudflare',
|
||||
'-e certbot-dns-cloudxns',
|
||||
'-e certbot-dns-digitalocean',
|
||||
'-e certbot-dns-dnsimple',
|
||||
'-e certbot-dns-dnsmadeeasy',
|
||||
'-e certbot-dns-gehirn',
|
||||
'-e certbot-dns-google',
|
||||
'-e certbot-dns-linode',
|
||||
'-e certbot-dns-luadns',
|
||||
'-e certbot-dns-nsone',
|
||||
'-e certbot-dns-ovh',
|
||||
'-e certbot-dns-rfc2136',
|
||||
'-e certbot-dns-route53',
|
||||
'-e certbot-dns-sakuracloud',
|
||||
'-e certbot-nginx',
|
||||
'-e certbot-compatibility-test',
|
||||
'-e certbot-ci',
|
||||
]
|
||||
|
||||
VERSION_PATTERN = re.compile(r'^(\d+)\.(\d+).*$')
|
||||
|
||||
|
||||
class PythonExecutableNotFoundError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def find_python_executable() -> str:
|
||||
"""
|
||||
Find the relevant python executable that is of the given python major version.
|
||||
Will test, in decreasing priority order:
|
||||
|
||||
* the current Python interpreter
|
||||
* 'pythonX' executable in PATH (with X the given major version) if available
|
||||
* 'python' executable in PATH if available
|
||||
* Windows Python launcher 'py' executable in PATH if available
|
||||
|
||||
Incompatible python versions for Certbot will be evicted (e.g. Python 3
|
||||
versions less than 3.6).
|
||||
|
||||
:rtype: str
|
||||
:return: the relevant python executable path
|
||||
:raise RuntimeError: if no relevant python executable path could be found
|
||||
"""
|
||||
python_executable_path = None
|
||||
|
||||
# First try, current python executable
|
||||
if _check_version('{0}.{1}.{2}'.format(
|
||||
sys.version_info[0], sys.version_info[1], sys.version_info[2])):
|
||||
return sys.executable
|
||||
|
||||
# Second try, with python executables in path
|
||||
for one_version in ('3', '',):
|
||||
try:
|
||||
one_python = 'python{0}'.format(one_version)
|
||||
output = subprocess.check_output([one_python, '--version'],
|
||||
universal_newlines=True, stderr=subprocess.STDOUT)
|
||||
if _check_version(output.strip().split()[1]):
|
||||
return subprocess.check_output([one_python, '-c',
|
||||
'import sys; sys.stdout.write(sys.executable);'],
|
||||
universal_newlines=True)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
# Last try, with Windows Python launcher
|
||||
try:
|
||||
output_version = subprocess.check_output(['py', '-3', '--version'],
|
||||
universal_newlines=True, stderr=subprocess.STDOUT)
|
||||
if _check_version(output_version.strip().split()[1]):
|
||||
return subprocess.check_output(['py', env_arg, '-c',
|
||||
'import sys; sys.stdout.write(sys.executable);'],
|
||||
universal_newlines=True)
|
||||
except (subprocess.CalledProcessError, OSError):
|
||||
pass
|
||||
|
||||
if not python_executable_path:
|
||||
raise RuntimeError('Error, no compatible Python executable for Certbot could be found.')
|
||||
|
||||
|
||||
def _check_version(version_str):
|
||||
search = VERSION_PATTERN.search(version_str)
|
||||
|
||||
if not search:
|
||||
return False
|
||||
|
||||
version = (int(search.group(1)), int(search.group(2)))
|
||||
|
||||
if version >= (3, 6):
|
||||
return True
|
||||
|
||||
print('Incompatible python version for Certbot found: {0}'.format(version_str))
|
||||
return False
|
||||
|
||||
|
||||
def subprocess_with_print(cmd, env=None, shell=False):
|
||||
if env is None:
|
||||
env = os.environ
|
||||
print('+ {0}'.format(subprocess.list2cmdline(cmd)) if isinstance(cmd, list) else cmd)
|
||||
subprocess.check_call(cmd, env=env, shell=shell)
|
||||
|
||||
|
||||
def subprocess_output_with_print(cmd, env=None, shell=False):
|
||||
if env is None:
|
||||
env = os.environ
|
||||
print('+ {0}'.format(subprocess.list2cmdline(cmd)) if isinstance(cmd, list) else cmd)
|
||||
return subprocess.check_output(cmd, env=env, shell=shell)
|
||||
|
||||
|
||||
def get_venv_python_path(venv_path):
|
||||
python_linux = os.path.join(venv_path, 'bin/python')
|
||||
if os.path.isfile(python_linux):
|
||||
return os.path.abspath(python_linux)
|
||||
python_windows = os.path.join(venv_path, 'Scripts\\python.exe')
|
||||
if os.path.isfile(python_windows):
|
||||
return os.path.abspath(python_windows)
|
||||
|
||||
raise ValueError((
|
||||
'Error, could not find python executable in venv path {0}: is it a valid venv ?'
|
||||
.format(venv_path)))
|
||||
|
||||
|
||||
def prepare_venv_path(venv_name):
|
||||
"""Determines the venv path and prepares it for use.
|
||||
|
||||
This function cleans up any Python eggs in the current working directory
|
||||
and ensures the venv path is available for use. The path used is the
|
||||
VENV_NAME environment variable if it is set and venv_name otherwise. If
|
||||
there is already a directory at the desired path, the existing directory is
|
||||
renamed by appending a timestamp to the directory name.
|
||||
|
||||
:param str venv_name: The name or path at where the virtual
|
||||
environment should be created if VENV_NAME isn't set.
|
||||
|
||||
:returns: path where the virtual environment should be created
|
||||
:rtype: str
|
||||
|
||||
"""
|
||||
for path in glob.glob('*.egg-info'):
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
|
||||
env_venv_name = os.environ.get('VENV_NAME')
|
||||
if env_venv_name:
|
||||
print('Creating venv at {0}'
|
||||
' as specified in VENV_NAME'.format(env_venv_name))
|
||||
venv_name = env_venv_name
|
||||
|
||||
if os.path.isdir(venv_name):
|
||||
os.rename(venv_name, '{0}.{1}.bak'.format(venv_name, int(time.time())))
|
||||
|
||||
return venv_name
|
||||
|
||||
|
||||
def install_packages(venv_name, pip_args):
|
||||
"""Installs packages in the given venv.
|
||||
|
||||
:param str venv_name: The name or path at where the virtual
|
||||
environment should be created.
|
||||
:param pip_args: Command line arguments that should be given to
|
||||
pip to install packages
|
||||
:type pip_args: `list` of `str`
|
||||
|
||||
"""
|
||||
# Using the python executable from venv, we ensure to execute following commands in this venv.
|
||||
py_venv = get_venv_python_path(venv_name)
|
||||
subprocess_with_print([py_venv, os.path.abspath('tools/pipstrap.py')])
|
||||
# We only use this value during pip install because:
|
||||
# 1) We're really only adding it for installing cryptography, which happens here, and
|
||||
# 2) There are issues with calling it along with VIRTUALENV_NO_DOWNLOAD, which applies at the
|
||||
# steps above, not during pip install.
|
||||
env_pip_no_binary = os.environ.get('CERTBOT_PIP_NO_BINARY')
|
||||
if env_pip_no_binary:
|
||||
# Check OpenSSL version. If it's too low, don't apply the env variable.
|
||||
openssl_version_string = str(subprocess_output_with_print(['openssl', 'version']))
|
||||
matches = re.findall(r'OpenSSL ([^ ]+) ', openssl_version_string)
|
||||
if not matches:
|
||||
print('Could not find OpenSSL version, not setting PIP_NO_BINARY.')
|
||||
else:
|
||||
openssl_version = matches[0]
|
||||
|
||||
if LooseVersion(openssl_version) >= LooseVersion('1.0.2'):
|
||||
print('Setting PIP_NO_BINARY to {0}'
|
||||
' as specified in CERTBOT_PIP_NO_BINARY'.format(env_pip_no_binary))
|
||||
os.environ['PIP_NO_BINARY'] = env_pip_no_binary
|
||||
else:
|
||||
print('Not setting PIP_NO_BINARY, as OpenSSL version is too old.')
|
||||
command = [py_venv, os.path.abspath('tools/pip_install.py')]
|
||||
command.extend(pip_args)
|
||||
subprocess_with_print(command)
|
||||
if 'PIP_NO_BINARY' in os.environ:
|
||||
del os.environ['PIP_NO_BINARY']
|
||||
|
||||
if os.path.isdir(os.path.join(venv_name, 'bin')):
|
||||
# Linux/OSX specific
|
||||
print('-------------------------------------------------------------------')
|
||||
print('Please run the following command to activate developer environment:')
|
||||
print('source {0}/bin/activate'.format(venv_name))
|
||||
print('-------------------------------------------------------------------')
|
||||
elif os.path.isdir(os.path.join(venv_name, 'Scripts')):
|
||||
# Windows specific
|
||||
print('---------------------------------------------------------------------------')
|
||||
print('Please run one of the following commands to activate developer environment:')
|
||||
print('{0}\\Scripts\\activate.bat (for Batch)'.format(venv_name))
|
||||
print('.\\{0}\\Scripts\\Activate.ps1 (for Powershell)'.format(venv_name))
|
||||
print('---------------------------------------------------------------------------')
|
||||
else:
|
||||
raise ValueError('Error, directory {0} is not a valid venv.'.format(venv_name))
|
||||
|
||||
|
||||
def create_venv(venv_path):
|
||||
"""Create a Python 2 virtual environment at venv_path.
|
||||
"""Create a Python virtual environment at venv_path.
|
||||
|
||||
:param str venv_path: path where the venv should be created
|
||||
|
||||
"""
|
||||
python2 = _venv_common.find_python_executable(2)
|
||||
command = [sys.executable, '-m', 'virtualenv', '--python', python2, venv_path]
|
||||
|
||||
environ = os.environ.copy()
|
||||
environ['VIRTUALENV_NO_DOWNLOAD'] = '1'
|
||||
_venv_common.subprocess_with_print(command, environ)
|
||||
python = find_python_executable()
|
||||
command = [python, '-m', 'venv', venv_path]
|
||||
subprocess_with_print(command)
|
||||
|
||||
|
||||
def main(pip_args=None):
|
||||
if os.name == 'nt':
|
||||
raise ValueError('Certbot for Windows is not supported on Python 2.x.')
|
||||
|
||||
venv_path = _venv_common.prepare_venv_path('venv')
|
||||
venv_path = prepare_venv_path('venv')
|
||||
create_venv(venv_path)
|
||||
|
||||
if not pip_args:
|
||||
pip_args = _venv_common.REQUIREMENTS
|
||||
pip_args = REQUIREMENTS
|
||||
|
||||
_venv_common.install_packages(venv_path, pip_args)
|
||||
install_packages(venv_path, pip_args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
# Developer virtualenv setup for Certbot client
|
||||
import sys
|
||||
|
||||
import _venv_common
|
||||
|
||||
|
||||
def create_venv(venv_path):
|
||||
"""Create a Python 3 virtual environment at venv_path.
|
||||
|
||||
:param str venv_path: path where the venv should be created
|
||||
|
||||
"""
|
||||
python3 = _venv_common.find_python_executable(3)
|
||||
command = [python3, '-m', 'venv', venv_path]
|
||||
_venv_common.subprocess_with_print(command)
|
||||
|
||||
|
||||
def main(pip_args=None):
|
||||
venv_path = _venv_common.prepare_venv_path('venv3')
|
||||
create_venv(venv_path)
|
||||
|
||||
if not pip_args:
|
||||
pip_args = _venv_common.REQUIREMENTS + ['-e certbot[dev3]']
|
||||
|
||||
_venv_common.install_packages(venv_path, pip_args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main(sys.argv[1:])
|
||||
Reference in New Issue
Block a user