From 6f2e3be31d16cb5108a9bdec85832101d85563c7 Mon Sep 17 00:00:00 2001 From: Felix Fontein Date: Wed, 5 Apr 2023 22:26:05 +0200 Subject: [PATCH] Began switching to hatchling, and requiring Python 3.9+. --- .flake8 | 12 + .github/workflows/antsibull-docs.yml | 86 ++--- .github/workflows/nox.yml | 72 ++++ .github/workflows/pythonlinters.yml | 82 ----- .github/workflows/pythontests.yml | 120 ------- .github/workflows/reuse.yml | 34 -- .mypy.ini | 14 - README.md | 96 +++--- changelogs/fragments/hatchling.yml | 9 + lint-flake8.sh | 7 - lint-mypy.sh | 7 - lint-pylint.sh | 7 - lint-pyre.sh | 10 - mypy.ini | 11 - noxfile.py | 314 ++++++++++++++++++ pyproject.toml | 163 +++++---- test-pytest.sh | 8 - {.github/workflows => tests}/validate-html.py | 0 18 files changed, 618 insertions(+), 434 deletions(-) create mode 100644 .flake8 create mode 100644 .github/workflows/nox.yml delete mode 100644 .github/workflows/pythonlinters.yml delete mode 100644 .github/workflows/pythontests.yml delete mode 100644 .github/workflows/reuse.yml delete mode 100644 .mypy.ini create mode 100644 changelogs/fragments/hatchling.yml delete mode 100755 lint-flake8.sh delete mode 100755 lint-mypy.sh delete mode 100755 lint-pylint.sh delete mode 100755 lint-pyre.sh delete mode 100644 mypy.ini create mode 100644 noxfile.py delete mode 100755 test-pytest.sh rename {.github/workflows => tests}/validate-html.py (100%) diff --git a/.flake8 b/.flake8 new file mode 100644 index 00000000..591be5b0 --- /dev/null +++ b/.flake8 @@ -0,0 +1,12 @@ +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or +# https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-FileCopyrightText: 2023 Maxwell G + +[flake8] +extend-ignore = E203 +count = true +max-complexity = 10 +# black's max-line-length is 89, but it doesn't touch long string literals. +max-line-length = 100 +statistics = true diff --git a/.github/workflows/antsibull-docs.yml b/.github/workflows/antsibull-docs.yml index 6b5e0851..8a1abef1 100644 --- a/.github/workflows/antsibull-docs.yml +++ b/.github/workflows/antsibull-docs.yml @@ -59,28 +59,25 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install poetry - sed -i -e 's/^python = .*/python = "^${{ matrix.python }}"/' pyproject.toml - poetry install - poetry update + pip install -e .[coverage] ../antsibull-core working-directory: antsibull-docs - name: Use antsibull-docs sphinx-init run: | - poetry run coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs sphinx-init --lenient --dest-dir . ${{ matrix.options }} + coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs sphinx-init --lenient --dest-dir . ${{ matrix.options }} working-directory: antsibull-docs - name: Patch build.sh to supply code coverage run: | - sed -i build.sh -e 's!antsibull-docs !poetry run coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs !g' - sed -i build.sh -e 's!sphinx-build !poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m sphinx.cmd.build !g' + sed -i build.sh -e 's!antsibull-docs !coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs !g' + sed -i build.sh -e 's!sphinx-build !coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m sphinx.cmd.build !g' cat build.sh working-directory: antsibull-docs - name: Install dependencies run: | - poetry run pip install ansible-core - poetry run pip install -r requirements.txt + pip install ansible-core + pip install -r requirements.txt working-directory: antsibull-docs - name: Install collections @@ -93,9 +90,9 @@ jobs: - name: Lint collection docs run: | - poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/docker --plugin-docs - poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/crypto - poetry run coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/sensu/sensu_go + coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/docker --plugin-docs + coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/community/crypto + coverage run -p --source antsibull_docs --source sphinx_antsibull_ext -m antsibull_docs.cli.antsibull_docs lint-collection-docs ~/.ansible/collections/ansible_collections/sensu/sensu_go working-directory: antsibull-docs if: contains(matrix.options, '--use-current') @@ -105,24 +102,29 @@ jobs: working-directory: antsibull-docs - name: Validate HTML - run: - python .github/workflows/validate-html.py build/html/ + run: | + pip install html5lib + python tests/validate-html.py build/html/ working-directory: antsibull-docs - name: Test plugin rendering - run: - poetry run coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs plugin --plugin-type module --dest-dir . community.crypto.acme_account_info + run: | + coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs plugin --plugin-type module --dest-dir . community.crypto.acme_account_info working-directory: antsibull-docs if: contains(matrix.options, '--use-current') - - name: Combine and upload coverage stats + - name: Combine coverage stats run: | - poetry run coverage combine .coverage.* - poetry run coverage report - poetry run coverage xml -i - poetry run codecov + coverage combine .coverage.* + coverage report + coverage xml -i working-directory: antsibull-docs + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + working-directory: antsibull-docs + build-stable: name: 'Build stable docsite' runs-on: ubuntu-latest @@ -147,10 +149,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install poetry - sed -i -e 's/^python = .*/python = "^3.11"/' pyproject.toml - poetry install - poetry update + pip install -e .[coverage] ../antsibull-core working-directory: antsibull-docs - name: Get hold of deps file @@ -161,17 +160,21 @@ jobs: - name: Build stable docs RST files run: | mkdir stable-docs - poetry run coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs stable --deps-file ansible.deps --dest-dir stable-docs --no-breadcrumbs --no-indexes + coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs stable --deps-file ansible.deps --dest-dir stable-docs --no-breadcrumbs --no-indexes working-directory: antsibull-docs - - name: Combine and upload coverage stats + - name: Combine coverage stats run: | - poetry run coverage combine .coverage.* - poetry run coverage report - poetry run coverage xml -i - poetry run codecov + coverage combine .coverage.* + coverage report + coverage xml -i working-directory: antsibull-docs + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + working-directory: antsibull-docs + build-devel: name: 'Build devel docsite' runs-on: ubuntu-latest @@ -196,10 +199,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install poetry - sed -i -e 's/^python = .*/python = "^3.11"/' pyproject.toml - poetry install - poetry update + pip install -e .[coverage] ../antsibull-core working-directory: antsibull-docs - name: Get hold of ansible.in file @@ -210,13 +210,17 @@ jobs: - name: Build devel docs RST files run: | mkdir devel-docs - poetry run coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs devel --pieces-file ansible.in --dest-dir devel-docs + coverage run -p --source antsibull_docs -m antsibull_docs.cli.antsibull_docs devel --pieces-file ansible.in --dest-dir devel-docs working-directory: antsibull-docs - - name: Combine and upload coverage stats + - name: Combine coverage stats run: | - poetry run coverage combine .coverage.* - poetry run coverage report - poetry run coverage xml -i - poetry run codecov + coverage combine .coverage.* + coverage report + coverage xml -i working-directory: antsibull-docs + + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + working-directory: antsibull-docs diff --git a/.github/workflows/nox.yml b/.github/workflows/nox.yml new file mode 100644 index 00000000..6973cbb7 --- /dev/null +++ b/.github/workflows/nox.yml @@ -0,0 +1,72 @@ +--- +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or +# https://www.gnu.org/licenses/gpl-3.0.txt) +# SPDX-License-Identifier: GPL-3.0-or-later +# SPDX-FileCopyrightText: 2023 Maxwell G - - ${{ contains(fromJson('["3.6", "3.7", "3.8"]'), matrix.python-version) && 'stable-1' || 'main' }} - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install poetry - if [ "${{ matrix.python-version }}" != "3.6" ]; then - # ^3.6 collides with 3.6.1 lower bound in antsibull-core - sed -i -e 's/^python = .*/python = "^${{ matrix.override-pyproject-python || matrix.python-version }}"/' pyproject.toml - fi - poetry install - poetry update - working-directory: antsibull-docs - - - name: Test with pytest and upload coverage stats - run: | - ./test-pytest.sh ${{ matrix.skip-is-error && '--error-for-skips' || '' }} - poetry run coverage xml -i - poetry run codecov - working-directory: antsibull-docs - - # Poetry won't install dependencies for antsibull-docs anymore on 3.6. So let's do this differently. - build-3-6: - runs-on: ubuntu-20.04 - strategy: - matrix: - python-version: - - 3.6 - - steps: - - name: Check out antsibull-docs - uses: actions/checkout@v3 - with: - path: antsibull-docs - - - name: Check out dependent project antsibull-core - uses: actions/checkout@v3 - with: - repository: ansible-community/antsibull-core - path: antsibull-core - ref: stable-1 - - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install --use-pep517 -e . -e ../antsibull-core \ - asynctest cryptography pytest "pytest-asyncio>=0.12" - working-directory: antsibull-docs - - - name: Test with pytest - run: >- - python -W 'ignore:"@coroutine" decorator is deprecated::asynctest.case' -m pytest -vv tests - working-directory: antsibull-docs diff --git a/.github/workflows/reuse.yml b/.github/workflows/reuse.yml deleted file mode 100644 index d44c17fb..00000000 --- a/.github/workflows/reuse.yml +++ /dev/null @@ -1,34 +0,0 @@ ---- -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -name: Verify REUSE - -on: - push: - branches: - - main - - stable-* - pull_request: - branches: - - main - - stable-* - # Run once per week (Friday at 06:00 UTC) - schedule: - - cron: '0 6 * * 5' - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - - name: Install dependencies - run: | - pip install reuse - - - name: Check REUSE compliance - run: | - reuse lint diff --git a/.mypy.ini b/.mypy.ini deleted file mode 100644 index b5aaf808..00000000 --- a/.mypy.ini +++ /dev/null @@ -1,14 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -[mypy] - -[mypy-sh.*] -ignore_missing_imports = True - -[mypy-aiofiles.*] -ignore_missing_imports = True - -[mypy-semantic_version.*] -ignore_missing_imports = True diff --git a/README.md b/README.md index daf3b54d..ff92bc03 100644 --- a/README.md +++ b/README.md @@ -6,8 +6,7 @@ SPDX-License-Identifier: GPL-3.0-or-later # antsibull-docs -- Ansible Documentation Build Scripts [![Discuss on Matrix at #docs:ansible.com](https://img.shields.io/matrix/docs:ansible.com.svg?server_fqdn=ansible-accounts.ems.host&label=Discuss%20on%20Matrix%20at%20%23docs:ansible.com&logo=matrix)](https://matrix.to/#/#docs:ansible.com) -[![Python linting badge](https://github.com/ansible-community/antsibull-docs/workflows/Python%20linting/badge.svg?event=push&branch=main)](https://github.com/ansible-community/antsibull-docs/actions?query=workflow%3A%22Python+linting%22+branch%3Amain) -[![Python testing badge](https://github.com/ansible-community/antsibull-docs/workflows/Python%20testing/badge.svg?event=push&branch=main)](https://github.com/ansible-community/antsibull-docs/actions?query=workflow%3A%22Python+testing%22+branch%3Amain) +[![Nox badge](https://github.com/ansible-community/antsibull-docs/actions/workflows/nox.yml/badge.svg)](https://github.com/ansible-community/antsibull-docs/actions/workflows/nox.yml) [![Build docs testing badge](https://github.com/ansible-community/antsibull-docs/workflows/antsibull-docs%20tests/badge.svg?event=push&branch=main)](https://github.com/ansible-community/antsibull-docs/actions?query=workflow%3A%22antsibull-docs+tests%22+branch%3Amain) [![Build CSS testing badge](https://github.com/ansible-community/antsibull-docs/workflows/Build%20CSS/badge.svg?event=push&branch=main)](https://github.com/ansible-community/antsibull-docs/actions?query=workflow%3A%22Build+CSS%22+branch%3Amain) [![Codecov badge](https://img.shields.io/codecov/c/github/ansible-community/antsibull-docs)](https://codecov.io/gh/ansible-community/antsibull-docs) @@ -33,26 +32,6 @@ From version 1.0.0 on, antsibull-docs sticks to semantic versioning and aims at We explicitly exclude code compatibility. **antsibull-docs is not supposed to be used as a library.** The only exception are dependencies with other antsibull projects (currently, only [antsibull](https://github.com/ansible-community/antsibull/) itself). If you want to use a certain part of antsibull-docs as a library, please create an issue so we can discuss whether we add a stable interface for **parts** of the Python code. We do not promise that this will actually happen though. -## Running from source - -Please note that to run antsibull-docs from source, you need to install some related projects adjacent to the antsibull-docs checkout. More precisely, assuming you checked out the antsibull-docs repository in a directory `./antsibull-docs/`, you need to check out the following projects in the following locations: - -- [antsibull-core](https://github.com/ansible-community/antsibull-core/) needs to be checked out in `./antsibull-core/`. - -This can be done as follows: - - git clone https://github.com/ansible-community/antsibull-core.git - git clone https://github.com/ansible-community/antsibull-docs.git - cd antsibull-docs - -Scripts are created by poetry at build time. So if you want to run from a checkout, you'll have to run them under poetry:: - - python3 -m pip install poetry - poetry install # Installs dependencies into a virtualenv - poetry run antsibull-docs --help - -Note: When installing a package published by poetry, it is best to use pip >= 19.0. Installing with pip-18.1 and below could create scripts which use pkg_resources which can slow down startup time (in some environments by quite a large amount). - ## Using the Sphinx extension Include it in your Sphinx configuration ``conf.py``:: @@ -83,20 +62,63 @@ apt-get install sassc npm install -g autoprefixer cssnano postcss postcss-cli ``` +## Development + +Install and run `nox` to run all tests. That's it for simple contributions! +`nox` will create virtual environments in `.nox` inside the checked out project +and install the requirements needed to run the tests there. + + +--- + +antsibull-docs depends on the sister antsibull-core project. +By default, `nox` will install a development version of this project from +Github. +If you're hacking on antsibull-core alongside antsibull-docs, nox will automatically +install the project from `../antsibull-core` when running tests if those paths +exist. +You can change this behavior through the `OTHER_ANTSIBULL_MODE` env var: + +- `OTHER_ANTSIBULL_MODE=auto` — the default behavior described above +- `OTHER_ANTSIBULL_MODE=local` — install the project from `../antsibull-core`. + Fail if that paths doesn't exist. +- `OTHER_ANTSIBULL_MODE=git` — install the projects from the Github main branch +- `OTHER_ANTSIBULL_MODE=pypi` — install the latest version from PyPI + + +To run specific tests: + +1. `nox -e test` to only run unit tests; +2. `nox -e lint` to run all linters; +3. `nox -e codeqa` to run `flake8`, `pylint`, and `reuse lint`; +4. `nox -e typing` to run `mypy` and `pyre`. +5. `nox -e coverage_release` to build a test ansible release. + This is expensive, so it's not run by default. + +To create a more complete local development env: + +``` console +git clone https://github.com/ansible-community/antsibull-core.git +git clone https://github.com/ansible-community/antsibull-docs.git +cd antsibull-docs +python3 -m venv venv +. ./venv/bin/activate +pip install -e '.[dev]' -e ../antsibull-core +[...] +nox +``` + ## Creating a new release: -If you want to create a new release:: - - vim pyproject.toml # Make sure version number is correct - vim changelogs/fragment/$VERSION_NUMBER.yml # create 'release_summary:' fragment - antsibull-changelog release --version $VERSION_NUMBER - git add CHANGELOG.rst changelogs - git commit -m "Release $VERSION_NUMBER." - poetry build - poetry publish # Uploads to pypi. Be sure you really want to do this - - git tag $VERSION_NUMBER - git push --tags - vim pyproject.toml # Bump the version number to X.Y.Z.post0 - git commit -m 'Update the version number for the next release' pyproject.toml - git push +1. Run `nox -e bump -- `. This: + * Bumps the package version in `pyproject.toml`. + * Creates `changelogs/fragments/.yml` with a `release_summary` section. + * Runs `antsibull-changelog release` and adds the changed files to git. + * Commits with message `Release .` and runs `git tag -a -m 'antsibull-docs ' `. + * Runs `hatch build`. +2. Run `git push` to the appropriate remotes. +3. Once CI passes on GitHub, run `nox -e publish`. This: + * Runs `hatch publish`; + * Bumps the version to `.post0`; + * Adds the changed file to git and run `git commit -m 'Post-release version bump.'`; +4. Run `git push --follow-tags` to the appropriate remotes and create a GitHub release. diff --git a/changelogs/fragments/hatchling.yml b/changelogs/fragments/hatchling.yml new file mode 100644 index 00000000..629032f1 --- /dev/null +++ b/changelogs/fragments/hatchling.yml @@ -0,0 +1,9 @@ +--- +major_changes: + - Change pyproject build backend from ``poetry-core`` to ``hatchling``. + ``pip install antsibull`` works exactly the same as before, + but some users may be affected depending on how they build/install the + project + (https://github.com/ansible-community/antsibull-docs/pull/115). +breaking_changes: + - Drop support for Python 3.6, 3.7, and 3.8 (https://github.com/ansible-community/antsibull-docs/pull/115)." diff --git a/lint-flake8.sh b/lint-flake8.sh deleted file mode 100755 index c9e7f727..00000000 --- a/lint-flake8.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -poetry run flake8 src/antsibull_docs src/sphinx_antsibull_ext --count --max-complexity=10 --max-line-length=100 --statistics "$@" diff --git a/lint-mypy.sh b/lint-mypy.sh deleted file mode 100755 index 65b8583d..00000000 --- a/lint-mypy.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -MYPYPATH=stubs/ poetry run mypy src/antsibull_docs src/sphinx_antsibull_ext "$@" diff --git a/lint-pylint.sh b/lint-pylint.sh deleted file mode 100755 index 94cc20b3..00000000 --- a/lint-pylint.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -poetry run pylint --rcfile .pylintrc.automated src/antsibull_docs src/sphinx_antsibull_ext "$@" diff --git a/lint-pyre.sh b/lint-pyre.sh deleted file mode 100755 index c48ef62c..00000000 --- a/lint-pyre.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e - -PURELIB=$(poetry run python -c 'import sysconfig; print(sysconfig.get_path("purelib"))') -PLATLIB=$(poetry run python -c 'import sysconfig; print(sysconfig.get_path("platlib"))') -poetry run pyre --source-directory src --search-path ../antsibull-core/src/ --search-path "$PURELIB" --search-path "$PLATLIB" --search-path stubs/ "$@" diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 172d7578..00000000 --- a/mypy.ini +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -[mypy] - -[mypy-sh.*] -ignore_missing_imports = True - -[mypy-semantic_version.*] -ignore_missing_imports = True diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 00000000..e6247475 --- /dev/null +++ b/noxfile.py @@ -0,0 +1,314 @@ +# Copyright (C) 2023 Maxwell G +# SPDX-License-Identifier: GPL-3.0-or-later +# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or +# https://www.gnu.org/licenses/gpl-3.0.txt) + +from __future__ import annotations + +import os +from pathlib import Path + +import nox + +DEFAULT_MODE = os.environ.get("OTHER_ANTSIBULL_MODE", "auto") +IN_CI = "GITHUB_ACTIONS" in os.environ +ALLOW_EDITABLE = os.environ.get("ALLOW_EDITABLE", str(not IN_CI)).lower() in ( + "1", + "true", +) + +# Always install latest pip version +os.environ["VIRTUALENV_DOWNLOAD"] = "1" +nox.options.sessions = "lint", "test" + + +def install(session: nox.Session, *args, editable=False, **kwargs): + # nox --no-venv + if isinstance(session.virtualenv, nox.virtualenv.PassthroughEnv): + session.warn(f"No venv. Skipping installation of {args}") + return + # Don't install in editable mode in CI or if it's explicitly disabled. + # This ensures that the wheel contains all of the correct files. + if editable and ALLOW_EDITABLE: + args = ("-e", *args) + session.install(*(str(arg) for arg in args), "-U", **kwargs) + + +def other_antsibull( + mode: str | None = None, +) -> list[str | Path]: + if mode is None: + mode = DEFAULT_MODE + to_install: list[str | Path] = [] + args = ("antsibull-core", ) + for project in args: + path = Path("../", project) + path_exists = path.is_dir() + if mode == "auto": + if path_exists: + mode = "local" + else: + mode = "git" + if mode == "local": + if not path_exists: + raise ValueError(f"Cannot install {project}! {path} does not exist!") + if ALLOW_EDITABLE: + to_install.append("-e") + to_install.append(path) + elif mode == "git": + to_install.append( + f"{project} @ " + f"https://github.com/ansible-community/{project}/archive/main.tar.gz" + ) + elif mode == "pypi": + to_install.append(project) + else: + raise ValueError(f"install_other_antsibull: invalid argument mode={mode!r}") + return to_install + + +@nox.session(python=["3.9", "3.10", "3.11"]) +def test(session: nox.Session): + install( + session, + ".[test, coverage]", + *other_antsibull(), + editable=True, + ) + covfile = Path(session.create_tmp(), ".coverage") + more_args = [] + if session.python == "3.11": + more_args.append("--error-for-skips") + session.run( + "pytest", + "--cov-branch", + "--cov=antsibull_docs", + "--cov=sphinx_antsibull_ext", + "--cov-report", + "term-missing", + *more_args, + *session.posargs, + env={"COVERAGE_FILE": f"{covfile}", **os.environ}, + ) + + +@nox.session +def coverage_release(session: nox.Session): + """ + Build a test release and report coverage + """ + install( + session, + ".", + *other_antsibull(), + "ansible-core", + "coverage", + editable=True, + ) + + build_command = ( + "coverage run -p --source antsibull -m antsibull.cli.antsibull_build" + ) + posargs = session.posargs + # Set default settings + if not posargs: + posargs = ( + "-e", + "antsibull_ansible_version=7.99.0", + "-e", + "antsibull_ansible_git_version=stable-2.14", + ) + collections = Path(session.create_tmp()).joinpath("collections") + os.environ["ANSIBLE_COLLECTIONS_PATH"] = str(collections) + session.run( + "ansible-galaxy", + "collection", + "install", + "git+https://github.com/ansible-collections/community.general", + ) + session.run( + "ansible-playbook", + "-vv", + "playbooks/build-single-release.yaml", + *posargs, + "-e", + f"antsibull_build_command={build_command!r}", + ) + session.run("coverage", "combine", *Path(".").glob(".coverage.*")) + session.run("coverage", "report") + session.run("coverage", "xml", "-i") + + +@nox.session +def coverage(session: nox.Session): + install(session, "coverage[toml]") + combined = map(str, Path().glob(".nox/test*/tmp/.coverage")) + # Combine the results into a single .coverage file in the root + session.run("coverage", "combine", "--keep", *combined) + # Create a coverage.xml for codecov + session.run("coverage", "xml") + # Display the combined results to the user + session.run("coverage", "report", "-m") + + +@nox.session +def lint(session: nox.Session): + session.notify("codeqa") + session.notify("typing") + + +@nox.session +def codeqa(session: nox.Session): + install(session, ".[codeqa]", *other_antsibull(), editable=True) + session.run("flake8", "src/antsibull_docs", "src/sphinx_antsibull_ext", *session.posargs) + session.run("pylint", "--rcfile", ".pylintrc.automated", "src/antsibull_docs", "src/sphinx_antsibull_ext") + session.run("reuse", "lint") + + +@nox.session +def typing(session: nox.Session): + others = other_antsibull() + install(session, ".[typing]", *others, editable=True) + session.run("mypy", "src/antsibull_docs", "src/sphinx_antsibull_ext") + + additional_libraries = [] + for path in others: + if isinstance(path, Path): + additional_libraries.extend(("--search-path", str(path / "src"))) + + purelib = session.run( + "python", + "-c", + "import sysconfig; print(sysconfig.get_path('purelib'))", + silent=True, + ).strip() + platlib = session.run( + "python", + "-c", + "import sysconfig; print(sysconfig.get_path('platlib'))", + silent=True, + ).strip() + session.run( + "pyre", + "--source-directory", + "src", + "--search-path", + purelib, + "--search-path", + platlib, + "--search-path", + "stubs/", + *additional_libraries, + ) + + +def _repl_version(session: nox.Session, new_version: str): + with open("pyproject.toml", "r+") as fp: + lines = tuple(fp) + fp.seek(0) + for line in lines: + if line.startswith("version = "): + line = f'version = "{new_version}"\n' + fp.write(line) + fp.truncate() + + +def check_no_modifications(session: nox.Session) -> None: + modified = session.run( + "git", + "status", + "--porcelain=v1", + "--untracked=normal", + external=True, + silent=True, + ) + if modified: + session.error( + "There are modified or untracked files. " + "Commit, restore, or remove them before running this" + ) + + +@nox.session +def bump(session: nox.Session): + check_no_modifications(session) + if len(session.posargs) not in (1, 2): + session.error( + "Must specify 1-2 positional arguments: nox -e bump -- " + "[ ]." + " If release_summary_message has not been specified, " + "a file changelogs/fragments/.yml must exist" + ) + version = session.posargs[0] + fragment_file = Path(f"changelogs/fragments/{version}.yml") + if len(session.posargs) == 1: + if not fragment_file.is_file(): + session.error( + f"Either {fragment_file} must already exist, " + "or two positional arguments must be provided." + ) + install(session, "antsibull-changelog", "hatch", "tomli ; python_version < '3.11'") + _repl_version(session, version) + if len(session.posargs) > 1: + fragment = session.run( + "python", + "-c", + "import yaml ; " + f"print(yaml.dump(dict(release_summary={repr(session.posargs[1])})))", + silent=True, + ) + with open(fragment_file, "w") as fp: + print(fragment, file=fp) + session.run("git", "add", "pyproject.toml", fragment_file, external=True) + session.run("git", "commit", "-m", f"Prepare {version}.", external=True) + session.run("antsibull-changelog", "release") + session.run( + "git", + "add", + "CHANGELOG.rst", + "changelogs/changelog.yaml", + "changelogs/fragments/", + external=True, + ) + install(session, ".") # Smoke test + session.run("git", "commit", "-m", f"Release {version}.", external=True) + session.run( + "git", + "tag", + "-a", + "-m", + f"antsibull-docs {version}", + "--edit", + version, + external=True, + ) + session.run("hatch", "build") + + +@nox.session +def publish(session: nox.Session): + check_no_modifications(session) + install(session, "hatch") + session.run("hatch", "publish", *session.posargs) + version = session.run("hatch", "version", silent=True).strip() + _repl_version(session, f"{version}.post0") + session.run("git", "commit", "pyproject.toml", external=True) + session.run("git", "commit", "-m", "Post-release version bump.", external=True) + + +@nox.session +def install_env(session: nox.Session): + """ + Install antsibull-docs and the other project in the the local environment. + Invoke with `nox -e install_env --no-venv` + """ + session.run( + "pip", + "install", + "-U", + ".", + *other_antsibull(), + *session.posargs, + external=True, + editable=True, + ) diff --git a/pyproject.toml b/pyproject.toml index d2d3fd25..cab8e1df 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,78 +3,129 @@ # SPDX-License-Identifier: GPL-3.0-or-later [build-system] -requires = ["poetry-core>=1.0.7"] -build-backend = "poetry.core.masonry.api" +requires = ["hatchling"] +build-backend = "hatchling.build" -[tool.poetry] +[project] name = "antsibull-docs" -version = "1.11.0.post0" +version = "2.0.0.a1" description = "Tools for building Ansible documentation" -authors = ["Toshio Kuratomi ", "Felix Fontein "] license = "GPL-3.0-or-later" +license-files = {globs=["LICENSES/*.txt"]} readme = "README.md" -repository = "https://github.com/ansible-community/antsibull-docs" -packages = [ - { include = "antsibull_docs", from="src" }, - { include = "sphinx_antsibull_ext", from="src" }, - { include = "tests", format = "sdist" } -] classifiers = [ - "Development Status :: 3 - Alpha", + "Development Status :: 5 - Production/Stable", "Framework :: Ansible", - "Intended Audience :: Developers" + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Typing :: Typed", +] +requires-python = ">=3.9" +dependencies = [ + "ansible-pygments", + "antsibull-core >= 1.2.0, < 3.0.0", + "asyncio-pool", + "docutils", + "jinja2 >= 3.0", + "packaging", + "rstcheck >= 3.0.0, < 7.0.0", + "sphinx", + # sh v2 has breaking changes. + # https://github.com/ansible-community/antsibull-core/issues/34 + "sh >= 1.0.0, < 2.0.0", + # pydantic v2 is a major rewrite + "pydantic >= 1.0.0, < 2.0.0", + "semantic_version", + "aiohttp >= 3.0.0", + "twiggy", + "PyYAML", ] -[tool.poetry.urls] +[[project.authors]] +name = "Toshio Kuratomi" +email = "a.badger@gmail.com" + +[[project.authors]] +name = "Felix Fontein" +email = "felix@fontein.de" + +[[project.maintainers]] +name = "Felix Fontein" +email = "felix@fontein.de" + +[[project.maintainers]] +name = "Maxwell G" +email = "maxwell@gtmx.me" + +[project.urls] +"Source code" = "https://github.com/ansible-community/antsibull-docs" "Code of Conduct" = "https://docs.ansible.com/ansible/latest/community/code_of_conduct.html" "Bug tracker" = "https://github.com/ansible-community/antsibull-docs/issues" -[tool.poetry.scripts] +[project.scripts] antsibull-docs = "antsibull_docs.cli.antsibull_docs:main" -[tool.poetry.dependencies] -python = "^3.6.1" -ansible-pygments = "*" -antsibull-core = ">= 1.2.0, < 3.0.0" -asyncio-pool = "*" -docutils = "*" -jinja2 = ">= 3.0" -packaging = "*" -rstcheck = ">= 3.0.0, < 7.0.0" -sphinx = "*" -# sh v2 has breaking changes. -# https://github.com/ansible-community/antsibull-core/issues/34 -sh = ">= 1.0.0 < 2.0.0" -# pydantic v2 is a major rewrite -pydantic = ">= 1.0.0 < 2.0.0" -semantic_version = "*" -aiohttp = ">= 3.0.0" -twiggy = "*" -PyYAML = "*" +[project.optional-dependencies] +codeqa = [ + "flake8 >= 3.8.0", + "pylint ~= 2.12.0", + "reuse", +] +coverage = [ + "coverage[toml]", +] +test = [ + "ansible-core >= 2.14.0", + "asynctest", + "cryptography", + "pytest", + "pytest-asyncio >= 0.12", + "pytest-cov", + "pytest-error-for-skips", +] +typing = [ + "mypy", + # https://github.com/facebook/pyre-check/issues/398 + "pyre-check ~= 0.9.17", + "types-aiofiles", + "types-docutils", + "types-PyYAML", +] +dev = [ + # Used by nox sessions + "antsibull-docs[codeqa]", + "antsibull-docs[test]", + "antsibull-docs[typing]", + # misc + "nox", +] -[tool.poetry.dev-dependencies] -ansible-core = {version = ">= 2.14.0b1", python = ">=3.9"} -asynctest = "*" -cryptography = "*" -codecov = "*" -flake8 = ">= 3.8.0" -mypy = "*" -# https://github.com/facebook/pyre-check/issues/398 -pyre-check = "^0.9.15" -pylint = "^2.12.0" -pytest = "*" -pytest-asyncio = ">= 0.12" -pytest-cov = "*" -pytest-error-for-skips = "*" -# Needed for TypedDict in rstcheck-core stubs -typing-extensions = {version = ">=3.7.4", python = "<3.8"} -types-aiofiles = "*" -types-docutils = "*" -types-PyYAML = "*" -# For development, we install dependent projects under our control in dev mode: -antsibull-core = { path = "../antsibull-core/", develop = true } +[tool.hatch.build.targets.wheel] +packages = ["src/antsibull_docs", "src/sphinx_antsibull_ext"] [tool.isort] -line_length = 100 +# Match black +profile = "black" +line_length = 89 +# multi_line_output = 3 include_trailing_comma = true + +[tool.coverage.paths] +source = [ + "src", + "*/site-packages", +] + +[tool.mypy] +mypy_path = "stubs/" + +[[tool.mypy.overrides]] +module = "sh" +ignore_missing_imports = true + +[[tool.mypy.overrides]] +module = "semantic_version" +ignore_missing_imports = true diff --git a/test-pytest.sh b/test-pytest.sh deleted file mode 100755 index c364300a..00000000 --- a/test-pytest.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -# Copyright (c) Ansible Project -# GNU General Public License v3.0+ (see LICENSES/GPL-3.0-or-later.txt or https://www.gnu.org/licenses/gpl-3.0.txt) -# SPDX-License-Identifier: GPL-3.0-or-later - -set -e -PYTHONPATH=src poetry run python -W 'ignore:"@coroutine" decorator is deprecated::asynctest.case' \ - -m pytest --cov-branch --cov=antsibull_docs --cov=sphinx_antsibull_ext --cov-report term-missing -vv tests "$@" diff --git a/.github/workflows/validate-html.py b/tests/validate-html.py similarity index 100% rename from .github/workflows/validate-html.py rename to tests/validate-html.py