diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bd9dd696..4b7a35a6 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -10,49 +10,54 @@ jobs: linting: runs-on: ubuntu-24.04 steps: - - name: Install dependencies - run: sudo DEBIAN_FRONTEND=noninteractive apt-get -qy install tox - name: Git checkout uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Run ruff and mypy checks - run: tox -e ruff,mypy - py310: - runs-on: ubuntu-22.04 - steps: - - name: Install dependencies - run: sudo DEBIAN_FRONTEND=noninteractive apt-get -qy install tox - - name: Git checkout - uses: actions/checkout@v4 - - name: Run tox - run: tox -e py310 - py312: - runs-on: ubuntu-24.04 + run: uvx --with tox-uv tox -e ruff,mypy + + unit-tests: + strategy: + matrix: + include: + - python: "3.10" + runner: ubuntu-22.04 + tox-env: py310 + - python: "3.12" + runner: ubuntu-24.04 + tox-env: py312 + runs-on: ${{ matrix.runner }} steps: - - name: Install dependencies - run: sudo DEBIAN_FRONTEND=noninteractive apt-get -qy install tox - name: Git checkout uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Run tox - run: tox -e py312 + run: uvx --with tox-uv tox -e ${{ matrix.tox-env }} + docs: runs-on: ubuntu-24.04 steps: - - name: Install dependencies - run: sudo DEBIAN_FRONTEND=noninteractive apt-get -qy install tox - name: Git checkout uses: actions/checkout@v4 + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Build rtd docs - run: tox -e docs + run: uvx --with tox-uv tox -e docs + integration-tests: runs-on: ubuntu-24.04 steps: - name: Install dependencies run: | sudo apt-get update -q - sudo apt-get install -qy distro-info tox + sudo apt-get install -qy distro-info sudo snap install lxd - name: Git checkout uses: actions/checkout@v3 + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Setup LXD uses: canonical/setup-lxd@v0.1.2 - name: Setup pycloudlib config and ssh key @@ -62,4 +67,4 @@ jobs: echo "[lxd]" > ~/.config/pycloudlib.toml - name: Run CI integration tests run: | - tox -e integration-tests-ci -- --color=yes tests/integration_tests/ + uvx --with tox-uv tox -e integration-tests-ci -- --color=yes tests/integration_tests/ diff --git a/.github/workflows/main_check.yaml b/.github/workflows/main_check.yaml index c5679fd4..5333c675 100644 --- a/.github/workflows/main_check.yaml +++ b/.github/workflows/main_check.yaml @@ -12,7 +12,7 @@ jobs: - name: Install dependencies run: | sudo apt-get update -q - sudo apt-get install -qy distro-info tox + sudo apt-get install -qy distro-info sudo apt-get remove --yes --purge azure-cli - name: Initialize Pycloudlib env: @@ -25,7 +25,9 @@ jobs: echo "$PYCLOUDLIB_TOML" > ~/.config/pycloudlib.toml - name: Git checkout uses: actions/checkout@v3 + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Run CI integration tests run: | - GOOGLE_APPLICATION_CREDENTIALS=~/.config/gce_credentials tox -e integration-tests-main-check \ + GOOGLE_APPLICATION_CREDENTIALS=~/.config/gce_credentials uvx --with tox-uv tox -e integration-tests-main-check \ -- --color=yes tests/integration_tests diff --git a/.github/workflows/version_check.yaml b/.github/workflows/version_check.yaml index 43c67c8a..6c566b71 100644 --- a/.github/workflows/version_check.yaml +++ b/.github/workflows/version_check.yaml @@ -11,10 +11,8 @@ jobs: with: fetch-depth: 0 # Necessary to compare with the main branch - - name: Set up Python - uses: actions/setup-python@v2 - with: - python-version: '3.x' # Use Python 3.x + - name: Install uv + uses: astral-sh/setup-uv@v6 - name: Check version bump run: | @@ -22,12 +20,10 @@ jobs: OLD_VERSION=$(git show origin/main:VERSION) # Read VERSION file from the current branch NEW_VERSION=$(cat VERSION) - + echo "Old version: $OLD_VERSION" echo "New version: $NEW_VERSION" - - pip install -r ci-requirements.txt # Run the version comparison Python script - python .github/workflows/version_check.py "$OLD_VERSION" "$NEW_VERSION" + uv run --with packaging .github/workflows/version_check.py "$OLD_VERSION" "$NEW_VERSION" diff --git a/.readthedocs.yaml b/.readthedocs.yaml index ae37cb38..84468f51 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -9,8 +9,10 @@ formats: all python: install: - - path: . - - requirements: docs/requirements.txt + - method: uv + path: . + extra-requirements: + - docs sphinx: configuration: docs/conf.py diff --git a/Makefile b/Makefile index 917dabc5..13afca55 100644 --- a/Makefile +++ b/Makefile @@ -1,35 +1,32 @@ PYTHON = python3 -SETUP := $(PYTHON) setup.py +UV = uv .PHONY: build clean install publish source test venv build: - $(SETUP) build + $(UV) build clean: - $(SETUP) clean - rm -rf .tox .eggs *.egg-info build dist venv + rm -rf .tox .eggs *.egg-info build dist .venv @find . -regex '.*\(__pycache__\|\.py[co]\)' -delete $(MAKE) -C docs clean install: - $(SETUP) install + $(UV) sync publish: rm -rf dist/ - $(SETUP) sdist - pip install twine + $(UV) build + $(UV) run pip install twine twine upload dist/* source: - $(SETUP) sdist + $(UV) build test: - $(SETUP) check -r -s - tox + $(UV) run tox venv: - $(PYTHON) -m virtualenv -p /usr/bin/$(PYTHON) venv - venv/bin/pip install -Ur requirements.txt + uv sync @echo "Now run the following to activate the virtual env:" - @echo ". venv/bin/activate" + @echo ". .venv/bin/activate" diff --git a/README.md b/README.md index df5050ab..456f6ab4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # pycloudlib -[![Build Status](https://travis-ci.com/canonical/pycloudlib.svg?branch=master)](https://travis-ci.com/canonical/pycloudlib) +[![CI](https://github.com/canonical/pycloudlib/actions/workflows/ci.yaml/badge.svg)](https://github.com/canonical/pycloudlib/actions/workflows/ci.yaml) Python library to launch, interact, and snapshot cloud instances @@ -9,17 +9,15 @@ Python library to launch, interact, and snapshot cloud instances Install directly from [PyPI](https://pypi.org/project/pycloudlib/): ```shell -pip3 install pycloudlib +pip install pycloudlib ``` -Project's requirements.txt file can include pycloudlib as a dependency. Check out the [pip documentation](https://pip.readthedocs.io/en/1.1/requirements.html) for instructions on how to include a particular version or git hash. - -Install from latest changes in `main` branch: +Install from the latest `main` branch: ```shell -git clone https://github.com/canonical/pycloudlib.git +git clone https://git.launchpad.net/pycloudlib cd pycloudlib -python3 setup.py install +uv sync ``` ## Usage diff --git a/VERSION b/VERSION index 63115ced..2f6094b5 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1!10.17.0 +1!10.18.0 diff --git a/ci-requirements.txt b/ci-requirements.txt deleted file mode 100644 index 75aa6c06..00000000 --- a/ci-requirements.txt +++ /dev/null @@ -1 +0,0 @@ -packaging \ No newline at end of file diff --git a/docs/contributing.md b/docs/contributing.md index 2ddd507d..adb23e17 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -25,7 +25,7 @@ The makefile can be used to create a Python virtual environment and do local tes ```shell # Creates a python virtual environment with all requirements make venv -. venv/bin/activate +. .venv/bin/activate ``` ### Documentation @@ -39,7 +39,7 @@ Documentation should be written in Markdown whenever possible. When making changes please keep the following in mind: * Keep pull requests limited to a single issue -* Code must be formatted to [Black](https://black.readthedocs.io/en/stable/index.html) standards +* Code must be formatted to [ruff](https://docs.astral.sh/ruff/) standards * Run `tox -e format` to reformat code accordingly * Run `tox` to execute style and lint checks * When adding new clouds please add detailed documentation under the `docs` directory and code examples under `examples` diff --git a/docs/index.rst b/docs/index.rst index c8044fd2..9fcbdc6f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,18 +21,15 @@ Install directly from `PyPI `_: .. code-block:: shell - pip3 install pycloudlib + uv pip install pycloudlib -Project's requirements.txt file can include pycloudlib as a dependency. Check -out the `pip documentation `_ for instructions on how to include a particular version or git hash. - -Install from latest master: +Install from the latest ``main`` branch: .. code-block:: shell git clone https://git.launchpad.net/pycloudlib cd pycloudlib - python3 setup.py install + uv sync ***** Usage @@ -49,7 +46,7 @@ for more information. Bugs **** -File bugs on Launchpad under the `pycloudlib project `_. +File bugs on `Launchpad `_. ******* Contact diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 2a4ec96e..00000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -myst-parser -sphinx -sphinx_rtd_theme diff --git a/examples/ibm_classic.py b/examples/ibm_classic.py index 177e0f7b..0cac4480 100644 --- a/examples/ibm_classic.py +++ b/examples/ibm_classic.py @@ -4,6 +4,7 @@ import logging import os +from typing import Optional import pycloudlib import pycloudlib.ibm_classic @@ -39,7 +40,7 @@ def manage_ssh_key(classic: pycloudlib.IBMClassic): def launch_with_basic_cloud_config( - ibm_classic: pycloudlib.IBMClassic, disk_size="25G", datacenter: str = None + ibm_classic: pycloudlib.IBMClassic, disk_size="25G", datacenter: Optional[str] = None ): """Launch a basic instance and demo basic functionality.""" image_gid = ibm_classic.released_image("22.04", disk_size=disk_size) @@ -72,7 +73,7 @@ def launch_with_basic_cloud_config( def launch_and_demo_all_lifecycle_features( - ibm_classic: pycloudlib.IBMClassic, disk_size="25G", datacenter: str = None + ibm_classic: pycloudlib.IBMClassic, disk_size="25G", datacenter: Optional[str] = None ): """Launch a basic instance and demo basic functionality.""" image_gid = ibm_classic.released_image("22.04", disk_size=disk_size) diff --git a/examples/oracle/oracle-cluster-demo.py b/examples/oracle/oracle-cluster-demo.py index 8ecc06bf..0b8dd945 100644 --- a/examples/oracle/oracle-cluster-demo.py +++ b/examples/oracle/oracle-cluster-demo.py @@ -4,14 +4,15 @@ import logging import sys +from typing import Optional import pycloudlib def demo_cluster( - availability_domain: str = None, - compartment_id: str = None, - vcn_name: str = None, + availability_domain: Optional[str] = None, + compartment_id: Optional[str] = None, + vcn_name: Optional[str] = None, ): """Show example of using the OCI library to launch a cluster instance and ping between them.""" with pycloudlib.OCI( diff --git a/pycloudlib/azure/cloud.py b/pycloudlib/azure/cloud.py index e49e6229..b2930723 100644 --- a/pycloudlib/azure/cloud.py +++ b/pycloudlib/azure/cloud.py @@ -11,7 +11,7 @@ from azure.core.exceptions import HttpResponseError, ResourceNotFoundError from azure.mgmt.compute import ComputeManagementClient from azure.mgmt.network import NetworkManagementClient -from azure.mgmt.resource import ResourceManagementClient +from azure.mgmt.resource.resources import ResourceManagementClient from pycloudlib.azure import security_types, util from pycloudlib.azure.instance import AzureInstance, VMInstanceStatus diff --git a/pycloudlib/ec2/cloud.py b/pycloudlib/ec2/cloud.py index 7a8fc314..72ef45c0 100644 --- a/pycloudlib/ec2/cloud.py +++ b/pycloudlib/ec2/cloud.py @@ -419,7 +419,7 @@ def list_keys(self): keypair_names.append(keypair["KeyName"]) return keypair_names - def snapshot(self, instance, clean=True): + def snapshot(self, instance, clean=True, **kwargs): """Snapshot an instance and generate an image from it. Args: diff --git a/pycloudlib/gce/instance.py b/pycloudlib/gce/instance.py index cbb67c8f..f8944468 100644 --- a/pycloudlib/gce/instance.py +++ b/pycloudlib/gce/instance.py @@ -174,7 +174,7 @@ def _wait_for_instance_start(self, **kwargs): self._wait_for_status("RUNNING") self._ip = self._get_ip() - def wait_for_delete(self, sleep_seconds=30, raise_on_fail=False): + def wait_for_delete(self, sleep_seconds=30, raise_on_fail=False, **kwargs): """Wait for instance to be deleted.""" get_instance_request = compute_v1.GetInstanceRequest( project=self.project, diff --git a/pycloudlib/lxd/cloud.py b/pycloudlib/lxd/cloud.py index 9efba4b7..d60e7b44 100644 --- a/pycloudlib/lxd/cloud.py +++ b/pycloudlib/lxd/cloud.py @@ -397,7 +397,7 @@ def delete_image(self, image_id, **kwargs): subp(["lxc", "image", "delete", image_id]) self._log.debug("Deleted %s", image_id) - def snapshot(self, instance, clean=True, name=None): + def snapshot(self, instance, clean=True, name=None, **kwargs): """Take a snapshot of the passed in instance for use as image. :param instance: The instance to create an image from diff --git a/pycloudlib/lxd/instance.py b/pycloudlib/lxd/instance.py index 45aa0ed3..ca034e95 100644 --- a/pycloudlib/lxd/instance.py +++ b/pycloudlib/lxd/instance.py @@ -462,7 +462,7 @@ def start(self, wait=True): if wait: self.wait() - def wait_for_delete(self): + def wait_for_delete(self, **kwargs): """Wait for delete. Not used for LXD. @@ -494,13 +494,13 @@ def wait_for_state(self, desired_state: str, num_retries: int = 100): time.sleep(1) raise PycloudlibTimeoutError - def wait_for_stop(self): + def wait_for_stop(self, **kwargs): """Wait for cloud instance to transition to stop state.""" # Ephemeral instances will not go to STOPPED. They get destroyed. if not self.ephemeral: self.wait_for_state("STOPPED") - def _wait_for_instance_start(self): + def _wait_for_instance_start(self, **kwargs): """Wait for the cloud instance to be up. LXD VMs need to install systemd units upon initialization. There is @@ -530,17 +530,17 @@ def _wait_for_instance_start(self): class LXDVirtualMachineInstance(LXDInstance): """LXD Virtual Machine backed instance.""" - def _run_command(self, command, stdin): + def _run_command(self, command, stdin, get_pty=False): """Run command in the instance.""" if self.execute_via_ssh: - return super()._run_command(command, stdin) + return super()._run_command(command, stdin, get_pty=get_pty) if self.series == "xenial": self._log.warning(MISSING_AGENT_MSG, "lxc exec") - return super()._run_command(command, stdin) + return super()._run_command(command, stdin, get_pty=get_pty) - def _wait_for_instance_start(self): + def _wait_for_instance_start(self, **kwargs): """Wait for the cloud instance to be up. LXD VMs need to install systemd units upon initialization. There is diff --git a/pycloudlib/oci/cloud.py b/pycloudlib/oci/cloud.py index 9aeae65d..9d9a6392 100644 --- a/pycloudlib/oci/cloud.py +++ b/pycloudlib/oci/cloud.py @@ -134,7 +134,7 @@ def delete_image(self, image_id, **kwargs): """ self.compute_client.delete_image(image_id, **kwargs) - def released_image(self, release, operating_system="Canonical Ubuntu"): + def released_image(self, release, operating_system="Canonical Ubuntu", **kwargs): """Get the released image. OCI just has periodic builds, so "released" and "daily" don't @@ -359,7 +359,7 @@ def launch( self.created_instances.append(instance) return instance - def snapshot(self, instance, clean=True, name=None): + def snapshot(self, instance, clean=True, name=None, **kwargs): """Snapshot an instance and generate an image from it. Args: diff --git a/pycloudlib/openstack/instance.py b/pycloudlib/openstack/instance.py index 46f567bb..d66226f1 100644 --- a/pycloudlib/openstack/instance.py +++ b/pycloudlib/openstack/instance.py @@ -178,7 +178,7 @@ def _wait_for_instance_start(self, **kwargs): """Wait for instance to be up.""" self.conn.compute.wait_for_server(self.server, status="ACTIVE") - def wait_for_delete(self): + def wait_for_delete(self, **kwargs): """Wait for instance to be deleted.""" try: self.conn.compute.wait_for_server(self.server, status="DELETED") diff --git a/pyproject.toml b/pyproject.toml index 93bc1e5b..36dc6b93 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,92 @@ [build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +dynamic = ["version"] +requires-python = ">=3.10" +name = "pycloudlib" +description = "Python library to launch, interact, and snapshot cloud instances" +classifiers = [ + "Development Status :: 4 - Beta", + "Environment :: Console", + "Intended Audience :: Developers", + "Natural Language :: English", + "Operating System :: POSIX :: Linux", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Software Development :: Libraries :: Python Modules", + "Topic :: Utilities", +] +dependencies = [ + "azure-cli-core >= 2.21.0", + "azure-identity", + "azure-mgmt-compute >= 17", + "azure-mgmt-network >= 16", + "azure-mgmt-resource >= 15", + "boto3 >= 1.14.20", + "botocore >= 1.17.20", + "google-cloud-compute", + "googleapis-common-protos >= 1.63.1", + "ibm-cloud-sdk-core >= 3.14.0", + "ibm-platform-services", + "ibm-vpc >= 0.10, < 0.29.0", + "knack >= 0.7.1", + "oci >= 2.17.0", + "openstacksdk >= 4.2.0, < 4.9.0", + "paramiko >= 2.9.2", + "protobuf", + "pyparsing >= 2, < 3.0.0", + "python-openstackclient >= 5.2.1, < 8.3.0", + "pyyaml >= 5.1", + "qemu.qmp >= 0.0.3", + "requests >= 2.22", + "Softlayer >= 6.0.0", + "toml == 0.10", +] + +[project.optional-dependencies] +test = [ + "mock", + "pytest", + "pytest-cov", + "pytest-mock", + "pytest-xdist", +] +docs = [ + "myst-parser", + "sphinx", + "sphinx_rtd_theme", +] +dev = [ + "mypy>=1.13", + "ruff==0.5.0", + "types-mock>=5.0.0", + "types-pyyaml>=6.0.12", + "types-requests>=2.31.0", + "types-setuptools>=67.0.0", + "types-toml>=0.10.8", + "packaging", +] +[dependency-groups] +dev = [ + "mypy>=1.13", + "ruff==0.5.0", + "types-mock>=5.0.0", + "types-pyyaml>=6.0.12", + "types-requests>=2.31.0", + "types-setuptools>=67.0.0", + "types-toml>=0.10.8", + "pytest", + "pytest-cov", + "pytest-mock", + "pytest-xdist", + "mock", + "packaging", +] + [tool.mypy] check_untyped_defs = true @@ -63,4 +149,10 @@ markers = [ "ci: run test as part of continuous integration on PRs using GitHub Actions", "main_check: run test as part of continuous integration after branch has merged to main using GitHub Actions", ] -testpaths = ["tests/unit_tests"] \ No newline at end of file +testpaths = ["tests/unit_tests"] +[tool.hatch.version] +path = "VERSION" +pattern = "(?P.+)" + +[tool.uv] +python-preference = "only-system" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index d6e1198b..00000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ --e . diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index e6239477..00000000 --- a/setup.cfg +++ /dev/null @@ -1,53 +0,0 @@ -[metadata] -name = pycloudlib -version = file:VERSION -url = https://github.com/canonical/pycloudlib -author = pycloudlib-devs -author_email = pycloudlib-devs@lists.launchpad.net -description = Python library to launch, interact, and snapshot cloud instances -long_description = file: README.md -long_description_content_type = text/markdown -license = GNU General Public License v3 (GPLv3) -classifiers = - Development Status :: 4 - Beta - Environment :: Console - Intended Audience :: Developers - Natural Language :: English - Operating System :: POSIX :: Linux - Programming Language :: Python :: 3 :: Only - Topic :: Software Development :: Libraries :: Python Modules - Topic :: Utilities - -[options] -python_requires = >=3.8 -packages = find: -zip_safe = True -install_requires = - azure-cli-core >= 2.21.0 - azure-identity - azure-mgmt-compute >= 17 - azure-mgmt-network >= 16 - azure-mgmt-resource >= 15 - boto3 >= 1.14.20 - botocore >= 1.17.20 - google-cloud-compute - googleapis-common-protos >= 1.63.1 - ibm-cloud-sdk-core >= 3.14.0 - ibm-platform-services - ibm-vpc >= 0.10, < 0.29.0 - knack >= 0.7.1 - oci >= 2.17.0 - openstacksdk >= 1.1.0, < 1.5.0 - paramiko >= 2.9.2 - protobuf - pyparsing >= 2, < 3.0.0 - python-openstackclient >= 5.2.1 - pyyaml >= 5.1 - qemu.qmp >= 0.0.3 - requests >= 2.22 - Softlayer >= 6.0.0 - toml == 0.10 - -[options.package_data] -pycloudlib = - py.typed diff --git a/setup.py b/setup.py deleted file mode 100644 index c654c14f..00000000 --- a/setup.py +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python3 -"""Legacy Python packaging entry-point.""" - -from setuptools import setup - -setup() diff --git a/test-requirements.txt b/test-requirements.txt deleted file mode 100644 index 9c0b714f..00000000 --- a/test-requirements.txt +++ /dev/null @@ -1,5 +0,0 @@ -mock -pytest -pytest-cov -pytest-mock -pytest-xdist diff --git a/tox.ini b/tox.ini index c0b0aede..380f76c4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,31 +1,20 @@ [tox] +requires = + tox-uv # As it may be undesired to make formatting changes, by default only check -envlist = ruff, mypy, py38 +envlist = ruff, mypy, py310 skipsdist = true [common] envdir = {toxworkdir}/.testenv deps = - mypy==0.950 - ruff==0.5.0 - types-mock==5.0.0 - types-pyyaml==6.0.12 - types-requests==2.31.0.6 - types-setuptools==67.3.0 - types-toml==0.10.8 - -rrequirements.txt - -rtest-requirements.txt + .[test,dev] [testenv:pytest] envdir = {[common]envdir} deps = {[common]deps} commands = {envpython} -m pytest --doctest-modules --cov=pycloudlib --cov-branch {posargs:tests/unit_tests} -[testenv:py38] -envdir = {[common]envdir} -deps = {[common]deps} -basepython = python3.8 -commands = {[testenv:pytest]commands} [testenv:py310] envdir = {[common]envdir} @@ -43,60 +32,54 @@ commands = {[testenv:pytest]commands} envdir = {[common]envdir} deps = {[common]deps} -commands = {envpython} -m mypy {posargs:pycloudlib examples setup.py} +commands = {envpython} -m mypy {posargs:pycloudlib examples} [testenv:ruff] envdir = {[common]envdir} deps = {[common]deps} commands = - {envpython} -m ruff check {posargs:pycloudlib examples setup.py} - {envpython} -m ruff format --check {posargs:pycloudlib examples setup.py} + {envpython} -m ruff check {posargs:pycloudlib examples} + {envpython} -m ruff format --check {posargs:pycloudlib examples} [testenv:format] envdir = {[common]envdir} deps = {[common]deps} commands = - {envpython} -m ruff format {posargs:pycloudlib examples setup.py} - {envpython} -m ruff check --fix {posargs:pycloudlib examples setup.py} + {envpython} -m ruff format {posargs:pycloudlib examples} + {envpython} -m ruff check --fix {posargs:pycloudlib examples} [testenv:docs] # Docs uses a separate environment because the changedir will # cause tox to recreate the environment. changedir = docs deps = - -rrequirements.txt - -rtest-requirements.txt - -rdocs/requirements.txt + .[test,docs] commands = sphinx-build -M html "." "_build" -W [tip] envdir = {toxworkdir}/.testenv-tip deps = ruff - -rrequirements.txt [testenv:tip-ruff] envdir = {[tip]envdir} deps = {[tip]deps} -commands = {envpython} -m ruff check -- pycloudlib examples setup.py +commands = {envpython} -m ruff check -- pycloudlib examples [testenv:integration-tests] commands = {envpython} -m pytest --log-cli-level=INFO -svv {posargs:tests/integration_tests} deps = - -rrequirements.txt - -rtest-requirements.txt + .[test] [testenv:integration-tests-fast] commands = {envpython} -m pytest -n auto --log-cli-level=INFO -svv {posargs:tests/integration_tests} deps = - -rrequirements.txt - -rtest-requirements.txt + .[test] [testenv:integration-tests-ci] commands = {envpython} -m pytest -m ci --log-cli-level=INFO -svv {posargs:tests/integration_tests} deps = - -rrequirements.txt - -rtest-requirements.txt + .[test] [testenv:integration-tests-main-check] # Since we can't use GH secrets from a forked PR, run the cloud-based