Merge branch 'master' of https://github.com/certbot/certbot into docs-install-rewite

This commit is contained in:
zoracon 2022-03-17 10:11:25 -07:00
commit d8cf52e80f
75 changed files with 766 additions and 616 deletions

View file

@ -8,12 +8,6 @@ jobs:
- group: certbot-common
strategy:
matrix:
linux-py36:
PYTHON_VERSION: 3.6
TOXENV: py36
linux-py37:
PYTHON_VERSION: 3.7
TOXENV: py37
linux-py38:
PYTHON_VERSION: 3.8
TOXENV: py38
@ -27,17 +21,13 @@ jobs:
linux-external-mock:
TOXENV: external-mock
linux-boulder-v2-integration-certbot-oldest:
PYTHON_VERSION: 3.6
PYTHON_VERSION: 3.7
TOXENV: integration-certbot-oldest
ACME_SERVER: boulder-v2
linux-boulder-v2-integration-nginx-oldest:
PYTHON_VERSION: 3.6
PYTHON_VERSION: 3.7
TOXENV: integration-nginx-oldest
ACME_SERVER: boulder-v2
linux-boulder-v2-py36-integration:
PYTHON_VERSION: 3.6
TOXENV: integration
ACME_SERVER: boulder-v2
linux-boulder-v2-py37-integration:
PYTHON_VERSION: 3.7
TOXENV: integration

View file

@ -55,12 +55,12 @@ jobs:
displayName: Run integration tests for Docker images
- job: installer_build
pool:
vmImage: vs2017-win2016
vmImage: windows-2019
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: 3.9
architecture: x86
architecture: x64
addToPath: true
- script: |
python -m venv venv
@ -87,17 +87,9 @@ jobs:
matrix:
win2019:
imageName: windows-2019
win2016:
imageName: vs2017-win2016
pool:
vmImage: $(imageName)
steps:
- powershell: |
if ($PSVersionTable.PSVersion.Major -ne 5) {
throw "Powershell version is not 5.x"
}
condition: eq(variables['imageName'], 'vs2017-win2016')
displayName: Check Powershell 5.x is used in vs2017-win2016
- task: UsePythonVersion@0
inputs:
versionSpec: 3.9
@ -115,11 +107,11 @@ jobs:
PIP_NO_BUILD_ISOLATION: no
displayName: Prepare Certbot-CI
- script: |
set PATH=%ProgramFiles(x86)%\Certbot\bin;%PATH%
venv\Scripts\python -m pytest certbot-ci\windows_installer_integration_tests --allow-persistent-changes --installer-path $(Build.SourcesDirectory)\bin\certbot-beta-installer-win32.exe
set PATH=%ProgramFiles%\Certbot\bin;%PATH%
venv\Scripts\python -m pytest certbot-ci\windows_installer_integration_tests --allow-persistent-changes --installer-path $(Build.SourcesDirectory)\bin\certbot-beta-installer-win_amd64.exe
displayName: Run windows installer integration tests
- script: |
set PATH=%ProgramFiles(x86)%\Certbot\bin;%PATH%
set PATH=%ProgramFiles%\Certbot\bin;%PATH%
venv\Scripts\python -m pytest certbot-ci\certbot_integration_tests\certbot_tests -n 4
displayName: Run certbot integration tests
- job: snaps_build

View file

@ -4,38 +4,38 @@ jobs:
PYTHON_VERSION: 3.10
strategy:
matrix:
macos-py36-cover:
macos-py37-cover:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.6
TOXENV: py36-cover
PYTHON_VERSION: 3.7
TOXENV: py37-cover
macos-py310-cover:
IMAGE_NAME: macOS-10.15
PYTHON_VERSION: 3.10
TOXENV: py310-cover
windows-py36:
IMAGE_NAME: vs2017-win2016
PYTHON_VERSION: 3.6
TOXENV: py36-win
windows-py37:
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.7
TOXENV: py37-win
windows-py39-cover:
IMAGE_NAME: vs2017-win2016
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.9
TOXENV: py39-cover-win
windows-integration-certbot:
IMAGE_NAME: vs2017-win2016
IMAGE_NAME: windows-2019
PYTHON_VERSION: 3.9
TOXENV: integration-certbot
linux-oldest-tests-1:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
PYTHON_VERSION: 3.7
TOXENV: '{acme,apache,apache-v2,certbot}-oldest'
linux-oldest-tests-2:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
PYTHON_VERSION: 3.7
TOXENV: '{dns,nginx}-oldest'
linux-py36:
linux-py37:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: py36
PYTHON_VERSION: 3.7
TOXENV: py37
linux-py310-cover:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.10
@ -58,11 +58,9 @@ jobs:
TOXENV: apache_compat
apacheconftest:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: apacheconftest-with-pebble
nginxroundtrip:
IMAGE_NAME: ubuntu-18.04
PYTHON_VERSION: 3.6
TOXENV: nginxroundtrip
pool:
vmImage: $(IMAGE_NAME)

View file

@ -3,7 +3,7 @@ stages:
jobs:
- job: prepare
pool:
vmImage: vs2017-win2016
vmImage: windows-2019
steps:
# If we change the output filename from `release_notes.md`, it should also be changed in tools/create_github_release.py
- bash: |

View file

@ -6,7 +6,6 @@ This module is an implementation of the `ACME protocol`_.
"""
import sys
import warnings
# This code exists to keep backwards compatibility with people using acme.jose
# before it became the standalone josepy package.
@ -20,11 +19,3 @@ for mod in list(sys.modules):
# preserved (acme.jose.* is josepy.*)
if mod == 'josepy' or mod.startswith('josepy.'):
sys.modules['acme.' + mod.replace('josepy', 'jose', 1)] = sys.modules[mod]
if sys.version_info[:2] == (3, 6):
warnings.warn(
"Python 3.6 support will be dropped in the next release of "
"acme. Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover

View file

@ -279,7 +279,7 @@ class DNS01(KeyAuthorizationChallenge):
:rtype: str
"""
return "{0}.{1}".format(self.LABEL, name)
return f"{self.LABEL}.{name}"
@ChallengeResponse.register

View file

@ -1142,8 +1142,7 @@ class ClientNetwork:
'response', response_ct)
if content_type == cls.JSON_CONTENT_TYPE and jobj is None:
raise errors.ClientError(
'Unexpected response Content-Type: {0}'.format(response_ct))
raise errors.ClientError(f'Unexpected response Content-Type: {response_ct}')
return response
@ -1196,7 +1195,7 @@ class ClientNetwork:
if m is None:
raise # pragma: no cover
host, path, _err_no, err_msg = m.groups()
raise ValueError("Requesting {0}{1}:{2}".format(host, path, err_msg))
raise ValueError(f"Requesting {host}{path}:{err_msg}")
# If the Content-Type is DER or an Accept header was sent in the
# request, the response may not be UTF-8 encoded. In this case, we

View file

@ -157,12 +157,11 @@ class _Constant(jose.JSONDeSerializable, Hashable):
@classmethod
def from_json(cls, jobj: str) -> '_Constant':
if jobj not in cls.POSSIBLE_NAMES: # pylint: disable=unsupported-membership-test
raise jose.DeserializationError(
'{0} not recognized'.format(cls.__name__))
raise jose.DeserializationError(f'{cls.__name__} not recognized')
return cls.POSSIBLE_NAMES[jobj]
def __repr__(self) -> str:
return '{0}({1})'.format(self.__class__.__name__, self.name)
return f'{self.__class__.__name__}({self.name})'
def __eq__(self, other: Any) -> bool:
return isinstance(other, type(self)) and other.name == self.name

View file

@ -65,4 +65,4 @@ def _safe_jobj_compliance(instance: Any, jobj_method: str,
jobj.pop(uncompliant_field, None)
return jobj
raise AttributeError('Method {0}() is not implemented.'.format(jobj_method)) # pragma: no cover
raise AttributeError(f'Method {jobj_method}() is not implemented.') # pragma: no cover

View file

@ -3,6 +3,6 @@ usage: jws [-h] [--compact] {sign,verify} ...
positional arguments:
{sign,verify}
options:
optional arguments:
-h, --help show this help message and exit
--compact

View file

@ -3,17 +3,17 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'cryptography>=2.5.0',
'josepy>=1.10.0',
'josepy>=1.13.0',
'PyOpenSSL>=17.3.0',
'pyrfc3339',
'pytz',
'requests>=2.14.2',
'pytz>=2019.3',
'requests>=2.20.0',
'requests-toolbelt>=0.3.0',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
docs_extras = [
@ -35,14 +35,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
# We specify the minimum acme and certbot version as the current plugin
@ -10,7 +10,7 @@ install_requires = [
f'acme>={version}',
f'certbot>={version}',
'python-augeas',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
dev_extras = [
@ -25,7 +25,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -34,7 +34,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -1,6 +1,7 @@
"""This module contains advanced assertions for the certbot integration tests."""
import io
import os
from typing import Optional
from typing import Type
from cryptography.hazmat.backends import default_backend
@ -62,14 +63,26 @@ def assert_hook_execution(probe_path: str, probe_content: str) -> None:
assert probe_content in lines
def assert_saved_lineage_option(config_dir: str, lineage: str,
option: str, value: Optional[str] = None) -> None:
"""
Assert that the option of a lineage has been saved.
:param str config_dir: location of the certbot configuration
:param str lineage: lineage domain name
:param str option: the option key
:param value: if desired, the expected option value
"""
with open(os.path.join(config_dir, 'renewal', '{0}.conf'.format(lineage))) as file_h:
assert f"{option} = {value if value else ''}" in file_h.read()
def assert_saved_renew_hook(config_dir: str, lineage: str) -> None:
"""
Assert that the renew hook configuration of a lineage has been saved.
:param str config_dir: location of the certbot configuration
:param str lineage: lineage domain name
"""
with open(os.path.join(config_dir, 'renewal', '{0}.conf'.format(lineage))) as file_h:
assert 'renew_hook' in file_h.read()
assert_saved_lineage_option(config_dir, lineage, 'renew_hook')
def assert_cert_count_for_lineage(config_dir: str, lineage: str, count: int) -> None:

View file

@ -29,8 +29,8 @@ class IntegrationTestsContext:
self.http_01_port = acme_xdist['http_port'][self.worker_id]
self.other_port = acme_xdist['other_port'][self.worker_id]
# Challtestsrv REST API, that exposes entrypoints to register new DNS entries,
# is listening on challtestsrv_port.
self.challtestsrv_port = acme_xdist['challtestsrv_port']
# is listening on challtestsrv_url.
self.challtestsrv_url = acme_xdist['challtestsrv_url']
self.workspace = tempfile.mkdtemp()
self.config_dir = os.path.join(self.workspace, 'conf')
@ -44,17 +44,17 @@ class IntegrationTestsContext:
"assert not os.environ.get('CERTBOT_DOMAIN').startswith('fail'); "
"data = {{'host':'_acme-challenge.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN')),"
"'value':os.environ.get('CERTBOT_VALIDATION')}}; "
"request = requests.post('http://localhost:{1}/set-txt', data=json.dumps(data)); "
"request = requests.post('{1}/set-txt', data=json.dumps(data)); "
"request.raise_for_status(); "
'"'
).format(sys.executable, self.challtestsrv_port)
).format(sys.executable, self.challtestsrv_url)
self.manual_dns_cleanup_hook = (
'{0} -c "import os; import requests; import json; '
"data = {{'host':'_acme-challenge.{{0}}.'.format(os.environ.get('CERTBOT_DOMAIN'))}}; "
"request = requests.post('http://localhost:{1}/clear-txt', data=json.dumps(data)); "
"request = requests.post('{1}/clear-txt', data=json.dumps(data)); "
"request.raise_for_status(); "
'"'
).format(sys.executable, self.challtestsrv_port)
).format(sys.executable, self.challtestsrv_url)
def cleanup(self) -> None:
"""Cleanup the integration test context."""

View file

@ -25,6 +25,7 @@ from certbot_integration_tests.certbot_tests.assertions import assert_equals_gro
from certbot_integration_tests.certbot_tests.assertions import assert_equals_world_read_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_hook_execution
from certbot_integration_tests.certbot_tests.assertions import assert_rsa_key
from certbot_integration_tests.certbot_tests.assertions import assert_saved_lineage_option
from certbot_integration_tests.certbot_tests.assertions import assert_saved_renew_hook
from certbot_integration_tests.certbot_tests.assertions import assert_world_no_permissions
from certbot_integration_tests.certbot_tests.assertions import assert_world_read_permissions
@ -102,6 +103,7 @@ def test_http_01(context: IntegrationTestsContext) -> None:
assert_hook_execution(context.hook_probe, 'deploy')
assert_saved_renew_hook(context.config_dir, certname)
assert_saved_lineage_option(context.config_dir, certname, 'key_type', 'rsa')
def test_manual_http_auth(context: IntegrationTestsContext) -> None:
@ -540,35 +542,47 @@ def test_renew_with_ec_keys(context: IntegrationTestsContext) -> None:
'--key-type', 'ecdsa', '--elliptic-curve', 'secp256r1',
'--force-renewal', '-d', certname,
])
key1 = join(context.config_dir, "archive", certname, 'privkey1.pem')
assert 200 < os.stat(key1).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key1, SECP256R1)
assert_cert_count_for_lineage(context.config_dir, certname, 1)
assert_saved_lineage_option(context.config_dir, certname, 'key_type', 'ecdsa')
context.certbot(['renew', '--elliptic-curve', 'secp384r1'])
assert_cert_count_for_lineage(context.config_dir, certname, 2)
key2 = join(context.config_dir, 'archive', certname, 'privkey2.pem')
assert_elliptic_key(key2, SECP384R1)
assert 280 < os.stat(key2).st_size < 320 # ec keys of 384 bits are ~310 bytes
assert_elliptic_key(key2, SECP384R1)
# We expect here that the command will fail because without --key-type specified,
# Certbot must error out to prevent changing an existing certificate key type,
# without explicit user consent (by specifying both --cert-name and --key-type).
with pytest.raises(subprocess.CalledProcessError):
context.certbot([
'certonly',
'--force-renewal',
'-d', certname
])
# When running non-interactively, if --key-type is unspecified but the default value differs
# to the lineage key type, Certbot should keep the lineage key type. The curve will still
# change to the default value, in order to stay consistent with the behavior of certonly.
context.certbot(['certonly', '--force-renewal', '-d', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 3)
key3 = join(context.config_dir, 'archive', certname, 'privkey3.pem')
assert 200 < os.stat(key3).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key3, SECP256R1)
# When running non-interactively, specifying a different --key-type requires user confirmation
# with both --key-type and --cert-name.
with pytest.raises(subprocess.CalledProcessError) as error:
context.certbot(['certonly', '--force-renewal', '-d', certname,
'--key-type', 'rsa'])
assert 'Please provide both --cert-name and --key-type' in error.value.stderr
context.certbot(['certonly', '--force-renewal', '-d', certname,
'--key-type', 'rsa', '--cert-name', certname])
assert_cert_count_for_lineage(context.config_dir, certname, 4)
key4 = join(context.config_dir, 'archive', certname, 'privkey4.pem')
assert_rsa_key(key4)
# We expect that the previous behavior of requiring both --cert-name and
# --key-type to be set to not apply to the renew subcommand.
context.certbot(['renew', '--force-renewal', '--key-type', 'rsa'])
assert_cert_count_for_lineage(context.config_dir, certname, 3)
key3 = join(context.config_dir, 'archive', certname, 'privkey3.pem')
assert_rsa_key(key3)
context.certbot(['renew', '--force-renewal', '--key-type', 'ecdsa'])
assert_cert_count_for_lineage(context.config_dir, certname, 5)
key5 = join(context.config_dir, 'archive', certname, 'privkey5.pem')
assert 200 < os.stat(key5).st_size < 250 # ec keys of 256 bits are ~225 bytes
assert_elliptic_key(key5, SECP256R1)
def test_ocsp_must_staple(context: IntegrationTestsContext) -> None:

