Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rw-r--r--tools/_changelog_top.txt6
-rwxr-xr-xtools/_release.sh78
-rw-r--r--[-rwxr-xr-x]tools/_venv_common.py95
-rw-r--r--tools/deactivate.py2
-rwxr-xr-xtools/deps.sh15
-rw-r--r--tools/dev_constraints.txt103
-rwxr-xr-xtools/docker-warning.sh4
-rwxr-xr-xtools/extract_changelog.py41
-rwxr-xr-xtools/install_and_test.py25
-rwxr-xr-xtools/merge_requirements.py52
-rw-r--r--tools/oldest_constraints.txt7
-rwxr-xr-xtools/pip_install.py46
-rwxr-xr-xtools/pip_install_editable.py1
-rwxr-xr-xtools/readlink.py1
-rwxr-xr-xtools/simple_http_server.py10
-rwxr-xr-xtools/sphinx-quickstart.sh2
-rwxr-xr-xtools/strip_hashes.py50
-rwxr-xr-xtools/venv.py54
-rwxr-xr-xtools/venv3.py54
19 files changed, 404 insertions, 242 deletions
diff --git a/tools/_changelog_top.txt b/tools/_changelog_top.txt
index 6983b3a43..a0f9b6553 100644
--- a/tools/_changelog_top.txt
+++ b/tools/_changelog_top.txt
@@ -12,10 +12,4 @@
*
-Despite us having broken lockstep, we are continuing to release new versions of
-all Certbot components during releases for the time being, however, the only
-package with changes other than its version number was:
-
-*
-
More details about these changes can be found on our GitHub repo.
diff --git a/tools/_release.sh b/tools/_release.sh
index d75a0f487..1819adad2 100755
--- a/tools/_release.sh
+++ b/tools/_release.sh
@@ -7,6 +7,24 @@ if [ "$RELEASE_DIR" = "" ]; then
exit 1
fi
+ExitWarning() {
+ exit_status="$?"
+ if [ "$exit_status" != 0 ]; then
+ # Don't print each command before executing it because it will disrupt
+ # the desired output.
+ set +x
+ echo '******************************'
+ echo '* *'
+ echo '* THE RELEASE SCRIPT FAILED! *'
+ echo '* *'
+ echo '******************************'
+ set -x
+ fi
+ exit "$exit_status"
+}
+
+trap ExitWarning EXIT
+
version="$1"
echo Releasing production version "$version"...
nextversion="$2"
@@ -30,7 +48,6 @@ SUBPKGS_NOT_IN_AUTO="certbot-dns-cloudflare certbot-dns-cloudxns certbot-dns-dig
SUBPKGS_IN_AUTO="certbot $SUBPKGS_IN_AUTO_NO_CERTBOT"
SUBPKGS_NO_CERTBOT="$SUBPKGS_IN_AUTO_NO_CERTBOT $SUBPKGS_NOT_IN_AUTO"
SUBPKGS="$SUBPKGS_IN_AUTO $SUBPKGS_NOT_IN_AUTO"
-subpkgs_modules="$(echo $SUBPKGS | sed s/-/_/g)"
# certbot_compatibility_test is not packaged because:
# - it is not meant to be used by anyone else than Certbot devs
# - it causes problems when running pytest - the latter tries to
@@ -66,17 +83,23 @@ fi
git checkout "$RELEASE_BRANCH"
# Update changelog
-sed -i "s/master/$(date +'%Y-%m-%d')/" CHANGELOG.md
-git add CHANGELOG.md
-git diff --cached
+sed -i "s/master/$(date +'%Y-%m-%d')/" certbot/CHANGELOG.md
+git add certbot/CHANGELOG.md
git commit -m "Update changelog for $version release"
-for pkg_dir in $SUBPKGS_NO_CERTBOT certbot-compatibility-test .
+for pkg_dir in $SUBPKGS certbot-compatibility-test
do
sed -i 's/\.dev0//' "$pkg_dir/setup.py"
git add "$pkg_dir/setup.py"
-done
+ if [ -f "$pkg_dir/local-oldest-requirements.txt" ]; then
+ sed -i "s/-e acme\[dev\]/acme[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
+ sed -i "s/-e acme/acme[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
+ sed -i "s/-e certbot\[dev\]/certbot[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
+ sed -i "s/-e certbot/certbot[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
+ git add "$pkg_dir/local-oldest-requirements.txt"
+ fi
+done
SetVersion() {
ver="$1"
@@ -90,7 +113,7 @@ SetVersion() {
fi
sed -i "s/^version.*/version = '$ver'/" $pkg_dir/setup.py
done
- init_file="certbot/__init__.py"
+ init_file="certbot/certbot/__init__.py"
if [ $(grep -c '^__version' "$init_file") != 1 ]; then
echo "Unexpected count of __version variables in $init_file"
exit 1
@@ -102,8 +125,11 @@ SetVersion() {
SetVersion "$version"
+# Unset CERTBOT_OLDEST to prevent wheels from being built improperly due to
+# conditionals like the one found in certbot-dns-dnsimple's setup.py file.
+unset CERTBOT_OLDEST
echo "Preparing sdists and wheels"
-for pkg_dir in . $SUBPKGS_NO_CERTBOT
+for pkg_dir in $SUBPKGS
do
cd $pkg_dir
@@ -123,8 +149,7 @@ done
mkdir "dist.$version"
-mv dist "dist.$version/certbot"
-for pkg_dir in $SUBPKGS_NO_CERTBOT
+for pkg_dir in $SUBPKGS
do
mv $pkg_dir/dist "dist.$version/$pkg_dir/"
done
@@ -153,7 +178,7 @@ cd ~-
# get a snapshot of the CLI help for the docs
# We set CERTBOT_DOCS to use dummy values in example user-agent string.
-CERTBOT_DOCS=1 certbot --help all > docs/cli-help.txt
+CERTBOT_DOCS=1 certbot --help all > certbot/docs/cli-help.txt
jws --help > acme/docs/jws-help.txt
cd ..
@@ -167,12 +192,12 @@ mkdir kgs
kgs="kgs/$version"
pip freeze | tee $kgs
python ../tools/pip_install.py pytest
-for module in $subpkgs_modules ; do
+cd ~-
+for module in $SUBPKGS ; do
echo testing $module
# use an empty configuration file rather than the one in the repo root
- pytest -c <(echo '') --pyargs $module
+ pytest -c <(echo '') $module
done
-cd ~-
# pin pip hashes of the things we just built
for pkg in $SUBPKGS_IN_AUTO ; do
@@ -221,8 +246,7 @@ mv letsencrypt-auto-source/letsencrypt-auto.asc letsencrypt-auto-source/certbot-
cp -p letsencrypt-auto-source/letsencrypt-auto certbot-auto
cp -p letsencrypt-auto-source/letsencrypt-auto letsencrypt-auto
-git add certbot-auto letsencrypt-auto letsencrypt-auto-source docs/cli-help.txt
-git diff --cached
+git add certbot-auto letsencrypt-auto letsencrypt-auto-source certbot/docs/cli-help.txt
while ! git commit --gpg-sign="$RELEASE_GPG_KEY" -m "Release $version"; do
echo "Unable to sign the release commit using git."
echo "You may have to configure git to use gpg2 by running:"
@@ -241,17 +265,16 @@ echo gpg2 -U $RELEASE_GPG_KEY --detach-sign --armor $name.$rev.tar.xz
cd ~-
# Add master section to CHANGELOG.md
-header=$(head -n 4 CHANGELOG.md)
+header=$(head -n 4 certbot/CHANGELOG.md)
body=$(sed s/nextversion/$nextversion/ tools/_changelog_top.txt)
-footer=$(tail -n +5 CHANGELOG.md)
+footer=$(tail -n +5 certbot/CHANGELOG.md)
echo "$header
$body
-$footer" > CHANGELOG.md
-git add CHANGELOG.md
-git diff --cached
-git commit -m "Add contents to CHANGELOG.md for next version"
+$footer" > certbot/CHANGELOG.md
+git add certbot/CHANGELOG.md
+git commit -m "Add contents to certbot/CHANGELOG.md for next version"
echo "New root: $root"
echo "Test commands (in the letstest repo):"
@@ -265,16 +288,5 @@ if [ "$RELEASE_BRANCH" = candidate-"$version" ] ; then
SetVersion "$nextversion".dev0
letsencrypt-auto-source/build.py
git add letsencrypt-auto-source/letsencrypt-auto
- for pkg_dir in $SUBPKGS_NO_CERTBOT .
- do
- if [ -f "$pkg_dir/local-oldest-requirements.txt" ]; then
- sed -i "s/-e acme\[dev\]/acme[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
- sed -i "s/-e acme/acme[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
- sed -i "s/-e \.\[dev\]/certbot[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
- sed -i "s/-e \./certbot[dev]==$version/" "$pkg_dir/local-oldest-requirements.txt"
- git add "$pkg_dir/local-oldest-requirements.txt"
- fi
- done
- git diff
git commit -m "Bump version to $nextversion"
fi
diff --git a/tools/_venv_common.py b/tools/_venv_common.py
index 540842773..c61385054 100755..100644
--- a/tools/_venv_common.py
+++ b/tools/_venv_common.py
@@ -12,14 +12,37 @@ VENV_NAME.
from __future__ import print_function
+import glob
import os
+import re
import shutil
-import glob
-import time
import subprocess
import sys
-import re
-import shlex
+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 letshelp-certbot',
+ '-e certbot-compatibility-test',
+ '-e certbot-ci',
+]
VERSION_PATTERN = re.compile(r'^(\d+)\.(\d+).*$')
@@ -107,29 +130,35 @@ def subprocess_with_print(cmd, env=os.environ, shell=False):
subprocess.check_call(cmd, env=env, shell=shell)
-def get_venv_bin_path(venv_path):
+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(os.path.dirname(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(os.path.dirname(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 main(venv_name, venv_args, args):
- """Creates a virtual environment and installs packages.
+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.
- :param str venv_args: Command line arguments for virtualenv
- :param str args: Command line arguments that should be given to pip
- to install packages
- """
+ 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)
@@ -145,21 +174,25 @@ def main(venv_name, venv_args, args):
if os.path.isdir(venv_name):
os.rename(venv_name, '{0}.{1}.bak'.format(venv_name, int(time.time())))
- command = [sys.executable, '-m', 'virtualenv', '--no-site-packages', '--setuptools', venv_name]
- command.extend(shlex.split(venv_args))
- subprocess_with_print(command)
+ return venv_name
+
- # We execute the following commands in the context of the virtual environment, to install
- # the packages in it. To do so, we append the venv binary to the PATH that will be used for
- # these commands. With this trick, correct python executable will be selected.
- new_environ = os.environ.copy()
- new_environ['PATH'] = os.pathsep.join([get_venv_bin_path(venv_name), new_environ['PATH']])
- subprocess_with_print('python {0}'.format('./letsencrypt-auto-source/pieces/pipstrap.py'),
- env=new_environ, shell=True)
- subprocess_with_print('python -m pip install --upgrade "setuptools>=30.3"',
- env=new_environ, shell=True)
- subprocess_with_print('python {0} {1}'.format('./tools/pip_install.py', ' '.join(args)),
- env=new_environ, shell=True)
+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('letsencrypt-auto-source/pieces/pipstrap.py')])
+ command = [py_venv, os.path.abspath('tools/pip_install.py')]
+ command.extend(pip_args)
+ subprocess_with_print(command)
if os.path.isdir(os.path.join(venv_name, 'bin')):
# Linux/OSX specific
@@ -176,9 +209,3 @@ def main(venv_name, venv_args, args):
print('---------------------------------------------------------------------------')
else:
raise ValueError('Error, directory {0} is not a valid venv.'.format(venv_name))
-
-
-if __name__ == '__main__':
- main('venv',
- '',
- sys.argv[1:])
diff --git a/tools/deactivate.py b/tools/deactivate.py
index d43b84552..10c9ecd35 100644
--- a/tools/deactivate.py
+++ b/tools/deactivate.py
@@ -16,8 +16,8 @@ import os
import sys
from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
+from cryptography.hazmat.primitives.asymmetric import rsa
import josepy as jose
from acme import client as acme_client
diff --git a/tools/deps.sh b/tools/deps.sh
deleted file mode 100755
index e12f201a5..000000000
--- a/tools/deps.sh
+++ /dev/null
@@ -1,15 +0,0 @@
-#!/bin/sh
-#
-# Find all Python imports.
-#
-# ./tools/deps.sh certbot
-# ./tools/deps.sh acme
-# ./tools/deps.sh certbot-apache
-# ...
-#
-# Manually compare the output with deps in setup.py.
-
-git grep -h -E '^(import|from.*import)' $1/ | \
- awk '{print $2}' | \
- grep -vE "^$1" | \
- sort -u
diff --git a/tools/dev_constraints.txt b/tools/dev_constraints.txt
index 88340cb00..265d967d8 100644
--- a/tools/dev_constraints.txt
+++ b/tools/dev_constraints.txt
@@ -1,77 +1,114 @@
-# Specifies Python package versions for development.
+# Specifies Python package versions for development and building Docker images.
# It includes in particular packages not specified in letsencrypt-auto's requirements file.
# Some dev package versions specified here may be overridden by higher level constraints
# files during tests (eg. letsencrypt-auto-source/pieces/dependency-requirements.txt).
alabaster==0.7.10
apipkg==1.4
+appnope==0.1.0
asn1crypto==0.22.0
-astroid==1.3.5
+astroid==2.3.3
attrs==17.3.0
Babel==2.5.1
+backports.functools-lru-cache==1.5
backports.shutil-get-terminal-size==1.0.0
-boto3==1.9.36
-botocore==1.12.36
-cloudflare==1.5.1
-coverage==4.4.2
-decorator==4.1.2
-dns-lexicon==3.0.8
+backports.ssl-match-hostname==3.7.0.1
+bcrypt==3.1.6
+boto3==1.11.7
+botocore==1.14.7
+cached-property==1.5.1
+cloudflare==2.3.1
+codecov==2.0.15
+configparser==3.7.4
+contextlib2==0.6.0.post1
+coverage==4.5.4
+decorator==4.4.1
+dns-lexicon==3.3.17
dnspython==1.15.0
-docutils==0.12
+docker==3.7.2
+docker-compose==1.25.0
+docker-pycreds==0.4.0
+dockerpty==0.4.1
+docopt==0.6.2
+docutils==0.15.2
execnet==1.5.0
+functools32==3.2.3.post2
future==0.16.0
-futures==3.1.1
-google-api-python-client==1.5
+futures==3.3.0
+filelock==3.0.12
+google-api-python-client==1.5.5
httplib2==0.10.3
imagesize==0.7.1
-ipdb==0.10.2
-ipython==5.5.0
+importlib-metadata==0.23
+ipdb==0.12.3
+ipython==5.8.0
ipython-genutils==0.2.0
+isort==4.3.21
Jinja2==2.9.6
-jmespath==0.9.3
+jmespath==0.9.4
josepy==1.1.0
+jsonschema==2.6.0
+lazy-object-proxy==1.4.3
logger==1.4
logilab-common==1.4.1
MarkupSafe==1.0
-mypy==0.600
+mccabe==0.6.1
+more-itertools==5.0.0
+mypy==0.710
+mypy-extensions==0.4.3
ndg-httpsclient==0.3.2
-oauth2client==2.0.0
-pathlib2==2.3.0
-pexpect==4.2.1
-pickleshare==0.7.4
+oauth2client==4.0.0
+packaging==19.2
+paramiko==2.4.2
+pathlib2==2.3.5
+pexpect==4.7.0
+pickleshare==0.7.5
pkginfo==1.4.2
-pluggy==0.5.2
-prompt-toolkit==1.0.15
-ptyprocess==0.5.2
-py==1.4.34
+pluggy==0.13.0
+prompt-toolkit==1.0.18
+ptyprocess==0.6.0
+py==1.8.0
pyasn1==0.1.9
pyasn1-modules==0.0.10
Pygments==2.2.0
-pylint==1.4.2
+pylint==2.4.3
+# If pynsist version is upgraded, our NSIS template windows-installer/template.nsi
+# must be upgraded if necessary using the new built-in one from pynsist.
+pynacl==1.3.0
+pynsist==2.4
pytest==3.2.5
pytest-cov==2.5.1
pytest-forked==0.2
pytest-xdist==1.22.5
-python-dateutil==2.6.1
+pytest-sugar==0.9.2
+pytest-rerunfailures==4.2
+python-dateutil==2.8.1
python-digitalocean==1.11
+pywin32==227
PyYAML==3.13
repoze.sphinx.autointerface==0.8
requests-file==1.4.2
requests-toolbelt==0.8.0
rsa==3.4.2
-s3transfer==0.1.11
-scandir==1.6
+s3transfer==0.3.1
+scandir==1.10.0
simplegeneric==0.8.1
+singledispatch==3.4.0.3
snowballstemmer==1.2.1
Sphinx==1.7.5
sphinx-rtd-theme==0.2.4
sphinxcontrib-websupport==1.0.1
+texttable==0.9.1
tldextract==2.2.0
-tox==2.9.1
+toml==0.10.0
+tox==3.14.0
tqdm==4.19.4
-traitlets==4.3.2
+traitlets==4.3.3
twine==1.11.0
-typed-ast==1.1.0
+typed-ast==1.4.0
typing==3.6.4
-uritemplate==0.6
-virtualenv==15.1.0
-wcwidth==0.1.7
+uritemplate==3.0.0
+virtualenv==16.6.2
+wcwidth==0.1.8
+websocket-client==0.56.0
+wrapt==1.11.2
+zipp==0.6.0
diff --git a/tools/docker-warning.sh b/tools/docker-warning.sh
deleted file mode 100755
index e4f5f40ee..000000000
--- a/tools/docker-warning.sh
+++ /dev/null
@@ -1,4 +0,0 @@
-#!/bin/sh -e
-echo "Warning: This Docker image will soon be switching to Alpine Linux." >&2
-echo "You can switch now using the certbot/certbot repo on Docker Hub." >&2
-exec /opt/certbot/venv/bin/certbot $@
diff --git a/tools/extract_changelog.py b/tools/extract_changelog.py
new file mode 100755
index 000000000..fb0b849aa
--- /dev/null
+++ b/tools/extract_changelog.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+from __future__ import print_function
+
+import os
+import re
+import sys
+
+CERTBOT_ROOT = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
+
+NEW_SECTION_PATTERN = re.compile(r'^##\s*[\d.]+\s*-\s*[\d-]+$')
+
+
+def main():
+ version = sys.argv[1]
+
+ section_pattern = re.compile(r'^##\s*{0}\s*-\s*[\d-]+$'
+ .format(version.replace('.', '\\.')))
+
+ with open(os.path.join(CERTBOT_ROOT, 'certbot', 'CHANGELOG.md')) as file_h:
+ lines = file_h.read().splitlines()
+
+ changelog = []
+
+ i = 0
+ while i < len(lines):
+ if section_pattern.match(lines[i]):
+ i = i + 1
+ while i < len(lines):
+ if NEW_SECTION_PATTERN.match(lines[i]):
+ break
+ changelog.append(lines[i])
+ i = i + 1
+ i = i + 1
+
+ changelog = [entry for entry in changelog if entry]
+
+ print('\n'.join(changelog))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/install_and_test.py b/tools/install_and_test.py
index b15c8eca5..192708957 100755
--- a/tools/install_and_test.py
+++ b/tools/install_and_test.py
@@ -8,19 +8,16 @@
from __future__ import print_function
import os
-import sys
-import tempfile
-import shutil
-import subprocess
import re
+import subprocess
+import sys
-SKIP_PROJECTS_ON_WINDOWS = [
- 'certbot-apache', 'certbot-nginx', 'certbot-postfix', 'letshelp-certbot']
+SKIP_PROJECTS_ON_WINDOWS = ['certbot-apache', 'letshelp-certbot']
-def call_with_print(command, cwd=None):
+def call_with_print(command):
print(command)
- subprocess.check_call(command, shell=True, cwd=cwd or os.getcwd())
+ subprocess.check_call(command, shell=True)
def main(args):
@@ -42,16 +39,8 @@ def main(args):
call_with_print(' '.join(current_command))
pkg = re.sub(r'\[\w+\]', '', requirement)
- if pkg == '.':
- pkg = 'certbot'
-
- temp_cwd = tempfile.mkdtemp()
- shutil.copy2("pytest.ini", temp_cwd)
- try:
- call_with_print(' '.join([
- sys.executable, '-m', 'pytest', '--pyargs', pkg.replace('-', '_')]), cwd=temp_cwd)
- finally:
- shutil.rmtree(temp_cwd)
+ call_with_print(' '.join([
+ sys.executable, '-m', 'pytest', pkg]))
if __name__ == '__main__':
main(sys.argv[1:])
diff --git a/tools/merge_requirements.py b/tools/merge_requirements.py
index 4205e6bcf..0d41d12c4 100755
--- a/tools/merge_requirements.py
+++ b/tools/merge_requirements.py
@@ -10,27 +10,36 @@ from __future__ import print_function
import sys
-def read_file(file_path):
- """Reads in a Python requirements file.
+def process_entries(entries):
+ """
Ignore empty lines, comments and editable requirements
- :param str file_path: path to requirements file
+ :param list entries: List of entries
:returns: mapping from a project to its pinned version
:rtype: dict
-
"""
data = {}
- with open(file_path) as file_h:
- for line in file_h:
- line = line.strip()
- if line and not line.startswith('#') and not line.startswith('-e'):
- project, version = line.split('==')
- if not version:
- raise ValueError("Unexpected syntax '{0}'".format(line))
- data[project] = version
+ for e in entries:
+ e = e.strip()
+ if e and not e.startswith('#') and not e.startswith('-e'):
+ project, version = e.split('==')
+ if not version:
+ raise ValueError("Unexpected syntax '{0}'".format(e))
+ data[project] = version
return data
+def read_file(file_path):
+ """Reads in a Python requirements file.
+
+ :param str file_path: path to requirements file
+
+ :returns: list of entries in the file
+ :rtype: list
+
+ """
+ with open(file_path) as file_h:
+ return file_h.readlines()
def output_requirements(requirements):
"""Prepare print requirements to stdout.
@@ -46,14 +55,25 @@ def main(*paths):
"""Merges multiple requirements files together and prints the result.
Requirement files specified later in the list take precedence over earlier
- files.
+ files. Files are read from file paths passed from the command line arguments.
- :param tuple paths: paths to requirements files
+ If no command line arguments are defined, data is read from stdin instead.
+
+ :param tuple paths: paths to requirements files provided on command line
"""
data = {}
- for path in paths:
- data.update(read_file(path))
+ if paths:
+ for path in paths:
+ data.update(process_entries(read_file(path)))
+ else:
+ # Need to check if interactive to avoid blocking if nothing is piped
+ if not sys.stdin.isatty():
+ stdin_data = []
+ for line in sys.stdin:
+ stdin_data.append(line)
+ data.update(process_entries(stdin_data))
+
return output_requirements(data)
diff --git a/tools/oldest_constraints.txt b/tools/oldest_constraints.txt
index e48d6b13c..c5a5c5aa0 100644
--- a/tools/oldest_constraints.txt
+++ b/tools/oldest_constraints.txt
@@ -16,6 +16,7 @@ pyOpenSSL==0.13.1
pyparsing==1.5.6
pyRFC3339==1.0
python-augeas==0.5.0
+oauth2client==4.0.0
six==1.9.0
# setuptools 0.9.8 is the actual version packaged, but some other dependencies
# in this file require setuptools>=1.0 and there are no relevant changes for us
@@ -35,11 +36,12 @@ idna==2.0
pbr==1.8.0
pytz==2012rc0
+# Debian Buster constraints
+google-api-python-client==1.5.5
+
# Our setup.py constraints
cloudflare==1.5.1
cryptography==1.2.3
-google-api-python-client==1.5
-oauth2client==2.0
parsedatetime==1.3
pyparsing==1.5.5
python-digitalocean==1.11
@@ -51,6 +53,7 @@ funcsigs==0.4
zope.hookable==4.0.4
# Ubuntu Bionic constraints.
+distro==1.0.1
# Lexicon oldest constraint is overridden appropriately on relevant DNS provider plugins
# using their local-oldest-requirements.txt
dns-lexicon==2.2.1
diff --git a/tools/pip_install.py b/tools/pip_install.py
index dd6302b48..0a3961384 100755
--- a/tools/pip_install.py
+++ b/tools/pip_install.py
@@ -8,17 +8,19 @@
# CERTBOT_OLDEST is set, this script must be run with `-e <package-name>` and
# no other arguments.
-from __future__ import print_function, absolute_import
+from __future__ import absolute_import
+from __future__ import print_function
-import subprocess
import os
-import sys
import re
import shutil
+import subprocess
+import sys
import tempfile
import merge_requirements as merge_module
import readlink
+import strip_hashes
def find_tools_path():
@@ -47,10 +49,8 @@ def certbot_normal_processing(tools_path, test_constraints):
with open(certbot_requirements, 'r') as fd:
data = fd.readlines()
with open(test_constraints, 'w') as fd:
- for line in data:
- search = re.search(r'^(\S*==\S*).*$', line)
- if search:
- fd.write('{0}{1}'.format(search.group(1), os.linesep))
+ data = "\n".join(strip_hashes.process_entries(data))
+ fd.write(data)
def merge_requirements(tools_path, requirements, test_constraints, all_constraints):
@@ -70,9 +70,15 @@ def merge_requirements(tools_path, requirements, test_constraints, all_constrain
fd.write(merged_requirements)
-def call_with_print(command, cwd=None):
+def call_with_print(command):
print(command)
- subprocess.check_call(command, shell=True, cwd=cwd or os.getcwd())
+ subprocess.check_call(command, shell=True)
+
+
+def pip_install_with_print(args_str):
+ command = '"{0}" -m pip install --disable-pip-version-check {1}'.format(sys.executable,
+ args_str)
+ call_with_print(command)
def main(args):
@@ -90,8 +96,7 @@ def main(args):
if os.environ.get('CERTBOT_NO_PIN') == '1':
# With unpinned dependencies, there is no constraint
- call_with_print('"{0}" -m pip install {1}'
- .format(sys.executable, ' '.join(args)))
+ pip_install_with_print(' '.join(args))
else:
# Otherwise, we merge requirements to build the constraints and pin dependencies
requirements = None
@@ -101,12 +106,19 @@ def main(args):
certbot_normal_processing(tools_path, test_constraints)
merge_requirements(tools_path, requirements, test_constraints, all_constraints)
- if requirements:
- call_with_print('"{0}" -m pip install --constraint "{1}" --requirement "{2}"'
- .format(sys.executable, all_constraints, requirements))
-
- call_with_print('"{0}" -m pip install --constraint "{1}" {2}'
- .format(sys.executable, all_constraints, ' '.join(args)))
+ if requirements: # This branch is executed during the oldest tests
+ # First step, install the transitive dependencies of oldest requirements
+ # in respect with oldest constraints.
+ pip_install_with_print('--constraint "{0}" --requirement "{1}"'
+ .format(all_constraints, requirements))
+ # Second step, ensure that oldest requirements themselves are effectively
+ # installed using --force-reinstall, and avoid corner cases like the one described
+ # in https://github.com/certbot/certbot/issues/7014.
+ pip_install_with_print('--force-reinstall --no-deps --requirement "{0}"'
+ .format(requirements))
+
+ pip_install_with_print('--constraint "{0}" {1}'.format(
+ all_constraints, ' '.join(args)))
finally:
if os.environ.get('TRAVIS'):
print('travis_fold:end:install_certbot_deps')
diff --git a/tools/pip_install_editable.py b/tools/pip_install_editable.py
index 8eaf3a9fa..3f7c02ba9 100755
--- a/tools/pip_install_editable.py
+++ b/tools/pip_install_editable.py
@@ -8,6 +8,7 @@ import sys
import pip_install
+
def main(args):
new_args = []
for arg in args:
diff --git a/tools/readlink.py b/tools/readlink.py
index 0199ce184..446c8ebdc 100755
--- a/tools/readlink.py
+++ b/tools/readlink.py
@@ -11,6 +11,7 @@ from __future__ import print_function
import os
import sys
+
def main(link):
return os.path.realpath(link)
diff --git a/tools/simple_http_server.py b/tools/simple_http_server.py
index 14ac9a3d3..24c55962d 100755
--- a/tools/simple_http_server.py
+++ b/tools/simple_http_server.py
@@ -1,9 +1,13 @@
#!/usr/bin/env python
-"""A version of Python 2.x's SimpleHTTPServer that flushes its output."""
-from BaseHTTPServer import HTTPServer
-from SimpleHTTPServer import SimpleHTTPRequestHandler
+"""A version of Python's SimpleHTTPServer that flushes its output."""
import sys
+try:
+ from http.server import HTTPServer, SimpleHTTPRequestHandler
+except ImportError:
+ from BaseHTTPServer import HTTPServer
+ from SimpleHTTPServer import SimpleHTTPRequestHandler
+
def serve_forever(port=0):
"""Spins up an HTTP server on all interfaces and the given port.
diff --git a/tools/sphinx-quickstart.sh b/tools/sphinx-quickstart.sh
index 72dc9e200..35a7f7fad 100755
--- a/tools/sphinx-quickstart.sh
+++ b/tools/sphinx-quickstart.sh
@@ -14,7 +14,7 @@ sed -i -e "s|\# import os|import os|" conf.py
sed -i -e "s|\# needs_sphinx = '1.0'|needs_sphinx = '1.0'|" conf.py
sed -i -e "s|intersphinx_mapping = {'https://docs.python.org/': None}|intersphinx_mapping = {\n 'python': ('https://docs.python.org/', None),\n 'acme': ('https://acme-python.readthedocs.org/en/latest/', None),\n 'certbot': ('https://certbot.eff.org/docs/', None),\n}|" conf.py
sed -i -e "s|html_theme = 'alabaster'|\n# http://docs.readthedocs.org/en/latest/theme.html#how-do-i-use-this-locally-and-on-read-the-docs\n# on_rtd is whether we are on readthedocs.org\non_rtd = os.environ.get('READTHEDOCS', None) == 'True'\nif not on_rtd: # only import and set the theme if we're building docs locally\n import sphinx_rtd_theme\n html_theme = 'sphinx_rtd_theme'\n html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]\n# otherwise, readthedocs.org uses their theme by default, so no need to specify it|" conf.py
-sed -i -e "s|# Add any paths that contain templates here, relative to this directory.|autodoc_member_order = 'bysource'\nautodoc_default_flags = ['show-inheritance', 'private-members']\n\n# Add any paths that contain templates here, relative to this directory.|" conf.py
+sed -i -e "s|# Add any paths that contain templates here, relative to this directory.|autodoc_member_order = 'bysource'\nautodoc_default_flags = ['show-inheritance']\n\n# Add any paths that contain templates here, relative to this directory.|" conf.py
sed -i -e "s|# The name of the Pygments (syntax highlighting) style to use.|default_role = 'py:obj'\n\n# The name of the Pygments (syntax highlighting) style to use.|" conf.py
echo "/_build/" >> .gitignore
echo "=================
diff --git a/tools/strip_hashes.py b/tools/strip_hashes.py
new file mode 100755
index 000000000..988e72eb8
--- /dev/null
+++ b/tools/strip_hashes.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python
+"""Removes hash information from requirement files passed to it as file path
+arguments or simply piped to stdin."""
+
+import re
+import sys
+
+
+def process_entries(entries):
+ """Strips off hash strings from dependencies.
+
+ :param list entries: List of entries
+
+ :returns: list of dependencies without hashes
+ :rtype: list
+ """
+ out_lines = []
+ for e in entries:
+ e = e.strip()
+ search = re.search(r'^(\S*==\S*).*$', e)
+ if search:
+ out_lines.append(search.group(1))
+ return out_lines
+
+def main(*paths):
+ """
+ Reads dependency definitions from a (list of) file(s) provided on the
+ command line. If no command line arguments are present, data is read from
+ stdin instead.
+
+ Hashes are removed from returned entries.
+ """
+
+ deps = []
+ if paths:
+ for path in paths:
+ with open(path) as file_h:
+ deps += process_entries(file_h.readlines())
+ else:
+ # Need to check if interactive to avoid blocking if nothing is piped
+ if not sys.stdin.isatty():
+ stdin_data = []
+ for line in sys.stdin:
+ stdin_data.append(line)
+ deps += process_entries(stdin_data)
+
+ return "\n".join(deps)
+
+if __name__ == '__main__':
+ print(main(*sys.argv[1:])) # pylint: disable=star-args
diff --git a/tools/venv.py b/tools/venv.py
index 93b012e76..f99386eff 100755
--- a/tools/venv.py
+++ b/tools/venv.py
@@ -1,41 +1,37 @@
#!/usr/bin/env python
# Developer virtualenv setup for Certbot client
import os
+import sys
import _venv_common
-REQUIREMENTS = [
- '-e acme[dev]',
- '-e .[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-postfix',
- '-e letshelp-certbot',
- '-e certbot-compatibility-test',
-]
-
-
-def main():
+
+def create_venv(venv_path):
+ """Create a Python 2 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)
+
+
+def main(pip_args=None):
if os.name == 'nt':
raise ValueError('Certbot for Windows is not supported on Python 2.x.')
- venv_args = '--python "{0}"'.format(_venv_common.find_python_executable(2))
- _venv_common.main('venv', venv_args, REQUIREMENTS)
+ venv_path = _venv_common.prepare_venv_path('venv')
+ create_venv(venv_path)
+
+ if not pip_args:
+ pip_args = _venv_common.REQUIREMENTS
+
+ _venv_common.install_packages(venv_path, pip_args)
if __name__ == '__main__':
- main()
+ main(sys.argv[1:])
diff --git a/tools/venv3.py b/tools/venv3.py
index c2374ba5a..7ead82bd5 100755
--- a/tools/venv3.py
+++ b/tools/venv3.py
@@ -1,36 +1,30 @@
-#!/usr/bin/env python
+#!/usr/bin/env python3
# Developer virtualenv setup for Certbot client
+import sys
+
import _venv_common
-REQUIREMENTS = [
- '-e acme[dev]',
- '-e .[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-postfix',
- '-e letshelp-certbot',
- '-e certbot-compatibility-test',
-]
-
-
-def main():
- venv_args = '--python "{0}"'.format(_venv_common.find_python_executable(3))
- _venv_common.main('venv3', venv_args, REQUIREMENTS)
+
+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()
+ main(sys.argv[1:])