diff --git a/docs/contributing.rst b/docs/contributing.rst index 43a8d2c22..5b141b6c1 100644 --- a/docs/contributing.rst +++ b/docs/contributing.rst @@ -27,6 +27,28 @@ Clone you github fork repo locally and install errbot in development mode from t From there, anytime you execute `errbot` it will run from the checked out version of Errbot with all your local changes taken into account. +Updating the dependency lockfile +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Errbot ships a pinned dependency lockfile (``pylock.toml``) in `PEP 751`_ format, +generated by uv_ from the top-level dependencies declared in ``pyproject.toml``. +The lockfile is the source of truth for reproducible installs in CI and for +downstream consumers that want exact versions. + +Regenerate the lockfile any time you change a dependency in ``pyproject.toml`` (or +to refresh the pinned versions). From the root of the repository:: + + # Install uv if you don't have it (https://docs.astral.sh/uv/) + pip install uv + + # Recompile the lockfile from pyproject.toml, pinned to the project's + # minimum supported Python version so the resolution covers 3.9+. + uv pip compile pyproject.toml -o pylock.toml \ + --format pylock.toml --python-version 3.9 + +Commit the resulting ``pylock.toml`` alongside your ``pyproject.toml`` change in +the same pull request so the two stay in sync. + Preparing your pull request ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -42,6 +64,56 @@ these guidelines as you open a pull request. * If you can, please add tests for your code. We know large parts of our codebase are missing tests, so we won't reject your code if it lacks tests, though. +Releasing Errbot +---------------- + +This section is for maintainers cutting a release. The flow assumes a CHANGES.rst +maintained by hand under a ``v9.9.9 (unreleased)`` heading on master. + +Cutting a release +~~~~~~~~~~~~~~~~~ + +1. **Open a release PR** that does two edits: + + - Bump ``errbot/version.py``:: + + VERSION = "X.Y.Z" + + - Rename the unreleased heading in ``CHANGES.rst`` from ``v9.9.9 (unreleased)`` + to ``vX.Y.Z (YYYY-MM-DD)`` and double-check the entries beneath it. + +2. **Let CI gate the change.** The test ``tests/release_metadata_test.py`` skips on the + ``9.9.9`` sentinel but, once ``version.py`` is bumped, asserts that ``CHANGES.rst`` + contains a section heading for the new version. A release PR that forgets the heading + rename will fail this test before it can merge. + +3. **Merge the release PR**, then run the release tooling from a clean checkout:: + + ./tools/releases.sh + + The script reruns the same pre-release gate, builds the sdist/wheel with + ``python -m build``, and calls ``twine`` to publish to PyPI. + +4. **Tag the commit** and push the tag:: + + git tag vX.Y.Z + git push origin vX.Y.Z + +5. **Return master to dev mode** in a follow-up PR: + + - Reset ``errbot/version.py`` to ``VERSION = "9.9.9"``. + - Add a fresh ``v9.9.9 (unreleased)`` heading at the top of ``CHANGES.rst``. + +After this, the gate test goes back to skipping on master and the next release +cycle starts. + +Refreshing the lockfile for a release +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If dependency pins have changed since the previous release, regenerate +``pylock.toml`` (see `Updating the dependency lockfile`_) and include the result +in the release PR. + Contributing documentation & making changes to the website ---------------------------------------------------------- @@ -94,3 +166,5 @@ to ask your question on Stack Overflow, `tagged errbot .. _repos.py: https://github.com/errbotio/errbot/blob/master/errbot/repos.py .. _`issue tracker`: https://github.com/errbotio/errbot/issues/ .. _Gitter: https://gitter.im/errbotio/errbot +.. _uv: https://docs.astral.sh/uv/ +.. _`PEP 751`: https://peps.python.org/pep-0751/ diff --git a/pylock.toml b/pylock.toml new file mode 100644 index 000000000..c6e392639 --- /dev/null +++ b/pylock.toml @@ -0,0 +1,216 @@ +# This file was autogenerated by uv via the following command: +# uv pip compile pyproject.toml -o pylock.toml --format pylock.toml --python-version 3.9 +lock-version = "1.0" +created-by = "uv" +requires-python = ">=3.9" + +[[packages]] +name = "ansi" +version = "0.3.6" +sdist = { url = "https://files.pythonhosted.org/packages/ec/ab/a779658c03d9d6d4e4b9e24e50708fc1d214424829407360bd5a1189b675/ansi-0.3.6.tar.gz", upload-time = 2022-02-08T14:22:06Z, size = 7252, hashes = { sha256 = "10545ce2508e470e9208d0cc200616032fdc81b849678826fb7c81c823fd48b1" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/19/d0/d6bc8e852dd8f7e29d9213820857eb69d057f6171cbefd4c7b26700b8bc5/ansi-0.3.6-py3-none-any.whl", upload-time = 2022-02-08T14:22:05Z, size = 8600, hashes = { sha256 = "a83f7e6e7a9a84f4cc2904e28e575516cf42cf862777649e57615d85ac76e11d" } }] + +[[packages]] +name = "beautifulsoup4" +version = "4.14.3" +sdist = { url = "https://files.pythonhosted.org/packages/c3/b0/1c6a16426d389813b48d95e26898aff79abbde42ad353958ad95cc8c9b21/beautifulsoup4-4.14.3.tar.gz", upload-time = 2025-11-30T15:08:26Z, size = 627737, hashes = { sha256 = "6292b1c5186d356bba669ef9f7f051757099565ad9ada5dd630bd9de5fa7fb86" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1a/39/47f9197bdd44df24d67ac8893641e16f386c984a0619ef2ee4c51fbbc019/beautifulsoup4-4.14.3-py3-none-any.whl", upload-time = 2025-11-30T15:08:24Z, size = 107721, hashes = { sha256 = "0918bfe44902e6ad8d57732ba310582e98da931428d231a5ecb9e7c703a735bb" } }] + +[[packages]] +name = "blinker" +version = "1.9.0" +sdist = { url = "https://files.pythonhosted.org/packages/21/28/9b3f50ce0e048515135495f198351908d99540d69bfdc8c1d15b73dc55ce/blinker-1.9.0.tar.gz", upload-time = 2024-11-08T17:25:47Z, size = 22460, hashes = { sha256 = "b4ce2265a7abece45e7cc896e98dbebe6cead56bcf805a3d23136d145f5445bf" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/10/cb/f2ad4230dc2eb1a74edf38f1a38b9b52277f75bef262d8908e60d957e13c/blinker-1.9.0-py3-none-any.whl", upload-time = 2024-11-08T17:25:46Z, size = 8458, hashes = { sha256 = "ba0efaa9080b619ff2f3459d1d500c57bddea4a6b424b60a91141db6fd2f08bc" } }] + +[[packages]] +name = "certifi" +version = "2026.4.22" +sdist = { url = "https://files.pythonhosted.org/packages/25/ee/6caf7a40c36a1220410afe15a1cc64993a1f864871f698c0f93acb72842a/certifi-2026.4.22.tar.gz", upload-time = 2026-04-22T11:26:11Z, size = 137077, hashes = { sha256 = "8d455352a37b71bf76a79caa83a3d6c25afee4a385d632127b6afb3963f1c580" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/22/30/7cd8fdcdfbc5b869528b079bfb76dcdf6056b1a2097a662e5e8c04f42965/certifi-2026.4.22-py3-none-any.whl", upload-time = 2026-04-22T11:26:09Z, size = 135707, hashes = { sha256 = "3cb2210c8f88ba2318d29b0388d1023c8492ff72ecdde4ebdaddbb13a31b1c4a" } }] + +[[packages]] +name = "cffi" +version = "2.0.0" +marker = "platform_python_implementation != 'PyPy'" +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", upload-time = 2025-09-08T23:24:04Z, size = 523588, hashes = { sha256 = "44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/3d/de/38d9726324e127f727b4ecc376bc85e505bfe61ef130eaf3f290c6847dd4/cffi-2.0.0-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-08T23:23:49Z, size = 180509, hashes = { sha256 = "de8dad4425a6ca6e4e5e297b27b5c824ecc7581910bf9aee86cb6835e6812aa7" } }] + +[[packages]] +name = "charset-normalizer" +version = "3.4.7" +sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", upload-time = 2026-04-02T09:28:39Z, size = 144271, hashes = { sha256 = "ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/01/1b/ef725f8eb19b5a261b30f78efa9252ef9d017985cb499102f6f49834cd12/charset_normalizer-3.4.7-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2026-04-02T09:28:14Z, size = 299121, hashes = { sha256 = "177a0ba5f0211d488e295aaf82707237e331c24788d8d76c96c5a41594723217" } }, + { url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", upload-time = 2026-04-02T09:28:37Z, size = 61958, hashes = { sha256 = "3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d" } }, +] + +[[packages]] +name = "click" +version = "8.1.8" +sdist = { url = "https://files.pythonhosted.org/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", upload-time = 2024-12-21T18:38:44Z, size = 226593, hashes = { sha256 = "ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", upload-time = 2024-12-21T18:38:41Z, size = 98188, hashes = { sha256 = "63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2" } }] + +[[packages]] +name = "colorlog" +version = "6.7.0" +sdist = { url = "https://files.pythonhosted.org/packages/78/6b/4e5481ddcdb9c255b2715f54c863629f1543e97bc8c309d1c5c131ad14f2/colorlog-6.7.0.tar.gz", upload-time = 2022-08-29T14:51:27Z, size = 29920, hashes = { sha256 = "bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/58/43/a363c213224448f9e194d626221123ce00e3fb3d87c0c22aed52b620bdd1/colorlog-6.7.0-py2.py3-none-any.whl", upload-time = 2022-08-29T14:51:26Z, size = 11286, hashes = { sha256 = "0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662" } }] + +[[packages]] +name = "cryptography" +version = "44.0.3" +sdist = { url = "https://files.pythonhosted.org/packages/53/d6/1411ab4d6108ab167d06254c5be517681f1e331f90edf1379895bcb87020/cryptography-44.0.3.tar.gz", upload-time = 2025-05-02T19:36:04Z, size = 711096, hashes = { sha256 = "fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/08/53/c776d80e9d26441bb3868457909b4e74dd9ccabd182e10b2b0ae7a07e265/cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", upload-time = 2025-05-02T19:34:50Z, size = 6670281, hashes = { sha256 = "962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88" } }, + { url = "https://files.pythonhosted.org/packages/34/a3/ad08e0bcc34ad436013458d7528e83ac29910943cea42ad7dd4141a27bbb/cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", upload-time = 2025-05-02T19:35:13Z, size = 6673501, hashes = { sha256 = "5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f" } }, +] + +[[packages]] +name = "daemonize" +version = "2.5.0" +marker = "sys_platform != 'win32'" +sdist = { url = "https://files.pythonhosted.org/packages/8c/20/96f7dbc23812cfe4cf479c87af3e4305d0d115fd1fffec32ddeee7b9c82b/daemonize-2.5.0.tar.gz", upload-time = 2018-12-12T19:47:39Z, size = 8759, hashes = { sha256 = "dd026e4ff8d22cb016ed2130bc738b7d4b1da597ef93c074d2adb9e4dea08bc3" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/45/ad/1b20db02287afd40d3130a218ac5ce2f7d2ab581cfda29bada5e1c4bee17/daemonize-2.5.0-py2.py3-none-any.whl", upload-time = 2018-12-12T19:47:37Z, size = 5231, hashes = { sha256 = "9b6b91311a9d934ff3f5f766666635ca280d3de8e7137e4cd7d3f052543b989f" } }] + +[[packages]] +name = "deepmerge" +version = "1.1.0" +sdist = { url = "https://files.pythonhosted.org/packages/82/d7/75916d41be4c45a8d9b93d1bb089ac46de0212630641972982023d785d1f/deepmerge-1.1.0.tar.gz", upload-time = 2022-10-25T05:24:36Z, size = 17785, hashes = { sha256 = "4c27a0db5de285e1a7ceac7dbc1531deaa556b627dea4900c8244581ecdfea2d" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/59/72/72489ad8d3fe1cf5e5be93bf27ca4b5c792d552a5f667367b32a671a2076/deepmerge-1.1.0-py3-none-any.whl", upload-time = 2022-10-25T05:24:34Z, size = 8501, hashes = { sha256 = "59e6ef80b77dc52af3882a1ea78da22bcfc91ae9cdabc0c80729049fe295ff8b" } }] + +[[packages]] +name = "dulwich" +version = "0.21.5" +sdist = { url = "https://files.pythonhosted.org/packages/8c/6c/57adedfc2e1debb62581e9b413d2be78ea62e1da47d21fdd9d0d10317ebc/dulwich-0.21.5.tar.gz", upload-time = 2023-05-04T02:47:53Z, size = 441948, hashes = { sha256 = "70955e4e249ddda6e34a4636b90f74e931e558f993b17c52570fa6144b993103" } } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/48/4308b462565ed6b52a25535fa4b6ed99de052314c35d2f1214991de8aec4/dulwich-0.21.5-cp39-cp39-macosx_10_9_universal2.whl", upload-time = 2023-05-04T03:20:28Z, size = 470766, hashes = { sha256 = "fd4ad079758514375f11469e081723ba8831ce4eaa1a64b41f06a3a866d5ac34" } }, + { url = "https://files.pythonhosted.org/packages/b7/57/37fd47365baad8da470bbf7569494a2707f4972d79111d20d03df971420b/dulwich-0.21.5-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2023-05-04T03:20:31Z, size = 470750, hashes = { sha256 = "aae448da7d80306dda4fc46292fed7efaa466294571ab3448be16714305076f1" } }, +] + +[[packages]] +name = "flask" +version = "2.3.3" +sdist = { url = "https://files.pythonhosted.org/packages/46/b7/4ace17e37abd9c21715dea5ee11774a25e404c486a7893fa18e764326ead/flask-2.3.3.tar.gz", upload-time = 2023-08-21T19:52:35Z, size = 672756, hashes = { sha256 = "09c347a92aa7ff4a8e7f3206795f30d826654baf38b873d0744cd571ca609efc" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/fd/56/26f0be8adc2b4257df20c1c4260ddd0aa396cf8e75d90ab2f7ff99bc34f9/flask-2.3.3-py3-none-any.whl", upload-time = 2023-08-21T19:52:33Z, size = 96112, hashes = { sha256 = "f69fcd559dc907ed196ab9df0e48471709175e696d6e698dd4dbe940f96ce66b" } }] + +[[packages]] +name = "idna" +version = "3.13" +sdist = { url = "https://files.pythonhosted.org/packages/ce/cc/762dfb036166873f0059f3b7de4565e1b5bc3d6f28a414c13da27e442f99/idna-3.13.tar.gz", upload-time = 2026-04-22T16:42:42Z, size = 194210, hashes = { sha256 = "585ea8fe5d69b9181ec1afba340451fba6ba764af97026f92a91d4eef164a242" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/5d/13/ad7d7ca3808a898b4612b6fe93cde56b53f3034dcde235acb1f0e1df24c6/idna-3.13-py3-none-any.whl", upload-time = 2026-04-22T16:42:40Z, size = 68629, hashes = { sha256 = "892ea0cde124a99ce773decba204c5552b69c3c67ffd5f232eb7696135bc8bb3" } }] + +[[packages]] +name = "importlib-metadata" +version = "8.7.1" +marker = "python_full_version < '3.10'" +sdist = { url = "https://files.pythonhosted.org/packages/f3/49/3b30cad09e7771a4982d9975a8cbf64f00d4a1ececb53297f1d9a7be1b10/importlib_metadata-8.7.1.tar.gz", upload-time = 2025-12-21T10:00:19Z, size = 57107, hashes = { sha256 = "49fef1ae6440c182052f407c8d34a68f72efc36db9ca90dc0113398f2fdde8bb" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/fa/5e/f8e9a1d23b9c20a551a8a02ea3637b4642e22c2626e3a13a9a29cdea99eb/importlib_metadata-8.7.1-py3-none-any.whl", upload-time = 2025-12-21T10:00:18Z, size = 27865, hashes = { sha256 = "5a1f80bf1daa489495071efbb095d75a634cf28a8bc299581244063b53176151" } }] + +[[packages]] +name = "itsdangerous" +version = "2.2.0" +sdist = { url = "https://files.pythonhosted.org/packages/9c/cb/8ac0172223afbccb63986cc25049b154ecfb5e85932587206f42317be31d/itsdangerous-2.2.0.tar.gz", upload-time = 2024-04-16T21:28:15Z, size = 54410, hashes = { sha256 = "e0050c0b7da1eea53ffaf149c0cfbb5c6e2e2b69c4bef22c81fa6eb73e5f6173" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/04/96/92447566d16df59b2a776c0fb82dbc4d9e07cd95062562af01e408583fc4/itsdangerous-2.2.0-py3-none-any.whl", upload-time = 2024-04-16T21:28:14Z, size = 16234, hashes = { sha256 = "c6242fc49e35958c8b15141343aa660db5fc54d4f13a1db01a3f5891b98700ef" } }] + +[[packages]] +name = "jinja2" +version = "3.1.6" +sdist = { url = "https://files.pythonhosted.org/packages/df/bf/f7da0350254c0ed7c72f3e33cef02e048281fec7ecec5f032d4aac52226b/jinja2-3.1.6.tar.gz", upload-time = 2025-03-05T20:05:02Z, size = 245115, hashes = { sha256 = "0137fb05990d35f1275a587e9aee6d56da821fc83491a0fb838183be43f66d6d" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/62/a1/3d680cbfd5f4b8f15abc1d571870c5fc3e594bb582bc3b64ea099db13e56/jinja2-3.1.6-py3-none-any.whl", upload-time = 2025-03-05T20:05:00Z, size = 134899, hashes = { sha256 = "85ece4451f492d0c13c5dd7c13a64681a86afae63a5f347908daf103ce6d2f67" } }] + +[[packages]] +name = "markdown" +version = "3.4.4" +sdist = { url = "https://files.pythonhosted.org/packages/87/2a/62841f4fb1fef5fa015ded48d02401cd95643ca03b6760b29437b62a04a4/Markdown-3.4.4.tar.gz", upload-time = 2023-07-25T15:13:45Z, size = 324459, hashes = { sha256 = "225c6123522495d4119a90b3a3ba31a1e87a70369e03f14799ea9c0d7183a3d6" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/1a/b5/228c1cdcfe138f1a8e01ab1b54284c8b83735476cb22b6ba251656ed13ad/Markdown-3.4.4-py3-none-any.whl", upload-time = 2023-07-25T15:13:43Z, size = 94174, hashes = { sha256 = "a4c1b65c0957b4bd9e7d86ddc7b3c9868fb9670660f6f99f6d1bca8954d5a941" } }] + +[[packages]] +name = "markupsafe" +version = "3.0.3" +sdist = { url = "https://files.pythonhosted.org/packages/7e/99/7690b6d4034fffd95959cbe0c02de8deb3098cc577c67bb6a24fe5d7caa7/markupsafe-3.0.3.tar.gz", upload-time = 2025-09-27T18:37:40Z, size = 80313, hashes = { sha256 = "722695808f4b6457b320fdc131280796bdceb04ab50fe1795cd540799ebe1698" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/fd/23/07a2cb9a8045d5f3f0890a8c3bc0859d7a47bfd9a560b563899bec7b72ed/markupsafe-3.0.3-cp39-cp39-macosx_11_0_arm64.whl", upload-time = 2025-09-27T18:37:30Z, size = 12049, hashes = { sha256 = "f71a396b3bf33ecaa1626c255855702aca4d3d9fea5e051b41ac59a9c1c41edc" } }] + +[[packages]] +name = "pycparser" +version = "2.23" +marker = "implementation_name != 'PyPy' and platform_python_implementation != 'PyPy'" +sdist = { url = "https://files.pythonhosted.org/packages/fe/cf/d2d3b9f5699fb1e4615c8e32ff220203e43b248e1dfcc6736ad9057731ca/pycparser-2.23.tar.gz", upload-time = 2025-09-09T13:23:47Z, size = 173734, hashes = { sha256 = "78816d4f24add8f10a06d6f05b4d424ad9e96cfebf68a4ddc99c65c0720d00c2" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/a0/e3/59cd50310fc9b59512193629e1984c1f95e5c8ae6e5d8c69532ccc65a7fe/pycparser-2.23-py3-none-any.whl", upload-time = 2025-09-09T13:23:46Z, size = 118140, hashes = { sha256 = "e5c6e8d3fbad53479cab09ac03729e0a9faf2bee3db8208a550daf5af81a5934" } }] + +[[packages]] +name = "pygments" +version = "2.16.1" +sdist = { url = "https://files.pythonhosted.org/packages/d6/f7/4d461ddf9c2bcd6a4d7b2b139267ca32a69439387cc1f02a924ff8883825/Pygments-2.16.1.tar.gz", upload-time = 2023-08-06T15:14:55Z, size = 4872980, hashes = { sha256 = "1daff0494820c69bc8941e407aa20f577374ee88364ee10a98fdbe0aece96e29" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/43/88/29adf0b44ba6ac85045e63734ae0997d3c58d8b1a91c914d240828d0d73d/Pygments-2.16.1-py3-none-any.whl", upload-time = 2023-08-06T15:14:51Z, size = 1164750, hashes = { sha256 = "13fc09fa63bc8d8671a6d247e1eb303c4b343eaee81d861f3404db2935653692" } }] + +[[packages]] +name = "pygments-markdown-lexer" +version = "0.1.0.dev39" +sdist = { url = "https://files.pythonhosted.org/packages/c3/12/674cdee66635d638cedb2c5d9c85ce507b7b2f91bdba29e482f1b1160ff6/pygments-markdown-lexer-0.1.0.dev39.zip", upload-time = 2015-07-06T11:08:10Z, size = 28039, hashes = { sha256 = "4c128c26450b5886521c674d759f95fc3768b8955a7d9c81866ee0213c2febdf" } } + +[[packages]] +name = "pyopenssl" +version = "24.3.0" +sdist = { url = "https://files.pythonhosted.org/packages/c1/d4/1067b82c4fc674d6f6e9e8d26b3dff978da46d351ca3bac171544693e085/pyopenssl-24.3.0.tar.gz", upload-time = 2024-11-27T20:43:12Z, size = 178944, hashes = { sha256 = "49f7a019577d834746bc55c5fce6ecbcec0f2b4ec5ce1cf43a9a173b8138bb36" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/42/22/40f9162e943f86f0fc927ebc648078be87def360d9d8db346619fb97df2b/pyOpenSSL-24.3.0-py3-none-any.whl", upload-time = 2024-11-27T20:43:21Z, size = 56111, hashes = { sha256 = "e474f5a473cd7f92221cc04976e48f4d11502804657a08a989fb3be5514c904a" } }] + +[[packages]] +name = "requests" +version = "2.32.3" +sdist = { url = "https://files.pythonhosted.org/packages/63/70/2bf7780ad2d390a8d301ad0b550f1581eadbd9a20f896afe06353c2a2913/requests-2.32.3.tar.gz", upload-time = 2024-05-29T15:37:49Z, size = 131218, hashes = { sha256 = "55365417734eb18255590a9ff9eb97e9e1da868d4ccd6402399eaf68af20a760" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/f9/9b/335f9764261e915ed497fcdeb11df5dfd6f7bf257d4a6a2a686d80da4d54/requests-2.32.3-py3-none-any.whl", upload-time = 2024-05-29T15:37:47Z, size = 64928, hashes = { sha256 = "70761cfe03c773ceb22aa2f671b4757976145175cdfca038c02654d061d6dcc6" } }] + +[[packages]] +name = "setuptools" +version = "82.0.1" +sdist = { url = "https://files.pythonhosted.org/packages/4f/db/cfac1baf10650ab4d1c111714410d2fbb77ac5a616db26775db562c8fab2/setuptools-82.0.1.tar.gz", upload-time = 2026-03-09T12:47:17Z, size = 1152316, hashes = { sha256 = "7d872682c5d01cfde07da7bccc7b65469d3dca203318515ada1de5eda35efbf9" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/9d/76/f789f7a86709c6b087c5a2f52f911838cad707cc613162401badc665acfe/setuptools-82.0.1-py3-none-any.whl", upload-time = 2026-03-09T12:47:15Z, size = 1006223, hashes = { sha256 = "a59e362652f08dcd477c78bb6e7bd9d80a7995bc73ce773050228a348ce2e5bb" } }] + +[[packages]] +name = "soupsieve" +version = "2.8.3" +sdist = { url = "https://files.pythonhosted.org/packages/7b/ae/2d9c981590ed9999a0d91755b47fc74f74de286b0f5cee14c9269041e6c4/soupsieve-2.8.3.tar.gz", upload-time = 2026-01-20T04:27:02Z, size = 118627, hashes = { sha256 = "3267f1eeea4251fb42728b6dfb746edc9acaffc4a45b27e19450b676586e8349" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", upload-time = 2026-01-20T04:27:01Z, size = 37016, hashes = { sha256 = "ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95" } }] + +[[packages]] +name = "typing-extensions" +version = "4.15.0" +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", upload-time = 2025-08-25T13:49:26Z, size = 109391, hashes = { sha256 = "0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", upload-time = 2025-08-25T13:49:24Z, size = 44614, hashes = { sha256 = "f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548" } }] + +[[packages]] +name = "urllib3" +version = "2.6.3" +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", upload-time = 2026-01-07T16:24:43Z, size = 435556, hashes = { sha256 = "1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", upload-time = 2026-01-07T16:24:42Z, size = 131584, hashes = { sha256 = "bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4" } }] + +[[packages]] +name = "waitress" +version = "3.0.2" +sdist = { url = "https://files.pythonhosted.org/packages/bf/cb/04ddb054f45faa306a230769e868c28b8065ea196891f09004ebace5b184/waitress-3.0.2.tar.gz", upload-time = 2024-11-16T20:02:35Z, size = 179901, hashes = { sha256 = "682aaaf2af0c44ada4abfb70ded36393f0e307f4ab9456a215ce0020baefc31f" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/8d/57/a27182528c90ef38d82b636a11f606b0cbb0e17588ed205435f8affe3368/waitress-3.0.2-py3-none-any.whl", upload-time = 2024-11-16T20:02:33Z, size = 56232, hashes = { sha256 = "c56d67fd6e87c2ee598b76abdd4e96cfad1f24cacdea5078d382b1f9d7b5ed2e" } }] + +[[packages]] +name = "webob" +version = "1.8.9" +sdist = { url = "https://files.pythonhosted.org/packages/85/0b/1732085540b01f65e4e7999e15864fe14cd18b12a95731a43fd6fd11b26a/webob-1.8.9.tar.gz", upload-time = 2024-10-24T03:19:20Z, size = 279775, hashes = { sha256 = "ad6078e2edb6766d1334ec3dee072ac6a7f95b1e32ce10def8ff7f0f02d56589" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/50/bd/c336448be43d40be28e71f2e0f3caf7ccb28e2755c58f4c02c065bfe3e8e/WebOb-1.8.9-py2.py3-none-any.whl", upload-time = 2024-10-24T03:19:18Z, size = 115364, hashes = { sha256 = "45e34c58ed0c7e2ecd238ffd34432487ff13d9ad459ddfd77895e67abba7c1f9" } }] + +[[packages]] +name = "webtest" +version = "3.0.0" +sdist = { url = "https://files.pythonhosted.org/packages/26/c8/8ffba1782700eb06e9b9169156698c6ba95c05b66cda3fc9e025b6b3b649/WebTest-3.0.0.tar.gz", upload-time = 2021-08-19T12:40:07Z, size = 75871, hashes = { sha256 = "54bd969725838d9861a9fa27f8d971f79d275d94ae255f5c501f53bb6d9929eb" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/41/c7/3897bd62366cb4a50bfb411d37efca9fa33bf07a7c1c22fce8f6ad2664ff/WebTest-3.0.0-py3-none-any.whl", upload-time = 2021-08-19T12:40:04Z, size = 31858, hashes = { sha256 = "2a001a9efa40d2a7e5d9cd8d1527c75f41814eb6afce2c3d207402547b1e5ead" } }] + +[[packages]] +name = "werkzeug" +version = "3.1.8" +sdist = { url = "https://files.pythonhosted.org/packages/dd/b2/381be8cfdee792dd117872481b6e378f85c957dd7c5bca38897b08f765fd/werkzeug-3.1.8.tar.gz", upload-time = 2026-04-02T18:49:14Z, size = 875852, hashes = { sha256 = "9bad61a4268dac112f1c5cd4630a56ede601b6ed420300677a869083d70a4c44" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/93/8c/2e650f2afeb7ee576912636c23ddb621c91ac6a98e66dc8d29c3c69446e1/werkzeug-3.1.8-py3-none-any.whl", upload-time = 2026-04-02T18:49:12Z, size = 226459, hashes = { sha256 = "63a77fb8892bf28ebc3178683445222aa500e48ebad5ec77b0ad80f8726b1f50" } }] + +[[packages]] +name = "zipp" +version = "3.23.1" +marker = "python_full_version < '3.10'" +sdist = { url = "https://files.pythonhosted.org/packages/30/21/093488dfc7cc8964ded15ab726fad40f25fd3d788fd741cc1c5a17d78ee8/zipp-3.23.1.tar.gz", upload-time = 2026-04-13T23:21:46Z, size = 25965, hashes = { sha256 = "32120e378d32cd9714ad503c1d024619063ec28aad2248dc6672ad13edfa5110" } } +wheels = [{ url = "https://files.pythonhosted.org/packages/08/8a/0861bec20485572fbddf3dfba2910e38fe249796cb73ecdeb74e07eeb8d3/zipp-3.23.1-py3-none-any.whl", upload-time = 2026-04-13T23:21:45Z, size = 10378, hashes = { sha256 = "0b3596c50a5c700c9cb40ba8d86d9f2cc4807e9bedb06bcdf7fac85633e444dc" } }] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 000000000..1702d4463 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,88 @@ +[build-system] +requires = ["setuptools>=77"] +build-backend = "setuptools.build_meta" + +[project] +name = "errbot" +dynamic = ["version", "readme"] +description = "Errbot is a chatbot designed to be simple to extend with plugins written in Python." +authors = [{name = "errbot.io", email = "info@errbot.io"}] +license = "GPL-3.0-or-later" +license-files = ["COPYING"] +requires-python = ">=3.9" +keywords = ["xmpp", "irc", "slack", "hipchat", "gitter", "tox", "chatbot", "bot", "plugin", "chatops"] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Topic :: Communications :: Chat", + "Topic :: Communications :: Chat :: Internet Relay Chat", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", +] +dependencies = [ + "webtest==3.0.0", + "setuptools>=78.1.1", + "flask==2.3.3", + "requests==2.32.3", + "jinja2==3.1.6", + "pyOpenSSL==24.3.0", + "colorlog==6.7.0", + "markdown==3.4.4", + "ansi==0.3.6", + "Pygments==2.16.1", + "pygments-markdown-lexer==0.1.0.dev39", + "dulwich==0.21.5", + "deepmerge==1.1.0", + 'legacy-cgi==2.6.3; python_version >= "3.13"', + 'daemonize==2.5.0; sys_platform != "win32"', +] + +[project.optional-dependencies] +slack = ["errbot-backend-slackv3==0.3.2"] +discord = ["err-backend-discord==3.0.1"] +mattermost = ["err-backend-mattermost==3.0.0"] +IRC = ["irc==20.3.0"] +telegram = ["python-telegram-bot==13.15"] +XMPP = [ + "slixmpp==1.8.4", + "pyasn1==0.6.3", + "pyasn1-modules==0.3.0", +] +test = ["pytest", "webtest", "requests"] + +[project.urls] +Homepage = "http://errbot.io/" + +[project.scripts] +errbot = "errbot.cli:main" + +[tool.setuptools.dynamic] +version = {attr = "errbot.version.VERSION"} +readme = {file = ["README.rst", "CHANGES.rst"], content-type = "text/x-rst"} + +[tool.setuptools.packages.find] +include = ["errbot", "errbot.*"] + +[tool.setuptools.package-data] +errbot = [ + "backends/*.plug", + "backends/*.html", + "backends/styles/*.css", + "backends/images/*.svg", + "core_plugins/*.plug", + "core_plugins/*.md", + "core_plugins/templates/*.md", + "storage/*.plug", + "templates/initdir/example.py", + "templates/initdir/example.plug", + "templates/initdir/config.py.tmpl", + "templates/*.md", + "templates/new_plugin.py.tmpl", +] + +[tool.ruff.lint.per-file-ignores] +"tests/borken_plugin/broken.py" = ["F401"] diff --git a/setup.py b/setup.py deleted file mode 100644 index 5cc556660..000000000 --- a/setup.py +++ /dev/null @@ -1,155 +0,0 @@ -#!/usr/bin/env python - -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - -import os -import sys - -from setuptools import find_packages, setup - -py_version = sys.version_info[:2] - -if py_version < (3, 9): - raise RuntimeError("Errbot requires Python 3.9 or later") - -VERSION_FILE = os.path.join("errbot", "version.py") - -deps = [ - "webtest==3.0.0", - "setuptools>=78.1.1", - "flask==2.3.3", - "requests==2.32.3", - "jinja2==3.1.6", - "pyOpenSSL==24.3.0", - "colorlog==6.7.0", - "markdown==3.4.4", - "ansi==0.3.6", - "Pygments==2.16.1", - "pygments-markdown-lexer==0.1.0.dev39", # sytax coloring to debug md - "dulwich==0.21.5", # python implementation of git - "deepmerge==1.1.0", - "legacy-cgi==2.6.3; python_version >= '3.13'", # stopgap fix for webtest after cgi dropped from stdlib in 3.13 -] - -src_root = os.curdir - - -def read_version(): - """ - Read directly the errbot/version.py and gives the version without loading Errbot. - :return: errbot.version.VERSION - """ - - variables = {} - with open(VERSION_FILE) as f: - exec(compile(f.read(), "version.py", "exec"), variables) - return variables["VERSION"] - - -def read(fname, encoding="ascii"): - return open( - os.path.join(os.path.dirname(__file__), fname), "r", encoding=encoding - ).read() - - -if __name__ == "__main__": - - VERSION = read_version() - - args = set(sys.argv) - - changes = read("CHANGES.rst", "utf8") - - if changes.find(VERSION) == -1: - raise Exception("You forgot to put a release note in CHANGES.rst ?!") - - if args & {"bdist", "bdist_dumb", "bdist_rpm", "bdist_wininst", "bdist_msi"}: - raise Exception("err doesn't support binary distributions") - - packages = find_packages(src_root, include=["errbot", "errbot.*"]) - - setup( - name="errbot", - version=VERSION, - packages=packages, - entry_points={ - "console_scripts": [ - "errbot = errbot.cli:main", - ] - }, - install_requires=deps, - tests_require=["nose", "webtest", "requests"], - package_data={ - "errbot": [ - "backends/*.plug", - "backends/*.html", - "backends/styles/*.css", - "backends/images/*.svg", - "core_plugins/*.plug", - "core_plugins/*.md", - "core_plugins/templates/*.md", - "storage/*.plug", - "templates/initdir/example.py", - "templates/initdir/example.plug", - "templates/initdir/config.py.tmpl", - "templates/*.md", - "templates/new_plugin.py.tmpl", - ], - }, - extras_require={ - "slack": [ - "errbot-backend-slackv3==0.3.2", - ], - "discord": [ - "err-backend-discord==3.0.1", - ], - "mattermost": [ - "err-backend-mattermost==3.0.0", - ], - "IRC": [ - "irc==20.3.0", - ], - "telegram": [ - "python-telegram-bot==13.15", - ], - "XMPP": [ - "slixmpp==1.8.4", - "pyasn1==0.6.3", - "pyasn1-modules==0.3.0", - ], - ':sys_platform!="win32"': ["daemonize==2.5.0"], - }, - author="errbot.io", - author_email="info@errbot.io", - description="Errbot is a chatbot designed to be simple to extend with plugins written in Python.", - long_description_content_type="text/x-rst", - long_description="".join([read("README.rst"), "\n\n", changes]), - license="GPL", - keywords="xmpp irc slack hipchat gitter tox chatbot bot plugin chatops", - url="http://errbot.io/", - classifiers=[ - "Development Status :: 5 - Production/Stable", - "Topic :: Communications :: Chat", - "Topic :: Communications :: Chat :: Internet Relay Chat", - "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - ], - src_root=src_root, - platforms="any", - ) diff --git a/tests/release_metadata_test.py b/tests/release_metadata_test.py new file mode 100644 index 000000000..3e27bf080 --- /dev/null +++ b/tests/release_metadata_test.py @@ -0,0 +1,38 @@ +"""Release-time gate. + +When ``errbot/version.py`` has been bumped past the dev sentinel ``9.9.9``, +``CHANGES.rst`` must contain a section heading for that version. On master +the sentinel keeps this test skipped; it only fires once a release is being +prepared (or a release branch is checked out), at which point it forces the +maintainer to update CHANGES.rst before the tag goes out. + +This replaces the install-time check that lived in setup.py. +""" +import re +from pathlib import Path + +import pytest + +REPO_ROOT = Path(__file__).resolve().parent.parent +DEV_SENTINEL = "9.9.9" + + +def _read_version() -> str: + """Read VERSION from errbot/version.py without importing the package.""" + ns: dict = {} + exec((REPO_ROOT / "errbot" / "version.py").read_text(), ns) + return ns["VERSION"] + + +def test_changes_rst_has_section_for_current_version(): + version = _read_version() + if version == DEV_SENTINEL: + pytest.skip(f"dev sentinel {DEV_SENTINEL} — no CHANGES.rst entry expected") + + changes = (REPO_ROOT / "CHANGES.rst").read_text() + pattern = rf"^v?{re.escape(version)}\b" + assert re.search(pattern, changes, re.MULTILINE), ( + f"CHANGES.rst is missing a section heading for version {version}. " + f"Rename the 'v{DEV_SENTINEL} (unreleased)' heading to the new " + "version, or add a new release section, before tagging." + ) diff --git a/tools/releases.sh b/tools/releases.sh new file mode 100755 index 000000000..daa625f50 --- /dev/null +++ b/tools/releases.sh @@ -0,0 +1,58 @@ +#!/bin/bash + +set -e + +# notes +## git cherry-pick ..master + +RELEASE=6.2.0 +BRANCH=6.2 +PYTHON_VERSION=3.9 + +REPO=git@github.com:errbotio/errbot.git +# REPO=/Users/saviles/data/git/sijis/errbot + +RELEASE_DIR=$(mktemp -d /tmp/errbot-release-${RELEASE}.XXX) + + +function header () { + title=$@ + ORANGE='\033[0;33m' + YELLOW='\033[1;33m' + NC='\033[0m' + + echo -e "${YELLOW}==================" + echo -e "${ORANGE}${title}" + echo -e "${YELLOW}==================" + echo -e ${NC} +} + + +header "git clone" +pushd ${RELEASE_DIR} +git clone ${REPO} errbot +pushd errbot +git checkout ${BRANCH} + +header "pypi build" +pipenv --python ${PYTHON_VERSION} +pipenv run pip3 install pytest twine build + +header "pre-release gate (version <-> CHANGES.rst)" +pipenv run python3 -m pytest tests/release_metadata_test.py -v + +pipenv run python3 -m build + +header "docker build" +# docker build -t errbot:release-test . +docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t errbotio/errbot:${RELEASE} -f Dockerfile . + +header "publish: git tag, docs, version files" +pipenv run twine check dist/* + +echo pipenv run twine upload dist/* +echo docker push tag errbot:release-test errbotio/errbot:${RELEASE} +echo docker push errbotio/errbot:${RELEASE} + +popd +popd \ No newline at end of file diff --git a/tox.ini b/tox.ini index 10f0be833..5bbba0ebd 100644 --- a/tox.ini +++ b/tox.ini @@ -17,9 +17,10 @@ commands = [testenv:dist-check] deps = + build twine commands = - python setup.py sdist + python -m build --sdist twine check {toxinidir}/dist/* [testenv:security]