From bceba9b536ac0ea97e5b4b79b53383b7e380d91b Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Tue, 2 Apr 2024 00:51:56 +0200 Subject: [PATCH 1/3] =?UTF-8?q?=F0=9F=93=9D=20Integrate=20Towncrier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows tracking change log entries within pull requests. Below is the script used to convert the old semi-dynamic changelog format into a static one: #! /usr/bin/env python3 import subprocess from pathlib import Path from re import sub as _re_replace from dateutil.parser import parse as parse_timestamp def _get_scm_timestamp_for(committish): """Retrieve the tag date from SCM.""" try: ts = subprocess.check_output( ('git', 'log', '-1', '--format=%aI', committish), stderr=subprocess.DEVNULL, text=True, ).strip() except subprocess.SubprocessError: raise ValueError( f'There is no `{committish}` in Git', ) from None return parse_timestamp(ts) def _retrieve_release_date(version_tag): try: version_date = _get_scm_timestamp_for(version_tag) except (ValueError, RuntimeError): return 'no Git tag matched' else: return f'{version_date:%Y-%m-%d}' changes_rst_path = Path('CHANGES.rst') changes_rst_txt = changes_rst_path.read_text() def _replace_version(ver_match_obj) -> str: version_str = ver_match_obj.group('version_string') prefixed_version_str = f'v{version_str !s}' release_date = _retrieve_release_date(prefixed_version_str) return f'{prefixed_version_str !s}\n{"=" * len(prefixed_version_str)}\n\n*({release_date !s})*' replaced_changes_rst_txt = _re_replace(r'.. scm-version-title:: v(?P\d+.\d+.\d+)', _replace_version, changes_rst_txt) changes_rst_path.write_text(replaced_changes_rst_txt) --- .github/CONTRIBUTING.rst | 11 +- .pre-commit-config.yaml | 54 ++- CHANGES.rst | 346 ++++++++++++++---- docs/changelog-fragments.d/.gitignore | 28 ++ .../.towncrier-template.rst.j2 | 90 +++++ docs/changelog-fragments.d/565.breaking.rst | 1 + docs/changelog-fragments.d/README.rst | 116 ++++++ docs/conf.py | 42 ++- docs/contributing/guidelines.rst | 2 + docs/history.rst | 23 +- docs/scm_tag_titles_ext.py | 132 ------- .../tox-build-docs-cp310-linux-x86_64.txt | 18 +- .../tox-build-docs-cp311-linux-x86_64.txt | 17 +- .../tox-build-docs-cp312-linux-x86_64.txt | 17 +- .../tox-build-docs-cp313-linux-x86_64.txt | 17 +- .../tox-build-docs-cp38-linux-x86_64.txt | 22 +- .../tox-build-docs-cp39-linux-x86_64.txt | 24 +- requirements/tox-docs.in | 2 +- towncrier.toml | 68 ++++ 19 files changed, 777 insertions(+), 253 deletions(-) create mode 100644 docs/changelog-fragments.d/.gitignore create mode 100644 docs/changelog-fragments.d/.towncrier-template.rst.j2 create mode 100644 docs/changelog-fragments.d/565.breaking.rst create mode 100644 docs/changelog-fragments.d/README.rst delete mode 100644 docs/scm_tag_titles_ext.py create mode 100644 towncrier.toml diff --git a/.github/CONTRIBUTING.rst b/.github/CONTRIBUTING.rst index ecc607e709..71ccb52075 100644 --- a/.github/CONTRIBUTING.rst +++ b/.github/CONTRIBUTING.rst @@ -1,5 +1,6 @@ -Read and contribute to Cheroot ------------------------------- +========================= +Contributing to |project| +========================= Make sure you read the `README `_. @@ -7,7 +8,7 @@ Also **ensure you set up pre-commit utility correctly** and tests pass in GitHub Actions CI/CD workflows. Submitting Pull Requests ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ If you're changing the structure of the repository please create an issue first. Don't forget to write appropriate test cases, add them into CI process if applicable and make the GitHub Actions CI/CD build pass. @@ -16,7 +17,7 @@ Sync (preferably rebase) your feature branch with upstream regularly to make us able to merge your PR seamlessly. Submitting bug reports ----------------------- +^^^^^^^^^^^^^^^^^^^^^^ Make sure you are on latest changes and that you re-ran this command ``tox`` after updating your local repository. If you can, please provide more @@ -25,6 +26,6 @@ python version, and any other related software versions. It is also helpful to post a markdown snippet demonstrating minimum reproducible example of an issue. Also ----- +^^^^ See `Contributing `_ in the CherryPy docs. diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 198a84381e..632dc47446 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,6 +6,56 @@ ci: - stubtest repos: +- repo: local + hooks: + - id: changelogs-rst + name: changelog filenames + language: fail + entry: >- + Changelog files must be named + ####.( + bugfix + | feature + | deprecation + | breaking + | doc + | packaging + | contrib + | misc + )(.#)?(.rst)? + exclude: >- + (?x) + ^ + docs/changelog-fragments.d/( + \.gitignore + |(\d+|[0-9a-f]{8}|[0-9a-f]{7}|[0-9a-f]{40})\.( + bugfix + |feature + |deprecation + |breaking + |doc + |packaging + |contrib + |misc + )(\.\d+)?(\.rst)? + |README\.rst + |\.towncrier-template\.rst\.j2 + ) + $ + files: ^docs/changelog-fragments\.d/ + types: [] + types_or: + - file + - symlink + - id: changelogs-user-role + name: Changelog files should use a non-broken :user:`name` role + language: pygrep + entry: :user:([^`]+`?|`[^`]+[\s,]) + pass_filenames: true + types: + - file + - rst + - repo: https://github.com/asottile/add-trailing-comma.git rev: v3.0.0 hooks: @@ -27,7 +77,9 @@ repos: - id: rst-linter exclude: >- (?x) - CHANGES.rst + \.github/CONTRIBUTING\.rst + | + CHANGES\.rst | docs/ files: >- diff --git a/CHANGES.rst b/CHANGES.rst index 8e06222dfd..f4128b107d 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,15 +1,33 @@ -.. scm-version-title:: v11.0.0 +========= +Changelog +========= -- :issue:`565`: Cheroot requires Python 3.8 or later. +.. + You should *NOT* be adding new change log entries to this file, this + file is managed by towncrier. You *may* edit previous change logs to + fix problems like typo corrections or such. + To add a new change log entry, please see + https://pip.pypa.io/en/latest/development/contributing/#news-entries + we named the news folder "docs/changelog-fragments.d/". -.. scm-version-title:: v10.0.0 + WARNING: Don't drop the next directive! + +.. towncrier release notes start + +v10.0.0 +======= + +*(2023-05-20)* - :issue:`504` via :pr:`505`: Cheroot now accepts a ``reuse_port`` parameter on the ``HTTPServer`` object. Subclasses overriding ``prepare_socket`` will no longer work and will need to adapt to the new interface. -.. scm-version-title:: v9.0.0 +v9.0.0 +====== + +*(2022-11-19)* - :issue:`252` via :pr:`339`: Cheroot now requires Python 3.6 or later. Python 3.5 and Python 2.7 are still supported @@ -17,7 +35,10 @@ ` and stabilizing bugfixes will be accepted to that branch. -.. scm-version-title:: v8.6.0 +v8.6.0 +====== + +*(2022-01-03)* Significant improvements: @@ -49,7 +70,10 @@ Internal changes: lock-file. - Added per-environment lock-files into the tox test environments. -.. scm-version-title:: v8.5.2 +v8.5.2 +====== + +*(2021-01-18)* - :issue:`358` via :pr:`359`: Fixed a regression from :pr:`199` that made the worker threads exit on invalid @@ -57,7 +81,10 @@ Internal changes: unresponsive once there was no workers left. -- by :user:`cameronbrunner`. -.. scm-version-title:: v8.5.1 +v8.5.1 +====== + +*(2020-12-12)* - :cp-issue:`1873` via :pr:`340`: Resurrected an unintentionally removed feature of interrupting a server @@ -71,7 +98,10 @@ Internal changes: and :pr:`331` but reintroduced by the changes in :pr:`311` -- by :user:`liamstask`. -.. scm-version-title:: v8.5.0 +v8.5.0 +====== + +*(2020-12-05)* - :issue:`305` via :pr:`311`: In :py:class:`~cheroot.connections.ConnectionManager`, @@ -82,20 +112,29 @@ Internal changes: - :issue:`341` via :pr:`342`: Suppress legitimate OS errors expected on shutdown -- by :user:`webknjaz`. -.. scm-version-title:: v8.4.8 +v8.4.8 +====== + +*(2020-11-24)* - :issue:`317` via :pr:`337`: Fixed a regression in 8.4.5 where the connections dictionary would change size during iteration, leading to a :py:exc:`RuntimeError` raised in the logs -- by :user:`liamstask`. -.. scm-version-title:: v8.4.7 +v8.4.7 +====== + +*(2020-11-15)* - :pr:`334`: Started filtering out TLS/SSL errors when the version requested by the client is unsupported -- by :user:`sanderjo` and :user:`Safihre`. -.. scm-version-title:: v8.4.6 +v8.4.6 +====== + +*(2020-11-15)* - :issue:`328` via :pr:`322` and :pr:`331`: Fixed a regression introduced in the earlier refactoring in v8.4.4 @@ -105,7 +144,10 @@ Internal changes: :py:data:`~signal.SIGTERM` -- by :user:`marc1n` and :user:`cristicbz`. -.. scm-version-title:: v8.4.5 +v8.4.5 +====== + +*(2020-08-24)* - :issue:`312` via :pr:`313`: Fixed a regression introduced in the earlier refactoring in v8.4.4 via :pr:`309` that @@ -117,7 +159,10 @@ Internal changes: ConnectionManager.get_conn` to ensure more stability -- by :user:`cyraxjoe`. -.. scm-version-title:: v8.4.4 +v8.4.4 +====== + +*(2020-08-12)* - :issue:`304` via :pr:`309`: Refactored :py:class:`~\ cheroot.connections.ConnectionManager` to use :py:meth:`~\ @@ -129,7 +174,10 @@ Internal changes: new connections while it is being terminated -- by :user:`liamstask`. -.. scm-version-title:: v8.4.3 +v8.4.3 +====== + +*(2020-08-12)* - :pr:`282`: Fixed a race condition happening when an HTTP client attempts to reuse a persistent HTTP connection after @@ -142,7 +190,10 @@ Internal changes: helps mitigate such race conditions by letting the client know not to reuse the connection after that time interval. -.. scm-version-title:: v8.4.2 +v8.4.2 +====== + +*(2020-07-28)* - Fixed a significant performance regression introduced in v8.1.0 (:issue:`305` via :pr:`308`) - by :user:`mar10`. @@ -151,14 +202,20 @@ Internal changes: connection processing. We've lowered that delay to mitigate the problem short-term, better fix is yet to come. -.. scm-version-title:: v8.4.1 +v8.4.1 +====== + +*(2020-07-26)* - Prevent :py:exc:`ConnectionAbortedError` traceback from being printed out to the terminal output during the app start-up on Windows when built-in TLS adapter is used (:issue:`302` via :pr:`306`) - by :user:`mxii-ca`. -.. scm-version-title:: v8.4.0 +v8.4.0 +====== + +*(2020-07-23)* - Converted management from low-level :py:func:`~select.select` to high-level :py:mod:`selectors` (:issue:`249` via :pr:`301`) @@ -167,14 +224,20 @@ Internal changes: This change also introduces a conditional dependency on ``selectors2`` as a fall-back for legacy Python interpreters. -.. scm-version-title:: v8.3.1 +v8.3.1 +====== + +*(2020-07-13)* - Fixed TLS socket related unclosed resource warnings (:pr:`291` and :pr:`298`). - Made terminating keep-alive connections more graceful (:issue:`263` via :pr:`277`). -.. scm-version-title:: v8.3.0 +v8.3.0 +====== + +*(2020-02-09)* - :cp-issue:`910` via :pr:`243`: Provide TLS-related details via WSGI environment interface. @@ -182,13 +245,19 @@ Internal changes: for abstract UNIX sockets. -.. scm-version-title:: v8.2.1 +v8.2.1 +====== + +*(2019-10-17)* - :cp-issue:`1818`: Restore support for ``None`` default argument to ``WebCase.getPage()``. -.. scm-version-title:: v8.2.0 +v8.2.0 +====== + +*(2019-10-14)* - Deprecated use of negative timeouts as alias for infinite timeouts in ``ThreadPool.stop``. @@ -196,14 +265,20 @@ Internal changes: bypass URI as path if it does not appear absolute. -.. scm-version-title:: v8.1.0 +v8.1.0 +====== + +*(2019-10-09)* - Workers are now request-based, addressing the long-standing issue with keep-alive connections (:issue:`91` via :pr:`199`). -.. scm-version-title:: v8.0.0 +v8.0.0 +====== + +*(2019-10-09)* - :issue:`231` via :pr:`232`: Remove custom ``setup.cfg`` parser handling, allowing the project (including ``sdist``) @@ -213,7 +288,10 @@ Internal changes: indicated in ``pyproject.toml``). -.. scm-version-title:: v7.0.0 +v7.0.0 +====== + +*(2019-09-26)* - :pr:`224`: Refactored "open URL" behavior in :py:mod:`~cheroot.test.webtest` to rely on `retry_call @@ -222,7 +300,10 @@ Internal changes: positionally, but must pass them as keyword arguments. -.. scm-version-title:: v6.6.0 +v6.6.0 +====== + +*(2019-09-25)* - Revisit :pr:`85` under :pr:`221`. Now ``backports.functools_lru_cache`` is only @@ -231,14 +312,20 @@ Internal changes: threadpool shrink code. -.. scm-version-title:: v6.5.8 +v6.5.8 +====== + +*(2019-09-05)* - :issue:`222` via :commit:`621f4ee`: Fix :py:const:`socket.SO_PEERCRED` constant fallback value under PowerPC. -.. scm-version-title:: v6.5.7 +v6.5.7 +====== + +*(2019-09-03)* - :issue:`198` via :commit:`9f7affe`: Fix race condition when toggling stats counting in the middle of request processing. @@ -249,13 +336,19 @@ Internal changes: `_. -.. scm-version-title:: v6.5.6 +v6.5.6 +====== + +*(2019-08-19)* - :issue:`218` via :pr:`219`: Fix HTTP parser to return 400 on invalid major-only HTTP version in Request-Line. -.. scm-version-title:: v6.5.5 +v6.5.5 +====== + +*(2019-04-25)* - :issue:`99` via :pr:`186`: Sockets now collect statistics (bytes read and written) on Python 3 same as Python 2. @@ -264,7 +357,10 @@ Internal changes: under any Python while wrapping a socket. -.. scm-version-title:: v6.5.4 +v6.5.4 +====== + +*(2019-01-01)* - :issue:`113`: Fix :py:mod:`cheroot.ssl.pyopenssl` under Python 3. @@ -301,12 +397,18 @@ Internal changes: * ``PEERCREDS`` lookup -.. scm-version-title:: v6.5.3 +v6.5.3 +====== + +*(2018-12-20)* - :pr:`149`: Make ``SCRIPT_NAME`` optional per PEP 333. -.. scm-version-title:: v6.5.2 +v6.5.2 +====== + +*(2018-09-03)* - :issue:`6` via :pr:`109`: Fix import of :py:mod:`cheroot.ssl.pyopenssl` by refactoring and separating @@ -316,20 +418,29 @@ Internal changes: of :py:mod:`trustme` -.. scm-version-title:: v6.5.1 +v6.5.1 +====== + +*(2018-09-02)* - :issue:`93` via :pr:`110`: Improve UNIX socket FS access mode in :py:meth:`cheroot.server.HTTPServer.prepare` on a file socket when starting to listen to it. -.. scm-version-title:: v6.5.0 +v6.5.0 +====== + +*(2018-08-29)* - :cp-issue:`1001` via :pr:`52` and :pr:`108`: Add support for validating client certificates. -.. scm-version-title:: v6.4.0 +v6.4.0 +====== + +*(2018-08-01)* - :issue:`68` via :pr:`98`: Factor out parts of :py:meth:`cheroot.server.HTTPServer.start` into @@ -337,31 +448,46 @@ Internal changes: :py:meth:`serve() ` -.. scm-version-title:: v6.3.3 +v6.3.3 +====== + +*(2018-07-10)* - Fix bug with returning empty result in :py:meth:`cheroot.ssl.builtin.BuiltinSSLAdapter.wrap` -.. scm-version-title:: v6.3.2 +v6.3.2 +====== + +*(2018-06-16)* - :issue:`100` via :pr:`101`: Respond with HTTP 400 to malicious ``Content-Length`` in request headers. -.. scm-version-title:: v6.3.1 +v6.3.1 +====== + +*(2018-05-21)* - :cp-issue:`1618`: Ignore OpenSSL's 1.1+ Error 0 under Python 2 while wrapping a socket. -.. scm-version-title:: v6.3.0 +v6.3.0 +====== + +*(2018-05-17)* - :pr:`87`: Add ``cheroot`` command and runpy launcher to launch a WSGI app from the command-line. -.. scm-version-title:: v6.2.4 +v6.2.4 +====== + +*(2018-04-19)* - Fix missing ``resolve_peer_creds`` argument in :py:class:`cheroot.wsgi.Server` being bypassed into @@ -372,26 +498,38 @@ Internal changes: intentionally. -.. scm-version-title:: v6.2.3 +v6.2.3 +====== + +*(2018-04-14)* - :pr:`85`: Skip installing dependencies from backports namespace under Python 3. -.. scm-version-title:: v6.2.2 +v6.2.2 +====== + +*(2018-04-14)* - :issue:`84` (:cp-issue:`1704`): Fix regression, causing :py:exc:`ModuleNotFoundError` under ``cygwin``. -.. scm-version-title:: v6.2.1 +v6.2.1 +====== + +*(2018-04-10)* - :pr:`83`: Fix regression, caused by inverted check for Windows OS. - Add more URLs to distribution metadata -.. scm-version-title:: v6.2.0 +v6.2.0 +====== + +*(2018-04-10)* - :pr:`37`: Implement PEERCRED lookup over UNIX-socket HTTP connection. @@ -412,7 +550,10 @@ Internal changes: * Per-connection caching to reduce lookup cost -.. scm-version-title:: v6.1.2 +v6.1.2 +====== + +*(2018-04-08)* - :issue:`81`: Fix regression introduced by :pr:`80`. @@ -421,7 +562,10 @@ Internal changes: :py:obj:`socket.AF_UNIX` -.. scm-version-title:: v6.1.1 +v6.1.1 +====== + +*(2018-04-07)* - :pr:`80`: Fix regression introduced by :commit:`68a5769`. @@ -429,7 +573,10 @@ Internal changes: :py:attr:`cheroot.server.HTTPServer.bind_addr` -.. scm-version-title:: v6.1.0 +v6.1.0 +====== + +*(2018-04-05)* - :pr:`67`: Refactor test suite to completely rely on pytest. @@ -456,7 +603,10 @@ Internal changes: - Minor refactoring. -.. scm-version-title:: v6.0.0 +v6.0.0 +====== + +*(2017-12-04)* - Drop support for Python 2.6, 3.1, 3.2, and 3.3. @@ -464,7 +614,10 @@ Internal changes: than 2.7.9. -.. scm-version-title:: v5.11.0 +v5.11.0 +======= + +*(2017-12-04)* - :cp-issue:`1621`: To support :py:mod:`~cheroot.test.webtest` applications that feed absolute URIs to @@ -474,7 +627,10 @@ Internal changes: for calling it in a subclass. -.. scm-version-title:: v5.10.0 +v5.10.0 +======= + +*(2017-11-23)* - Minor refactorings of ``cheroot/server.py`` to reduce redundancy of behavior. @@ -484,18 +640,27 @@ Internal changes: - Restored license to BSD. -.. scm-version-title:: v5.9.2 +v5.9.2 +====== + +*(2017-11-23)* - :issue:`61`: Re-release without spurious files in the distribution. -.. scm-version-title:: v5.9.1 +v5.9.1 +====== + +*(2017-11-17)* - :issue:`58`: Reverted encoding behavior in wsgi module to correct regression in CherryPy tests. -.. scm-version-title:: v5.9.0 +v5.9.0 +====== + +*(2017-11-16)* - :cp-issue:`1088` and :pr:`53`: Avoid using SO_REUSEADDR on Windows where it has different semantics. @@ -509,7 +674,10 @@ Internal changes: - License unintentionally changed to MIT. BSD still declared and intended. -.. scm-version-title:: v5.8.3 +v5.8.3 +====== + +*(2017-08-11)* - Improve HTTP request line validation: @@ -526,14 +694,20 @@ Internal changes: - :pr:`44`: Fix EPROTOTYPE @ Mac OS -.. scm-version-title:: v5.8.2 +v5.8.2 +====== + +*(2017-08-07)* - Fix :pr:`39` regression. Add HTTP request line check: absolute URI path must start with a forward slash ("/"). -.. scm-version-title:: v5.8.1 +v5.8.1 +====== + +*(2017-08-05)* - CI improvements: @@ -545,7 +719,10 @@ Internal changes: Unicode -.. scm-version-title:: v5.8.0 +v5.8.0 +====== + +*(2017-08-01)* - CI improvements: @@ -568,7 +745,10 @@ Internal changes: * Fix decoding of Unicode URIs in WSGI 1.0 gateway -.. scm-version-title:: v5.7.0 +v5.7.0 +====== + +*(2017-06-24)* - CI improvements: @@ -605,7 +785,10 @@ Internal changes: - Cleanup :py:mod:`~cheroot._compat` functions from server module -.. scm-version-title:: v5.6.0 +v5.6.0 +====== + +*(2017-06-20)* - Fix all :pep:`257` related errors in all non-test modules. @@ -619,7 +802,10 @@ Internal changes: - :cp-issue:`1131`: Add missing size hint to SizeCheckWrapper -.. scm-version-title:: v5.5.2 +v5.5.2 +====== + +*(2017-06-18)* - :pr:`32`: Ignore ``"unknown error"`` and ``"https proxy request"`` SSL errors. @@ -629,7 +815,10 @@ Internal changes: Ref: :gh:`sabnzbd/sabnzbd#860 ` -.. scm-version-title:: v5.5.1 +v5.5.1 +====== + +*(2017-06-18)* - Make AppVeyor list separate tests in corresponding tab. @@ -646,7 +835,10 @@ Internal changes: Ref: https://forums.sabnzbd.org/viewtopic.php?f=2&t=22728&p=112251 -.. scm-version-title:: v5.5.0 +v5.5.0 +====== + +*(2017-05-02)* - :issue:`17` via :pr:`25`: Instead of a read_headers function, cheroot now supplies a :py:class:`HeaderReader ` class to @@ -665,12 +857,18 @@ Internal changes: - :pr:`26`: Configured TravisCI to run tests under OS X. -.. scm-version-title:: v5.4.0 +v5.4.0 +====== + +*(2017-03-19)* - :pr:`22`: Add "ciphers" parameter to SSLAdapter. -.. scm-version-title:: v5.3.0 +v5.3.0 +====== + +*(2017-03-12)* - :pr:`8`: Updated style to better conform to :pep:`8`. @@ -681,7 +879,10 @@ Internal changes: `_. -.. scm-version-title:: v5.2.0 +v5.2.0 +====== + +*(2017-03-02)* - :issue:`5`: Set ``Server.version`` to Cheroot version instead of CherryPy version. @@ -692,7 +893,10 @@ Internal changes: - :issue:`3`: Test suite now runs and many tests pass. Some are still failing. -.. scm-version-title:: v5.1.0 +v5.1.0 +====== + +*(2017-01-22)* - Removed the WSGI prefix from classes in :py:mod:`cheroot.wsgi`. Kept aliases for compatibility. @@ -704,12 +908,18 @@ Internal changes: cheroot distribution. -.. scm-version-title:: v5.0.1 +v5.0.1 +====== + +*(2017-01-14)* - Fix error in ``parse_request_uri`` created in :commit:`68a5769`. -.. scm-version-title:: v5.0.0 +v5.0.0 +====== + +*(2017-01-14)* - Initial release based on :gh:`cherrypy.cherrypy.wsgiserver 8.8.0 `. diff --git a/docs/changelog-fragments.d/.gitignore b/docs/changelog-fragments.d/.gitignore new file mode 100644 index 0000000000..4db39ea08a --- /dev/null +++ b/docs/changelog-fragments.d/.gitignore @@ -0,0 +1,28 @@ +* +!.gitignore +!.towncrier-template.rst.j2 +!README.rst +!*.bugfix +!*.bugfix.rst +!*.bugfix.*.rst +!*.breaking +!*.breaking.rst +!*.breaking.*.rst +!*.contrib +!*.contrib.rst +!*.contrib.*.rst +!*.deprecation +!*.deprecation.rst +!*.deprecation.*.rst +!*.doc +!*.doc.rst +!*.doc.*.rst +!*.feature +!*.feature.rst +!*.feature.*.rst +!*.misc +!*.misc.rst +!*.misc.*.rst +!*.packaging +!*.packaging.rst +!*.packaging.*.rst diff --git a/docs/changelog-fragments.d/.towncrier-template.rst.j2 b/docs/changelog-fragments.d/.towncrier-template.rst.j2 new file mode 100644 index 0000000000..2879ab2fdd --- /dev/null +++ b/docs/changelog-fragments.d/.towncrier-template.rst.j2 @@ -0,0 +1,90 @@ +{# TOWNCRIER TEMPLATE #} + +*({{ versiondata.date }})* + +{% for section, _ in sections.items() %} +{% set underline = underlines[0] %}{% if section %}{{section}} +{{ underline * section|length }}{% set underline = underlines[1] %} + +{% endif %} + +{% if sections[section] %} +{% for category, val in definitions.items() if category in sections[section]%} +{{ definitions[category]['name'] }} +{{ underline * definitions[category]['name']|length }} + +{% if definitions[category]['showcontent'] %} +{% for text, change_note_refs in sections[section][category].items() %} +- {{ text }} + + {{- '\n' * 2 -}} + + {#- + NOTE: Replacing 'e' with 'f' is a hack that prevents Jinja's `int` + NOTE: filter internal implementation from treating the input as an + NOTE: infinite float when it looks like a scientific notation (with a + NOTE: single 'e' char in between digits), raising an `OverflowError`, + NOTE: subsequently. 'f' is still a hex letter so it won't affect the + NOTE: check for whether it's a (short or long) commit hash or not. + Ref: https://github.com/pallets/jinja/issues/1921 + -#} + {%- + set pr_issue_numbers = change_note_refs + | map('lower') + | map('replace', 'e', 'f') + | map('int', default=None) + | select('integer') + | map('string') + | list + -%} + {%- set arbitrary_refs = [] -%} + {%- set commit_refs = [] -%} + {%- with -%} + {%- set commit_ref_candidates = change_note_refs | reject('in', pr_issue_numbers) -%} + {%- for cf in commit_ref_candidates -%} + {%- if cf | length in (7, 8, 40) and cf | int(default=None, base=16) is not none -%} + {%- set _ = commit_refs.append(cf) -%} + {%- else -%} + {%- set _ = arbitrary_refs.append(cf) -%} + {%- endif -%} + {%- endfor -%} + {%- endwith -%} + + {% if pr_issue_numbers %} + *Related issues and pull requests on GitHub:* + :issue:`{{ pr_issue_numbers | join('`, :issue:`') }}`. + {{- '\n' * 2 -}} + {%- endif -%} + + {% if commit_refs %} + *Related commits on GitHub:* + :commit:`{{ commit_refs | join('`, :commit:`') }}`. + {{- '\n' * 2 -}} + {%- endif -%} + + {% if arbitrary_refs %} + *Unlinked references:* + {{ arbitrary_refs | join(', ') }}. + {{- '\n' * 2 -}} + {%- endif -%} + +{% endfor %} +{% else %} +- {{ sections[section][category]['']|join(', ') }} + +{% endif %} +{% if sections[section][category]|length == 0 %} +No significant changes. + +{% else %} +{% endif %} + +{% endfor %} +{% else %} +No significant changes. + + +{% endif %} +{% endfor %} +---- +{{ '\n' * 2 }} diff --git a/docs/changelog-fragments.d/565.breaking.rst b/docs/changelog-fragments.d/565.breaking.rst new file mode 100644 index 0000000000..3769d3e442 --- /dev/null +++ b/docs/changelog-fragments.d/565.breaking.rst @@ -0,0 +1 @@ +Cheroot requires Python 3.8 or later. diff --git a/docs/changelog-fragments.d/README.rst b/docs/changelog-fragments.d/README.rst new file mode 100644 index 0000000000..a6e1a254c5 --- /dev/null +++ b/docs/changelog-fragments.d/README.rst @@ -0,0 +1,116 @@ +.. _Adding change notes with your PRs: + +Adding change notes with your PRs +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is very important to maintain a log for news of how +updating to the new version of the software will affect +end-users. This is why we enforce collection of the change +fragment files in pull requests as per `Towncrier philosophy`_. + +The idea is that when somebody makes a change, they must record +the bits that would affect end-users only including information +that would be useful to them. Then, when the maintainers publish +a new release, they'll automatically use these records to compose +a change log for the respective version. It is important to +understand that including unnecessary low-level implementation +related details generates noise that is not particularly useful +to the end-users most of the time. And so such details should be +recorded in the Git history rather than a changelog. + +Alright! So how do I add a news fragment? +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +*Cheroot* uses `towncrier `_ +for changelog management. +To submit a change note about your PR, add a text file into the +``docs/changelog-fragments.d/`` folder. It should contain an +explanation of what applying this PR will change in the way +end-users interact with the project. One sentence is usually +enough but feel free to add as many details as you feel necessary +for the users to understand what it means. + +**Use the past tense** for the text in your fragment because, +combined with others, it will be a part of the "news digest" +telling the readers **what changed** in a specific version of +the library *since the previous version*. You should also use +*reStructuredText* syntax for highlighting code (inline or block), +linking parts of the docs or external sites. +However, you do not need to reference the issue or PR numbers here +as *towncrier* will automatically add a reference to all of the +affected issues when rendering the news file. +If you wish to sign your change, feel free to add ``-- by +:user:`github-username``` at the end (replace ``github-username`` +with your own!). + +Finally, name your file following the convention that Towncrier +understands: it should start with the number of an issue or a +PR followed by a dot, then add a patch type, like ``feature``, +``doc``, ``contrib`` etc., and add ``.rst`` as a suffix. If you +need to add more than one fragment, you may add an optional +sequence number (delimited with another period) between the type +and the suffix. + +In general the name will follow ``..rst`` pattern, +where the categories are: + +- ``bugfix``: A bug fix for something we deemed an improper undesired + behavior that got corrected in the release to match pre-agreed + expectations. +- ``feature``: A new behavior, public APIs. That sort of stuff. +- ``deprecation``: A declaration of future API removals and breaking + changes in behavior. +- ``breaking``: When something public gets removed in a breaking way. + Could be deprecated in an earlier release. +- ``doc``: Notable updates to the documentation structure or build + process. +- ``packaging``: Notes for downstreams about unobvious side effects + and tooling. Changes in the test invocation considerations and + runtime assumptions. +- ``contrib``: Stuff that affects the contributor experience. e.g. + Running tests, building the docs, setting up the development + environment. +- ``misc``: Changes that are hard to assign to any of the above + categories. + +A pull request may have more than one of these components, for example +a code change may introduce a new feature that deprecates an old +feature, in which case two fragments should be added. It is not +necessary to make a separate documentation fragment for documentation +changes accompanying the relevant code changes. + +Examples for adding changelog entries to your Pull Requests +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +File :file:`docs/changelog-fragments.d/77.doc.rst`: + +.. code-block:: rst + + Fixed a WSGI documentation example to support Python 3 + -- by :user:`jaymcgrath`. + +File :file:`docs/changelog-fragments.d/384.feature.rst` (could be symlinked +to :file:`docs/changelog-fragments.d/384.doc.rst` so it shows up in several +changelog sections, and to :file:`docs/changelog-fragments.d/385.feature.rst` +and :file:`docs/changelog-fragments.d/406.feature.rst` referencing several +pull requests at once): + +.. code-block:: rst + + Exposed type stubs with annotations for public API -- by :user:`kasium`. + +File :file:`docs/changelog-fragments.d/359.bugfix.rst`: + +.. code-block:: rst + + Fixed a regression from :pr:`199` that made the worker threads exit on + invalid connection attempts and could make the whole server unresponsive + once there was no workers left -- by :user:`cameronbrunner`. + +.. tip:: + + See :file:`towncrier.toml` for all available categories + (``tool.towncrier.type``). + +.. _Towncrier philosophy: + https://towncrier.readthedocs.io/en/stable/#philosophy diff --git a/docs/conf.py b/docs/conf.py index 2cc04ee577..5cf155d845 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -3,15 +3,28 @@ # pylint: disable=invalid-name """Configuration of Sphinx documentation generator.""" -from pathlib import Path +import os import sys +from pathlib import Path + +# -- Path setup -------------------------------------------------------------- + +PROJECT_ROOT_DIR = Path(__file__).parents[1].resolve() +IS_RELEASE_ON_RTD = ( + os.getenv('READTHEDOCS', 'False') == 'True' + and os.environ['READTHEDOCS_VERSION_TYPE'] == 'tag' +) +if IS_RELEASE_ON_RTD: + tags: set[str] + # pylint: disable-next=used-before-assignment + tags.add('is_release') # noqa: F821 # Make in-tree extension importable in non-tox setups/envs, like RTD. # Refs: # https://github.com/readthedocs/readthedocs.org/issues/6311 # https://github.com/readthedocs/readthedocs.org/issues/7182 -sys.path.insert(0, str(Path(__file__).parent.resolve())) +sys.path.insert(0, str(PROJECT_ROOT_DIR)) extensions = [ @@ -23,6 +36,7 @@ 'jaraco.packaging.sphinx', 'sphinx_tabs.tabs', 'sphinxcontrib.apidoc', + 'sphinxcontrib.towncrier.ext', # provides `.. towncrier-draft-entries::` ] # Conditional third-party extensions: @@ -34,12 +48,16 @@ del _sphinxcontrib_spelling # noqa: WPS100 extensions.append('sphinxcontrib.spelling') -# Tree-local extensions: -extensions.append('scm_tag_titles_ext') - # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + 'changelog-fragments.d/**', # Towncrier-managed change notes +] + master_doc = 'index' apidoc_excluded_paths = [] @@ -63,11 +81,6 @@ 'spelling_wordlist.txt', ] -scm_version_title_settings = { - 'scm': 'git', - 'date_format': '%d %b %Y', -} - github_url = 'https://github.com' github_repo_org = 'cherrypy' github_repo_name = 'cheroot' @@ -138,6 +151,15 @@ ('py:class', 'pyopenssl:OpenSSL.SSL.Context'), ] +# -- Options for towncrier_draft extension ----------------------------------- + +# or: 'sphinx-version', 'sphinx-release' +towncrier_draft_autoversion_mode = 'draft' +towncrier_draft_include_empty = True +towncrier_draft_working_directory = PROJECT_ROOT_DIR +towncrier_draft_config_path = 'towncrier.toml' # relative to cwd + + # Ref: # * https://github.com/djungelorm/sphinx-tabs/issues/26#issuecomment-422160463 sphinx_tabs_valid_builders = ['linkcheck'] # prevent linkcheck warning diff --git a/docs/contributing/guidelines.rst b/docs/contributing/guidelines.rst index 58320369f7..4a03e0837f 100644 --- a/docs/contributing/guidelines.rst +++ b/docs/contributing/guidelines.rst @@ -134,3 +134,5 @@ After that, you can open `http://localhost:8000/ `_ in y Read more about `Sphinx`_. .. _Sphinx: https://www.sphinx-doc.org + +.. include:: ../changelog-fragments.d/README.rst diff --git a/docs/history.rst b/docs/history.rst index c3a2c4af00..b58a7923cf 100644 --- a/docs/history.rst +++ b/docs/history.rst @@ -4,9 +4,24 @@ reproducibility -.. _changes: +********** +Change log +********** -History -******* +.. only:: not is_release -.. include:: ../CHANGES.rst + *To be included in the next release* + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + + .. towncrier-draft-entries:: |release| :sub:`/UNRELEASED DRAFT/` + + Released versions + ^^^^^^^^^^^^^^^^^ + + .. include:: ../CHANGES.rst + :start-after: .. towncrier release notes start + +.. only:: is_release + + .. include:: ../CHANGES.rst + :start-after: .. towncrier release notes start diff --git a/docs/scm_tag_titles_ext.py b/docs/scm_tag_titles_ext.py deleted file mode 100644 index 2362cb3b19..0000000000 --- a/docs/scm_tag_titles_ext.py +++ /dev/null @@ -1,132 +0,0 @@ -#! /usr/bin/env python3 -# Requires Python 3.6+ -"""Sphinx extension for making titles with dates from Git tags.""" - - -import subprocess -from typing import List - -from sphinx.application import Sphinx -from sphinx.util.docutils import SphinxDirective -from sphinx.util.nodes import nodes - -import dateutil.parser - -from cheroot import __version__ - - -_SCM_COMMANDS = { - 'hg': ( - 'hg', 'log', - '-l', '1', - '--template', '{date|isodate}', - '-r', - ), - 'git': ( - 'git', 'log', - '-1', '--format=%aI', - ), -} - - -def _get_scm_timestamp_for(commitish, *, scm=None): - """Retrieve the tag date from SCM.""" - if scm is None: - scm = 'git' - - try: - ts = subprocess.check_output( - _SCM_COMMANDS[scm] + (commitish,), - stderr=subprocess.DEVNULL, - text=True, - ).strip() - except subprocess.SubprocessError: - raise ValueError( - f'There is no `{commitish}` in {scm.title()}', - ) from None - - return dateutil.parser.parse(ts) - - -class version_subtitle(nodes.subtitle, nodes.Element): - """Version subtitle node.""" - - -def _visit_version_subtitle(self, node): - """Inject an opening tag.""" - self.visit_title(node) - - -def _depart_version_subtitle(self, node): - """Inject a closing tag.""" - self.depart_title(node) - - -class SCMVersionTitle(SphinxDirective): - """Definition of the scm-version-title directive.""" - - has_content = True # False - - def run(self) -> List[nodes.Node]: - """Generate the node tree in place of the directive.""" - env = self.state.document.settings.env - ext_conf = env.config.scm_version_title_settings - # conf_py_path = pathlib.Path(conf._raw_config['__file__']) - - self.assert_has_content() - first_line = self.content[:1] - inner_content = self.content[1:] - - version_tag = ''.join(first_line) - try: - version_date = _get_scm_timestamp_for( - version_tag, - scm=ext_conf['scm'], - ) - except (ValueError, RuntimeError): - release_date = '(no Git tag matched)' - else: - release_date = f'{{:{ext_conf["date_format"]}}}'.format( - version_date, - ) - - release_section = nodes.section() - release_section.tagname = 'div' - release_section['ids'] = [version_tag.replace('.', '-')] - release_section['class'] = ['section'] - - release_section += version_subtitle(version_tag, version_tag) - release_section += nodes.paragraph(release_date, release_date) - - self.state.nested_parse( - inner_content, self.content_offset, - release_section, - ) - return [release_section] - - -def setup(app: Sphinx) -> None: - """Initialize the extension.""" - app.add_config_value( - 'scm_version_title_settings', - { - 'scm': 'git', - 'date_format': '%d %b %Y', - }, - 'html', - ) - app.add_node( - version_subtitle, - html=( - _visit_version_subtitle, - _depart_version_subtitle, - ), - ) - app.add_directive( - 'scm-version-title', SCMVersionTitle, - ) - - return { - 'parallel_read_safe': True, - 'version': __version__, - } diff --git a/requirements/tox-build-docs-cp310-linux-x86_64.txt b/requirements/tox-build-docs-cp310-linux-x86_64.txt index e3c45cb77c..988cfe03b0 100644 --- a/requirements/tox-build-docs-cp310-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp310-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -56,6 +58,8 @@ idna==3.6 # trustme imagesize==1.4.1 # via sphinx +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -75,7 +79,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -153,8 +159,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp310-linux-x86_64.in pytz==2024.1 # via tempora requests==2.31.0 @@ -169,8 +173,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -183,6 +185,7 @@ sphinx==7.2.6 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -201,6 +204,8 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp310-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 @@ -212,6 +217,9 @@ tomli==2.0.1 # build # coverage # pyproject-hooks + # towncrier +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 diff --git a/requirements/tox-build-docs-cp311-linux-x86_64.txt b/requirements/tox-build-docs-cp311-linux-x86_64.txt index 913b1b5876..2ebaf91ecd 100644 --- a/requirements/tox-build-docs-cp311-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp311-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -56,6 +58,8 @@ idna==3.6 # trustme imagesize==1.4.1 # via sphinx +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -75,7 +79,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -153,8 +159,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp311-linux-x86_64.in pytz==2024.1 # via tempora requests==2.31.0 @@ -169,8 +173,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -183,6 +185,7 @@ sphinx==7.2.6 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -201,12 +204,16 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp311-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 # via pytest-sugar toml==0.10.2 # via pytest +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 diff --git a/requirements/tox-build-docs-cp312-linux-x86_64.txt b/requirements/tox-build-docs-cp312-linux-x86_64.txt index f5d261eefe..91140deb9c 100644 --- a/requirements/tox-build-docs-cp312-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp312-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -56,6 +58,8 @@ idna==3.6 # trustme imagesize==1.4.1 # via sphinx +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -75,7 +79,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -153,8 +159,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp312-linux-x86_64.in pytz==2024.1 # via tempora requests==2.31.0 @@ -169,8 +173,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -183,6 +185,7 @@ sphinx==7.2.6 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -201,12 +204,16 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp312-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 # via pytest-sugar toml==0.10.2 # via pytest +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 diff --git a/requirements/tox-build-docs-cp313-linux-x86_64.txt b/requirements/tox-build-docs-cp313-linux-x86_64.txt index df32aced81..8fbaf5dcbb 100644 --- a/requirements/tox-build-docs-cp313-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp313-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -56,6 +58,8 @@ idna==3.6 # trustme imagesize==1.4.1 # via sphinx +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -75,7 +79,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -153,8 +159,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp313-linux-x86_64.in pytz==2024.1 # via tempora requests==2.31.0 @@ -169,8 +173,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -183,6 +185,7 @@ sphinx==7.2.6 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -201,12 +204,16 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp313-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 # via pytest-sugar toml==0.10.2 # via pytest +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 diff --git a/requirements/tox-build-docs-cp38-linux-x86_64.txt b/requirements/tox-build-docs-cp38-linux-x86_64.txt index 09a547899a..8a8732e096 100644 --- a/requirements/tox-build-docs-cp38-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp38-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -63,7 +65,11 @@ importlib-metadata==7.1.0 # sphinx # typeguard importlib-resources==6.4.0 - # via jaraco-text + # via + # jaraco-text + # towncrier +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -83,7 +89,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -161,8 +169,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp38-linux-x86_64.in pytz==2024.1 # via # babel @@ -179,8 +185,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -193,6 +197,7 @@ sphinx==7.1.2 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -211,6 +216,8 @@ sphinxcontrib-qthelp==1.0.3 # via sphinx sphinxcontrib-serializinghtml==1.1.5 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp38-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 @@ -222,6 +229,9 @@ tomli==2.0.1 # build # coverage # pyproject-hooks + # towncrier +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 diff --git a/requirements/tox-build-docs-cp39-linux-x86_64.txt b/requirements/tox-build-docs-cp39-linux-x86_64.txt index e53ad65669..ba4f91850f 100644 --- a/requirements/tox-build-docs-cp39-linux-x86_64.txt +++ b/requirements/tox-build-docs-cp39-linux-x86_64.txt @@ -24,6 +24,8 @@ chardet==5.2.0 # via requests charset-normalizer==3.3.2 # via requests +click==8.1.7 + # via towncrier colorama==0.4.6 # via pytest-watch coverage==7.4.4 @@ -61,6 +63,10 @@ importlib-metadata==7.1.0 # build # sphinx # typeguard +importlib-resources==6.4.0 + # via towncrier +incremental==22.10.0 + # via towncrier inflect==7.2.0 # via jaraco-text iniconfig==2.0.0 @@ -80,7 +86,9 @@ jaraco-packaging==9.5.0 jaraco-text==3.12.0 # via -r requirements/tests.in jinja2==3.1.3 - # via sphinx + # via + # sphinx + # towncrier markdown-it-py==3.0.0 # via rich markupsafe==2.1.5 @@ -158,8 +166,6 @@ pytest-watch==4.2.0 # via -r requirements/tests.in pytest-xdist==3.5.0 # via -r requirements/tests.in -python-dateutil==2.9.0.post0 - # via -r requirements/tox-build-docs-cp39-linux-x86_64.in pytz==2024.1 # via tempora requests==2.31.0 @@ -174,8 +180,6 @@ requests-unixsocket==0.3.0 # via -r requirements/tests.in rich==13.7.1 # via pytest-clarity -six==1.16.0 - # via python-dateutil snowballstemmer==2.2.0 # via sphinx soupsieve==2.5 @@ -188,6 +192,7 @@ sphinx==7.2.6 # sphinx-basic-ng # sphinx-tabs # sphinxcontrib-apidoc + # sphinxcontrib-towncrier sphinx-basic-ng==1.0.0b2 # via furo sphinx-tabs==3.4.5 @@ -206,6 +211,8 @@ sphinxcontrib-qthelp==1.0.7 # via sphinx sphinxcontrib-serializinghtml==1.1.10 # via sphinx +sphinxcontrib-towncrier==0.4.0a0 + # via -r requirements/tox-build-docs-cp39-linux-x86_64.in tempora==5.5.1 # via portend termcolor==2.4.0 @@ -217,6 +224,9 @@ tomli==2.0.1 # build # coverage # pyproject-hooks + # towncrier +towncrier==23.11.0 + # via sphinxcontrib-towncrier trustme==1.1.0 # via -r requirements/tests.in typeguard==4.2.1 @@ -235,4 +245,6 @@ virtualenv==20.25.1 watchdog==4.0.0 # via pytest-watch zipp==3.18.1 - # via importlib-metadata + # via + # importlib-metadata + # importlib-resources diff --git a/requirements/tox-docs.in b/requirements/tox-docs.in index 2eb8a7de47..42ccf0d343 100644 --- a/requirements/tox-docs.in +++ b/requirements/tox-docs.in @@ -5,5 +5,5 @@ jaraco.packaging >= 3.2 sphinx-tabs >= 1.1.0 furo -python-dateutil # `scm_tag_titles_ext` extension dep in docs/ sphinxcontrib-apidoc >= 0.3.0 +sphinxcontrib-towncrier diff --git a/towncrier.toml b/towncrier.toml new file mode 100644 index 0000000000..85b514f825 --- /dev/null +++ b/towncrier.toml @@ -0,0 +1,68 @@ +[tool.towncrier] + package = "cheroot" + filename = "CHANGES.rst" + directory = "docs/changelog-fragments.d/" + title_format = "v{version}" + template = "docs/changelog-fragments.d/.towncrier-template.rst.j2" + issue_format = "{issue}" + + # NOTE: The types are declared because: + # NOTE: - there is no mechanism to override just the value of + # NOTE: `tool.towncrier.type.misc.showcontent`; + # NOTE: - and, we want to declare extra non-default types for + # NOTE: clarity and flexibility. + + [[tool.towncrier.section]] + path = "" + + [[tool.towncrier.type]] + # Something we deemed an improper undesired behavior that got corrected + # in the release to match pre-agreed expectations. + directory = "bugfix" + name = "Bug fixes" + showcontent = true + + [[tool.towncrier.type]] + # New behaviors, public APIs. That sort of stuff. + directory = "feature" + name = "Features" + showcontent = true + + [[tool.towncrier.type]] + # Declarations of future API removals and breaking changes in behavior. + directory = "deprecation" + name = "Deprecations (removal in next major release)" + showcontent = true + + [[tool.towncrier.type]] + # When something public gets removed in a breaking way. Could be + # deprecated in an earlier release. + directory = "breaking" + name = "Removals and backward incompatible breaking changes" + showcontent = true + + [[tool.towncrier.type]] + # Notable updates to the documentation structure or build process. + directory = "doc" + name = "Improved documentation" + showcontent = true + + [[tool.towncrier.type]] + # Notes for downstreams about unobvious side effects and tooling. Changes + # in the test invocation considerations and runtime assumptions. + directory = "packaging" + name = "Packaging updates and notes for downstreams" + showcontent = true + + [[tool.towncrier.type]] + # Stuff that affects the contributor experience. e.g. Running tests, + # building the docs, setting up the development environment. + directory = "contrib" + name = "Contributor-facing changes" + showcontent = true + + [[tool.towncrier.type]] + # Changes that are hard to assign to any of the above categories. + directory = "misc" + name = "Miscellaneous internal changes" + showcontent = true From d76e62a7a576b0784c7de4ea923a2f3d2712e052 Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Thu, 4 Apr 2024 16:32:20 +0200 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=93=9D=F0=9F=8E=A8=20Mark=20versions?= =?UTF-8?q?=20as=20known=20word=20spellings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, `sphinxcontrib.spelling` would complain about `dev` and two/three-letter local version identifier segments as misspelled[[1]]. [1]: https://github.com/sphinx-contrib/spelling/issues/224 --- docs/conf.py | 12 +++------- docs/spelling_stub_ext.py | 48 ++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5cf155d845..a5103f5cd8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -37,16 +37,10 @@ 'sphinx_tabs.tabs', 'sphinxcontrib.apidoc', 'sphinxcontrib.towncrier.ext', # provides `.. towncrier-draft-entries::` -] -# Conditional third-party extensions: -try: - import sphinxcontrib.spelling as _sphinxcontrib_spelling -except ImportError: - extensions.append('spelling_stub_ext') -else: - del _sphinxcontrib_spelling # noqa: WPS100 - extensions.append('sphinxcontrib.spelling') + # In-tree extensions: + 'spelling_stub_ext', # auto-loads `sphinxcontrib.spelling` if installed +] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] diff --git a/docs/spelling_stub_ext.py b/docs/spelling_stub_ext.py index f04c55ded6..0eda52b3f6 100644 --- a/docs/spelling_stub_ext.py +++ b/docs/spelling_stub_ext.py @@ -3,11 +3,54 @@ from typing import List from sphinx.application import Sphinx +from sphinx.config import Config as _SphinxConfig +from sphinx.util import logging from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import nodes from cheroot import __version__ +try: + from enchant.tokenize import ( # noqa: WPS433 + Filter as _EnchantTokenizeFilterBase, + ) +except ImportError: + _EnchantTokenizeFilterBase = object # noqa: WPS440 + + +logger = logging.getLogger(__name__) + + +def _configure_spelling_ext(app: Sphinx, config: _SphinxConfig) -> None: + class VersionFilter(_EnchantTokenizeFilterBase): # noqa: WPS431 + # NOTE: It's nested because we need to reference the config by closure. + """Filter for treating version words as known.""" + + def _skip(self, word: str) -> bool: + # NOTE: Only accessing the config values in the method since they + # NOTE: aren't yet populated when the config-inited event happens. + known_version_words = { + config.release, + config.version, + __version__, + } + if word not in known_version_words: + return False + + logger.debug( + 'Known version words: %r', # noqa: WPS323 + known_version_words, + ) + logger.debug( + 'Ignoring %r because it is a known version', # noqa: WPS323 + word, + ) + + return True + + app.config.spelling_filters = [VersionFilter] + app.setup_extension('sphinxcontrib.spelling') + class SpellingNoOpDirective(SphinxDirective): """Definition of the stub spelling directive.""" @@ -21,7 +64,10 @@ def run(self) -> List[nodes.Node]: def setup(app: Sphinx) -> None: """Initialize the extension.""" - app.add_directive('spelling', SpellingNoOpDirective) + if _EnchantTokenizeFilterBase is object: + app.add_directive('spelling', SpellingNoOpDirective) + else: + app.connect('config-inited', _configure_spelling_ext) return { 'parallel_read_safe': True, From f8babb3ae135fd36cd7d5d5a38c91ae2814e6a9b Mon Sep 17 00:00:00 2001 From: Sviatoslav Sydorenko Date: Wed, 3 Apr 2024 02:19:40 +0200 Subject: [PATCH 3/3] =?UTF-8?q?=F0=9F=A7=AA=20Integrate=20changelog=20gene?= =?UTF-8?q?ration=20into=20CI/CD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This includes GitHub Actions CI/CD, tox and lockfiles. --- .github/workflows/ci-cd.yml | 308 ++++++++++++++++-- docs/spelling_wordlist.txt | 4 + ...tox-check-changelog-cp310-linux-x86_64.txt | 1 + ...tox-check-changelog-cp311-linux-x86_64.txt | 1 + ...tox-check-changelog-cp312-linux-x86_64.txt | 1 + ...tox-check-changelog-cp313-linux-x86_64.txt | 1 + .../tox-check-changelog-cp38-linux-x86_64.txt | 1 + .../tox-check-changelog-cp39-linux-x86_64.txt | 1 + ...tox-draft-changelog-cp310-linux-x86_64.txt | 1 + ...tox-draft-changelog-cp311-linux-x86_64.txt | 1 + ...tox-draft-changelog-cp312-linux-x86_64.txt | 1 + ...tox-draft-changelog-cp313-linux-x86_64.txt | 1 + .../tox-draft-changelog-cp38-linux-x86_64.txt | 1 + .../tox-draft-changelog-cp39-linux-x86_64.txt | 1 + .../tox-make-changelog-cp310-linux-x86_64.txt | 1 + .../tox-make-changelog-cp311-linux-x86_64.txt | 1 + .../tox-make-changelog-cp312-linux-x86_64.txt | 1 + .../tox-make-changelog-cp313-linux-x86_64.txt | 1 + .../tox-make-changelog-cp38-linux-x86_64.txt | 1 + .../tox-make-changelog-cp39-linux-x86_64.txt | 1 + tox.ini | 81 +++++ 21 files changed, 389 insertions(+), 22 deletions(-) create mode 120000 requirements/tox-check-changelog-cp310-linux-x86_64.txt create mode 120000 requirements/tox-check-changelog-cp311-linux-x86_64.txt create mode 120000 requirements/tox-check-changelog-cp312-linux-x86_64.txt create mode 120000 requirements/tox-check-changelog-cp313-linux-x86_64.txt create mode 120000 requirements/tox-check-changelog-cp38-linux-x86_64.txt create mode 120000 requirements/tox-check-changelog-cp39-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp310-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp311-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp312-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp313-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp38-linux-x86_64.txt create mode 120000 requirements/tox-draft-changelog-cp39-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp310-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp311-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp312-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp313-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp38-linux-x86_64.txt create mode 120000 requirements/tox-make-changelog-cp39-linux-x86_64.txt diff --git a/.github/workflows/ci-cd.yml b/.github/workflows/ci-cd.yml index b98d6de79a..a6fb96e4f6 100644 --- a/.github/workflows/ci-cd.yml +++ b/.github/workflows/ci-cd.yml @@ -142,6 +142,11 @@ jobs: git-tag: ${{ steps.git-tag.outputs.tag }} sdist-artifact-name: ${{ steps.artifact-name.outputs.sdist }} wheel-artifact-name: ${{ steps.artifact-name.outputs.wheel }} + changelog-patch-name: ${{ steps.changelog-patch-name.outputs.filename }} + changelog-draft-name-md: >- + ${{ steps.changelog-draft-name.outputs.filename-base }}.md + changelog-draft-name-rst: >- + ${{ steps.changelog-draft-name.outputs.filename-base }}.rst steps: - name: Switch to using Python 3.11 by default uses: actions/setup-python@v5 @@ -296,6 +301,10 @@ jobs: mode=FILE_APPEND_MODE, ) as outputs_file: print(f'dist-version={ver}', file=outputs_file) + print( + f'dist-version-for-filenames={ver.replace("+", "-")}', + file=outputs_file, + ) - name: Set the target Git tag id: git-tag run: | @@ -342,6 +351,228 @@ jobs: }}-py3-none-any.whl", file=outputs_file, ) + - name: Set the expected changelog patch filename + id: changelog-patch-name + run: | + from os import environ + from pathlib import Path + + FILE_APPEND_MODE = 'a' + + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print('filename=0001-Generate-a-changelog-entry-for-v${{ + steps.request-check.outputs.release-requested == 'true' + && github.event.inputs.release-version + || steps.scm-version.outputs.dist-version-for-filenames + }}.patch', file=outputs_file) + - name: Set the expected changelog draft filename + id: changelog-draft-name + run: | + from os import environ + from pathlib import Path + + FILE_APPEND_MODE = 'a' + + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print('filename-base=change-notes-v${{ + steps.request-check.outputs.release-requested == 'true' + && github.event.inputs.release-version + || steps.scm-version.outputs.dist-version-for-filenames + }}', file=outputs_file) + + build-changelog: + name: >- + 👷📝 ${{ needs.pre-setup.outputs.git-tag }} changelog + [mode: ${{ + fromJSON(needs.pre-setup.outputs.is-untagged-devel) + && 'nightly' || '' + }}${{ + fromJSON(needs.pre-setup.outputs.release-requested) + && 'release' || '' + }}${{ + ( + !fromJSON(needs.pre-setup.outputs.is-untagged-devel) + && !fromJSON(needs.pre-setup.outputs.release-requested) + ) && 'test' || '' + }}] + needs: + - pre-setup + runs-on: ubuntu-latest + + env: + TOXENV: make-changelog + + steps: + - name: Switch to using Python 3.11 + uses: actions/setup-python@v5.0.0 + with: + python-version: 3.11 + + - name: Grab the source from Git + uses: actions/checkout@v4.1.1 + with: + fetch-depth: 1 # Enough for this job to generate the changelog + ref: ${{ github.event.inputs.release-committish }} + + - name: >- + Calculate Python interpreter version hash value + for use in the cache key + id: calc-cache-key-py + run: | + from hashlib import sha512 + from os import environ + from pathlib import Path + from sys import version + + FILE_APPEND_MODE = 'a' + + hash = sha512(version.encode()).hexdigest() + + with Path(environ['GITHUB_OUTPUT']).open( + mode=FILE_APPEND_MODE, + ) as outputs_file: + print(f'py-hash-key={hash}', file=outputs_file) + shell: python + - name: Set up pip cache + uses: actions/cache@v4.0.0 + with: + path: >- + ${{ + runner.os == 'Linux' + && '~/.cache/pip' + || '~/Library/Caches/pip' + }} + key: >- + ${{ runner.os }}-pip-${{ + steps.calc-cache-key-py.outputs.py-hash-key }}-${{ + needs.pre-setup.outputs.cache-key-files }} + restore-keys: | + ${{ runner.os }}-pip-${{ + steps.calc-cache-key-py.outputs.py-hash-key + }}- + ${{ runner.os }}-pip- + ${{ runner.os }}- + - name: Install tox + run: >- + python -m + pip install + --user + '${{ env.TOX_VERSION }}' + + - name: Pre-populate the tox env + run: >- + python -m + tox + --parallel auto + --parallel-live + --skip-missing-interpreters false + --notest + + - name: Drop Git tags from HEAD for non-tag-create events + if: >- + !fromJSON(needs.pre-setup.outputs.release-requested) + run: >- + git tag --points-at HEAD + | + xargs git tag --delete + shell: bash + + - name: Setup git user as [bot] + # Refs: + # * https://github.community/t/github-actions-bot-email-address/17204/6 + # * https://github.com/actions/checkout/issues/13#issuecomment-724415212 + uses: fregante/setup-git-user@v2.0.1 + + - name: Generate changelog draft to a temporary file + run: >- + 2>/dev/null + python -m + tox + --skip-missing-interpreters false + --skip-pkg-install + -qq + -- + '${{ needs.pre-setup.outputs.dist-version }}' + --draft + | + tee + '${{ needs.pre-setup.outputs.changelog-draft-name-rst }}' + shell: bash + - name: Sanitize the markdown changelog version + run: >- + sed + -i + -e 's/:commit:`\([0-9a-f]\+\)`/${{ + '' + }}https:\/\/github.com\/cherrypy\/cheroot\/commit\/\1/g' + -e 's/:gh:`\([-.a-zA-Z0-9]\+\)`/https:\/\/github.com\/\1/g' + -e 's/:\(issue\|pr\):`\([0-9]\+\)`/#\2/g' + -e 's/:user:`\([-.a-zA-Z0-9]\+\)`/@\1/g' + '${{ needs.pre-setup.outputs.changelog-draft-name-rst }}' + shell: bash + - name: Install pandoc via apt + run: sudo apt install -y pandoc + - name: >- + Convert ${{ needs.pre-setup.outputs.changelog-draft-name-rst }} + into ${{ needs.pre-setup.outputs.changelog-draft-name-md }} + with a native pandoc run + run: >- + pandoc + --from=rst + --to=gfm + --output='${{ needs.pre-setup.outputs.changelog-draft-name-md }}' + '${{ needs.pre-setup.outputs.changelog-draft-name-rst }}' + - name: Render the changelog draft in the GitHub Job Summary + run: | + echo "# Changelog for ${{ + needs.pre-setup.outputs.git-tag + }}" >> "${GITHUB_STEP_SUMMARY}" + echo >> "${GITHUB_STEP_SUMMARY}" + echo >> "${GITHUB_STEP_SUMMARY}" + cat '${{ + needs.pre-setup.outputs.changelog-draft-name-md + }}' >> "${GITHUB_STEP_SUMMARY}" + shell: bash + - name: Generate changelog update with tox and stage it in Git + run: >- + python -m + tox + --parallel auto + --parallel-live + --skip-missing-interpreters false + --skip-pkg-install + -- + '${{ needs.pre-setup.outputs.dist-version }}' + --yes + - name: >- + Commit the changelog updates for release + ${{ needs.pre-setup.outputs.git-tag }} in the local Git repo + run: >- + git commit -m + 'Generate a changelog entry for ${{ + needs.pre-setup.outputs.git-tag + }}' + - name: Log the changelog commit + run: git show --color + - name: Create a changelog update patch from the last Git commit + run: >- + git format-patch + --output='${{ needs.pre-setup.outputs.changelog-patch-name }}' + -1 HEAD + - name: Verify that expected patch got created + run: ls -1 '${{ needs.pre-setup.outputs.changelog-patch-name }}' + - name: Save the package bump patch as a GHA artifact + uses: actions/upload-artifact@v3 + with: + name: changelog + path: | + ${{ needs.pre-setup.outputs.changelog-patch-name }} + ${{ needs.pre-setup.outputs.changelog-draft-name-md }} + ${{ needs.pre-setup.outputs.changelog-draft-name-rst }} build: name: >- @@ -359,6 +590,7 @@ jobs: ) && 'test' || '' }}] needs: + - build-changelog - pre-setup # transitive, for accessing settings runs-on: ubuntu-latest @@ -1036,6 +1268,26 @@ jobs: # * https://github.community/t/github-actions-bot-email-address/17204/6 # * https://github.com/actions/checkout/issues/13#issuecomment-724415212 uses: fregante/setup-git-user@v2 + - name: Fetch the GHA artifact with the version patch + if: steps.existing-remote-tag-check.outputs.already-exists != 'true' + uses: actions/download-artifact@v3 + with: + name: changelog + + - name: Apply the changelog patch + if: steps.existing-remote-tag-check.outputs.already-exists != 'true' + run: git am '${{ needs.pre-setup.outputs.changelog-patch-name }}' + shell: bash + + - name: >- + Create a local 'release/${{ + needs.pre-setup.outputs.dist-version + }}' branch + if: steps.existing-remote-tag-check.outputs.already-exists != 'true' + run: >- + git checkout -b 'release/${{ + needs.pre-setup.outputs.dist-version + }}' - name: >- Tag the release in the local Git repo @@ -1055,14 +1307,15 @@ jobs: github.run_id }}' '${{ needs.pre-setup.outputs.git-tag }}' - -- - ${{ github.event.inputs.release-committish }} + - name: >- Push ${{ needs.pre-setup.outputs.git-tag }} tag corresponding to the just published release back to GitHub if: steps.existing-remote-tag-check.outputs.already-exists != 'true' run: >- - git push --atomic origin '${{ needs.pre-setup.outputs.git-tag }}' + git push --atomic origin + 'release/${{ needs.pre-setup.outputs.dist-version }}' + '${{ needs.pre-setup.outputs.git-tag }}' publish-github-release: name: >- @@ -1083,6 +1336,35 @@ jobs: with: name: ${{ env.dists-artifact-name }} path: dist/ + - name: Fetch the GHA artifact with the version patch + uses: actions/download-artifact@v3 + with: + name: changelog + + - name: Prepare the release notes file for the GitHub Releases + run: | + echo '## 📝 Release notes' | tee -a release-notes.md + echo | tee -a release-notes.md + echo | tee -a release-notes.md + echo '📦 PyPI page: https://pypi.org/project/cheroot/${{ + needs.pre-setup.outputs.dist-version + }}' | tee -a release-notes.md + echo | tee -a release-notes.md + echo | tee -a release-notes.md + echo '🔗 This release has been produced by ' \ + 'the following workflow run: ${{ + github.server_url + }}/${{ + github.repository + }}/actions/runs/${{ + github.run_id + }}' | tee -a release-notes.md + echo | tee -a release-notes.md + echo | tee -a release-notes.md + cat '${{ + needs.pre-setup.outputs.changelog-draft-name-md + }}' | tee -a release-notes.md + shell: bash - name: >- Publish a GitHub Release for @@ -1095,25 +1377,7 @@ jobs: dist/${{ needs.pre-setup.outputs.sdist-artifact-name }} dist/${{ needs.pre-setup.outputs.wheel-artifact-name }} artifactContentType: raw # Because whl and tgz are of different types - body: > - # Release ${{ needs.pre-setup.outputs.git-tag }} - - - This release is published to - https://pypi.org/project/cheroot/${{ - needs.pre-setup.outputs.dist-version - }}. - - - This release has been produced by the following workflow run: ${{ - github.server_url - }}/${{ - github.repository - }}/actions/runs/${{ - github.run_id - }}. - # bodyFile: # FIXME: Use once Towncrier is integrated. - commit: ${{ github.event.inputs.release-committish }} + bodyFile: release-notes.md discussionCategory: Announcements draft: false name: ${{ needs.pre-setup.outputs.git-tag }} diff --git a/docs/spelling_wordlist.txt b/docs/spelling_wordlist.txt index 2a6696106d..fe34b5182c 100644 --- a/docs/spelling_wordlist.txt +++ b/docs/spelling_wordlist.txt @@ -10,6 +10,7 @@ conftest conn chunked docstrings +downstreams ef environ Faux @@ -32,6 +33,7 @@ noqa PIL pipelining positionally +pre py pytest pythonic @@ -49,11 +51,13 @@ stdout subclasses submodules subpackages +symlinked syscall systemd threadpool Tidelift TLS +Towncrier tracebacks tuple tuples diff --git a/requirements/tox-check-changelog-cp310-linux-x86_64.txt b/requirements/tox-check-changelog-cp310-linux-x86_64.txt new file mode 120000 index 0000000000..7ae658e5cc --- /dev/null +++ b/requirements/tox-check-changelog-cp310-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp310-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-check-changelog-cp311-linux-x86_64.txt b/requirements/tox-check-changelog-cp311-linux-x86_64.txt new file mode 120000 index 0000000000..e84f76d584 --- /dev/null +++ b/requirements/tox-check-changelog-cp311-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp311-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-check-changelog-cp312-linux-x86_64.txt b/requirements/tox-check-changelog-cp312-linux-x86_64.txt new file mode 120000 index 0000000000..79587652f0 --- /dev/null +++ b/requirements/tox-check-changelog-cp312-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp312-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-check-changelog-cp313-linux-x86_64.txt b/requirements/tox-check-changelog-cp313-linux-x86_64.txt new file mode 120000 index 0000000000..61ba303ace --- /dev/null +++ b/requirements/tox-check-changelog-cp313-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp313-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-check-changelog-cp38-linux-x86_64.txt b/requirements/tox-check-changelog-cp38-linux-x86_64.txt new file mode 120000 index 0000000000..0c71a1aee9 --- /dev/null +++ b/requirements/tox-check-changelog-cp38-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp38-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-check-changelog-cp39-linux-x86_64.txt b/requirements/tox-check-changelog-cp39-linux-x86_64.txt new file mode 120000 index 0000000000..a821f37958 --- /dev/null +++ b/requirements/tox-check-changelog-cp39-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp39-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp310-linux-x86_64.txt b/requirements/tox-draft-changelog-cp310-linux-x86_64.txt new file mode 120000 index 0000000000..7ae658e5cc --- /dev/null +++ b/requirements/tox-draft-changelog-cp310-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp310-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp311-linux-x86_64.txt b/requirements/tox-draft-changelog-cp311-linux-x86_64.txt new file mode 120000 index 0000000000..e84f76d584 --- /dev/null +++ b/requirements/tox-draft-changelog-cp311-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp311-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp312-linux-x86_64.txt b/requirements/tox-draft-changelog-cp312-linux-x86_64.txt new file mode 120000 index 0000000000..79587652f0 --- /dev/null +++ b/requirements/tox-draft-changelog-cp312-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp312-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp313-linux-x86_64.txt b/requirements/tox-draft-changelog-cp313-linux-x86_64.txt new file mode 120000 index 0000000000..61ba303ace --- /dev/null +++ b/requirements/tox-draft-changelog-cp313-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp313-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp38-linux-x86_64.txt b/requirements/tox-draft-changelog-cp38-linux-x86_64.txt new file mode 120000 index 0000000000..0c71a1aee9 --- /dev/null +++ b/requirements/tox-draft-changelog-cp38-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp38-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-draft-changelog-cp39-linux-x86_64.txt b/requirements/tox-draft-changelog-cp39-linux-x86_64.txt new file mode 120000 index 0000000000..a821f37958 --- /dev/null +++ b/requirements/tox-draft-changelog-cp39-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp39-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp310-linux-x86_64.txt b/requirements/tox-make-changelog-cp310-linux-x86_64.txt new file mode 120000 index 0000000000..7ae658e5cc --- /dev/null +++ b/requirements/tox-make-changelog-cp310-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp310-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp311-linux-x86_64.txt b/requirements/tox-make-changelog-cp311-linux-x86_64.txt new file mode 120000 index 0000000000..e84f76d584 --- /dev/null +++ b/requirements/tox-make-changelog-cp311-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp311-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp312-linux-x86_64.txt b/requirements/tox-make-changelog-cp312-linux-x86_64.txt new file mode 120000 index 0000000000..79587652f0 --- /dev/null +++ b/requirements/tox-make-changelog-cp312-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp312-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp313-linux-x86_64.txt b/requirements/tox-make-changelog-cp313-linux-x86_64.txt new file mode 120000 index 0000000000..61ba303ace --- /dev/null +++ b/requirements/tox-make-changelog-cp313-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp313-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp38-linux-x86_64.txt b/requirements/tox-make-changelog-cp38-linux-x86_64.txt new file mode 120000 index 0000000000..0c71a1aee9 --- /dev/null +++ b/requirements/tox-make-changelog-cp38-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp38-linux-x86_64.txt \ No newline at end of file diff --git a/requirements/tox-make-changelog-cp39-linux-x86_64.txt b/requirements/tox-make-changelog-cp39-linux-x86_64.txt new file mode 120000 index 0000000000..a821f37958 --- /dev/null +++ b/requirements/tox-make-changelog-cp39-linux-x86_64.txt @@ -0,0 +1 @@ +tox-build-docs-cp39-linux-x86_64.txt \ No newline at end of file diff --git a/tox.ini b/tox.ini index 49dd345e17..0dd093aed8 100644 --- a/tox.ini +++ b/tox.ini @@ -103,8 +103,11 @@ commands = `file://\{index_file\}`\n\nTo serve docs, use \ `python3 -m http.server --directory \ \N\{QUOTATION MARK\}\{docs_dir\}\N\{QUOTATION MARK\} 0`\n")' +depends = + make-changelog deps = -rrequirements{/}tox-docs.in +envdir = {toxworkdir}/build-docs [testenv:doctest-docs] allowlist_externals = @@ -131,6 +134,7 @@ commands = . "{toxworkdir}{/}docs_out" deps = {[testenv:build-docs]deps} +envdir = {[testenv:build-docs]envdir} [testenv:linkcheck-docs] allowlist_externals = @@ -157,6 +161,7 @@ commands = . "{toxworkdir}{/}docs_out" deps = {[testenv:build-docs]deps} +envdir = {[testenv:build-docs]envdir} [testenv:spellcheck-docs] allowlist_externals = @@ -183,6 +188,7 @@ commands = . "{toxworkdir}{/}docs_out" deps = -rrequirements{/}tox-docs-linkcheck.in +envdir = {[testenv:build-docs]envdir} [testenv:watch] commands = ptw --runner=pytest @@ -193,6 +199,81 @@ deps = commands_pre = commands = pre-commit run --all-files --show-diff-on-failure {posargs} + +[testenv:check-changelog] +basepython = {[testenv:make-changelog]basepython} +description = + Check Towncrier change notes +commands_pre = +commands = + {envpython} \ + {[python-cli-options]byteerrors} \ + {[python-cli-options]isolate} \ + -m towncrier.check \ + --compare-with origin/devel {posargs:} +deps = + {[testenv:make-changelog]deps} +envdir = {[testenv:make-changelog]envdir} +isolated_build = {[testenv:make-changelog]isolated_build} +skip_install = {[testenv:make-changelog]skip_install} + + +[testenv:make-changelog] +basepython = python3 +depends = + check-changelog +description = + Generate a changelog from fragments using Towncrier. Getting an + unreleased changelog preview does not require extra arguments. + When invoking to update the changelog, pass the desired version as an + argument after `--`. For example, `tox -e {envname} -- 1.3.2`. +envdir = {[testenv:build-docs]envdir} +commands_pre = +commands = + {envpython} \ + {[python-cli-options]byteerrors} \ + {[python-cli-options]isolate} \ + -m towncrier.build \ + --version \ + {posargs:'[UNRELEASED DRAFT]' --draft} +deps = + {[testenv:build-docs]deps} + # -r{toxinidir}/docs/requirements.txt + # FIXME: re-enable the "-r" + "-c" paradigm once the pip bug is fixed. + # Ref: https://github.com/pypa/pip/issues/9243 + # towncrier + # -r{toxinidir}/docs/requirements.in + # -c{toxinidir}/docs/requirements.txt +isolated_build = true +skip_install = true + + +[testenv:draft-changelog] +allowlist_externals = + sh +basepython = {[testenv:make-changelog]basepython} +description = + Print out the Towncrier-managed change notes + draft for the next release to stdout +commands_pre = +commands = + # NOTE: `sh` invocation is required to suppress stderr from + # NOTE: towncrier since it does not have own CLI flags for + # NOTE: doing this. + sh -c "2>/dev/null \ + {envpython} \ + {[python-cli-options]byteerrors} \ + {[python-cli-options]isolate} \ + -m towncrier.build \ + --version '[UNRELEASED DRAFT]' \ + --draft" +envdir = {[testenv:make-changelog]envdir} +deps = + {[testenv:make-changelog]deps} +isolated_build = {[testenv:make-changelog]isolated_build} +skip_install = {[testenv:make-changelog]skip_install} + + [testenv:cleanup-dists] description = Wipe the the dist{/} folder