diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml new file mode 100644 index 0000000..7530c79 --- /dev/null +++ b/.gitea/workflows/ci.yml @@ -0,0 +1,15 @@ +name: CI + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + +jobs: + driver: + uses: NAPALM/driver-standard/.gitea/workflows/driver.yml@main + with: + standard-ref: main + secrets: + PYPI_TOKEN: ${{ secrets.PYPI_TOKEN }} diff --git a/.gitignore b/.gitignore index 3b4fdc1..cca36af 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ build/ dist/ .venv/ .pytest_cache/ +.coverage +.mypy_cache/ +.ruff_cache/ diff --git a/CHANGELOG.md b/CHANGELOG.md index a07f774..af37fb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed +- The driver follows the NAPALM driver standard + (https://git.netork.io/NAPALM/driver-standard): CI on Gitea runs ruff, mypy, the + tests on Python 3.10–3.13 and at the lowest declared versions, bandit, pip-audit and + a build check, and a release tag publishes to PyPI. Python 3.10 stays the floor; the + package now ships `py.typed`, and the license is declared as the SPDX string + `Apache-2.0`. napalm-device-types now comes from PyPI (4.21.1 or later). +- Requires redfish 3.4.1 and pyghmi 1.5.1: older redfish releases return no answer + with `retries=0`, and pyghmi 1.5.0 misses a dependency it imports. +- A failed IPMI logout on close is now logged at debug level. + ## [0.5.0] - 2026-10-17 ### Added diff --git a/README.md b/README.md index 0a55dd4..156ada5 100644 --- a/README.md +++ b/README.md @@ -78,8 +78,6 @@ These follow the other drivers in the `NAPALM` organisation: - Parsing lives in pure functions, tested on recorded responses under `tests/fixtures/--/`, with a `COMMANDS.txt` (here: the request paths) listing what produced each file. -- `napalm-device-types` is not on PyPI: install it from - `git+https://git.netork.io/NAPALM/napalm-device-types.git` before this package. ## Usage @@ -104,11 +102,12 @@ to basic auth only if the service cannot create sessions), `retries` (1). ## Development +The rules are in [STANDARDS.md](https://git.netork.io/NAPALM/driver-standard/src/branch/main/STANDARDS.md). + ```bash python -m venv .venv && . .venv/bin/activate -pip install git+https://git.netork.io/NAPALM/napalm-device-types.git -pip install -e '.[test]' -pytest +pip install -e '.[dev]' +pytest --cov ``` Fixtures from the DMTF mockups live under `tests/fixtures/dmtf-/`, unmodified, with diff --git a/napalm_redfish/py.typed b/napalm_redfish/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml index b0509f3..afb06a9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools>=64"] +requires = ["setuptools>=77"] build-backend = "setuptools.build_meta" [project] @@ -7,27 +7,30 @@ name = "napalm-redfish" version = "0.5.0" description = "NAPALM driver for server baseboard management controllers over DMTF Redfish" readme = "README.md" -license = { file = "LICENSE" } +license = "Apache-2.0" requires-python = ">=3.10" dependencies = [ "napalm>=5.0", - # Not on PyPI: install from git before this package (see README). - "napalm-device-types>=4.15", - "redfish>=3.2", - "pyghmi>=1.5", + "napalm-device-types>=4.21.1", + # Before 3.4.1, max_retry=0 (retries=0) gets no response back at all. + "redfish>=3.4.1", + # 1.5.0 imports python-dateutil without declaring it. + "pyghmi>=1.5.1", ] classifiers = [ - "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 3", "Topic :: System :: Hardware", "Topic :: System :: Networking", ] [project.optional-dependencies] -test = [ - "pytest>=7", - "responses>=0.23", -] +# responses mocks the BMC's HTTP answers in the tests. +dev = ["pytest>=8", "pytest-cov>=5", "mypy>=1.11", "ruff>=0.6", "responses>=0.23"] + +[project.urls] +Repository = "https://git.netork.io/NAPALM/napalm-redfish" +Issues = "https://git.netork.io/NAPALM/napalm-redfish/issues" +Changelog = "https://git.netork.io/NAPALM/napalm-redfish/src/branch/main/CHANGELOG.md" [project.entry-points."napalm.drivers"] redfish = "napalm_redfish:RedfishDriver" @@ -37,8 +40,8 @@ bmc = "napalm_redfish.bmc:BmcDriver" [tool.setuptools.packages.find] include = ["napalm_redfish*"] -[tool.pytest.ini_options] -testpaths = ["tests"] +[tool.setuptools.package-data] +napalm_redfish = ["py.typed"] [tool.ruff] line-length = 100 @@ -50,3 +53,38 @@ select = ["E", "F", "I", "UP", "B"] [tool.ruff.lint.per-file-ignores] # Recorded device output keeps its lines. "tests/**" = ["E501"] + +[tool.mypy] +python_version = "3.10" +strict = true +# napalm itself is untyped, so every driver subclasses Any. +disallow_subclassing_any = false + +[[tool.mypy.overrides]] +module = ["napalm.*", "redfish.*", "pyghmi.*", "requests.*"] +ignore_missing_imports = true + +[[tool.mypy.overrides]] +# Not yet under strict mypy (quality baseline); may only shrink. +module = [ + "napalm_redfish.bmc", + "napalm_redfish.driver", + "napalm_redfish.ipmi", + "napalm_redfish.parsers.common", + "napalm_redfish.parsers.environment", + "napalm_redfish.parsers.manager", + "napalm_redfish.parsers.system", + "napalm_redfish.transport", +] +ignore_errors = true + +[tool.pytest.ini_options] +testpaths = ["tests"] +# The tests import their helpers as tests.conftest, also under a bare pytest. +pythonpath = ["."] + +[tool.coverage.run] +source = ["napalm_redfish"] + +[tool.coverage.report] +fail_under = 94 diff --git a/tests/quality_baseline.json b/tests/quality_baseline.json new file mode 100644 index 0000000..efda9c9 --- /dev/null +++ b/tests/quality_baseline.json @@ -0,0 +1,25 @@ +{ + "coverage_floor": 94.0, + "function_complexity": { + "napalm_redfish/driver.py::RedfishDriver._accepted_actions": 14, + "napalm_redfish/driver.py::RedfishDriver.get_environment": 14, + "napalm_redfish/driver.py::RedfishDriver.get_facts": 15, + "napalm_redfish/parsers/environment.py::build_environment": 13, + "napalm_redfish/parsers/manager.py::parse_network_protocol": 14, + "napalm_redfish/transport.py::RedfishTransport._request": 14, + "napalm_redfish/transport.py::RedfishTransport.members": 15 + }, + "function_lines": {}, + "mypy_relaxed": [ + "napalm_redfish.bmc", + "napalm_redfish.driver", + "napalm_redfish.ipmi", + "napalm_redfish.parsers.common", + "napalm_redfish.parsers.environment", + "napalm_redfish.parsers.manager", + "napalm_redfish.parsers.system", + "napalm_redfish.transport" + ], + "python_file_lines": {}, + "untyped_public": [] +}