View file

@ -122,14 +122,16 @@ class ACMEServer:
def _construct_acme_xdist(self, acme_server: str, nodes: List[str]) -> None:
"""Generate and return the acme_xdist dict"""
acme_xdist = {'acme_server': acme_server, 'challtestsrv_port': CHALLTESTSRV_PORT}
acme_xdist: Dict[str, Any] = {'acme_server': acme_server}
# Directory and ACME port are set implicitly in the docker-compose.yml
# files of Boulder/Pebble.
if acme_server == 'pebble':
acme_xdist['directory_url'] = PEBBLE_DIRECTORY_URL
acme_xdist['challtestsrv_url'] = PEBBLE_CHALLTESTSRV_URL
else: # boulder
acme_xdist['directory_url'] = BOULDER_V2_DIRECTORY_URL
acme_xdist['challtestsrv_url'] = BOULDER_V2_CHALLTESTSRV_URL
acme_xdist['http_port'] = {
node: port for (node, port) in # pylint: disable=unnecessary-comprehension
@ -182,7 +184,7 @@ class ACMEServer:
# Wait for the ACME CA server to be up.
print('=> Waiting for pebble instance to respond...')
misc.check_until_timeout(self.acme_xdist['directory_url']) # type: ignore[arg-type]
misc.check_until_timeout(self.acme_xdist['directory_url'])
print('=> Finished pebble instance deployment.')
@ -216,12 +218,13 @@ class ACMEServer:
# Wait for the ACME CA server to be up.
print('=> Waiting for boulder instance to respond...')
misc.check_until_timeout(
self.acme_xdist['directory_url'], attempts=300) # type: ignore[arg-type]
self.acme_xdist['directory_url'], attempts=300)
if not self._dns_server:
# Configure challtestsrv to answer any A record request with ip of the docker host.
response = requests.post('http://localhost:{0}/set-default-ipv4'.format(
CHALLTESTSRV_PORT), json={'ip': '10.77.77.1'}
response = requests.post(
f'{BOULDER_V2_CHALLTESTSRV_URL}/set-default-ipv4',
json={'ip': '10.77.77.1'}
)
response.raise_for_status()
except BaseException:

View file

@ -2,8 +2,10 @@
DEFAULT_HTTP_01_PORT = 5002
TLS_ALPN_01_PORT = 5001
CHALLTESTSRV_PORT = 8055
BOULDER_V2_CHALLTESTSRV_URL = f'http://10.77.77.77:{CHALLTESTSRV_PORT}'
BOULDER_V2_DIRECTORY_URL = 'http://localhost:4001/directory'
PEBBLE_DIRECTORY_URL = 'https://localhost:14000/dir'
PEBBLE_MANAGEMENT_URL = 'https://localhost:15000'
PEBBLE_CHALLTESTSRV_URL = f'http://localhost:{CHALLTESTSRV_PORT}'
MOCK_OCSP_SERVER_PORT = 4002
PEBBLE_ALTERNATE_ROOTS = 2

View file

@ -40,14 +40,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -20,9 +20,9 @@ def pytest_addoption(parser):
"""
parser.addoption('--installer-path',
default=os.path.join(ROOT_PATH, 'windows-installer', 'build',
'nsis', 'certbot-beta-installer-win32.exe'),
'nsis', 'certbot-beta-installer-win_amd64.exe'),
help='set the path of the windows installer to use, default to '
'CERTBOT_ROOT_PATH\\windows-installer\\build\\nsis\\certbot-beta-installer-win32.exe') # pylint: disable=line-too-long
'CERTBOT_ROOT_PATH\\windows-installer\\build\\nsis\\certbot-beta-installer-win_amd64.exe') # pylint: disable=line-too-long
parser.addoption('--allow-persistent-changes', action='store_true',
help='needs to be set, and confirm that the test will make persistent changes on this machine') # pylint: disable=line-too-long

View file

@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'certbot',
@ -18,14 +18,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'cloudflare>=1.5.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,13 +4,13 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
# This version of lexicon is required to address the problem described in
# https://github.com/AnalogJ/lexicon/issues/387.
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -40,7 +40,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -49,7 +49,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,12 +4,12 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'google-api-python-client>=1.5.5',
'oauth2client>=4.0',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
# already a dependency of google-api-python-client, but added for consistency
'httplib2'
]
@ -41,7 +41,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -50,7 +50,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dnspython>=1.15.0',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'boto3',
'setuptools>=39.0.1',
'boto3>=1.15.15',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -4,11 +4,11 @@ import sys
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
'dns-lexicon>=3.2.1',
'setuptools>=39.0.1',
'setuptools>=41.6.0',
]
if not os.environ.get('SNAP_BUILD'):
@ -38,7 +38,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -47,7 +47,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -28,7 +28,7 @@ from pyparsing import White
from pyparsing import ZeroOrMore
if TYPE_CHECKING:
from typing_extensions import SupportsIndex # typing.SupportsIndex not supported on Python 3.6
from typing_extensions import SupportsIndex # typing.SupportsIndex not supported on Python 3.7
logger = logging.getLogger(__name__)

View file

@ -1,7 +1,7 @@
from setuptools import find_packages
from setuptools import setup
version = '1.24.0.dev0'
version = '1.26.0.dev0'
install_requires = [
# We specify the minimum acme and certbot version as the current plugin
@ -10,8 +10,8 @@ install_requires = [
f'acme>={version}',
f'certbot>={version}',
'PyOpenSSL>=17.3.0',
'pyparsing>=2.2.0',
'setuptools>=39.0.1',
'pyparsing>=2.2.1',
'setuptools>=41.6.0',
]
setup(
@ -22,7 +22,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Plugins',
@ -31,7 +31,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -2,7 +2,7 @@
Certbot adheres to [Semantic Versioning](https://semver.org/).
## 1.24.0 - master
## 1.26.0 - master
### Added
@ -14,6 +14,55 @@ Certbot adheres to [Semantic Versioning](https://semver.org/).
### Fixed
*
More details about these changes can be found on our GitHub repo.
## 1.25.0 - 2022-03-16
### Added
*
### Changed
* Dropped 32 bit support for the Windows beta installer
* Windows beta installer is now distributed as "certbot-beta-installer-win_amd64.exe".
Users of the Windows beta should uninstall the old version before running this.
* Added a check whether OCSP stapling is supported by the installer when requesting a
certificate with the `run` subcommand in combination with the `--must-staple` option.
If the installer does not support OCSP and the `--must-staple` option is used, Certbot
will raise an error and quit.
* Certbot and its acme module now depend on josepy>=1.13.0 due to better type annotation
support.
### Fixed
* Updated dependencies to use new version of cryptography that uses OpenSSL 1.1.1n, in
response to https://www.openssl.org/news/secadv/20220315.txt.
More details about these changes can be found on our GitHub repo.
## 1.24.0 - 2022-03-01
### Added
* When the `--debug-challenges` option is used in combination with `-v`, Certbot
now displays the challenge URLs (for `http-01` challenges) or FQDNs (for
`dns-01` challenges) and their expected return values.
*
### Changed
* Support for Python 3.6 was removed.
* All Certbot components now require setuptools>=41.6.0.
* The acme library now requires requests>=2.20.0.
* Certbot and its acme library now require pytz>=2019.3.
* certbot-nginx now requires pyparsing>=2.2.1.
* certbot-dns-route53 now requires boto3>=1.15.15.
### Fixed
* Nginx plugin now checks included files for the singleton server_names_hash_bucket_size directive.
*

View file

@ -10,7 +10,7 @@ Certbot is meant to be run directly on your web server, not on your personal com
Certbot is a fully-featured, extensible client for the Let's
Encrypt CA (or any other CA that speaks the `ACME
<https://github.com/ietf-wg-acme/acme/blob/master/draft-ietf-acme-acme.md>`_
<https://datatracker.ietf.org/doc/html/rfc8555>`_
protocol) that can automate the tasks of obtaining certificates and
configuring webservers to use them. This client runs on Unix-based operating
systems.

View file

@ -1,13 +1,3 @@
"""Certbot client."""
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
import sys
import warnings
__version__ = '1.24.0.dev0'
if sys.version_info[:2] == (3, 6):
warnings.warn(
"Python 3.6 support will be dropped in the next release of "
"certbot. Please upgrade your Python version.",
PendingDeprecationWarning,
) # pragma: no cover
__version__ = '1.26.0.dev0'

View file

@ -226,20 +226,15 @@ class AccountFileStorage(interfaces.AccountStorage):
else:
self._symlink_to_accounts_dir(prev_server_path, server_path)
return prev_loaded_account
raise errors.AccountNotFound(
"Account at %s does not exist" % account_dir_path)
raise errors.AccountNotFound(f"Account at {account_dir_path} does not exist")
try:
with open(self._regr_path(account_dir_path)) as regr_file:
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
regr = cast(messages.RegistrationResource,
messages.RegistrationResource.json_loads(regr_file.read()))
regr = messages.RegistrationResource.json_loads(regr_file.read())
with open(self._key_path(account_dir_path)) as key_file:
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
key = cast(jose.JWK, jose.JWK.json_loads(key_file.read()))
key = jose.JWK.json_loads(key_file.read())
with open(self._metadata_path(account_dir_path)) as metadata_file:
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
meta = cast(Account.Meta, Account.Meta.json_loads(metadata_file.read()))
meta = Account.Meta.json_loads(metadata_file.read())
except IOError as error:
raise errors.AccountStorageError(error)
@ -296,8 +291,7 @@ class AccountFileStorage(interfaces.AccountStorage):
"""
account_dir_path = self._account_dir_path(account_id)
if not os.path.isdir(account_dir_path):
raise errors.AccountNotFound(
"Account at %s does not exist" % account_dir_path)
raise errors.AccountNotFound(f"Account at {account_dir_path} does not exist")
# Step 1: Delete account specific links and the directory
self._delete_account_dir_for_server_path(account_id, self.config.server_path)

View file

@ -88,8 +88,8 @@ class AuthHandler:
# If debug is on, wait for user input before starting the verification process.
if config.debug_challenges:
display_util.notification(
'Challenges loaded. Press continue to submit to CA. '
'Pass "-v" for more info about challenges.', pause=True)
'Challenges loaded. Press continue to submit to CA.\n' +
self._debug_challenges_msg(achalls, config), pause=True)
except errors.AuthorizationError as error:
logger.critical('Failure in setting up challenges.')
logger.info('Attempting to clean up outstanding challenges...')
@ -324,6 +324,44 @@ class AuthHandler:
display_util.notify("".join(msg))
def _debug_challenges_msg(self, achalls: List[achallenges.AnnotatedChallenge],
config: configuration.NamespaceConfig) -> str:
"""Construct message for debug challenges prompt
:param list achalls: A list of
:class:`certbot.achallenges.AnnotatedChallenge`.
:param certbot.configuration.NamespaceConfig config: current Certbot configuration
:returns: Message containing challenge debug info
:rtype: str
"""
if config.verbose_count > 0:
msg = []
http01_achalls = {}
dns01_achalls = {}
for achall in achalls:
if isinstance(achall.chall, challenges.HTTP01):
http01_achalls[achall.chall.uri(achall.domain)] = (
achall.validation(achall.account_key) + "\n"
)
if isinstance(achall.chall, challenges.DNS01):
dns01_achalls[achall.validation_domain_name(achall.domain)] = (
achall.validation(achall.account_key) + "\n"
)
if http01_achalls:
msg.append("The following URLs should be accessible from the "
"internet and return the value mentioned:\n")
for uri, key_authz in http01_achalls.items():
msg.append(f"URL: {uri}\nExpected value: {key_authz}")
if dns01_achalls:
msg.append("The following FQDNs should return a TXT resource "
"record with the value mentioned:\n")
for fqdn, key_authz_hash in dns01_achalls.items():
msg.append(f"FQDN: {fqdn}\nExpected value: {key_authz_hash}")
return "\n" + "\n".join(msg)
else:
return 'Pass "-v" for more info about challenges.'
def challb_to_achall(challb: messages.ChallengeBody, account_key: josepy.JWK,
domain: str) -> achallenges.AnnotatedChallenge:
@ -345,8 +383,7 @@ def challb_to_achall(challb: messages.ChallengeBody, account_key: josepy.JWK,
challb=challb, domain=domain, account_key=account_key)
elif isinstance(chall, challenges.DNS):
return achallenges.DNS(challb=challb, domain=domain)
raise errors.Error(
"Received unsupported challenge of type: {0}".format(chall.typ))
raise errors.Error(f"Received unsupported challenge of type: {chall.typ}")
def gen_challenge_path(challbs: List[messages.ChallengeBody],

View file

@ -285,7 +285,7 @@ def match_and_check_overlaps(cli_config: configuration.NamespaceConfig,
matched: List[str] = _search_lineages(cli_config, find_matches, [], acceptable_matches)
if not matched:
raise errors.Error("No match found for cert-path {0}!".format(cli_config.cert_path))
raise errors.Error(f"No match found for cert-path {cli_config.cert_path}!")
elif len(matched) > 1:
raise errors.OverlappingMatchFound()
return matched
@ -318,26 +318,19 @@ def human_readable_cert_info(config: configuration.NamespaceConfig, cert: storag
if diff.days == 1:
status = "VALID: 1 day"
elif diff.days < 1:
status = "VALID: {0} hour(s)".format(diff.seconds // 3600)
status = f"VALID: {diff.seconds // 3600} hour(s)"
else:
status = "VALID: {0} days".format(diff.days)
status = f"VALID: {diff.days} days"
valid_string = "{0} ({1})".format(cert.target_expiry, status)
serial = format(crypto_util.get_serial_from_cert(cert.cert_path), 'x')
certinfo.append(" Certificate Name: {}\n"
" Serial Number: {}\n"
" Key Type: {}\n"
" Domains: {}\n"
" Expiry Date: {}\n"
" Certificate Path: {}\n"
" Private Key Path: {}".format(
cert.lineagename,
serial,
cert.private_key_type,
" ".join(cert.names()),
valid_string,
cert.fullchain,
cert.privkey))
certinfo.append(f" Certificate Name: {cert.lineagename}\n"
f" Serial Number: {serial}\n"
f" Key Type: {cert.private_key_type}\n"
f' Domains: {" ".join(cert.names())}\n'
f" Expiry Date: {valid_string}\n"
f" Certificate Path: {cert.fullchain}\n"
f" Private Key Path: {cert.privkey}")
return "".join(certinfo)

View file

@ -264,7 +264,9 @@ def prepare_and_parse_args(plugins: plugins_disco.PluginsRegistry, args: List[st
[None, "certonly", "run"], "--debug-challenges", action="store_true",
default=flag_default("debug_challenges"),
help="After setting up challenges, wait for user input before "
"submitting to CA")
"submitting to CA. When used in combination with the `-v` "
"option, the challenge URLs or FQDNs and their expected "
"return values are shown.")
helpful.add(
"testing", "--no-verify-ssl", action="store_true",
help=config_help("no_verify_ssl"),

View file

@ -271,9 +271,9 @@ def perform_registration(acme: acme_client.ClientV2, config: configuration.Names
except messages.Error as e:
if e.code in ("invalidEmail", "invalidContact"):
if config.noninteractive_mode:
msg = ("The ACME server believes %s is an invalid email address. "
msg = (f"The ACME server believes {config.email} is an invalid email address. "
"Please ensure it is a valid email and attempt "
"registration again." % config.email)
"registration again.")
raise errors.Error(msg)
config.email = display_ops.get_email(invalid=True)
return perform_registration(acme, config, tos_cb)

View file

@ -356,16 +356,15 @@ class FileDisplay:
"""
# Can take either tuples or single items in choices list
if choices and isinstance(choices[0], tuple):
choices = ["%s - %s" % (c[0], c[1]) for c in choices]
choices = [f"{c[0]} - {c[1]}" for c in choices]
# Write out the message to the user
self.outfile.write(
"{new}{msg}{new}".format(new=os.linesep, msg=message))
self.outfile.write(f"{os.linesep}{message}{os.linesep}")
self.outfile.write(SIDE_FRAME + os.linesep)
# Write out the menu choices
for i, desc in enumerate(choices, 1):
msg = "{num}: {desc}".format(num=i, desc=desc)
msg = f"{i}: {desc}"
self.outfile.write(util.wrap_lines(msg))
# Keep this outside of the textwrap

View file

@ -1,6 +1,5 @@
"""Subscribes users to the EFF newsletter."""
import logging
from typing import cast
from typing import Optional
import requests
@ -33,11 +32,9 @@ def prepare_subscription(config: configuration.NamespaceConfig, acc: Account) ->
if config.email is None:
_report_failure("you didn't provide an e-mail address")
else:
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
acc.meta = cast(Account.Meta, acc.meta.update(register_to_eff=config.email))
acc.meta = acc.meta.update(register_to_eff=config.email)
elif config.email and _want_subscription():
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
acc.meta = cast(Account.Meta, acc.meta.update(register_to_eff=config.email))
acc.meta = acc.meta.update(register_to_eff=config.email)
if acc.meta.register_to_eff:
storage = AccountFileStorage(config)
@ -56,11 +53,9 @@ def handle_subscription(config: configuration.NamespaceConfig, acc: Optional[Acc
if config.dry_run or not acc:
return
if acc.meta.register_to_eff:
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
subscribe(cast(str, acc.meta.register_to_eff))
subscribe(acc.meta.register_to_eff)
# TODO: Remove cast when https://github.com/certbot/certbot/pull/9073 is merged.
acc.meta = cast(Account.Meta, acc.meta.update(register_to_eff=None))
acc.meta = acc.meta.update(register_to_eff=None)
storage = AccountFileStorage(config)
storage.update_meta(acc)

View file

@ -52,10 +52,11 @@ def validate_hook(shell_cmd: str, hook_name: str) -> None:
if not _prog(cmd):
path = os.environ["PATH"]
if os.path.exists(cmd):
msg = "{1}-hook command {0} exists, but is not executable.".format(cmd, hook_name)
msg = f"{cmd}-hook command {hook_name} exists, but is not executable."
else:
msg = "Unable to find {2}-hook command {0} in the PATH.\n(PATH is {1})".format(
cmd, path, hook_name)
msg = (
f"Unable to find {hook_name}-hook command {cmd} in the PATH.\n(PATH is {path})"
)
raise errors.HookCommandNotFound(msg)

View file

@ -156,17 +156,39 @@ def _handle_unexpected_key_type_migration(config: configuration.NamespaceConfig,
:param config: Current configuration provided by the client
:param cert: Matching certificate that could be renewed
"""
if not cli.set_by_cli("key_type") or not cli.set_by_cli("certname"):
new_key_type = config.key_type.upper()
cur_key_type = cert.private_key_type.upper()
new_key_type = config.key_type.upper()
cur_key_type = cert.private_key_type.upper()
if new_key_type == cur_key_type:
return
if new_key_type != cur_key_type:
msg = ('Are you trying to change the key type of the certificate named {0} '
'from {1} to {2}? Please provide both --cert-name and --key-type on '
'the command line to confirm the change you are trying to make.')
msg = msg.format(cert.lineagename, cur_key_type, new_key_type)
raise errors.Error(msg)
# If both --key-type and --cert-name are provided, we consider the user's intent to
# be unambiguous: to change the key type of this lineage.
is_confirmed_via_cli = cli.set_by_cli("key_type") and cli.set_by_cli("certname")
# Failing that, we interactively prompt the user to confirm the change.
if is_confirmed_via_cli or display_util.yesno(
f'An {cur_key_type} certificate named {cert.lineagename} already exists. Do you want to '
f'update its key type to {new_key_type}?',
yes_label='Update key type', no_label='Keep existing key type',
default=False, force_interactive=False,
):
return
# If --key-type was set on the CLI but the user did not confirm the key type change using
# one of the two above methods, their intent is ambiguous. Error out.
if cli.set_by_cli("key_type"):
raise errors.Error(
'Are you trying to change the key type of the certificate named '
f'{cert.lineagename} from {cur_key_type} to {new_key_type}? Please provide '
'both --cert-name and --key-type on the command line to confirm the change '
'you are trying to make.'
)
# The mismatch between the lineage's key type and config.key_type is caused by Certbot's
# default value. The user is not asking for a key change: keep the key type of the existing
# lineage.
config.key_type = cur_key_type.lower()
def _handle_subset_cert_request(config: configuration.NamespaceConfig,
@ -1372,6 +1394,20 @@ def run(config: configuration.NamespaceConfig,
except errors.PluginSelectionError as e:
return str(e)
if config.must_staple and installer and "staple-ocsp" not in installer.supported_enhancements():
raise errors.NotSupportedError(
"Must-Staple extension requested, but OCSP stapling is not supported by the selected "
f"installer ({config.installer})\n\n"
"You can either:\n"
" * remove the --must-staple option from the command line and obtain a certificate "
"without the Must-Staple extension, or;\n"
" * use the `certonly` subcommand and manually install the certificate into the "
"intended service (e.g. webserver). You must also then manually enable OCSP stapling, "
"as it is required for certificates with the Must-Staple extension to "
"function properly.\n"
" * choose a different installer plugin (such as --nginx or --apache), if possible."
)
# Preflight check for enhancement support by the selected installer
if not enhancements.are_supported(config, installer):
raise errors.NotSupportedError("One ore more of the requested enhancements "
@ -1673,10 +1709,6 @@ def main(cli_args: List[str] = None) -> Optional[Union[str, int]]:
zope.component.provideUtility(report, interfaces.IReporter)
util.atexit_register(report.print_messages)
if sys.version_info[:2] == (3, 6):
logger.warning("Python 3.6 support will be dropped in the next release "
"of Certbot - please upgrade your Python version.")
with make_displayer(config) as displayer:
display_obj.set_display(displayer)

View file

@ -247,8 +247,7 @@ def _restore_bool(name: str, value: str) -> bool:
"""
lowercase_value = value.lower()
if lowercase_value not in ("true", "false"):
raise errors.Error(
"Expected True or False for {0} but found {1}".format(name, value))
raise errors.Error(f"Expected True or False for {name} but found {value}")
return lowercase_value == "true"
@ -271,7 +270,7 @@ def _restore_int(name: str, value: str) -> int:
try:
return int(value)
except ValueError:
raise errors.Error("Expected a numeric value for {0}".format(name))
raise errors.Error(f"Expected a numeric value for {name}")
def _restore_str(name: str, value: str) -> Optional[str]:
@ -323,8 +322,8 @@ def _avoid_invalidating_lineage(config: configuration.NamespaceConfig,
names = ", ".join(lineage.names())
raise errors.Error(
"You've asked to renew/replace a seemingly valid certificate with "
"a test certificate (domains: {0}). We will not do that "
"unless you use the --break-my-certs flag!".format(names))
f"a test certificate (domains: {names}). We will not do that "
"unless you use the --break-my-certs flag!")
def renew_cert(config: configuration.NamespaceConfig, domains: Optional[List[str]],
@ -375,7 +374,7 @@ def _renew_describe_results(config: configuration.NamespaceConfig, renew_success
notify = display_util.notify
notify_error = logger.error
notify('\n{}'.format(display_obj.SIDE_FRAME))
notify(f'\n{display_obj.SIDE_FRAME}')
renewal_noun = "simulated renewal" if config.dry_run else "renewal"
@ -383,19 +382,19 @@ def _renew_describe_results(config: configuration.NamespaceConfig, renew_success
notify("The following certificates are not due for renewal yet:")
notify(report(renew_skipped, "skipped"))
if not renew_successes and not renew_failures:
notify("No {renewal}s were attempted.".format(renewal=renewal_noun))
notify(f"No {renewal_noun}s were attempted.")
if (config.pre_hook is not None or
config.renew_hook is not None or config.post_hook is not None):
notify("No hooks were run.")
elif renew_successes and not renew_failures:
notify("Congratulations, all {renewal}s succeeded: ".format(renewal=renewal_noun))
notify(f"Congratulations, all {renewal_noun}s succeeded: ")
notify(report(renew_successes, "success"))
elif renew_failures and not renew_successes:
notify_error("All %ss failed. The following certificates could "
"not be renewed:", renewal_noun)
notify_error(report(renew_failures, "failure"))
elif renew_failures and renew_successes:
notify("The following {renewal}s succeeded:".format(renewal=renewal_noun))
notify(f"The following {renewal_noun}s succeeded:")
notify(report(renew_successes, "success") + "\n")
notify_error("The following %ss failed:", renewal_noun)
notify_error(report(renew_failures, "failure"))
@ -508,8 +507,8 @@ def handle_renewal_request(config: configuration.NamespaceConfig) -> None:
renew_skipped, parse_failures)
if renew_failures or parse_failures:
raise errors.Error("{0} renew failure(s), {1} parse failure(s)".format(
len(renew_failures), len(parse_failures)))
raise errors.Error(
f"{len(renew_failures)} renew failure(s), {len(parse_failures)} parse failure(s)")
# Windows installer integration tests rely on handle_renewal_request behavior here.
# If the text below changes, these tests will need to be updated accordingly.
@ -526,4 +525,4 @@ def _update_renewal_params_from_key(key_path: str, config: configuration.Namespa
config.key_type = 'ecdsa'
config.elliptic_curve = key.curve.name
else:
raise errors.Error('Key at {0} is of an unsupported type: {1}.'.format(key_path, type(key)))
raise errors.Error(f'Key at {key_path} is of an unsupported type: {type(key)}.')

View file

@ -54,7 +54,8 @@ def prepare_env(cli_args: List[str]) -> List[str]:
session.mount('http://snapd/', _SnapdAdapter())
try:
response = session.get('http://snapd/v2/connections?snap=certbot&interface=content')
response = session.get('http://snapd/v2/connections?snap=certbot&interface=content',
timeout=30.0)
response.raise_for_status()
except RequestException as e:
if isinstance(e, HTTPError) and e.response.status_code == 404:

View file

@ -60,10 +60,10 @@ def renewal_conf_files(config: configuration.NamespaceConfig) -> List[str]:
def renewal_file_for_certname(config: configuration.NamespaceConfig, certname: str) -> str:
"""Return /path/to/certname.conf in the renewal conf directory"""
path = os.path.join(config.renewal_configs_dir, "{0}.conf".format(certname))
path = os.path.join(config.renewal_configs_dir, f"{certname}.conf")
if not os.path.exists(path):
raise errors.CertStorageError("No certificate found with name {0} (expected "
"{1}).".format(certname, path))
raise errors.CertStorageError(
f"No certificate found with name {certname} (expected {path}).")
return path
@ -298,6 +298,11 @@ def relevant_values(all_values: Mapping[str, Any]) -> Dict[str, Any]:
# and behavioral consistency when versions of Certbot with different
# server defaults are used.
rv["server"] = all_values["server"]
# Save key type to help with forward compatibility on Certbot's transition
# from RSA to ECDSA certificates by default.
rv["key_type"] = all_values["key_type"]
return rv
@ -1174,7 +1179,7 @@ class RenewableCert(interfaces.RenewableCert):
if os.path.islink(old_privkey):
old_privkey = filesystem.readlink(old_privkey)
else:
old_privkey = "privkey{0}.pem".format(prior_version)
old_privkey = f"privkey{prior_version}.pem"
logger.debug("Writing symlink to old private key, %s.", old_privkey)
os.symlink(old_privkey, target["privkey"])
else:

View file

@ -120,7 +120,7 @@ class NamespaceConfig:
@property
def must_staple(self) -> bool:
"""Adds the OCSP Must Staple extension to the certificate.
"""Adds the OCSP Must-Staple extension to the certificate.
Autoconfigures OCSP Stapling for supported setups
(Apache version >= 2.3.3 ).

View file

@ -143,7 +143,7 @@ def generate_csr(privkey: util.Key, names: Union[List[str], Set[str]], path: str
:type privkey: :class:`certbot.util.Key`
:param set names: `str` names to include in the CSR
:param str path: Certificate save directory.
:param bool must_staple: If true, include the TLS Feature extension "OCSP Must Staple"
:param bool must_staple: If true, include the TLS Feature extension "OCSP Must-Staple"
:param bool strict_permissions: If true and path exists, an exception is raised if
the directory doesn't have 0755 permissions or isn't owned by the current user.

View file

@ -241,8 +241,7 @@ class Reverter:
except (IOError, OSError):
# This file is required in all checkpoints.
logger.error("Unable to recover files from %s", cp_dir)
raise errors.ReverterError(
"Unable to recover files from %s" % cp_dir)
raise errors.ReverterError(f"Unable to recover files from {cp_dir}")
# Remove any newly added files if they exist
self._remove_contained_files(os.path.join(cp_dir, "NEW_FILES"))
@ -295,9 +294,7 @@ class Reverter:
# Verify no save_file is in protected_files
for filename in protected_files:
if filename in save_files:
raise errors.ReverterError(
"Attempting to overwrite challenge "
"file - %s" % filename)
raise errors.ReverterError(f"Attempting to overwrite challenge file - {filename}")
def register_file_creation(self, temporary: bool, *files: str) -> None:
r"""Register the creation of all files during certbot execution.

View file

@ -35,7 +35,7 @@ manage your account:
--agree-tos Agree to the ACME server's Subscriber Agreement
-m EMAIL Email address for important account notifications
options:
optional arguments:
-h, --help show this help message and exit
-c CONFIG_FILE, --config CONFIG_FILE
path to config file (default: /etc/letsencrypt/cli.ini
@ -97,7 +97,9 @@ options:
necessary to accurately simulate renewal. --deploy-
hook commands are not called. (default: False)
--debug-challenges After setting up challenges, wait for user input
before submitting to CA (default: False)
before submitting to CA. When used in combination with
the `-v` option, the challenge URLs or FQDNs and their
expected return values are shown. (default: False)
--preferred-chain PREFERRED_CHAIN
Set the preferred certificate chain. If the CA offers
multiple certificate chains, prefer the chain whose
@ -124,7 +126,7 @@ options:
case, and to know when to deprecate support for past
Python versions and flags. If you wish to hide this
information from the Let's Encrypt server, set this to
"". (default: CertbotACMEClient/1.23.0 (certbot;
"". (default: CertbotACMEClient/1.25.0 (certbot;
OS_NAME OS_VERSION) Authenticator/XXX Installer/YYY
(SUBCOMMAND; flags: FLAGS) Py/major.minor.patchlevel).
The flags encoded in the user agent are: --duplicate,
@ -192,7 +194,7 @@ security:
rsa)
--elliptic-curve N The SECG elliptic curve name to use. Please see RFC
8446 for supported values. (default: secp256r1)
--must-staple Adds the OCSP Must Staple extension to the
--must-staple Adds the OCSP Must-Staple extension to the
certificate. Autoconfigures OCSP Stapling for
supported setups (Apache version >= 2.3.3 ). (default:
False)

View file

@ -12,12 +12,15 @@ Get Certbot
System Requirements
===================
- Python 3.6+
- Python 3.7+
- UNIX-like operating system
- Root access
- Port 80 Open
.. Note:: The Apache plugin currently requires an OS with augeas version 1.0; currently it supports modern OSes based on Debian, Ubuntu, Fedora, SUSE, Gentoo and Darwin.
.. Note:: To run without root privileges, but for most users who want to avoid running an ACME client as root, either letsencrypt-nosudo or simp_le are more appropriate choices.
.. Note:: The Apache plugin currently requires an OS with augeas version 1.0; currently `it supports <https://github.com/certbot/certbot/blob/master/certbot-apache/certbot_apache/_internal/constants.py>`_
modern OSes based on Debian, Ubuntu, Fedora, SUSE, Gentoo and Darwin.
Installation

View file

@ -91,9 +91,9 @@ with ``--preferred-challenges``.
There are also many third-party-plugins_ available. Below we describe in more detail
the circumstances in which each plugin can be used, and how to use it.
.. _challenges: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7
.. _http-01: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.2
.. _dns-01: https://tools.ietf.org/html/draft-ietf-acme-acme-03#section-7.4
.. _challenges: https://datatracker.ietf.org/doc/html/rfc8555#section-8
.. _http-01: https://datatracker.ietf.org/doc/html/rfc8555#section-8.3
.. _dns-01: https://datatracker.ietf.org/doc/html/rfc8555#section-8.4
Apache
------

View file

@ -8,7 +8,7 @@ from setuptools import __version__ as setuptools_version
from setuptools import find_packages
from setuptools import setup
min_setuptools_version='39.0.1'
min_setuptools_version='41.6.0'
# This conditional isn't necessary, but it provides better error messages to
# people who try to install this package with older versions of setuptools.
if parse_version(setuptools_version) < parse_version(min_setuptools_version):
@ -52,10 +52,10 @@ install_requires = [
'configobj>=5.0.6',
'cryptography>=2.5.0',
'distro>=1.0.1',
'josepy>=1.9.0',
'josepy>=1.13.0',
'parsedatetime>=2.4',
'pyrfc3339',
'pytz',
'pytz>=2019.3',
# This dependency needs to be added using environment markers to avoid its
# installation on Linux.
'pywin32>=300 ; sys_platform == "win32"',
@ -101,7 +101,7 @@ test_extras = [
'types-setuptools',
'types-six',
# typing-extensions is required to import typing.Protocol and make the mypy checks
# pass (along with pylint about non-existent objects) on Python 3.6 & 3.7
# pass (along with pylint about non-existent objects) on Python 3.7
'typing-extensions',
'wheel',
]
@ -118,7 +118,7 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Environment :: Console',
@ -128,7 +128,6 @@ setup(
'Operating System :: POSIX :: Linux',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -3,6 +3,7 @@ import functools
import logging
import unittest
from josepy import b64encode
try:
import mock
except ImportError: # pragma: no cover
@ -72,13 +73,13 @@ class HandleAuthorizationsTest(unittest.TestCase):
with mock.patch("zope.component.provideUtility"):
display_obj.set_display(self.mock_display)
self.mock_auth = mock.MagicMock(name="ApacheConfigurator")
self.mock_auth = mock.MagicMock(name="Authenticator")
self.mock_auth.get_chall_pref.return_value = [challenges.HTTP01]
self.mock_auth.perform.side_effect = gen_auth_resp
self.mock_account = mock.Mock(key=util.Key("file_path", "PEM"))
self.mock_account = mock.MagicMock()
self.mock_net = mock.MagicMock(spec=acme_client.ClientV2)
self.mock_net.acme_version = 1
self.mock_net.retry_after.side_effect = acme_client.ClientV2.retry_after
@ -190,16 +191,56 @@ class HandleAuthorizationsTest(unittest.TestCase):
self._test_name3_http_01_3_common(combos=False)
def test_debug_challenges(self):
config = mock.Mock(debug_challenges=True)
config = mock.Mock(debug_challenges=True, verbose_count=0)
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]
mock_order = mock.MagicMock(authorizations=authzrs)
account_key_thumbprint = b"foobarbaz"
self.mock_account.key.thumbprint.return_value = account_key_thumbprint
self.mock_net.poll.side_effect = _gen_mock_on_poll()
self.handler.handle_authorizations(mock_order, config)
self.assertEqual(self.mock_net.answer_challenge.call_count, 1)
self.assertEqual(self.mock_display.notification.call_count, 1)
self.assertIn('Pass "-v" for more info',
self.mock_display.notification.call_args[0][0])
self.assertNotIn(f"http://{authzrs[0].body.identifier.value}/.well-known/acme-challenge/" +
b64encode(authzrs[0].body.challenges[0].chall.token).decode(),
self.mock_display.notification.call_args[0][0])
self.assertNotIn(b64encode(account_key_thumbprint).decode(),
self.mock_display.notification.call_args[0][0])
def test_debug_challenges_verbose(self):
config = mock.Mock(debug_challenges=True, verbose_count=1)
authzrs = [gen_dom_authzr(domain="0", challs=[acme_util.HTTP01]),
gen_dom_authzr(domain="1", challs=[acme_util.DNS01])]
mock_order = mock.MagicMock(authorizations=authzrs)
account_key_thumbprint = b"foobarbaz"
self.mock_account.key.thumbprint.return_value = account_key_thumbprint
self.mock_net.poll.side_effect = _gen_mock_on_poll()
self.mock_auth.get_chall_pref.return_value = [challenges.HTTP01,
challenges.DNS01]
self.handler.handle_authorizations(mock_order, config)
self.assertEqual(self.mock_net.answer_challenge.call_count, 2)
self.assertEqual(self.mock_display.notification.call_count, 1)
self.assertNotIn('Pass "-v" for more info',
self.mock_display.notification.call_args[0][0])
self.assertIn(f"http://{authzrs[0].body.identifier.value}/.well-known/acme-challenge/" +
b64encode(authzrs[0].body.challenges[0].chall.token).decode(),
self.mock_display.notification.call_args[0][0])
self.assertIn(b64encode(account_key_thumbprint).decode(),
self.mock_display.notification.call_args[0][0])
self.assertIn(f"_acme-challenge.{authzrs[1].body.identifier.value}",
self.mock_display.notification.call_args[0][0])
self.assertIn(authzrs[1].body.challenges[0].validation(self.mock_account.key),
self.mock_display.notification.call_args[0][0])
def test_perform_failure(self):
authzrs = [gen_dom_authzr(domain="0", challs=acme_util.CHALLENGES)]

View file

@ -70,30 +70,50 @@ class TestHandleCerts(unittest.TestCase):
self.assertEqual(ret, ("renew", mock_lineage))
self.assertTrue(mock_handle_migration.called)
@mock.patch("certbot._internal.main.display_util.yesno")
@mock.patch("certbot._internal.main.cli.set_by_cli")
def test_handle_unexpected_key_type_migration(self, mock_set):
def test_handle_unexpected_key_type_migration(self, mock_set, mock_yesno):
config = mock.Mock()
config.key_type = "rsa"
cert = mock.Mock()
cert.private_key_type = "ecdsa"
# If the key types do not differ, it should be a no-op.
config.key_type = "rsa"
cert.private_key_type = "rsa"
main._handle_unexpected_key_type_migration(config, cert)
mock_yesno.assert_not_called()
self.assertEqual(config.key_type, cert.private_key_type)
# If the user confirms the change interactively, the key change should proceed silently.
cert.private_key_type = "ecdsa"
mock_yesno.return_value = True
main._handle_unexpected_key_type_migration(config, cert)
self.assertEqual(mock_set.call_count, 2)
self.assertEqual(config.key_type, "rsa")
# User does not interactively confirm the key type change.
mock_yesno.return_value = False
# If --key-type and --cert-name are both set, the key type change should proceed silently.
mock_set.return_value = True
main._handle_unexpected_key_type_migration(config, cert)
self.assertEqual(config.key_type, "rsa")
# If neither --key-type nor --cert-name are set, Certbot should keep the old key type.
mock_set.return_value = False
with self.assertRaises(errors.Error) as raised:
main._handle_unexpected_key_type_migration(config, cert)
self.assertIn("Please provide both --cert-name and --key-type", str(raised.exception))
main._handle_unexpected_key_type_migration(config, cert)
self.assertEqual(config.key_type, "ecdsa")
# If --key-type is set and --cert-name isn't, Certbot should error.
config.key_type = "rsa"
mock_set.side_effect = lambda var: var != "certname"
with self.assertRaises(errors.Error) as raised:
main._handle_unexpected_key_type_migration(config, cert)
self.assertIn("Please provide both --cert-name and --key-type", str(raised.exception))
# If --key-type is not set, Certbot should keep the old key type.
mock_set.side_effect = lambda var: var != "key_type"
with self.assertRaises(errors.Error) as raised:
main._handle_unexpected_key_type_migration(config, cert)
self.assertIn("Please provide both --cert-name and --key-type", str(raised.exception))
main._handle_unexpected_key_type_migration(config, cert)
self.assertEqual(config.key_type, "ecdsa")
class RunTest(test_util.ConfigTestCase):
@ -177,6 +197,15 @@ class RunTest(test_util.ConfigTestCase):
# The final success message shouldn't be shown
self.mock_success_installation.assert_not_called()
@mock.patch('certbot._internal.main.plug_sel.choose_configurator_plugins')
def test_run_must_staple_not_supported(self, mock_choose):
mock_choose.return_value = (null.Installer(self.config, "null"), None)
plugins = disco.PluginsRegistry.find_all()
self.config.must_staple = True
self.assertRaises(errors.NotSupportedError,
main.run,
self.config, plugins)
class CertonlyTest(unittest.TestCase):
"""Tests for certbot._internal.main.certonly."""

View file

@ -39,7 +39,7 @@ class RelevantValuesTest(unittest.TestCase):
"""Tests for certbot._internal.storage.relevant_values."""
def setUp(self):
self.values = {"server": "example.org"}
self.values = {"server": "example.org", "key_type": "rsa"}
def _call(self, *args, **kwargs):
from certbot._internal.storage import relevant_values

View file

@ -9,14 +9,13 @@ setup(
author='Certbot Project',
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -27,8 +27,6 @@
# 7) botocore's default TLS settings raise deprecation warnings in Python
# 3.10+, but their values are sane from a security perspective. See
# https://github.com/boto/botocore/issues/2550.
# 8) Ignore our own PendingDeprecationWarning about Python 3.6 soon to be dropped.
# See https://github.com/certbot/certbot/pull/9160.
filterwarnings =
error
ignore:The external mock module:PendingDeprecationWarning
@ -38,4 +36,3 @@ filterwarnings =
ignore:decodestring\(\) is a deprecated alias:DeprecationWarning:dns
ignore:_SixMetaPathImporter.:ImportWarning
ignore:ssl.PROTOCOL_TLS:DeprecationWarning:botocore
ignore:Python 3.6 support will be dropped:PendingDeprecationWarning

View file

@ -80,6 +80,10 @@ parts:
- python3-dev
- cargo
build-environment:
# We set this environment variable while building to try and increase the
# stability of fetching the rust crates needed to build the cryptography
# library.
- CARGO_NET_GIT_FETCH_WITH_CLI: "true"
- SNAPCRAFT_PYTHON_VENV_ARGS: --upgrade
# Constraints are passed through the environment variable PIP_CONSTRAINTS instead of using the
# parts.[part_name].constraints option available in snapcraft.yaml when the Python plugin is

View file

@ -26,6 +26,10 @@ RUN apk add --no-cache --virtual .certbot-deps \
ca-certificates \
binutils
# We set this environment variable and install git while building to try and
# increase the stability of fetching the rust crates needed to build the
# cryptography library
ARG CARGO_NET_GIT_FETCH_WITH_CLI=true
# Install certbot from sources
RUN apk add --no-cache --virtual .build-deps \
gcc \
@ -35,6 +39,7 @@ RUN apk add --no-cache --virtual .build-deps \
libffi-dev \
python3-dev \
cargo \
git \
&& python tools/pipstrap.py \
&& python tools/pip_install.py --no-cache-dir \
--editable src/acme \

View file

@ -135,7 +135,7 @@ def create_github_release(github_access_token, tempdir, version):
# Upload windows installer to release
print("Uploading windows installer")
release.upload_asset(tempdir + '/windows-installer/certbot-beta-installer-win32.exe')
release.upload_asset(tempdir + '/windows-installer/certbot-beta-installer-win_amd64.exe')
release.update_release(release.title, release.body, draft=False)

View file

@ -2,116 +2,115 @@
# that script.
apacheconfig==0.3.2
asn1crypto==0.24.0
astroid==2.9.0; python_version >= "3.6" and python_version < "4.0"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
bcrypt==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
boto3==1.4.7
botocore==1.7.41
cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
astroid==2.9.3; python_version >= "3.7"
atomicwrites==1.4.0; sys_platform == "win32" and python_version >= "3.7"
attrs==21.4.0; python_version >= "3.7"
bcrypt==3.2.0; python_version >= "3.7"
boto3==1.15.15
botocore==1.18.15
cached-property==1.5.2; python_version >= "3.7"
certifi==2021.10.8; python_version >= "3.7"
cffi==1.9.1
chardet==2.2.1
chardet==3.0.4
cloudflare==1.5.1
colorama==0.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0"
colorama==0.4.4; sys_platform == "win32" and python_version >= "3.7"
configargparse==0.10.0
configobj==5.0.6
coverage==6.2; python_version >= "3.6" or python_version >= "3.6"
cryptography==3.2.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.5.0")
cython==0.29.26; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
distlib==0.3.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
coverage==6.3.2; python_version >= "3.7"
cryptography==3.2.1
cython==0.29.28
distlib==0.3.4; python_version >= "3.7"
distro==1.0.1
dns-lexicon==3.2.1
dnspython==1.15.0
docker-compose==1.24.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docker-pycreds==0.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docker==3.7.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docopt==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docutils==0.18.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
filelock==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
docker-compose==1.25.5; python_version >= "3.7"
docker==4.2.2; python_version >= "3.7"
dockerpty==0.4.1; python_version >= "3.7"
docopt==0.6.2; python_version >= "3.7"
execnet==1.9.0; python_version >= "3.7"
filelock==3.6.0; python_version >= "3.7"
funcsigs==0.4
future==0.18.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
future==0.18.2; python_version >= "3.7"
google-api-python-client==1.5.5
httplib2==0.9.2
idna==2.6
importlib-metadata==4.8.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.8" or python_version < "3.8" and python_version >= "3.6"
importlib-resources==5.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7"
iniconfig==1.1.1; python_version >= "3.6"
importlib-metadata==4.11.2; python_version < "3.8" and python_version >= "3.7"
iniconfig==1.1.1; python_version >= "3.7"
ipaddress==1.0.16
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
josepy==1.12.0; python_version >= "3.6"
jsonschema==2.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
lazy-object-proxy==1.7.1; python_version >= "3.6" and python_version < "4.0"
logger==1.4; python_version >= "3.6"
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
isort==5.10.1; python_version >= "3.7" and python_version < "4.0"
jmespath==0.10.0; python_version >= "3.7"
josepy==1.13.0; python_version >= "3.7"
jsonschema==3.2.0; python_version >= "3.7"
lazy-object-proxy==1.7.1; python_version >= "3.7"
logger==1.4; python_version >= "3.7"
mccabe==0.6.1; python_version >= "3.7"
mock==1.0.1
mypy-extensions==0.4.3; python_version >= "3.6"
mypy==0.931; python_version >= "3.6"
mypy-extensions==0.4.3; python_version >= "3.7"
mypy==0.940; python_version >= "3.7"
ndg-httpsclient==0.3.2
oauth2client==4.0.0
packaging==21.3; python_version >= "3.6"
paramiko==2.9.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
packaging==21.3; python_version >= "3.7"
paramiko==2.10.1; python_version >= "3.7"
parsedatetime==2.4
pbr==1.8.0
pip==21.3.1; python_version >= "3.6"
platformdirs==2.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0"
pluggy==1.0.0; python_version >= "3.6"
pip==22.0.4; python_version >= "3.7"
platformdirs==2.5.1; python_version >= "3.7"
pluggy==1.0.0; python_version >= "3.7"
ply==3.4
py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pyasn1-modules==0.0.10; python_version >= "3.6"
py==1.11.0; python_version >= "3.7"
pyasn1-modules==0.0.10; python_version >= "3.7"
pyasn1==0.1.9
pycparser==2.14
pylint==2.12.0; python_version >= "3.6" and python_version < "4.0"
pynacl==1.5.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pylint==2.12.2
pylint==2.12.2; python_version >= "3.7"
pynacl==1.5.0; python_version >= "3.7"
pyopenssl==17.3.0
pyparsing==2.2.0
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
pyparsing==2.2.1
pypiwin32==223; sys_platform == "win32" and python_version >= "3.7"
pyrfc3339==1.0
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
pytest-forked==1.4.0; python_version >= "3.6"
pytest-xdist==2.5.0; python_version >= "3.6" or python_version >= "3.6"
pytest==6.2.5; python_version >= "3.6" or python_version >= "3.6"
pyrsistent==0.18.1; python_version >= "3.7"
pytest-cov==3.0.0; python_version >= "3.7"
pytest-forked==1.4.0; python_version >= "3.7"
pytest-xdist==2.5.0; python_version >= "3.7"
pytest==7.0.1; python_version >= "3.7"
python-augeas==0.5.0
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
python-dateutil==2.8.2; python_version >= "3.7"
python-digitalocean==1.11
pytz==2012c
pywin32==303; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6")
pyyaml==3.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
requests-file==1.5.1; python_version >= "3.6"
requests-toolbelt==0.9.1; python_version >= "3.6"
requests==2.14.2
rsa==4.8; python_version >= "3.6" and python_version < "4"
s3transfer==0.1.13; python_version >= "3.6"
setuptools==39.0.1; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
pytz==2019.3
pywin32==303; sys_platform == "win32" and python_version >= "3.7"
pyyaml==5.4.1; python_version >= "3.7"
requests-file==1.5.1; python_version >= "3.7"
requests-toolbelt==0.9.1; python_version >= "3.7"
requests==2.20.0
rsa==4.8; python_version >= "3.7" and python_version < "4"
s3transfer==0.3.7; python_version >= "3.7"
setuptools==41.6.0
six==1.11.0
texttable==0.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
tldextract==3.1.2; python_version >= "3.6"
toml==0.10.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0"
tomli==1.2.3; python_version >= "3.6"
tox==1.9.2; python_version >= "3.6"
typed-ast==1.5.1; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
types-cryptography==3.3.14; python_version >= "3.6"
types-enum34==1.1.7; python_version >= "3.6"
types-ipaddress==1.0.7; python_version >= "3.6"
types-mock==4.0.8; python_version >= "3.6"
types-pyopenssl==21.0.3; python_version >= "3.6"
types-pyrfc3339==1.1.1; python_version >= "3.6"
types-python-dateutil==2.8.7; python_version >= "3.6"
types-pytz==2021.3.4; python_version >= "3.6"
types-requests==2.27.7; python_version >= "3.6"
types-setuptools==57.4.7; python_version >= "3.6"
types-six==1.16.10; python_version >= "3.6"
types-urllib3==1.26.7; python_version >= "3.6"
typing-extensions==4.0.1; python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" or python_version < "3.8" and python_version >= "3.6"
uritemplate==3.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
urllib3==1.10.2
virtualenv==20.13.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
wheel==0.33.6; (python_version >= "2.7" and python_full_version < "3.0.0") or (python_full_version >= "3.4.0")
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
zipp==3.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_full_version >= "3.5.0" and python_version < "3.7" or python_version < "3.8" and python_version >= "3.6"
texttable==1.6.4; python_version >= "3.7"
tldextract==3.2.0; python_version >= "3.7"
toml==0.10.2; python_version >= "3.7"
tomli==2.0.1; python_version >= "3.7"
tox==1.9.2; python_version >= "3.7"
typed-ast==1.5.2; python_version >= "3.7" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.7"
types-cryptography==3.3.18; python_version >= "3.7"
types-mock==4.0.11; python_version >= "3.7"
types-pyopenssl==22.0.0; python_version >= "3.7"
types-pyrfc3339==1.1.1; python_version >= "3.7"
types-python-dateutil==2.8.9; python_version >= "3.7"
types-pytz==2021.3.5; python_version >= "3.7"
types-requests==2.27.11; python_version >= "3.7"
types-setuptools==57.4.10; python_version >= "3.7"
types-six==1.16.12; python_version >= "3.7"
types-urllib3==1.26.10; python_version >= "3.7"
typing-extensions==4.1.1; python_version >= "3.7" or python_version < "3.10" and python_version >= "3.7" or python_version < "3.8" and python_version >= "3.7"
uritemplate==3.0.1; python_version >= "3.7"
urllib3==1.24.2
virtualenv==20.13.3; python_version >= "3.7"
websocket-client==0.59.0; python_version >= "3.7"
wheel==0.33.6
wheel==0.33.6; python_version >= "3.7"
wrapt==1.13.3; python_version >= "3.7"
zipp==3.7.0; python_version < "3.8" and python_version >= "3.7"
zope.component==4.1.0
zope.event==4.0.3
zope.hookable==4.0.4

View file

@ -6,7 +6,7 @@ authors = ["Certbot Project"]
license = "Apache License 2.0"
[tool.poetry.dependencies]
python = "^3.6"
python = "^3.7"
# Local dependencies
# Any local packages that have dependencies on other local packages must be
@ -69,11 +69,11 @@ poetry = ">=1.2.0a1"
# point, it's probably worth enumerating and pinning them (and recursing to
# THEIR build dependencies) as well.
setuptools-rust = "*"
# Library traitlets is a transitive dependency of ipdb (traitlets -> ipython -> ipdb).
# Version 5.x is incompatible with Python 3.6 but for some reasons, poetry fails to
# add the appropriate marker and allows this version to be installed under Python 3.6.
# We add a pinning to not create a set of requirements incompatible with Python 3.6.
traitlets = "<5"
# A bad python_requires constraint in pylint 2.6.2 can sometimes crash poetry
# before it has finished resolving. No newer releases have the same issue. Remove
# this once we can upgrade to a release of Poetry containing this commit:
# https://github.com/python-poetry/poetry-core/commit/4e1f2ab582d1fef0033c0d3f35a3f2f2365a4bc9
pylint = ">2.6.2"
[tool.poetry.dev-dependencies]

View file

@ -10,7 +10,7 @@ license = "Apache License 2.0"
[tool.poetry.dependencies]
# The Python version here should be kept in sync with the one used in our
# oldest tests in tox.ini.
python = "3.6"
python = "3.7"
# Local dependencies
# Any local packages that have dependencies on other local packages must be
@ -37,27 +37,20 @@ certbot = {path = "../../../certbot", extras = ["test"]}
acme = {path = "../../../acme", extras = ["test"]}
# Oldest dependencies
# We specify the oldest versions of our dependencies that we keep
# support for below. We should only update these packages as needed to make use
# of features in newer versions of our dependencies. Keeping compatibility with
# older packages makes it much easier for OS maintainers to update their
# Certbot packages if needed or desired.
#
# When updating these dependencies, we should try to update them no further
# than the oldest version of the dependency found in CentOS/RHEL 8 + EPEL (or
# newer versions of CentOS/RHEL + EPEL) as our Certbot packages there see
# frequent updates. If the dependency being updated is a direct dependency of
# one of our own packages, the minimum required version of that dependency
# should be updated in our setup.py files as well to communicate this
# information to our users.
# We specify the oldest versions of our dependencies that we keep support for
# below. These dependencies can be updated as desired to simplify or improve
# Certbot or its development. If the dependency being updated is a direct
# dependency of one of our own packages, the minimum required version of that
# dependency should be updated in our setup.py files as well to communicate
# this information to our users.
ConfigArgParse = "0.10.0"
apacheconfig = "0.3.2"
asn1crypto = "0.24.0"
boto3 = "1.4.7"
botocore = "1.7.41"
boto3 = "1.15.15"
botocore = "1.18.15"
cffi = "1.9.1"
chardet = "2.2.1"
chardet = "3.0.4"
cloudflare = "1.5.1"
configobj = "5.0.6"
cryptography = "3.2.1"
@ -79,14 +72,14 @@ pyOpenSSL = "17.3.0"
pyRFC3339 = "1.0"
pyasn1 = "0.1.9"
pycparser = "2.14"
pyparsing = "2.2.0"
pyparsing = "2.2.1"
python-augeas = "0.5.0"
python-digitalocean = "1.11"
pytz = "2012rc0"
requests = "2.14.2"
setuptools = "39.0.1"
pytz = "2019.3"
requests = "2.20.0"
setuptools = "41.6.0"
six = "1.11.0"
urllib3 = "1.10.2"
urllib3 = "1.24.2"
# Package names containing "." need to be quoted.
"zope.component" = "4.1.0"
"zope.event" = "4.0.3"
@ -111,6 +104,12 @@ cython = "*"
# wheel 0.34.0 is buggy).
wheel = "<0.34.0"
# A bad python_requires constraint in pylint 2.6.2 can sometimes crash poetry
# before it has finished resolving. No newer releases have the same issue. Remove
# this once we can upgrade to a release of Poetry containing this commit:
# https://github.com/python-poetry/poetry-core/commit/4e1f2ab582d1fef0033c0d3f35a3f2f2365a4bc9
pylint = ">2.6.2"
[tool.poetry.dev-dependencies]
[build-system]

View file

@ -5,198 +5,192 @@
# requirements.txt so that is scanned by GitHub. See
# https://docs.github.com/en/github/visualizing-repository-data-with-graphs/about-the-dependency-graph#supported-package-ecosystems
# for more info.
alabaster==0.7.12; python_version >= "3.6"
apacheconfig==0.3.2; python_version >= "3.6"
appdirs==1.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
appnope==0.1.2; python_version == "3.6" and sys_platform == "darwin" or python_version >= "3.7" and sys_platform == "darwin"
astroid==2.9.0; python_version >= "3.6" and python_version < "4.0"
atomicwrites==1.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.6" and python_full_version >= "3.4.0"
attrs==21.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
awscli==1.22.52; python_version >= "3.6"
azure-devops==6.0.0b4; python_version >= "3.6"
babel==2.9.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
backcall==0.2.0; python_version == "3.6" or python_version >= "3.7"
bcrypt==3.2.0; python_version >= "3.6"
beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and python_full_version > "3.0.0"
bleach==4.1.0; python_version >= "3.6"
boto3==1.20.52; python_version >= "3.6"
botocore==1.23.52; python_version >= "3.6"
cachecontrol==0.12.10; python_version >= "3.6" and python_version < "4.0"
cached-property==1.5.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
cachetools==4.2.4; python_version >= "3.5" and python_version < "4.0" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
cachy==0.3.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
certifi==2021.10.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
cffi==1.15.0; python_version >= "3.6" or python_version >= "3.6"
charset-normalizer==2.0.11; python_full_version >= "3.6.0" and python_version >= "3.6"
cleo==1.0.0a4; python_version >= "3.6" and python_version < "4.0"
cloudflare==2.8.15; python_version >= "3.6"
colorama==0.4.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.6" and sys_platform == "win32" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and platform_system == "Windows" or python_version >= "3.6" and python_full_version >= "3.5.0" and platform_system == "Windows" or python_version == "3.6" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version == "3.6" and sys_platform == "win32" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "3.7" and sys_platform == "win32" and python_full_version >= "3.5.0"
configargparse==1.5.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
configobj==5.0.6; python_version >= "3.6"
coverage==6.2; python_version >= "3.6" or python_version >= "3.6"
crashtest==0.3.1; python_version >= "3.6" and python_version < "4.0"
cryptography==36.0.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
cython==0.29.27; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
dataclasses==0.8; python_version >= "3.6" and python_version < "3.7"
decorator==5.1.1; python_version == "3.6" or python_version > "3.6" or python_version >= "3.5" or python_version >= "3.7"
deprecated==1.2.13; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
distlib==0.3.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6"
distro==1.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6"
dns-lexicon==3.8.5; python_version >= "3.6" and python_version < "4.0"
dnspython==2.2.0; python_version >= "3.6" and python_version < "4.0"
docker-compose==1.26.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docker==4.2.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
dockerpty==0.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docopt==0.6.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
docutils==0.15.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.4.0"
entrypoints==0.3; python_version >= "3.6" and python_version < "4.0"
execnet==1.9.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
fabric==2.6.0; python_version >= "3.6"
filelock==3.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_version < "4.0"
google-api-core==2.5.0; python_version >= "3.6"
google-api-python-client==2.37.0; python_version >= "3.6"
google-auth-httplib2==0.1.0; python_version >= "3.6"
google-auth==2.6.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6"
googleapis-common-protos==1.54.0; python_version >= "3.6"
html5lib==1.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
httplib2==0.20.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
idna==3.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0"
imagesize==1.3.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
importlib-metadata==1.7.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0"
importlib-resources==5.4.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.4.0"
iniconfig==1.1.1; python_version >= "3.6"
invoke==1.6.0; python_version >= "3.6"
ipdb==0.13.9; python_version >= "3.6"
ipython-genutils==0.2.0
ipython==7.16.3; python_version == "3.6"
ipython==7.31.1; python_version >= "3.7"
isodate==0.6.1; python_version >= "3.6"
isort==5.8.0; python_version >= "3.6" and python_version < "4.0"
jedi==0.17.2; python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0"
jeepney==0.7.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
jinja2==3.0.3; python_version >= "3.6" or python_version >= "3.6"
jmespath==0.10.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
josepy==1.12.0; python_version >= "3.6"
jsonlines==3.0.0; python_version >= "3.6"
jsonpickle==2.1.0; python_version >= "3.6"
jsonschema==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
keyring==22.3.0; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
lazy-object-proxy==1.7.1; python_version >= "3.6" and python_version < "4.0"
alabaster==0.7.12; python_version >= "3.7"
apacheconfig==0.3.2; python_version >= "3.7"
appdirs==1.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0"
appnope==0.1.2; python_version >= "3.7" and sys_platform == "darwin"
astroid==2.9.3; python_version >= "3.7" and python_full_version >= "3.6.2"
atomicwrites==1.4.0; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or sys_platform == "win32" and python_version >= "3.7" and python_full_version >= "3.4.0"
attrs==21.4.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
awscli==1.22.75; python_version >= "3.6"
azure-devops==6.0.0b4; python_version >= "3.7"
babel==2.9.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
backcall==0.2.0; python_version >= "3.7"
bcrypt==3.2.0; python_version >= "3.7"
beautifulsoup4==4.10.0; python_full_version > "3.0.0" and python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0" and python_full_version > "3.0.0"
bleach==4.1.0; python_version >= "3.7"
boto3==1.21.20; python_version >= "3.7"
botocore==1.24.20; python_version >= "3.7"
cachecontrol==0.12.10; python_version >= "3.7" and python_version < "4.0"
cached-property==1.5.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
cachetools==5.0.0; python_version >= "3.7" and python_version < "4.0" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7")
cachy==0.3.0; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0"
certifi==2021.10.8; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7"
cffi==1.15.0; python_version >= "3.7" or python_version >= "3.7"
charset-normalizer==2.0.12; python_full_version >= "3.6.0" and python_version >= "3.7"
cleo==1.0.0a4; python_version >= "3.7" and python_version < "4.0"
cloudflare==2.8.15; python_version >= "3.7"
colorama==0.4.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" or sys_platform == "win32" and python_full_version >= "3.6.2" and python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_full_version >= "3.5.0" and python_version >= "3.7" and sys_platform == "win32" or python_version >= "3.7" and python_full_version < "3.0.0" and platform_system == "Windows" or python_version >= "3.7" and python_full_version >= "3.5.0" and platform_system == "Windows"
configargparse==1.5.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
configobj==5.0.6; python_version >= "3.7"
coverage==6.3.2; python_version >= "3.7" or python_version >= "3.7"
crashtest==0.3.1; python_version >= "3.7" and python_version < "4.0"
cryptography==36.0.2; python_version >= "3.7" and python_version < "4.0" or python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0" and sys_platform == "linux"
cython==0.29.28; (python_version >= "2.6" and python_full_version < "3.0.0") or (python_full_version >= "3.3.0")
decorator==5.1.1; python_version >= "3.7"
deprecated==1.2.13; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
distlib==0.3.4; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.7"
distro==1.7.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" or python_version >= "3.7"
dns-lexicon==3.9.4; python_version >= "3.7" and python_version < "4.0"
dnspython==2.2.1; python_version >= "3.7" and python_version < "4.0"
docker-compose==1.26.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
docker==4.2.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
dockerpty==0.4.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
docopt==0.6.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
docutils==0.15.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version >= "3.4.0"
entrypoints==0.3; python_version >= "3.7" and python_version < "4.0"
execnet==1.9.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
fabric==2.6.0; python_version >= "3.7"
filelock==3.6.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.7" and python_version < "4.0"
google-api-core==2.7.1; python_version >= "3.7"
google-api-python-client==2.41.0; python_version >= "3.7"
google-auth-httplib2==0.1.0; python_version >= "3.7"
google-auth==2.6.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7"
googleapis-common-protos==1.55.0; python_version >= "3.7"
html5lib==1.1; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.5.0"
httplib2==0.20.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
idna==3.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0"
imagesize==1.3.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
importlib-metadata==1.7.0; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.7" and python_version < "3.8" and python_full_version >= "3.5.0"
iniconfig==1.1.1; python_version >= "3.7"
invoke==1.6.0; python_version >= "3.7"
ipdb==0.13.9; python_version >= "3.7"
ipython==7.32.0; python_version >= "3.7"
isodate==0.6.1; python_version >= "3.7"
isort==5.10.1; python_full_version >= "3.6.2" and python_version < "4.0" and python_version >= "3.7"
jedi==0.18.1; python_version >= "3.7"
jeepney==0.7.1; python_version >= "3.7" and python_version < "4.0" and sys_platform == "linux"
jinja2==3.0.3; python_version >= "3.7" or python_version >= "3.7"
jmespath==0.10.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7"
josepy==1.13.0; python_version >= "3.7"
jsonlines==3.0.0; python_version >= "3.7"
jsonpickle==2.1.0; python_version >= "3.7"
jsonschema==3.2.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
keyring==22.3.0; python_version >= "3.7" and python_version < "4.0" or python_version >= "3.7"
lazy-object-proxy==1.7.1; python_version >= "3.7" and python_full_version >= "3.6.2"
lockfile==0.12.2
markupsafe==2.0.1; python_version >= "3.6"
markupsafe==2.1.1; python_version >= "3.7"
matplotlib-inline==0.1.3; python_version >= "3.7"
mccabe==0.6.1; python_version >= "3.6" and python_version < "4.0"
mccabe==0.6.1; python_version >= "3.7" and python_full_version >= "3.6.2"
mock==4.0.3; python_version >= "3.6"
msgpack==1.0.3; python_version >= "3.6" and python_version < "4.0"
msrest==0.6.21; python_version >= "3.6"
mypy-extensions==0.4.3; python_version >= "3.6"
mypy==0.931; python_version >= "3.6"
oauth2client==4.1.3; python_version >= "3.6"
oauthlib==3.2.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
packaging==20.9; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0"
paramiko==2.9.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_version >= "3.6"
parsedatetime==2.6; python_version >= "3.6"
parso==0.7.1; python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.5.0"
pathlib2==2.3.7.post1; python_version >= "3.6"
pexpect==4.8.0; python_version >= "3.6" and python_version < "4.0" or python_version == "3.6" and sys_platform != "win32" or python_version >= "3.7" and sys_platform != "win32"
pickleshare==0.7.5; python_version == "3.6" or python_version >= "3.7"
pip==21.3.1; python_version >= "3.6"
pkginfo==1.8.2; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6"
platformdirs==2.4.0; python_version >= "3.6" and python_version < "4.0"
pluggy==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6"
ply==3.11; python_version >= "3.6"
poetry-core==1.1.0a6; python_version >= "3.6" and python_version < "4.0"
msgpack==1.0.3; python_version >= "3.7" and python_version < "4.0"
msrest==0.6.21; python_version >= "3.7"
mypy-extensions==0.4.3; python_version >= "3.7"
mypy==0.941; python_version >= "3.7"
oauth2client==4.1.3; python_version >= "3.7"
oauthlib==3.2.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
packaging==20.9; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version >= "3.5.0"
paramiko==2.10.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" or python_version >= "3.7"
parsedatetime==2.6; python_version >= "3.7"
parso==0.8.3; python_version >= "3.7"
pathlib2==2.3.7.post1; python_version >= "3.7"
pexpect==4.8.0; python_version >= "3.7" and python_version < "4.0" or python_version >= "3.7" and sys_platform != "win32"
pickleshare==0.7.5; python_version >= "3.7"
pip==22.0.4; python_version >= "3.7"
pkginfo==1.8.2; python_version >= "3.7" and python_version < "4.0" or python_version >= "3.7"
platformdirs==2.5.1; python_version >= "3.7" and python_full_version >= "3.6.2"
pluggy==1.0.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0" or python_version >= "3.7"
ply==3.11; python_version >= "3.7"
poetry-core==1.1.0a7; python_version >= "3.7" and python_version < "4.0"
poetry==1.2.0a2; python_version >= "3.6" and python_version < "4.0"
prompt-toolkit==3.0.3; python_version == "3.6" or python_version >= "3.7"
protobuf==3.19.4; python_version >= "3.6"
ptyprocess==0.7.0; python_version >= "3.6" and python_version < "4.0"
py==1.11.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
pyasn1-modules==0.2.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6"
pyasn1==0.4.8; python_version >= "3.6" and python_version < "4" or python_version >= "3.6"
pycparser==2.21; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pygithub==1.55; python_version >= "3.6"
pygments==2.11.2; python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7"
pyjwt==2.3.0; python_version >= "3.6"
pylev==1.4.0; python_version >= "3.6" and python_version < "4.0"
pylint==2.12.0; python_version >= "3.6" and python_version < "4.0"
pynacl==1.5.0; python_version >= "3.6" or python_version >= "3.6"
pynsist==2.7; python_version >= "3.6"
pyopenssl==22.0.0; python_version >= "3.6"
pyparsing==3.0.7; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0"
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
pyrfc3339==1.1; python_version >= "3.6"
pyrsistent==0.18.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pytest-cov==3.0.0; python_version >= "3.6" or python_version >= "3.6"
pytest-forked==1.4.0; python_version >= "3.6"
pytest-xdist==2.5.0; python_version >= "3.6" or python_version >= "3.6"
pytest==7.0.0; python_version >= "3.6" or python_version >= "3.6"
python-augeas==1.1.0; python_version >= "3.6"
python-dateutil==2.8.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.6"
python-digitalocean==1.17.0; python_version >= "3.6"
python-dotenv==0.19.2; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
pytz==2021.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0" or python_version >= "3.6"
pywin32-ctypes==0.2.0; python_version >= "3.6" and python_version < "4.0" and sys_platform == "win32"
pywin32==303; sys_platform == "win32" and python_version >= "3.6" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6")
pyyaml==5.4.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
readme-renderer==32.0; python_version >= "3.6"
requests-download==0.1.2; python_version >= "3.6"
requests-file==1.5.1; python_version >= "3.6" and python_version < "4.0"
requests-oauthlib==1.3.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
requests-toolbelt==0.9.1; python_version >= "3.6" and python_version < "4.0" or python_version >= "3.6" or python_version >= "3.6"
requests==2.27.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.6.0"
rfc3986==1.5.0; python_version >= "3.6"
rsa==4.7.2; python_version >= "3.6" and python_version < "4" or python_version >= "3.5" and python_version < "4" and (python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.6")
s3transfer==0.5.1; python_version >= "3.6"
secretstorage==3.3.1; python_version >= "3.6" and python_version < "4.0" and sys_platform == "linux"
prompt-toolkit==3.0.28; python_version >= "3.7" and python_full_version >= "3.6.2"
protobuf==3.19.4; python_version >= "3.7"
ptyprocess==0.7.0; python_version >= "3.7" and python_version < "4.0"
py==1.11.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
pyasn1-modules==0.2.8; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7"
pyasn1==0.4.8; python_version >= "3.7" and python_version < "4" or python_version >= "3.7"
pycparser==2.21; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
pygithub==1.55; python_version >= "3.7"
pygments==2.11.2; python_version >= "3.7"
pyjwt==2.3.0; python_version >= "3.7"
pylev==1.4.0; python_version >= "3.7" and python_version < "4.0"
pylint==2.12.2; python_full_version >= "3.6.2"
pynacl==1.5.0; python_version >= "3.7" or python_version >= "3.7"
pynsist==2.7; python_version >= "3.7"
pyopenssl==22.0.0; python_version >= "3.7"
pyparsing==3.0.7; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" or python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0"
pypiwin32==223; sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7")
pyrfc3339==1.1; python_version >= "3.7"
pyrsistent==0.18.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
pytest-cov==3.0.0; python_version >= "3.7" or python_version >= "3.7"
pytest-forked==1.4.0; python_version >= "3.7"
pytest-xdist==2.5.0; python_version >= "3.7" or python_version >= "3.7"
pytest==7.1.0; python_version >= "3.7" or python_version >= "3.7"
python-augeas==1.1.0; python_version >= "3.7"
python-dateutil==2.8.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" and python_version >= "3.7"
python-digitalocean==1.17.0; python_version >= "3.7"
python-dotenv==0.19.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
pytz==2021.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0" or python_version >= "3.7"
pywin32-ctypes==0.2.0; python_version >= "3.7" and python_version < "4.0" and sys_platform == "win32"
pywin32==303; sys_platform == "win32" and python_version >= "3.7" or sys_platform == "win32" and python_version >= "3.6" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7")
pyyaml==5.4.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.6.0"
readme-renderer==34.0; python_version >= "3.7"
requests-download==0.1.2; python_version >= "3.7"
requests-file==1.5.1; python_version >= "3.7" and python_version < "4.0"
requests-oauthlib==1.3.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
requests-toolbelt==0.9.1; python_version >= "3.7" and python_version < "4.0" or python_version >= "3.7" or python_version >= "3.7"
requests==2.27.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.6.0"
rfc3986==2.0.0; python_version >= "3.7"
rsa==4.7.2; python_version >= "3.7" and python_version < "4" or python_version >= "3.5" and python_version < "4" and (python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version >= "3.7")
s3transfer==0.5.2; python_version >= "3.7"
secretstorage==3.3.1; python_version >= "3.7" and python_version < "4.0" and sys_platform == "linux"
semantic-version==2.9.0; python_version >= "3.6"
setuptools-rust==1.1.2; python_version >= "3.6"
setuptools==59.6.0; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_version == "3.6" or python_version >= "3.7" or python_version >= "3.6" and python_version < "4.0"
shellingham==1.4.0; python_version >= "3.6" and python_version < "4.0"
six==1.16.0; python_version >= "2.7" and python_full_version < "3.0.0" or python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.3.0" and python_version >= "3.6" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_full_version >= "3.6.0" and python_version >= "3.6" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version == "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0"
snowballstemmer==2.2.0; python_version >= "3.6"
soupsieve==2.3.1; python_full_version > "3.0.0" and python_version >= "3.6"
sphinx-rtd-theme==1.0.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
sphinx==4.3.2; python_version >= "3.6" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
sphinxcontrib-applehelp==1.0.2; python_version >= "3.6"
sphinxcontrib-devhelp==1.0.2; python_version >= "3.6"
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.6"
sphinxcontrib-jsmath==1.0.1; python_version >= "3.6"
sphinxcontrib-qthelp==1.0.3; python_version >= "3.6"
sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.6"
texttable==1.6.4; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6"
tldextract==3.1.2; python_version >= "3.6" and python_version < "4.0"
toml==0.10.2; python_version == "3.6" and python_full_version < "3.0.0" or python_version > "3.6" and python_full_version < "3.0.0" or python_version == "3.6" and python_full_version >= "3.3.0" or python_version > "3.6" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.3.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
tomli==1.2.3; python_version >= "3.6" or python_version >= "3.6"
tomlkit==0.9.2; python_version >= "3.6" and python_version < "4.0"
tox==3.24.5; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
tqdm==4.62.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.4.0"
traitlets==4.3.3
twine==3.3.0; python_version >= "3.6"
typed-ast==1.5.2; python_version >= "3.6" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.6"
types-cryptography==3.3.15; python_version >= "3.6"
types-enum34==1.1.8; python_version >= "3.6"
types-ipaddress==1.0.8; python_version >= "3.6"
types-mock==4.0.10; python_version >= "3.6"
types-pyopenssl==22.0.0; python_version >= "3.6"
types-pyrfc3339==1.1.1; python_version >= "3.6"
types-python-dateutil==2.8.9; python_version >= "3.6"
types-pytz==2021.3.4; python_version >= "3.6"
types-requests==2.27.9; python_version >= "3.6"
types-setuptools==57.4.9; python_version >= "3.6"
types-six==1.16.10; python_version >= "3.6"
types-urllib3==1.26.9; python_version >= "3.6"
typing-extensions==4.0.1; python_version >= "3.6" or python_version >= "3.6" or python_version >= "3.6" and python_version < "3.10" or python_version < "3.8" and python_version >= "3.6"
uritemplate==4.1.1; python_version >= "3.6"
urllib3==1.26.8; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.6"
virtualenv==20.4.4; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
wcwidth==0.2.5; python_version == "3.6"
webencodings==0.5.1; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.6"
websocket-client==0.59.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.6" or python_full_version >= "3.5.0" and python_version >= "3.6"
wheel==0.37.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0"
wrapt==1.13.3; python_version >= "3.6" and python_full_version < "3.0.0" or python_version >= "3.6" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.6" and python_version < "4.0" and python_full_version >= "3.5.0"
yarg==0.1.9; python_version >= "3.6"
zipp==3.6.0; python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.6" and python_version < "3.8" and python_full_version >= "3.5.0" or python_version >= "3.6" and python_full_version < "3.0.0" and python_version < "3.7" or python_version >= "3.6" and python_version < "3.7" and python_full_version >= "3.4.0"
zope.component==5.0.1; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
zope.event==4.5.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
zope.hookable==5.1.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
zope.interface==5.4.0; python_version >= "3.6" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.6"
setuptools==60.9.3; python_version >= "3.7" or python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version >= "3.6.2" or python_full_version >= "3.4.0" and python_version >= "3.7"
shellingham==1.4.0; python_version >= "3.7" and python_version < "4.0"
six==1.16.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" or python_full_version >= "3.3.0" and python_version >= "3.7" or python_version >= "3.7" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0" or python_full_version >= "3.6.0" and python_version >= "3.7" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.3.0"
snowballstemmer==2.2.0; python_version >= "3.7"
soupsieve==2.3.1; python_full_version > "3.0.0" and python_version >= "3.7"
sphinx-rtd-theme==1.0.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
sphinx==4.3.2; python_version >= "3.7" or python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
sphinxcontrib-applehelp==1.0.2; python_version >= "3.7"
sphinxcontrib-devhelp==1.0.2; python_version >= "3.7"
sphinxcontrib-htmlhelp==2.0.0; python_version >= "3.7"
sphinxcontrib-jsmath==1.0.1; python_version >= "3.7"
sphinxcontrib-qthelp==1.0.3; python_version >= "3.7"
sphinxcontrib-serializinghtml==1.1.5; python_version >= "3.7"
texttable==1.6.4; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7"
tldextract==3.2.0; python_version >= "3.7" and python_version < "4.0"
toml==0.10.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.3.0" or python_version >= "3.7" and python_full_version >= "3.6.2" or python_version >= "3.7" and python_full_version >= "3.5.0"
tomli==2.0.1; python_version >= "3.7" or python_version >= "3.7"
tomlkit==0.10.0; python_version >= "3.7" and python_version < "4.0"
tox==3.24.5; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0"
tqdm==4.63.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.4.0"
traitlets==5.1.1; python_version >= "3.7"
twine==3.3.0; python_version >= "3.7"
typed-ast==1.5.2; python_version >= "3.7" and python_version < "3.8" or implementation_name == "cpython" and python_version < "3.8" and python_version >= "3.7" and python_full_version >= "3.6.2"
types-cryptography==3.3.18; python_version >= "3.7"
types-mock==4.0.11; python_version >= "3.7"
types-pyopenssl==22.0.0; python_version >= "3.7"
types-pyrfc3339==1.1.1; python_version >= "3.7"
types-python-dateutil==2.8.9; python_version >= "3.7"
types-pytz==2021.3.5; python_version >= "3.7"
types-requests==2.27.12; python_version >= "3.7"
types-setuptools==57.4.10; python_version >= "3.7"
types-six==1.16.12; python_version >= "3.7"
types-urllib3==1.26.11; python_version >= "3.7"
typing-extensions==4.1.1; python_version >= "3.7" or python_version >= "3.6" or python_version < "3.10" and python_full_version >= "3.6.2" and python_version >= "3.7" or python_version < "3.8" and python_version >= "3.7"
uritemplate==4.1.1; python_version >= "3.7"
urllib3==1.26.8; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7" or python_full_version >= "3.5.0" and python_version < "4" and python_version >= "3.7"
virtualenv==20.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.4.0" or python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0"
wcwidth==0.2.5; python_version >= "3.7" and python_full_version >= "3.6.2"
webencodings==0.5.1; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4.0" or python_version >= "3.7" and python_version < "4.0" and python_full_version >= "3.5.0" or python_version >= "3.7"
websocket-client==0.59.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.4.0" and python_version >= "3.7" or python_full_version >= "3.5.0" and python_version >= "3.7"
wheel==0.37.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0"
wrapt==1.13.3; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version >= "3.5.0" or python_version >= "3.7" and python_full_version >= "3.6.2"
yarg==0.1.9; python_version >= "3.7"
zipp==3.7.0; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "3.8" or python_version >= "3.7" and python_version < "3.8" and python_full_version >= "3.5.0"
zope.component==5.0.1; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
zope.event==4.5.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
zope.hookable==5.1.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"
zope.interface==5.4.0; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.5.0" and python_version >= "3.7"

View file

@ -27,6 +27,10 @@ parts:
snapcraftctl pull
snapcraftctl set-version \`grep ^version \$SNAPCRAFT_PART_SRC/setup.py | cut -f2 -d= | tr -d "'[:space:]"\`
build-environment:
# We set this environment variable while building to try and increase the
# stability of fetching the rust crates needed to build the cryptography
# library.
- CARGO_NET_GIT_FETCH_WITH_CLI: "true"
# Constraints are passed through the environment variable PIP_CONSTRAINTS instead of using the
# parts.[part_name].constraints option available in snapcraft.yaml when the Python plugin is
# used. This is done to let these constraints be applied not only on the certbot package

View file

@ -69,7 +69,7 @@ def find_python_executable() -> str:
* 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).
versions less than 3.7).
:rtype: str
:return: the relevant python executable path
@ -118,7 +118,7 @@ def _check_version(version_str):
version = (int(search.group(1)), int(search.group(2)))
if version >= (3, 6):
if version >= (3, 7):
return True
print('Incompatible python version for Certbot found: {0}'.format(version_str))

View file

@ -26,8 +26,8 @@ source_paths = acme/acme certbot/certbot certbot-apache/certbot_apache certbot-c
passenv =
CERTBOT_NO_PIN
platform =
win: win32
posix: ^(?!.*win32).*$
win: win64
posix: ^(?!.*win64).*$
commands_pre = python {toxinidir}/tools/pipstrap.py
commands =
!cover-win: {[base]install_and_test} {[base]win_all_packages}
@ -51,7 +51,7 @@ setenv =
#
# This version should be kept in sync with the one declared in
# tools/pinning/oldest/pyproject.toml.
basepython = python3.6
basepython = python3.7
commands =
{[testenv]commands}
setenv =

View file

@ -11,14 +11,13 @@ setup(
author="Certbot Project",
author_email='certbot-dev@eff.org',
license='Apache License 2.0',
python_requires='>=3.6',
python_requires='>=3.7',
classifiers=[
'Development Status :: 4 - Beta',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
'Programming Language :: Python :: 3.9',

View file

@ -8,7 +8,7 @@ import sys
import time
PYTHON_VERSION = (3, 9, 7)
PYTHON_BITNESS = 32
PYTHON_BITNESS = 64
NSIS_VERSION = '3.06.1'
@ -94,7 +94,7 @@ def _generate_pynsist_config(repo_path, build_path):
certbot_version = subprocess.check_output([sys.executable, '-c', 'import certbot; print(certbot.__version__)'],
universal_newlines=True, cwd=certbot_pkg_path).strip()
# If we change the installer name from `certbot-beta-installer-win32.exe`, it should
# If we change the installer name from `certbot-beta-installer-win_amd64.exe`, it should
# also be changed in tools/create_github_release.py
with open(installer_cfg_path, 'w') as file_h:
file_h.write('''\