Compare commits
35 Commits
mutable-va
...
test-defau
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c4265bd3b2 | ||
|
|
226f044a6b | ||
|
|
be4d9b538f | ||
|
|
282df74ee9 | ||
|
|
0a565815f9 | ||
|
|
d33bbf35c2 | ||
|
|
714a0b348d | ||
|
|
7ca1b8f286 | ||
|
|
be40e377d9 | ||
|
|
01cf4bae75 | ||
|
|
ef949f9149 | ||
|
|
926d0c7e0f | ||
|
|
9d8eb6ccfd | ||
|
|
585f70e700 | ||
|
|
21e24264f4 | ||
|
|
cf78ad3a3d | ||
|
|
dccb92d57f | ||
|
|
f9d31faadc | ||
|
|
e9225d1cc2 | ||
|
|
3dd1f0eea9 | ||
|
|
917e3aba6b | ||
|
|
3833255980 | ||
|
|
619654f317 | ||
|
|
76f9a33e45 | ||
|
|
5f67bb99a8 | ||
|
|
d8392bf394 | ||
|
|
6a89fcbc56 | ||
|
|
2adaacab82 | ||
|
|
2ae810c45a | ||
|
|
b62133e3e1 | ||
|
|
a92bb44ff9 | ||
|
|
9650c25968 | ||
|
|
c3c29afdca | ||
|
|
dca4ddd3d8 | ||
|
|
bf5475fa74 |
@@ -4,18 +4,10 @@ jobs:
|
||||
- name: IMAGE_NAME
|
||||
value: ubuntu-22.04
|
||||
- name: PYTHON_VERSION
|
||||
value: 3.11
|
||||
value: 3.12
|
||||
- group: certbot-common
|
||||
strategy:
|
||||
matrix:
|
||||
linux-py39:
|
||||
PYTHON_VERSION: 3.9
|
||||
TOXENV: py39
|
||||
linux-py310:
|
||||
PYTHON_VERSION: 3.10
|
||||
TOXENV: py310
|
||||
linux-isolated:
|
||||
TOXENV: 'isolated-{acme,certbot,apache,cloudflare,digitalocean,dnsimple,dnsmadeeasy,gehirn,google,linode,luadns,nsone,ovh,rfc2136,route53,sakuracloud,nginx}'
|
||||
linux-boulder-v2-integration-certbot-oldest:
|
||||
PYTHON_VERSION: 3.8
|
||||
TOXENV: integration-certbot-oldest
|
||||
@@ -40,18 +32,10 @@ jobs:
|
||||
PYTHON_VERSION: 3.11
|
||||
TOXENV: integration
|
||||
ACME_SERVER: boulder-v2
|
||||
nginx-compat:
|
||||
TOXENV: nginx_compat
|
||||
linux-integration-rfc2136:
|
||||
IMAGE_NAME: ubuntu-22.04
|
||||
PYTHON_VERSION: 3.8
|
||||
TOXENV: integration-dns-rfc2136
|
||||
le-modification:
|
||||
IMAGE_NAME: ubuntu-22.04
|
||||
TOXENV: modification
|
||||
farmtest-apache2:
|
||||
PYTHON_VERSION: 3.8
|
||||
TOXENV: test-farm-apache2
|
||||
linux-boulder-v2-py312-integration:
|
||||
PYTHON_VERSION: 3.12
|
||||
TOXENV: integration
|
||||
ACME_SERVER: boulder-v2
|
||||
pool:
|
||||
vmImage: $(IMAGE_NAME)
|
||||
steps:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
jobs:
|
||||
- job: test
|
||||
variables:
|
||||
PYTHON_VERSION: 3.11
|
||||
PYTHON_VERSION: 3.12
|
||||
strategy:
|
||||
matrix:
|
||||
macos-py38-cover:
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
# See https://github.com/certbot/certbot/pull/9717#issuecomment-1610861794.
|
||||
PIP_USE_PEP517: "true"
|
||||
macos-cover:
|
||||
IMAGE_NAME: macOS-12
|
||||
IMAGE_NAME: macOS-13
|
||||
TOXENV: cover
|
||||
# See explanation under macos-py38-cover.
|
||||
PIP_USE_PEP517: "true"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
stages:
|
||||
- stage: TestAndPackage
|
||||
jobs:
|
||||
- template: ../jobs/standard-tests-jobs.yml
|
||||
- template: ../jobs/extended-tests-jobs.yml
|
||||
- template: ../jobs/packaging-jobs.yml
|
||||
|
||||
@@ -69,7 +69,7 @@ ignored-modules=
|
||||
# CERTBOT COMMENT
|
||||
# This is needed for pylint to import linter_plugin.py since
|
||||
# https://github.com/PyCQA/pylint/pull/3396.
|
||||
init-hook="import pylint.config, os, sys; sys.path.append(os.path.dirname(pylint.config.PYLINTRC))"
|
||||
init-hook="import pylint.config, os, sys; sys.path.append(os.path.dirname(next(pylint.config.find_default_config_files())))"
|
||||
|
||||
# Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the
|
||||
# number of processors available to use, and will cap the count on Windows to
|
||||
@@ -266,8 +266,8 @@ valid-metaclass-classmethod-first-arg=cls
|
||||
[EXCEPTIONS]
|
||||
|
||||
# Exceptions that will emit a warning when caught.
|
||||
overgeneral-exceptions=BaseException,
|
||||
Exception
|
||||
overgeneral-exceptions=builtins.BaseException,
|
||||
builtins.Exception
|
||||
|
||||
|
||||
[FORMAT]
|
||||
@@ -524,7 +524,7 @@ ignored-classes=optparse.Values,thread._local,_thread._local,argparse.Namespace,
|
||||
# List of module names for which member attributes should not be checked
|
||||
# (useful for modules/projects where namespaces are manipulated during runtime
|
||||
# and thus existing member attributes cannot be deduced by static analysis
|
||||
ignored-modules=pkg_resources,confargparse,argparse
|
||||
ignored-modules=confargparse,argparse
|
||||
|
||||
# Show a hint with possible names when a member name was not found. The aspect
|
||||
# of finding the hint is based on edit distance.
|
||||
|
||||
@@ -94,6 +94,7 @@ Authors
|
||||
* [Felix Yan](https://github.com/felixonmars)
|
||||
* [Filip Ochnik](https://github.com/filipochnik)
|
||||
* [Florian Klink](https://github.com/flokli)
|
||||
* [Francesco Colista](https://github.com/fcolista)
|
||||
* [Francois Marier](https://github.com/fmarier)
|
||||
* [Frank](https://github.com/Frankkkkk)
|
||||
* [Frederic BLANC](https://github.com/fblanc)
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: acme/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: acme/readthedocs.org.requirements.txt
|
||||
@@ -12,7 +12,6 @@ from typing import List
|
||||
from typing import Mapping
|
||||
from typing import Optional
|
||||
from typing import Set
|
||||
from typing import Text
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
||||
@@ -517,7 +516,7 @@ class ClientNetwork:
|
||||
self.account = account
|
||||
self.alg = alg
|
||||
self.verify_ssl = verify_ssl
|
||||
self._nonces: Set[Text] = set()
|
||||
self._nonces: Set[str] = set()
|
||||
self.user_agent = user_agent
|
||||
self.session = requests.Session()
|
||||
self._default_timeout = timeout
|
||||
|
||||
@@ -29,7 +29,7 @@ class Header(jose.Header):
|
||||
|
||||
class Signature(jose.Signature):
|
||||
"""ACME-specific Signature. Uses ACME-specific Header for customer fields."""
|
||||
__slots__ = jose.Signature._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access,no-member
|
||||
__slots__ = jose.Signature._orig_slots # pylint: disable=protected-access,no-member
|
||||
|
||||
# TODO: decoder/encoder should accept cls? Otherwise, subclassing
|
||||
# JSONObjectWithFields is tricky...
|
||||
@@ -44,7 +44,7 @@ class Signature(jose.Signature):
|
||||
class JWS(jose.JWS):
|
||||
"""ACME-specific JWS. Includes none, url, and kid in protected header."""
|
||||
signature_cls = Signature
|
||||
__slots__ = jose.JWS._orig_slots # type: ignore[attr-defined] # pylint: disable=protected-access
|
||||
__slots__ = jose.JWS._orig_slots # pylint: disable=protected-access
|
||||
|
||||
@classmethod
|
||||
# type: ignore[override] # pylint: disable=arguments-differ
|
||||
|
||||
@@ -3,6 +3,6 @@ usage: jws [-h] [--compact] {sign,verify} ...
|
||||
positional arguments:
|
||||
{sign,verify}
|
||||
|
||||
optional arguments:
|
||||
options:
|
||||
-h, --help show this help message and exit
|
||||
--compact
|
||||
|
||||
@@ -3,7 +3,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'cryptography>=3.2.1',
|
||||
@@ -55,6 +55,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -257,6 +257,6 @@ def find_ssl_apache_conf(prefix: str) -> str:
|
||||
"""
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files("certbot_apache").joinpath(
|
||||
"_internal", "tls_configs", "{0}-options-ssl-apache.conf".format(prefix))
|
||||
ref = (importlib_resources.files("certbot_apache").joinpath("_internal")
|
||||
.joinpath("tls_configs").joinpath("{0}-options-ssl-apache.conf".format(prefix)))
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
@@ -4,6 +4,7 @@ from typing import Type
|
||||
|
||||
from certbot import util
|
||||
from certbot_apache._internal import configurator
|
||||
from certbot_apache._internal import override_alpine
|
||||
from certbot_apache._internal import override_arch
|
||||
from certbot_apache._internal import override_centos
|
||||
from certbot_apache._internal import override_darwin
|
||||
@@ -14,6 +15,7 @@ from certbot_apache._internal import override_suse
|
||||
from certbot_apache._internal import override_void
|
||||
|
||||
OVERRIDE_CLASSES: Dict[str, Type[configurator.ApacheConfigurator]] = {
|
||||
"alpine": override_alpine.AlpineConfigurator,
|
||||
"arch": override_arch.ArchConfigurator,
|
||||
"cloudlinux": override_centos.CentOSConfigurator,
|
||||
"darwin": override_darwin.DarwinConfigurator,
|
||||
|
||||
19
certbot-apache/certbot_apache/_internal/override_alpine.py
Normal file
19
certbot-apache/certbot_apache/_internal/override_alpine.py
Normal file
@@ -0,0 +1,19 @@
|
||||
""" Distribution specific override class for Alpine Linux """
|
||||
from certbot_apache._internal import configurator
|
||||
from certbot_apache._internal.configurator import OsOptions
|
||||
|
||||
|
||||
class AlpineConfigurator(configurator.ApacheConfigurator):
|
||||
"""Alpine Linux specific ApacheConfigurator override class"""
|
||||
|
||||
OS_DEFAULTS = OsOptions(
|
||||
server_root="/etc/apache2",
|
||||
vhost_root="/etc/apache2/conf.d",
|
||||
vhost_files="*.conf",
|
||||
logs_root="/var/log/apache2",
|
||||
ctl="apachectl",
|
||||
version_cmd=['apachectl', '-v'],
|
||||
restart_cmd=['apachectl', 'graceful'],
|
||||
conftest_cmd=['apachectl', 'configtest'],
|
||||
challenge_location="/etc/apache2/conf.d",
|
||||
)
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
# We specify the minimum acme and certbot version as the current plugin
|
||||
@@ -43,6 +43,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -32,13 +32,15 @@ def construct_nginx_config(nginx_root: str, nginx_webroot: str, http_port: int,
|
||||
if not key_path:
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files('certbot_integration_tests').joinpath('assets', 'key.pem')
|
||||
ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
|
||||
.joinpath('key.pem'))
|
||||
key_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
if not cert_path:
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files('certbot_integration_tests').joinpath('assets', 'cert.pem')
|
||||
ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
|
||||
.joinpath('cert.pem'))
|
||||
cert_path = str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
return '''\
|
||||
|
||||
@@ -48,9 +48,8 @@ class IntegrationTestsContext(certbot_context.IntegrationTestsContext):
|
||||
:yields: Path to credentials file
|
||||
:rtype: str
|
||||
"""
|
||||
src_ref_file = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'bind-config', f'rfc2136-credentials-{label}.ini.tpl'
|
||||
)
|
||||
src_ref_file = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
|
||||
.joinpath('bind-config').joinpath(f'rfc2136-credentials-{label}.ini.tpl'))
|
||||
with importlib_resources.as_file(src_ref_file) as src_file:
|
||||
with open(src_file, 'r') as f:
|
||||
contents = f.read().format(
|
||||
|
||||
@@ -184,10 +184,6 @@ class ACMEServer:
|
||||
'--single-branch', '--depth=1', instance_path])
|
||||
process.wait(MAX_SUBPROCESS_WAIT)
|
||||
|
||||
# Allow Boulder to ignore usual limit rate policies, useful for tests.
|
||||
os.rename(join(instance_path, 'test/rate-limit-policies-b.yml'),
|
||||
join(instance_path, 'test/rate-limit-policies.yml'))
|
||||
|
||||
if self._dns_server:
|
||||
# Change Boulder config to use the provided DNS server
|
||||
for suffix in ["", "-remote-a", "-remote-b"]:
|
||||
@@ -215,7 +211,7 @@ 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)
|
||||
self.acme_xdist['directory_url'], attempts=600)
|
||||
|
||||
if not self._dns_server:
|
||||
# Configure challtestsrv to answer any A record request with ip of the docker host.
|
||||
|
||||
@@ -125,8 +125,8 @@ def generate_test_file_hooks(config_dir: str, hook_probe: str) -> None:
|
||||
"""
|
||||
file_manager = contextlib.ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
hook_path_ref = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'hook.py')
|
||||
hook_path_ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
|
||||
.joinpath('hook.py'))
|
||||
hook_path = str(file_manager.enter_context(importlib_resources.as_file(hook_path_ref)))
|
||||
|
||||
for hook_dir in list_renewal_hooks_dirs(config_dir):
|
||||
@@ -262,9 +262,8 @@ def load_sample_data_path(workspace: str) -> str:
|
||||
:returns: the path to the loaded sample data directory
|
||||
:rtype: str
|
||||
"""
|
||||
original_ref = importlib_resources.files('certbot_integration_tests').joinpath(
|
||||
'assets', 'sample-config'
|
||||
)
|
||||
original_ref = (importlib_resources.files('certbot_integration_tests').joinpath('assets')
|
||||
.joinpath('sample-config'))
|
||||
with importlib_resources.as_file(original_ref) as original:
|
||||
copied = os.path.join(workspace, 'sample-config')
|
||||
shutil.copytree(original, copied, symlinks=True)
|
||||
|
||||
@@ -45,6 +45,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -75,7 +75,7 @@ def _get_server_root(config: str) -> str:
|
||||
if os.path.isdir(os.path.join(config, name))]
|
||||
|
||||
if len(subdirs) != 1:
|
||||
errors.Error("Malformed configuration directory {0}".format(config))
|
||||
raise errors.Error("Malformed configuration directory {0}".format(config))
|
||||
|
||||
return os.path.join(config, subdirs[0].rstrip())
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'certbot',
|
||||
@@ -29,6 +29,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-cloudflare/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-cloudflare/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'cloudflare>=1.5.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-digitalocean/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-digitalocean/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'python-digitalocean>=1.11', # 1.15.0 or newer is recommended for TTL support
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-dnsimple/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-dnsimple/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
# This version of lexicon is required to address the problem described in
|
||||
@@ -54,6 +54,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-dnsmadeeasy/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-dnsmadeeasy/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-gehirn/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-gehirn/readthedocs.org.requirements.txt
|
||||
@@ -1,7 +1,6 @@
|
||||
"""Tests for certbot_dns_gehirn._internal.dns_gehirn."""
|
||||
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-google/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-google/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'google-api-python-client>=1.6.5',
|
||||
@@ -53,6 +53,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-linode/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-linode/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-luadns/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-luadns/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-nsone/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-nsone/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-ovh/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-ovh/readthedocs.org.requirements.txt
|
||||
@@ -22,6 +22,7 @@ class AuthenticatorTest(test_util.TempDirTestCase,
|
||||
|
||||
DOMAIN_NOT_FOUND = Exception('Domain example.com not found')
|
||||
LOGIN_ERROR = HTTPError('403 Client Error: Forbidden for url: https://eu.api.ovh.com/1.0/...', response=Response())
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.15.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-rfc2136/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-rfc2136/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dnspython>=1.15.0',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-route53/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-route53/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'boto3>=1.15.15',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot-dns-sakuracloud/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot-dns-sakuracloud/readthedocs.org.requirements.txt
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
'dns-lexicon>=3.14.1',
|
||||
@@ -52,6 +52,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -18,7 +18,6 @@ from typing import Mapping
|
||||
from typing import Optional
|
||||
from typing import Sequence
|
||||
from typing import Set
|
||||
from typing import Text
|
||||
from typing import Tuple
|
||||
from typing import Type
|
||||
from typing import Union
|
||||
@@ -172,8 +171,8 @@ class NginxConfigurator(common.Configurator):
|
||||
|
||||
file_manager = ExitStack()
|
||||
atexit.register(file_manager.close)
|
||||
ref = importlib_resources.files("certbot_nginx").joinpath(
|
||||
"_internal", "tls_configs", config_filename)
|
||||
ref = (importlib_resources.files("certbot_nginx").joinpath("_internal")
|
||||
.joinpath("tls_configs").joinpath(config_filename))
|
||||
|
||||
return str(file_manager.enter_context(importlib_resources.as_file(ref)))
|
||||
|
||||
@@ -702,7 +701,7 @@ class NginxConfigurator(common.Configurator):
|
||||
# TODO: generate only once
|
||||
tmp_dir = os.path.join(self.config.work_dir, "snakeoil")
|
||||
le_key = crypto_util.generate_key(
|
||||
key_size=1024, key_dir=tmp_dir, keyname="key.pem",
|
||||
key_size=2048, key_dir=tmp_dir, keyname="key.pem",
|
||||
strict_permissions=self.config.strict_permissions)
|
||||
assert le_key.file is not None
|
||||
key = OpenSSL.crypto.load_privatekey(
|
||||
@@ -1275,7 +1274,7 @@ def nginx_restart(nginx_ctl: str, nginx_conf: str, sleep_duration: int) -> None:
|
||||
|
||||
"""
|
||||
try:
|
||||
reload_output: Text = ""
|
||||
reload_output: str = ""
|
||||
with tempfile.TemporaryFile() as out:
|
||||
proc = subprocess.run([nginx_ctl, "-c", nginx_conf, "-s", "reload"],
|
||||
env=util.env_no_snap_for_external_calls(),
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from setuptools import find_packages
|
||||
from setuptools import setup
|
||||
|
||||
version = '2.8.0.dev0'
|
||||
version = '2.9.0.dev0'
|
||||
|
||||
install_requires = [
|
||||
# We specify the minimum acme and certbot version as the current plugin
|
||||
@@ -41,6 +41,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -14,14 +14,14 @@ build:
|
||||
|
||||
# Build documentation in the "docs/" directory with Sphinx
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
||||
configuration: certbot/docs/conf.py
|
||||
# You can configure Sphinx to use a different builder, for instance use the dirhtml builder for simpler URLs
|
||||
# builder: "dirhtml"
|
||||
# Fail on all warnings to avoid broken references
|
||||
fail_on_warning: true
|
||||
|
||||
# Optionally build your docs in additional formats such as PDF and ePub
|
||||
# formats:
|
||||
formats:
|
||||
- pdf
|
||||
- epub
|
||||
|
||||
@@ -30,4 +30,4 @@ sphinx:
|
||||
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
|
||||
python:
|
||||
install:
|
||||
- requirements: ../tools/requirements.txt
|
||||
- requirements: certbot/readthedocs.org.requirements.txt
|
||||
@@ -2,18 +2,47 @@
|
||||
|
||||
Certbot adheres to [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## 2.8.0 - master
|
||||
## 2.9.0 - master
|
||||
|
||||
### Added
|
||||
|
||||
* Support for Python 3.12 was added.
|
||||
|
||||
### Changed
|
||||
|
||||
*
|
||||
|
||||
### Fixed
|
||||
|
||||
* Updates `joinpath` syntax to only use one addition per call, because the multiple inputs
|
||||
version was causing mypy errors on Python 3.10.
|
||||
* Makes the `reconfigure` verb actually use the staging server for the dry run to check the new
|
||||
configuration.
|
||||
|
||||
More details about these changes can be found on our GitHub repo.
|
||||
|
||||
## 2.8.0 - 2023-12-05
|
||||
|
||||
### Added
|
||||
|
||||
* Added support for [Alpine Linux](https://www.alpinelinux.org) distribution when is used the apache plugin
|
||||
|
||||
### Changed
|
||||
|
||||
* Support for Python 3.7 was removed.
|
||||
|
||||
### Fixed
|
||||
|
||||
* Stop using the deprecated `pkg_resources` API included in `setuptools`.
|
||||
|
||||
More details about these changes can be found on our GitHub repo.
|
||||
|
||||
## 2.7.4 - 2023-11-01
|
||||
|
||||
### Fixed
|
||||
|
||||
* Fixed a bug introduced in version 2.7.0 that caused interactively entered
|
||||
webroot plugin values to not be saved for renewal.
|
||||
* Fixed a bug introduced in version 2.7.0 of our Lexicon based DNS plugins that
|
||||
caused them to fail to find the DNS zone that needs to be modified in some
|
||||
cases.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Certbot client."""
|
||||
|
||||
# version number like 1.2.3a0, must have at least 2 parts, like 1.2
|
||||
__version__ = '2.8.0.dev0'
|
||||
__version__ = '2.9.0.dev0'
|
||||
|
||||
@@ -36,6 +36,7 @@ from certbot._internal.cli.cli_utils import HelpfulArgumentGroup
|
||||
from certbot._internal.cli.cli_utils import nonnegative_int
|
||||
from certbot._internal.cli.cli_utils import parse_preferred_challenges
|
||||
from certbot._internal.cli.cli_utils import read_file
|
||||
from certbot._internal.cli.cli_utils import set_test_server_options
|
||||
from certbot._internal.cli.group_adder import _add_all_groups
|
||||
from certbot._internal.cli.helpful import HelpfulArgumentParser
|
||||
from certbot._internal.cli.paths_parser import _paths_parser
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Certbot command line util function"""
|
||||
import argparse
|
||||
import copy
|
||||
import glob
|
||||
import inspect
|
||||
from typing import Any
|
||||
from typing import Iterable
|
||||
@@ -250,3 +251,48 @@ def nonnegative_int(value: str) -> int:
|
||||
if int_value < 0:
|
||||
raise argparse.ArgumentTypeError("value must be non-negative")
|
||||
return int_value
|
||||
|
||||
def set_test_server_options(verb: str, config: configuration.NamespaceConfig) -> None:
|
||||
"""Updates server, break_my_certs, staging, tos, and
|
||||
register_unsafely_without_email in config as necessary to prepare
|
||||
to use the test server.
|
||||
|
||||
We have --staging/--dry-run; perform sanity check and set config.server
|
||||
|
||||
:param str verb: subcommand called
|
||||
|
||||
:param config: parsed command line arguments
|
||||
:type config: configuration.NamespaceConfig
|
||||
|
||||
:raises errors.Error: if non-default server is used and --staging is set
|
||||
:raises errors.Error: if inapplicable verb is used and --dry-run is set
|
||||
"""
|
||||
|
||||
# Flag combinations should produce these results:
|
||||
# | --staging | --dry-run |
|
||||
# ------------------------------------------------------------
|
||||
# | --server acme-v02 | Use staging | Use staging |
|
||||
# | --server acme-staging-v02 | Use staging | Use staging |
|
||||
# | --server <other> | Conflict error | Use <other> |
|
||||
|
||||
default_servers = (flag_default("server"), constants.STAGING_URI)
|
||||
|
||||
if config.staging and config.server not in default_servers:
|
||||
raise errors.Error("--server value conflicts with --staging")
|
||||
|
||||
if config.server == flag_default("server"):
|
||||
config.server = constants.STAGING_URI
|
||||
# If the account has already been loaded (such as by calling reconstitute before this),
|
||||
# clear it so that we don't try to use the prod account on the staging server.
|
||||
config.account = None
|
||||
|
||||
if config.dry_run:
|
||||
if verb not in ["certonly", "renew", "reconfigure"]:
|
||||
raise errors.Error("--dry-run currently only works with the "
|
||||
"'certonly' or 'renew' subcommands (%r)" % verb)
|
||||
config.break_my_certs = config.staging = True
|
||||
if glob.glob(os.path.join(config.config_dir, constants.ACCOUNTS_DIR, "*")):
|
||||
# The user has a prod account, but might not have a staging
|
||||
# one; we don't want to start trying to perform interactive registration
|
||||
config.tos = True
|
||||
config.register_unsafely_without_email = True
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import glob
|
||||
import sys
|
||||
from typing import Any
|
||||
from typing import Dict
|
||||
@@ -26,11 +25,11 @@ from certbot._internal.cli.cli_utils import add_domains
|
||||
from certbot._internal.cli.cli_utils import CustomHelpFormatter
|
||||
from certbot._internal.cli.cli_utils import flag_default
|
||||
from certbot._internal.cli.cli_utils import HelpfulArgumentGroup
|
||||
from certbot._internal.cli.cli_utils import set_test_server_options
|
||||
from certbot._internal.cli.verb_help import VERB_HELP
|
||||
from certbot._internal.cli.verb_help import VERB_HELP_MAP
|
||||
from certbot._internal.display import obj as display_obj
|
||||
from certbot._internal.plugins import disco
|
||||
from certbot.compat import os
|
||||
from certbot.configuration import ArgumentSource
|
||||
from certbot.configuration import NamespaceConfig
|
||||
|
||||
@@ -165,6 +164,7 @@ class HelpfulArgumentParser:
|
||||
def remove_config_file_domains_for_renewal(self, config: NamespaceConfig) -> None:
|
||||
"""Make "certbot renew" safe if domains are set in cli.ini."""
|
||||
# Works around https://github.com/certbot/certbot/issues/4096
|
||||
assert config.argument_sources is not None
|
||||
if (config.argument_sources['domains'] == ArgumentSource.CONFIG_FILE and
|
||||
self.verb == "renew"):
|
||||
config.domains = []
|
||||
@@ -317,33 +317,10 @@ class HelpfulArgumentParser:
|
||||
return config
|
||||
|
||||
def set_test_server(self, config: NamespaceConfig) -> None:
|
||||
"""We have --staging/--dry-run; perform sanity check and set config.server"""
|
||||
|
||||
# Flag combinations should produce these results:
|
||||
# | --staging | --dry-run |
|
||||
# ------------------------------------------------------------
|
||||
# | --server acme-v02 | Use staging | Use staging |
|
||||
# | --server acme-staging-v02 | Use staging | Use staging |
|
||||
# | --server <other> | Conflict error | Use <other> |
|
||||
|
||||
default_servers = (flag_default("server"), constants.STAGING_URI)
|
||||
|
||||
if config.staging and config.server not in default_servers:
|
||||
raise errors.Error("--server value conflicts with --staging")
|
||||
|
||||
if config.server == flag_default("server"):
|
||||
config.server = constants.STAGING_URI
|
||||
|
||||
if config.dry_run:
|
||||
if self.verb not in ["certonly", "renew"]:
|
||||
raise errors.Error("--dry-run currently only works with the "
|
||||
"'certonly' or 'renew' subcommands (%r)" % self.verb)
|
||||
config.break_my_certs = config.staging = True
|
||||
if glob.glob(os.path.join(config.config_dir, constants.ACCOUNTS_DIR, "*")):
|
||||
# The user has a prod account, but might not have a staging
|
||||
# one; we don't want to start trying to perform interactive registration
|
||||
config.tos = True
|
||||
config.register_unsafely_without_email = True
|
||||
"""Updates server, break_my_certs, staging, tos, and
|
||||
register_unsafely_without_email in config as necessary to prepare
|
||||
to use the test server."""
|
||||
return set_test_server_options(self.verb, config)
|
||||
|
||||
def handle_csr(self, config: NamespaceConfig) -> None:
|
||||
"""Process a --csr flag."""
|
||||
|
||||
@@ -21,7 +21,7 @@ SETUPTOOLS_PLUGINS_ENTRY_POINT = "certbot.plugins"
|
||||
OLD_SETUPTOOLS_PLUGINS_ENTRY_POINT = "letsencrypt.plugins"
|
||||
"""Plugins Setuptools entry point before rename."""
|
||||
|
||||
CLI_DEFAULTS: Dict[str, Any] = dict( # noqa
|
||||
CLI_DEFAULTS: Dict[str, Any] = dict( # pylint: disable=use-dict-literal
|
||||
config_files=[
|
||||
os.path.join(misc.get_default_folder('config'), 'cli.ini'),
|
||||
# https://freedesktop.org/wiki/Software/xdg-user-dirs/
|
||||
|
||||
@@ -1727,10 +1727,8 @@ def reconfigure(config: configuration.NamespaceConfig,
|
||||
# to say nothing of the difficulty in explaining what exactly this subcommand can modify
|
||||
|
||||
|
||||
# To make sure that the requested changes work, do a dry run. While setting up the dry run,
|
||||
# we will set all the needed fields in config, which will then be saved upon success.
|
||||
config.dry_run = True
|
||||
|
||||
# To make sure that the requested changes work, we're going to do a dry run, and only save
|
||||
# upon success. First, modify the config as the user requested.
|
||||
if not config.certname:
|
||||
certname_question = "Which certificate would you like to reconfigure?"
|
||||
config.certname = cert_manager.get_certnames(
|
||||
@@ -1772,17 +1770,44 @@ def reconfigure(config: configuration.NamespaceConfig,
|
||||
if not renewal_candidate:
|
||||
raise errors.ConfigurationError("Could not load certificate. See logs for errors.")
|
||||
|
||||
renewalparams = orig_renewal_conf['renewalparams']
|
||||
# If server was set but hasn't changed and no account is loaded,
|
||||
# load the old account because reconstitute won't have
|
||||
if lineage_config.set_by_user('server') and lineage_config.server == renewalparams['server']\
|
||||
and lineage_config.account is None:
|
||||
lineage_config.account = renewalparams['account']
|
||||
for param in ('account', 'server',):
|
||||
if getattr(lineage_config, param) != renewalparams.get(param):
|
||||
msg = ("Using reconfigure to change the ACME account or server is not supported. "
|
||||
"If you would like to do so, use renew with the --force-renewal flag instead "
|
||||
"of reconfigure. Note that doing so will count against any rate limits. For "
|
||||
"more information on this method, see "
|
||||
"https://certbot.org/renew-reconfiguration")
|
||||
raise errors.ConfigurationError(msg)
|
||||
|
||||
# this is where lineage_config gets fully filled out (e.g. --apache will set auth and installer)
|
||||
installer, auth = plug_sel.choose_configurator_plugins(lineage_config, plugins, "certonly")
|
||||
le_client = _init_le_client(lineage_config, auth, installer)
|
||||
|
||||
# make a deep copy of lineage_config because we're about to modify it for a test dry run
|
||||
dry_run_lineage_config = copy.deepcopy(lineage_config)
|
||||
|
||||
# we also set noninteractive_mode to more accurately simulate renewal (since `certbot renew`
|
||||
# implies noninteractive mode) and to avoid prompting the user as changes made to
|
||||
# dry_run_lineage_config beyond this point will not be applied to the original lineage_config
|
||||
dry_run_lineage_config.noninteractive_mode = True
|
||||
dry_run_lineage_config.dry_run = True
|
||||
cli.set_test_server_options("reconfigure", dry_run_lineage_config)
|
||||
|
||||
le_client = _init_le_client(dry_run_lineage_config, auth, installer)
|
||||
|
||||
# renews cert as dry run to test that the new values are ok
|
||||
# at this point, renewal_candidate.configuration has the old values, but will use
|
||||
# the values from lineage_config when doing the dry run
|
||||
_get_and_save_cert(le_client, lineage_config, certname=certname,
|
||||
_get_and_save_cert(le_client, dry_run_lineage_config, certname=certname,
|
||||
lineage=renewal_candidate)
|
||||
|
||||
# this function will update lineage.configuration with the new values, and save it to disk
|
||||
# use the pre-dry-run version
|
||||
renewal_candidate.save_new_config_values(lineage_config)
|
||||
|
||||
_report_reconfigure_results(renewal_file, orig_renewal_conf)
|
||||
|
||||
@@ -207,6 +207,7 @@ class PluginsRegistry(Mapping):
|
||||
plugin2_dist = other_ep.entry_point.dist
|
||||
plugin1 = plugin1_dist.name.lower() if plugin1_dist else "unknown"
|
||||
plugin2 = plugin2_dist.name.lower() if plugin2_dist else "unknown"
|
||||
# pylint: disable=broad-exception-raised
|
||||
raise Exception("Duplicate plugin name {0} from {1} and {2}.".format(
|
||||
plugin_ep.name, plugin1, plugin2))
|
||||
if issubclass(plugin_ep.plugin_cls, interfaces.Plugin):
|
||||
|
||||
@@ -165,17 +165,22 @@ class NamespaceConfigTest(test_util.ConfigTestCase):
|
||||
|
||||
def test_set_by_user_exception(self):
|
||||
from certbot.configuration import NamespaceConfig
|
||||
|
||||
|
||||
# a newly created NamespaceConfig has no argument sources dict, so an
|
||||
# exception is raised
|
||||
config = NamespaceConfig(self.config.namespace)
|
||||
with pytest.raises(RuntimeError):
|
||||
config.set_by_user('whatever')
|
||||
|
||||
|
||||
# now set an argument sources dict
|
||||
config.set_argument_sources({})
|
||||
assert not config.set_by_user('whatever')
|
||||
|
||||
def test_set_by_user_mutables(self):
|
||||
assert not self.config.set_by_user('domains')
|
||||
self.config.domains.append('example.org')
|
||||
assert self.config.set_by_user('domains')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main(sys.argv[1:] + [__file__])) # pragma: no cover
|
||||
|
||||
@@ -168,7 +168,7 @@ class MakeKeyTest(unittest.TestCase):
|
||||
from certbot.crypto_util import make_key
|
||||
|
||||
# Do not test larger keys as it takes too long.
|
||||
OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, make_key(1024))
|
||||
OpenSSL.crypto.load_privatekey(OpenSSL.crypto.FILETYPE_PEM, make_key(2048))
|
||||
|
||||
def test_ec(self): # pylint: disable=no-self-use
|
||||
# ECDSA Key Type Tests
|
||||
@@ -185,8 +185,8 @@ class MakeKeyTest(unittest.TestCase):
|
||||
from certbot.crypto_util import make_key
|
||||
|
||||
# Try a bad key size for RSA and ECDSA
|
||||
with pytest.raises(errors.Error, match='Unsupported RSA key length: 512'):
|
||||
make_key(bits=512, key_type='rsa')
|
||||
with pytest.raises(errors.Error, match='Unsupported RSA key length: 1024'):
|
||||
make_key(bits=1024, key_type='rsa')
|
||||
|
||||
def test_bad_elliptic_curve_name(self):
|
||||
from certbot.crypto_util import make_key
|
||||
@@ -200,7 +200,7 @@ class MakeKeyTest(unittest.TestCase):
|
||||
with pytest.raises(errors.Error,
|
||||
match=re.escape('Invalid key_type specified: unf. Use [rsa|ecdsa]')):
|
||||
OpenSSL.crypto.load_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, make_key(1024, key_type='unf'))
|
||||
OpenSSL.crypto.FILETYPE_PEM, make_key(2048, key_type='unf'))
|
||||
|
||||
|
||||
class VerifyCertSetup(unittest.TestCase):
|
||||
|
||||
@@ -563,7 +563,7 @@ class ReconfigureTest(test_util.TempDirTestCase):
|
||||
# Options used in the renewal process
|
||||
[renewalparams]
|
||||
account = ee43634db0aa4e6804f152be39990e6a
|
||||
server = https://acme-staging-v02.api.letsencrypt.org/directory
|
||||
server = https://acme-v02.api.letsencrypt.org/directory
|
||||
authenticator = nginx
|
||||
installer = nginx
|
||||
key_type = rsa
|
||||
@@ -621,6 +621,72 @@ class ReconfigureTest(test_util.TempDirTestCase):
|
||||
new_config = self._call('--cert-name example.com --apache'.split())
|
||||
assert new_config['renewalparams']['authenticator'] == 'apache'
|
||||
|
||||
def test_only_intended_changes(self):
|
||||
""" Check that we don't accidentally modify anything that we didn't mean to """
|
||||
named_mock = mock.Mock()
|
||||
named_mock.name = 'apache'
|
||||
|
||||
self.mocks['pick_installer'].return_value = named_mock
|
||||
self.mocks['pick_auth'].return_value = named_mock
|
||||
self.mocks['find_init'].return_value = named_mock
|
||||
|
||||
new_config = self._call('--cert-name example.com --apache'.split())
|
||||
# Undo the changes we made in calling and in testing
|
||||
new_config['renewalparams']['authenticator'] = 'nginx'
|
||||
new_config['renewalparams']['installer'] = 'nginx'
|
||||
del new_config['renewalparams']['config_dir']
|
||||
new_config['version'] = self.original_config['version']
|
||||
|
||||
assert new_config == self.original_config
|
||||
|
||||
@mock.patch('certbot._internal.hooks.validate_hooks')
|
||||
def test_staging_used(self, unused_validate_hooks):
|
||||
""" Check that we use the staging server for the dry run """
|
||||
assert self.original_config['renewalparams']['server'] == \
|
||||
'https://acme-v02.api.letsencrypt.org/directory'
|
||||
|
||||
self._call('--cert-name example.com --pre-hook'.split() + ['echo pre'])
|
||||
|
||||
assert 'staging' in self.mocks['_init_le_client'].call_args.args[0].server
|
||||
assert 'staging' in self.mocks['_get_and_save_cert'].call_args.args[1].server
|
||||
|
||||
def test_new_account_or_server_errors(self):
|
||||
""" Check that we error when attempting to change the account id or server,
|
||||
but not when it's the same
|
||||
"""
|
||||
orig_account_id = self.original_config['renewalparams']['account']
|
||||
orig_server = self.original_config['renewalparams']['server']
|
||||
|
||||
# new account
|
||||
try:
|
||||
self._call(f'--cert-name example.com --account newaccountid'.split())
|
||||
except errors.ConfigurationError as err:
|
||||
assert "Using reconfigure to change the ACME account" in str(err)
|
||||
|
||||
# check that config isn't modified
|
||||
with open(self.renewal_file, 'r') as f:
|
||||
new_config = configobj.ConfigObj(f, encoding='utf-8', default_encoding='utf-8')
|
||||
assert new_config['renewalparams']['account'] == orig_account_id
|
||||
|
||||
# same account
|
||||
new_config = self._call(f'--cert-name example.com --account {orig_account_id}'.split())
|
||||
assert new_config['renewalparams']['account'] == orig_account_id
|
||||
|
||||
# new server
|
||||
try:
|
||||
self._call(f'--cert-name example.com --server x.com'.split())
|
||||
except errors.ConfigurationError as err:
|
||||
assert "Using reconfigure to change the ACME account" in str(err)
|
||||
|
||||
# check that config isn't modified
|
||||
with open(self.renewal_file, 'r') as f:
|
||||
new_config = configobj.ConfigObj(f, encoding='utf-8', default_encoding='utf-8')
|
||||
assert new_config['renewalparams']['server'] == orig_server
|
||||
|
||||
# same server
|
||||
new_config = self._call(f'--cert-name example.com --server {orig_server}'.split())
|
||||
assert new_config['renewalparams']['server'] == orig_server
|
||||
|
||||
@mock.patch('certbot._internal.hooks.validate_hooks')
|
||||
def test_update_hooks(self, unused_validate_hooks):
|
||||
assert 'pre_hook' not in self.original_config
|
||||
|
||||
@@ -66,7 +66,8 @@ class NamespaceConfig:
|
||||
self.namespace: argparse.Namespace
|
||||
# Avoid recursion loop because of the delegation defined in __setattr__
|
||||
object.__setattr__(self, 'namespace', namespace)
|
||||
object.__setattr__(self, 'argument_sources', None)
|
||||
object.__setattr__(self, '_argument_sources', None)
|
||||
object.__setattr__(self, '_previously_accessed_mutables', {})
|
||||
|
||||
self.namespace.config_dir = os.path.abspath(self.namespace.config_dir)
|
||||
self.namespace.work_dir = os.path.abspath(self.namespace.work_dir)
|
||||
@@ -90,7 +91,7 @@ class NamespaceConfig:
|
||||
"""
|
||||
|
||||
# Avoid recursion loop because of the delegation defined in __setattr__
|
||||
object.__setattr__(self, 'argument_sources', argument_sources)
|
||||
object.__setattr__(self, '_argument_sources', argument_sources)
|
||||
|
||||
|
||||
def set_by_user(self, var: str) -> bool:
|
||||
@@ -145,15 +146,48 @@ class NamespaceConfig:
|
||||
"""
|
||||
If an argument_sources dict was set, overwrites an argument's source to
|
||||
be ArgumentSource.RUNTIME. Used when certbot sets an argument's values
|
||||
at runtime.
|
||||
at runtime. This also clears the modified value from
|
||||
_previously_accessed_mutables since it is no longer needed.
|
||||
"""
|
||||
if self.argument_sources is not None:
|
||||
self.argument_sources[name] = ArgumentSource.RUNTIME
|
||||
if self._argument_sources is not None:
|
||||
self._argument_sources[name] = ArgumentSource.RUNTIME
|
||||
if name in self._previously_accessed_mutables:
|
||||
del self._previously_accessed_mutables[name]
|
||||
|
||||
@property
|
||||
def argument_sources(self) -> Optional[Dict[str, ArgumentSource]]:
|
||||
"""Returns _argument_sources after handling any changes to accessed mutable values."""
|
||||
# We keep values in _previously_accessed_mutables until we've detected a modification to try
|
||||
# to provide up-to-date information when argument_sources is accessed. Once a mutable object
|
||||
# has been accessed, it can be modified at any time if a reference to it was kept somewhere
|
||||
# else.
|
||||
|
||||
# We copy _previously_accessed_mutables because _mark_runtime_override modifies it.
|
||||
for name, prev_value in self._previously_accessed_mutables.copy().items():
|
||||
current_value = getattr(self.namespace, name)
|
||||
if current_value != prev_value:
|
||||
self._mark_runtime_override(name)
|
||||
return self._argument_sources
|
||||
|
||||
# Delegate any attribute not explicitly defined to the underlying namespace object.
|
||||
#
|
||||
# If any mutable namespace attributes are explicitly defined in the future, you'll probably want
|
||||
# to take an approach like the one used in __getattr__ and the argument_sources property.
|
||||
|
||||
def __getattr__(self, name: str) -> Any:
|
||||
return getattr(self.namespace, name)
|
||||
arg_sources = self.argument_sources
|
||||
value = getattr(self.namespace, name)
|
||||
if arg_sources is not None:
|
||||
# If the requested attribute was already modified at runtime, we don't need to track any
|
||||
# future changes.
|
||||
if name not in arg_sources or arg_sources[name] != ArgumentSource.RUNTIME:
|
||||
# If name is already in _previously_accessed_mutables, we don't need to make a copy
|
||||
# of it again. If its value was changed, this would have been caught while preparing
|
||||
# the return value of the property self.argument_sources accessed earlier in this
|
||||
# function.
|
||||
if name not in self._previously_accessed_mutables and not _is_immutable(value):
|
||||
self._previously_accessed_mutables[name] = copy.deepcopy(value)
|
||||
return value
|
||||
|
||||
def __setattr__(self, name: str, value: Any) -> None:
|
||||
self._mark_runtime_override(name)
|
||||
@@ -425,9 +459,10 @@ class NamespaceConfig:
|
||||
# Work around https://bugs.python.org/issue1515 for py26 tests :( :(
|
||||
new_ns = copy.deepcopy(self.namespace)
|
||||
new_config = type(self)(new_ns)
|
||||
if self.set_argument_sources is not None:
|
||||
new_sources = copy.deepcopy(self.argument_sources)
|
||||
new_config.set_argument_sources(new_sources)
|
||||
# Avoid recursion loop because of the delegation defined in __setattr__
|
||||
object.__setattr__(new_config, '_argument_sources', copy.deepcopy(self.argument_sources))
|
||||
object.__setattr__(new_config, '_previously_accessed_mutables',
|
||||
copy.deepcopy(self._previously_accessed_mutables))
|
||||
return new_config
|
||||
|
||||
|
||||
@@ -450,3 +485,15 @@ def _check_config_sanity(config: NamespaceConfig) -> None:
|
||||
for domain in config.namespace.domains:
|
||||
# This may be redundant, but let's be paranoid
|
||||
util.enforce_domain_sanity(domain)
|
||||
|
||||
|
||||
def _is_immutable(value: Any) -> bool:
|
||||
"""Is value of an immutable type?"""
|
||||
if isinstance(value, tuple):
|
||||
# tuples are only immutable if all contained values are immutable.
|
||||
return all(_is_immutable(subvalue) for subvalue in value)
|
||||
for immutable_type in (int, float, complex, str, bytes, bool, frozenset,):
|
||||
if isinstance(value, immutable_type):
|
||||
return True
|
||||
# The last case we consider here is None which is also immutable.
|
||||
return value is None
|
||||
|
||||
@@ -208,11 +208,11 @@ def import_csr_file(csrfile: str, data: bytes) -> Tuple[int, util.CSR, List[str]
|
||||
return PEM, util.CSR(file=csrfile, data=data_pem, form="pem"), domains
|
||||
|
||||
|
||||
def make_key(bits: int = 1024, key_type: str = "rsa",
|
||||
def make_key(bits: int = 2048, key_type: str = "rsa",
|
||||
elliptic_curve: Optional[str] = None) -> bytes:
|
||||
"""Generate PEM encoded RSA|EC key.
|
||||
|
||||
:param int bits: Number of bits if key_type=rsa. At least 1024 for RSA.
|
||||
:param int bits: Number of bits if key_type=rsa. At least 2048 for RSA.
|
||||
:param str key_type: The type of key to generate, but be rsa or ecdsa
|
||||
:param str elliptic_curve: The elliptic curve to use.
|
||||
|
||||
@@ -221,7 +221,7 @@ def make_key(bits: int = 1024, key_type: str = "rsa",
|
||||
:rtype: str
|
||||
"""
|
||||
if key_type == 'rsa':
|
||||
if bits < 1024:
|
||||
if bits < 2048:
|
||||
raise errors.Error("Unsupported RSA key length: {}".format(bits))
|
||||
|
||||
key = crypto.PKey()
|
||||
|
||||
@@ -465,7 +465,7 @@ def dir_setup(test_dir: str, pkg: str) -> Tuple[str, str, str]: # pragma: no co
|
||||
filesystem.chmod(config_dir, constants.CONFIG_DIRS_MODE)
|
||||
filesystem.chmod(work_dir, constants.CONFIG_DIRS_MODE)
|
||||
|
||||
test_dir_ref = importlib_resources.files(pkg).joinpath("testdata", test_dir)
|
||||
test_dir_ref = importlib_resources.files(pkg).joinpath("testdata").joinpath(test_dir)
|
||||
with importlib_resources.as_file(test_dir_ref) as path:
|
||||
shutil.copytree(
|
||||
path, os.path.join(temp_dir, test_dir), symlinks=True)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Test utilities."""
|
||||
import atexit
|
||||
from contextlib import ExitStack
|
||||
import copy
|
||||
from importlib import reload as reload_module
|
||||
import io
|
||||
import logging
|
||||
@@ -403,7 +404,8 @@ class ConfigTestCase(TempDirTestCase):
|
||||
def setUp(self) -> None:
|
||||
super().setUp()
|
||||
self.config = configuration.NamespaceConfig(
|
||||
mock.MagicMock(**constants.CLI_DEFAULTS),
|
||||
# We make a copy here so any mutable values from CLI_DEFAULTS do not get modified.
|
||||
mock.MagicMock(**copy.deepcopy(constants.CLI_DEFAULTS)),
|
||||
)
|
||||
self.config.set_argument_sources({})
|
||||
self.config.namespace.verb = "certonly"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
usage:
|
||||
usage:
|
||||
certbot [SUBCOMMAND] [options] [-d DOMAIN] [-d DOMAIN] ...
|
||||
|
||||
Certbot can obtain and install HTTPS/TLS/SSL certificates. By default,
|
||||
@@ -122,7 +122,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/2.7.3 (certbot;
|
||||
"". (default: CertbotACMEClient/2.8.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,
|
||||
|
||||
@@ -69,7 +69,7 @@ master_doc = 'index'
|
||||
# General information about the project.
|
||||
project = u'Certbot'
|
||||
# this is now overridden by the footer.html template
|
||||
#copyright = u'2014-2018 - The Certbot software and documentation are licensed under the Apache 2.0 license as described at https://eff.org/cb-license.'
|
||||
copyright = u'2014-2018 - The Certbot software and documentation are licensed under the Apache 2.0 license as described at https://eff.org/cb-license.'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
|
||||
@@ -328,8 +328,8 @@ Writing your own plugin
|
||||
for one example of that.
|
||||
|
||||
Certbot client supports dynamic discovery of plugins through the
|
||||
`setuptools entry points`_ using the `certbot.plugins` group. This
|
||||
way you can, for example, create a custom implementation of
|
||||
`importlib.metadata entry points`_ using the `certbot.plugins` group.
|
||||
This way you can, for example, create a custom implementation of
|
||||
`~certbot.interfaces.Authenticator` or the
|
||||
`~certbot.interfaces.Installer` without having to merge it
|
||||
with the core upstream source code. An example is provided in
|
||||
@@ -352,8 +352,8 @@ users install it system-wide with `pip install`. Note that this will
|
||||
only work for users who have Certbot installed from OS packages or via
|
||||
pip.
|
||||
|
||||
.. _`setuptools entry points`:
|
||||
https://setuptools.readthedocs.io/en/latest/pkg_resources.html#entry-points
|
||||
.. _`importlib.metadata entry points`:
|
||||
https://importlib-metadata.readthedocs.io/en/latest/using.html#entry-points
|
||||
|
||||
Writing your own plugin snap
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
@@ -325,6 +325,7 @@ dns-multi_ Y N DNS authentication of 100+ providers using go-acme/
|
||||
dns-dnsmanager_ Y N DNS Authentication for dnsmanager.io
|
||||
standalone-nfq_ Y N HTTP Authentication that works with any webserver (Linux only)
|
||||
dns-solidserver_ Y N DNS Authentication using SOLIDserver (EfficientIP)
|
||||
dns-stackit_ Y N DNS Authentication using STACKIT DNS
|
||||
================== ==== ==== ===============================================================
|
||||
|
||||
.. _haproxy: https://github.com/greenhost/certbot-haproxy
|
||||
@@ -351,6 +352,7 @@ dns-solidserver_ Y N DNS Authentication using SOLIDserver (EfficientIP)
|
||||
.. _dns-dnsmanager: https://github.com/stayallive/certbot-dns-dnsmanager
|
||||
.. _standalone-nfq: https://github.com/alexzorin/certbot-standalone-nfq
|
||||
.. _dns-solidserver: https://gitlab.com/charlyhong/certbot-dns-solidserver
|
||||
.. _dns-stackit: https://github.com/stackitcloud/certbot-dns-stackit
|
||||
|
||||
If you're interested, you can also :ref:`write your own plugin <dev-plugin>`.
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
'Topic :: System :: Installation/Setup',
|
||||
|
||||
@@ -20,6 +20,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Topic :: Internet :: WWW/HTTP',
|
||||
'Topic :: Security',
|
||||
],
|
||||
|
||||
@@ -10,7 +10,6 @@ import os.path
|
||||
import re
|
||||
|
||||
from pylint.checkers import BaseChecker
|
||||
from pylint.interfaces import IAstroidChecker
|
||||
|
||||
# Modules whose file is matching one of these paths can import the os module.
|
||||
WHITELIST_PATHS = [
|
||||
@@ -25,7 +24,6 @@ class ForbidStandardOsModule(BaseChecker):
|
||||
This checker ensures that standard os module (and submodules) is not imported by certbot
|
||||
modules. Otherwise an 'os-module-forbidden' error will be registered for the faulty lines.
|
||||
"""
|
||||
__implements__ = IAstroidChecker
|
||||
|
||||
name = 'forbid-os-module'
|
||||
msgs = {
|
||||
|
||||
18
pytest.ini
18
pytest.ini
@@ -21,21 +21,15 @@
|
||||
# updated.
|
||||
# 4) Ignore our own PendingDeprecationWarning about update_symlinks soon to be dropped.
|
||||
# See https://github.com/certbot/certbot/issues/6284.
|
||||
# 5) Ignore pkg_resources.declare_namespace used in a large number of Google's Python
|
||||
# libs. e.g. https://github.com/googleapis/google-auth-library-python/issues/1229.
|
||||
# It is also is used in sphinxcontrib-devhelp 1.0.2 which as of writing this
|
||||
# is the latest version of that library. See
|
||||
# https://github.com/sphinx-doc/sphinxcontrib-devhelp/blob/1.0.2/setup.py#L69.
|
||||
# 6) Ignore DeprecationWarning from using pkg_resources API
|
||||
# 7) Ignore DeprecationWarning for datetime.utcfromtimestamp() triggered
|
||||
# when importing the pytz.tzinfo module
|
||||
# https://github.com/stub42/pytz/issues/105
|
||||
# 5) Ignore DeprecationWarning for datetime.utcfromtimestamp() triggered
|
||||
# from dateutil. See https://github.com/dateutil/dateutil/issues/1314.
|
||||
# 6) Ignoring this allows us to continue to update pyOpenSSL (one of our crypto
|
||||
# dependencies) until https://github.com/certbot/certbot/issues/9828 is resolved.
|
||||
filterwarnings =
|
||||
error
|
||||
ignore:decodestring\(\) is a deprecated alias:DeprecationWarning:dns
|
||||
ignore:.*rsyncdir:DeprecationWarning
|
||||
ignore:'urllib3.contrib.pyopenssl:DeprecationWarning:requests_toolbelt
|
||||
ignore:update_symlinks is deprecated:PendingDeprecationWarning
|
||||
ignore:.*declare_namespace\(':DeprecationWarning
|
||||
ignore:pkg_resources is deprecated as an API:DeprecationWarning
|
||||
ignore:.*datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:pytz.tzinfo
|
||||
ignore:.*datetime.utcfromtimestamp\(\) is deprecated:DeprecationWarning:dateutil
|
||||
ignore:X509Extension support in pyOpenSSL is deprecated:DeprecationWarning
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
# that script.
|
||||
apacheconfig==0.3.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
asn1crypto==0.24.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
astroid==3.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
astroid==3.0.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
boto3==1.15.15 ; python_version >= "3.8" and python_version < "3.9"
|
||||
botocore==1.18.15 ; python_version >= "3.8" and python_version < "3.9"
|
||||
cachetools==5.3.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
certifi==2023.7.22 ; python_version >= "3.8" and python_version < "3.9"
|
||||
cachetools==5.3.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
certifi==2023.11.17 ; python_version >= "3.8" and python_version < "3.9"
|
||||
cffi==1.12.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
chardet==3.0.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
cloudflare==1.5.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
@@ -22,9 +22,9 @@ distlib==0.3.7 ; python_version >= "3.8" and python_version < "3.9"
|
||||
distro==1.0.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
dns-lexicon==3.15.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
dnspython==1.15.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
exceptiongroup==1.1.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
exceptiongroup==1.2.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
execnet==2.0.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
filelock==3.12.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
filelock==3.13.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
funcsigs==0.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
future==0.18.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
google-api-python-client==1.6.5 ; python_version >= "3.8" and python_version < "3.9"
|
||||
@@ -32,37 +32,37 @@ google-auth==2.16.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
httplib2==0.9.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
idna==2.6 ; python_version >= "3.8" and python_version < "3.9"
|
||||
importlib-metadata==4.6.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
importlib-resources==6.1.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
importlib-resources==6.1.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
iniconfig==2.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
ipaddress==1.0.16 ; python_version >= "3.8" and python_version < "3.9"
|
||||
isort==5.12.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
jmespath==0.10.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
josepy==1.13.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
josepy==1.14.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
logger==1.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
mccabe==0.7.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
mypy==1.6.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
mypy==1.7.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
ndg-httpsclient==0.3.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
oauth2client==4.1.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
packaging==23.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
parsedatetime==2.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pbr==1.8.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pip==23.2.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
platformdirs==3.11.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pip==23.3.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
platformdirs==4.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pluggy==1.3.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
ply==3.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
py==1.11.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyasn1-modules==0.3.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyasn1==0.4.8 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pycparser==2.14 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pylint==3.0.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pylint==3.0.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyopenssl==17.5.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyotp==2.9.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyparsing==2.2.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pyrfc3339==1.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pytest-cov==4.1.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pytest-xdist==3.3.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pytest==7.4.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pytest-xdist==3.5.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
pytest==7.4.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
python-augeas==0.5.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
python-digitalocean==1.11 ; python_version >= "3.8" and python_version < "3.9"
|
||||
@@ -76,9 +76,9 @@ s3transfer==0.3.7 ; python_version >= "3.8" and python_version < "3.9"
|
||||
setuptools==41.6.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
six==1.11.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
soupsieve==2.5 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tldextract==5.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tldextract==5.1.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tomli==2.0.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tomlkit==0.12.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tomlkit==0.12.3 ; python_version >= "3.8" and python_version < "3.9"
|
||||
tox==1.9.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-cryptography==3.3.23.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-httplib2==0.22.0.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
@@ -86,14 +86,14 @@ types-pyopenssl==23.0.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-pyrfc3339==1.1.1.5 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-python-dateutil==2.8.19.14 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-pytz==2023.3.1.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-pywin32==306.0.0.4 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-pywin32==306.0.0.6 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-requests==2.31.0.6 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-setuptools==68.2.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-setuptools==69.0.0.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-six==1.16.21.9 ; python_version >= "3.8" and python_version < "3.9"
|
||||
types-urllib3==1.26.25.14 ; python_version >= "3.8" and python_version < "3.9"
|
||||
typing-extensions==4.8.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
uritemplate==3.0.1 ; python_version >= "3.8" and python_version < "3.9"
|
||||
urllib3==1.24.2 ; python_version >= "3.8" and python_version < "3.9"
|
||||
virtualenv==20.24.5 ; python_version >= "3.8" and python_version < "3.9"
|
||||
virtualenv==20.25.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
wheel==0.33.6 ; python_version >= "3.8" and python_version < "3.9"
|
||||
zipp==3.17.0 ; python_version >= "3.8" and python_version < "3.9"
|
||||
|
||||
@@ -13,7 +13,7 @@ set -euo pipefail
|
||||
# If this script wasn't given a command line argument, print usage and exit.
|
||||
if [ -z ${1+x} ]; then
|
||||
echo "Usage:" >&2
|
||||
echo "$0 PYPROJECT_TOML_DIRECTORY" >&2
|
||||
echo "$0 PYPROJECT_TOML_DIRECTORY [POETRY_ARGS]" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -37,10 +37,14 @@ if [ -f poetry.lock ]; then
|
||||
rm poetry.lock
|
||||
fi
|
||||
|
||||
echo "If this takes more than a few minutes, you can try running this script again" >&2
|
||||
echo "with arguments for poetry like -vvv on the command line to help see where" >&2
|
||||
echo "poetry is getting stuck." >&2
|
||||
extra_args="${*:2}"
|
||||
# If you're running this with different Python versions (say to update both our
|
||||
# "current" and "oldest" pinnings), poetry's cache can become corrupted causing
|
||||
# poetry to hang indefinitely. --no-cache avoids this.
|
||||
poetry lock --no-cache >&2
|
||||
poetry lock --no-cache ${extra_args:+"$extra_args"} >&2
|
||||
trap 'rm poetry.lock' EXIT
|
||||
|
||||
# We need to remove local packages from the output.
|
||||
|
||||
@@ -58,7 +58,7 @@ setuptools-rust = "*"
|
||||
#
|
||||
# If this pinning is removed, we may still need to add a lower bound for the
|
||||
# pylint version. See https://github.com/certbot/certbot/pull/9229.
|
||||
pylint = "2.15.5"
|
||||
pylint = "3.0.2"
|
||||
|
||||
# Bug in poetry, where still installes yanked versions from pypi (source: https://github.com/python-poetry/poetry/issues/2453)
|
||||
# this version of cryptography introduced a security vulnrability.
|
||||
@@ -70,11 +70,6 @@ cryptography = "!= 37.0.3"
|
||||
# https://github.com/python-poetry/poetry-plugin-export/issues/168 is resolved.
|
||||
poetry = "<1.3.0"
|
||||
|
||||
# setuptools 67.5.0 deprecated pkg_resources which we still use. Let's pin it
|
||||
# back until this is fixed. Doing this work is being tracked by
|
||||
# https://github.com/certbot/certbot/issues/9606.
|
||||
setuptools = "<67.5.0"
|
||||
|
||||
# Branch 4.x of tox introduces backward incompatibility changes. The tox.ini
|
||||
# file in the project must be adapted accordingly before moving out of the 3.x
|
||||
# branch. Once done, the following constraint should become tox >= 4 to keep
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
# This script accepts no arguments and automates the process of updating
|
||||
# Certbot's dependencies including automatically updating the correct file.
|
||||
# This script automates the process of updating Certbot's dependencies
|
||||
# including automatically updating the correct file. It is usually run without
|
||||
# any arguments, but if any are provided, they will be passed to Poetry.
|
||||
# Dependencies can be pinned to older versions by modifying pyproject.toml in
|
||||
# the same directory as this file.
|
||||
set -euo pipefail
|
||||
@@ -11,7 +12,7 @@ REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
RELATIVE_SCRIPT_PATH="$(realpath --relative-to "$REPO_ROOT" "$WORK_DIR")/$(basename "${BASH_SOURCE[0]}")"
|
||||
REQUIREMENTS_FILE="$REPO_ROOT/tools/requirements.txt"
|
||||
|
||||
PINNINGS=$("${COMMON_DIR}/export-pinned-dependencies.sh" "${WORK_DIR}")
|
||||
PINNINGS=$("${COMMON_DIR}/export-pinned-dependencies.sh" "${WORK_DIR}" "$@")
|
||||
cat << EOF > "$REQUIREMENTS_FILE"
|
||||
# This file was generated by $RELATIVE_SCRIPT_PATH and can be updated using
|
||||
# that script.
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
# This script accepts no arguments and automates the process of updating
|
||||
# Certbot's dependencies including automatically updating the correct file.
|
||||
# This script automates the process of updating Certbot's dependencies
|
||||
# including automatically updating the correct file. It is usually run without
|
||||
# any arguments, but if any are provided, they will be passed to Poetry.
|
||||
# Dependencies can be pinned to older versions by modifying pyproject.toml in
|
||||
# the same directory as this file.
|
||||
set -euo pipefail
|
||||
@@ -11,7 +12,7 @@ REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||
RELATIVE_SCRIPT_PATH="$(realpath --relative-to "$REPO_ROOT" "$WORK_DIR")/$(basename "${BASH_SOURCE[0]}")"
|
||||
CONSTRAINTS_FILE="$REPO_ROOT/tools/oldest_constraints.txt"
|
||||
|
||||
PINNINGS=$("${COMMON_DIR}/export-pinned-dependencies.sh" "${WORK_DIR}")
|
||||
PINNINGS=$("${COMMON_DIR}/export-pinned-dependencies.sh" "${WORK_DIR}" "$@")
|
||||
cat << EOF > "$CONSTRAINTS_FILE"
|
||||
# This file was generated by $RELATIVE_SCRIPT_PATH and can be updated using
|
||||
# that script.
|
||||
|
||||
@@ -8,57 +8,57 @@
|
||||
alabaster==0.7.13 ; python_version >= "3.8" and python_version < "4.0"
|
||||
apacheconfig==0.3.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
appnope==0.1.3 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "darwin"
|
||||
astroid==2.13.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
asttokens==2.4.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
astroid==3.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
asttokens==2.4.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
attrs==23.1.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
azure-core==1.29.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
azure-devops==7.1.0b3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
babel==2.13.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
azure-core==1.29.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
azure-devops==7.1.0b4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
babel==2.13.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
backcall==0.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
bcrypt==4.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
bcrypt==4.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
beautifulsoup4==4.12.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
boto3==1.28.63 ; python_version >= "3.8" and python_version < "4.0"
|
||||
botocore==1.31.63 ; python_version >= "3.8" and python_version < "4.0"
|
||||
boto3==1.33.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
botocore==1.33.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cachecontrol==0.12.14 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cachetools==5.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cachetools==5.3.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cachy==0.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
certifi==2023.7.22 ; python_version >= "3.8" and python_version < "4.0"
|
||||
certifi==2023.11.17 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cffi==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
charset-normalizer==3.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
charset-normalizer==3.3.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cleo==1.0.0a5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cloudflare==2.12.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cloudflare==2.14.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
colorama==0.4.6 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32" or python_version >= "3.8" and python_version < "4.0" and platform_system == "Windows"
|
||||
configargparse==1.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
configobj==5.0.8 ; python_version >= "3.8" and python_version < "4.0"
|
||||
coverage==7.3.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
crashtest==0.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cryptography==41.0.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cryptography==41.0.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
cython==0.29.36 ; python_version >= "3.8" and python_version < "4.0"
|
||||
decorator==5.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
deprecated==1.2.14 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dill==0.3.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dill==0.3.7 ; python_version < "4.0" and python_version >= "3.8"
|
||||
distlib==0.3.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
distro==1.8.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dns-lexicon==3.15.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dns-lexicon==3.17.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dnspython==2.4.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
docutils==0.18.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
docutils==0.20.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
dulwich==0.20.50 ; python_version >= "3.8" and python_version < "4.0"
|
||||
exceptiongroup==1.1.3 ; python_version >= "3.8" and python_version < "3.11"
|
||||
exceptiongroup==1.2.0 ; python_version >= "3.8" and python_version < "3.11"
|
||||
execnet==2.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
executing==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
executing==2.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
fabric==3.2.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
filelock==3.12.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-api-core==2.12.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-api-python-client==2.103.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
filelock==3.13.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-api-core==2.14.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-api-python-client==2.109.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-auth-httplib2==0.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-auth==2.23.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
google-auth==2.24.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
googleapis-common-protos==1.61.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
html5lib==1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
httplib2==0.22.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
idna==3.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
idna==3.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
imagesize==1.4.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
importlib-metadata==4.13.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
importlib-resources==6.1.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
importlib-resources==6.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
iniconfig==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
invoke==2.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
ipdb==0.13.13 ; python_version >= "3.8" and python_version < "4.0"
|
||||
@@ -70,13 +70,12 @@ jedi==0.19.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jeepney==0.8.0 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "linux"
|
||||
jinja2==3.1.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jmespath==1.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
josepy==1.13.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
josepy==1.14.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonlines==4.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonpickle==3.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonschema-specifications==2023.7.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonschema==4.19.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
keyring==24.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
lazy-object-proxy==1.9.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonschema-specifications==2023.11.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
jsonschema==4.20.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
keyring==24.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
lockfile==0.12.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
markdown-it-py==3.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
markupsafe==2.1.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
@@ -87,16 +86,16 @@ more-itertools==10.1.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
msgpack==1.0.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
msrest==0.7.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
mypy-extensions==1.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
mypy==1.6.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
mypy==1.7.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
nh3==0.2.14 ; python_version >= "3.8" and python_version < "4.0"
|
||||
oauthlib==3.2.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
packaging==23.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
paramiko==3.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
parsedatetime==2.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
parso==0.8.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pexpect==4.8.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pexpect==4.9.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pickleshare==0.7.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pip==23.2.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pip==23.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pkginfo==1.9.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pkgutil-resolve-name==1.3.10 ; python_version >= "3.8" and python_version < "3.9"
|
||||
platformdirs==2.6.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
@@ -105,26 +104,26 @@ ply==3.11 ; python_version >= "3.8" and python_version < "4.0"
|
||||
poetry-core==1.3.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
poetry-plugin-export==1.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
poetry==1.2.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
prompt-toolkit==3.0.39 ; python_version >= "3.8" and python_version < "4.0"
|
||||
protobuf==4.24.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
prompt-toolkit==3.0.41 ; python_version >= "3.8" and python_version < "4.0"
|
||||
protobuf==4.25.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
ptyprocess==0.7.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pure-eval==0.2.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
py==1.11.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyasn1-modules==0.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyasn1==0.5.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyasn1==0.5.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pycparser==2.21 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pygments==2.16.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pygments==2.17.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pylev==1.4.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pylint==2.15.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pylint==3.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pynacl==1.5.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pynsist==2.7 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyopenssl==23.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyopenssl==23.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyotp==2.9.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyparsing==3.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pyrfc3339==1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pytest-cov==4.1.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pytest-xdist==3.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pytest==7.4.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pytest-xdist==3.5.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
pytest==7.4.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
python-augeas==1.1.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
python-dateutil==2.8.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
python-digitalocean==1.17.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
@@ -133,26 +132,26 @@ pywin32-ctypes==0.2.2 ; python_version >= "3.8" and python_version < "4.0" and s
|
||||
pywin32==306 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "win32"
|
||||
pyyaml==6.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
readme-renderer==42.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
referencing==0.30.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
referencing==0.31.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
requests-download==0.1.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
requests-file==1.5.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
requests-oauthlib==1.3.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
requests-toolbelt==0.9.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
requests==2.31.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rfc3986==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rich==13.6.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rpds-py==0.10.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rich==13.7.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rpds-py==0.13.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
rsa==4.9 ; python_version >= "3.8" and python_version < "4"
|
||||
s3transfer==0.7.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
s3transfer==0.8.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
secretstorage==3.3.3 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "linux"
|
||||
semantic-version==2.10.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
setuptools-rust==1.7.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
setuptools==67.4.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
shellingham==1.5.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
setuptools-rust==1.8.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
setuptools==69.0.2 ; python_version < "4.0" and python_version >= "3.8"
|
||||
shellingham==1.5.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
six==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
snowballstemmer==2.2.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
soupsieve==2.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinx-rtd-theme==1.3.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinx-rtd-theme==2.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinx==7.1.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinxcontrib-applehelp==1.0.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinxcontrib-devhelp==1.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
@@ -162,30 +161,30 @@ sphinxcontrib-jsmath==1.0.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinxcontrib-qthelp==1.0.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
sphinxcontrib-serializinghtml==1.1.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
stack-data==0.6.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
tldextract==5.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
tldextract==5.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
tomli==2.0.1 ; python_version >= "3.8" and python_full_version <= "3.11.0a6"
|
||||
tomlkit==0.12.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
tomlkit==0.12.3 ; python_version >= "3.8" and python_version < "4.0"
|
||||
tox==3.28.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
traitlets==5.11.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
traitlets==5.14.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
twine==4.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-httplib2==0.22.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pyopenssl==23.2.0.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pyopenssl==23.3.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pyrfc3339==1.1.1.5 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-python-dateutil==2.8.19.14 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pytz==2023.3.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pywin32==306.0.0.4 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-pywin32==306.0.0.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-requests==2.31.0.6 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-setuptools==68.2.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-setuptools==69.0.0.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-six==1.16.21.9 ; python_version >= "3.8" and python_version < "4.0"
|
||||
types-urllib3==1.26.25.14 ; python_version >= "3.8" and python_version < "4.0"
|
||||
typing-extensions==4.8.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
uritemplate==4.1.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
urllib3==1.26.17 ; python_version < "4.0" and python_version >= "3.8"
|
||||
urllib3==1.26.18 ; python_version < "4.0" and python_version >= "3.8"
|
||||
virtualenv==20.21.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wcwidth==0.2.8 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wcwidth==0.2.12 ; python_version >= "3.8" and python_version < "4.0"
|
||||
webencodings==0.5.1 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wheel==0.41.2 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wrapt==1.15.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wheel==0.42.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
wrapt==1.16.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
xattr==0.9.9 ; python_version >= "3.8" and python_version < "4.0" and sys_platform == "darwin"
|
||||
yarg==0.1.9 ; python_version >= "3.8" and python_version < "4.0"
|
||||
zipp==3.17.0 ; python_version >= "3.8" and python_version < "4.0"
|
||||
|
||||
4
tox.ini
4
tox.ini
@@ -56,10 +56,10 @@ commands =
|
||||
commands =
|
||||
{[testenv:py-win]commands} certbot-apache
|
||||
|
||||
[testenv:py3{,8,9,10,11}]
|
||||
[testenv:py3{,8,9,10,11,12}]
|
||||
commands = {[testenv:py]commands}
|
||||
|
||||
[testenv:py3.{8,9,10,11}]
|
||||
[testenv:py3.{8,9,10,11,12}]
|
||||
commands = {[testenv:py]commands}
|
||||
|
||||
[testenv:oldest]
|
||||
|
||||
@@ -22,6 +22,7 @@ setup(
|
||||
'Programming Language :: Python :: 3.9',
|
||||
'Programming Language :: Python :: 3.10',
|
||||
'Programming Language :: Python :: 3.11',
|
||||
'Programming Language :: Python :: 3.12',
|
||||
'Operating System :: Microsoft :: Windows',
|
||||
'Topic :: Software Development :: Build Tools',
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user