From 12bb63b4d44a68e8dde68fb8059df4654f01588c Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:31:41 +0200 Subject: [PATCH 1/8] build: Add project tooling --- Makefile | 34 ++++++++++++++++++++++++++++++++++ requirements-utils.txt | 5 +++++ 2 files changed, 39 insertions(+) create mode 100644 Makefile create mode 100644 requirements-utils.txt diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f6bb000 --- /dev/null +++ b/Makefile @@ -0,0 +1,34 @@ +$(eval venv := .venv) +$(eval pip := $(venv)/bin/pip) +$(eval python := $(venv)/bin/python) +$(eval black := $(venv)/bin/black) +$(eval isort := $(venv)/bin/isort) +$(eval pytest := $(venv)/bin/pytest) +$(eval twine := $(venv)/bin/twine) +$(eval flake8 := $(venv)/bin/pflake8) +$(eval proselint := $(venv)/bin/proselint) + +setup-virtualenv: + @test -e $(python) || python3 -m venv $(venv) || python -m venv $(venv) + +format: setup-virtualenv + $(pip) install --requirement=requirements-utils.txt + $(black) . + $(isort) . + +lint: setup-virtualenv + $(pip) install --requirement=requirements-utils.txt + $(flake8) --exit-zero *.py + $(MAKE) proselint + +proselint: + $(proselint) *.md || true + +test: setup-virtualenv + $(pip) install --editable=.[test] + $(pytest) + +publish: setup-virtualenv + $(pip) install build twine + $(python) -m build + $(twine) upload --skip-existing --verbose dist/*{.tar.gz,.whl} diff --git a/requirements-utils.txt b/requirements-utils.txt new file mode 100644 index 0000000..15f02a6 --- /dev/null +++ b/requirements-utils.txt @@ -0,0 +1,5 @@ +black +isort +pyproject-flake8 +flake8<5 +proselint From 9fcc332ff67aee2294d91fb1d1edf2c566553f69 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:32:16 +0200 Subject: [PATCH 2/8] test: Add software test framework Just run `git clone ...` and `make test`. --- .gitignore | 2 ++ README.md | 14 ++++++-------- pyproject.toml | 28 ++++++++++++++++++++++++++++ setup.py | 7 +++++++ testing/test_cli.py | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 pyproject.toml create mode 100644 testing/test_cli.py diff --git a/.gitignore b/.gitignore index 8468f27..d4d043a 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ .venv* __pycache__ *.egg-info +.coverage +coverage.xml diff --git a/README.md b/README.md index 719f74c..41cfb4e 100644 --- a/README.md +++ b/README.md @@ -81,20 +81,18 @@ ln -s $(which check_synology) /usr/lib/nagios/plugins/check_synology ## Development -For setting up a development sandbox, you might want to follow this walkthrough. +For setting up a development sandbox and running the software tests, you might +want to follow this walkthrough. -Acquire sources: ```shell git clone https://github.com/wernerfred/check_synology cd check_synology +make test ``` -Install program in development mode into a Python virtual environment: -```shell -python3 -m venv .venv -source .venv/bin/activate -pip install --editable=. -``` +By running `make test`, a Python virtual environment will be created within the +`.venv` folder of your working tree. Use `source .venv/bin/activate` to +activate it. ## Contributors ✨ diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5d74761 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,28 @@ +[tool.black] +line-length = 120 + +[tool.isort] +profile = "black" +line_length = 120 +multi_line_output = 3 + +[tool.flake8] +max-line-length = 120 + +[tool.pytest.ini_options] +minversion = "2.0" +addopts = "-rsfEX -p pytester --strict-markers --verbosity=3 --cov --cov-report=term-missing --cov-report=xml" +log_level = "DEBUG" +testpaths = ["testing"] +xfail_strict = true +markers = [ +] + +[tool.coverage.run] +omit = [ + "testing/*", +] + +[tool.coverage.report] +fail_under = 0 +show_missing = true diff --git a/setup.py b/setup.py index abc930f..607d042 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,13 @@ def read(path): entry_points={"console_scripts": ["check_synology = check_synology"]}, python_requires=">=3.4", install_requires=["easysnmp>=0.2.6,<1"], + extras_require={ + "test": [ + "pytest<8", + "pytest-mock<4", + "pytest-cov<4", + ], + }, classifiers=[ "Development Status :: 4 - Beta", "License :: OSI Approved :: GNU Affero General Public License v3", diff --git a/testing/test_cli.py b/testing/test_cli.py new file mode 100644 index 0000000..8055d34 --- /dev/null +++ b/testing/test_cli.py @@ -0,0 +1,40 @@ +import sys + + +def run_program(*args) -> int: + """ + Run `check_synology` program and return exit code. + + Support a variable number of command line options. + """ + sys.argv = ["check_synology", *args] + try: + import check_synology + except SystemExit as ex: + return ex.code % 256 + + +def test_no_options(capsys): + """ + Verify running the program without options croaks as expected. + """ + exitcode = run_program() + response = capsys.readouterr() + assert exitcode == 2 + assert response.out == "" + assert ( + "check_synology: error: the following arguments are required: " + "hostname, username, authkey, privkey, mode" in response.err + ) + + +def test_help(capsys): + """ + Verify running the program with the `--help` option works as expected. + """ + exitcode = run_program("--help") + response = capsys.readouterr() + assert exitcode == 0 + assert "the hostname" in response.out + assert "critical value for selected mode" in response.out + assert response.err == "" From f4c4ad322d99a88151097ab2ee63145ae7e0335f Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:32:44 +0200 Subject: [PATCH 3/8] ci: Run software tests on GHA --- .github/workflows/tests.yml | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 .github/workflows/tests.yml diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..11cde2a --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,56 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + + tests: + runs-on: ${{ matrix.os }} + + strategy: + matrix: + os: [ "ubuntu-latest", "macos-latest" ] + python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10" ] + + env: + OS: ${{ matrix.os }} + PYTHON: ${{ matrix.python-version }} + + defaults: + run: + shell: bash + + name: Python ${{ matrix.python-version }} on ${{ matrix.os }} + steps: + + - name: Acquire sources + uses: actions/checkout@v3 + + - name: Setup Python + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + architecture: x64 + cache: 'pip' + cache-dependency-path: 'setup.py' + + - name: Run linter + run: | + make lint + + - name: Run tests, with coverage + run: | + make test + + - name: Upload coverage results to Codecov + uses: codecov/codecov-action@v3 + with: + file: ./coverage.xml + flags: unittests + env_vars: OS,PYTHON + name: codecov-umbrella + fail_ci_if_error: false From a510e7b1e93e989251beffa5df1ed9bfca0a7b6a Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:40:54 +0200 Subject: [PATCH 4/8] ci: Install Net-SNMP package as prerequisite --- .github/workflows/tests.yml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 11cde2a..ab147ec 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,6 +15,7 @@ jobs: matrix: os: [ "ubuntu-latest", "macos-latest" ] python-version: [ "3.6", "3.7", "3.8", "3.9", "3.10" ] + fail-fast: false env: OS: ${{ matrix.os }} @@ -38,6 +39,16 @@ jobs: cache: 'pip' cache-dependency-path: 'setup.py' + - name: Install prerequisites (Linux) + if: matrix.os == 'ubuntu-latest' + run: | + sudo apt install --yes libsnmp-dev snmp-mibs-downloader + + - name: Install prerequisites (macOS) + if: matrix.os == 'macos-latest' + run: | + brew install net-snmp + - name: Run linter run: | make lint From 95bdaf2053246eaff5d2467f2df5ae5b39fe1f1c Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:33:39 +0200 Subject: [PATCH 5/8] chore: Update package metadata with corresponding Python versions --- setup.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 607d042..8e1809d 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ def read(path): keywords="synology, synology-diskstation, snmp, snmpv3, monitoring, monitoring-plugin, nagios, icinga2, icinga2-plugin", py_modules=["check_synology"], entry_points={"console_scripts": ["check_synology = check_synology"]}, - python_requires=">=3.4", + python_requires=">=3.6", install_requires=["easysnmp>=0.2.6,<1"], extras_require={ "test": [ @@ -41,7 +41,13 @@ def read(path): "Operating System :: OS Independent", "Environment :: Console", "Programming Language :: Python", + "Programming Language :: Python", "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: Implementation :: CPython", "Intended Audience :: Developers", "Intended Audience :: Education", From 5de95a45353a404a9294a4d716ddd7700dd2d121 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Mon, 1 Aug 2022 22:46:02 +0200 Subject: [PATCH 6/8] docs: Update badges in README --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 41cfb4e..fed9e28 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# check_synology [![Release](https://img.shields.io/github/release/wernerfred/check_synology.svg)](https://github.com/wernerfred/check_synology/releases) +# check_synology + +[![Tests](https://github.com/wernerfred/check_synology/actions/workflows/tests.yml/badge.svg)](https://github.com/wernerfred/check_synology/actions/workflows/tests.yml) +[![Code coverage](https://codecov.io/gh/wernerfred/check_synology/branch/master/graph/badge.svg)](https://codecov.io/gh/wernerfred/check_synology) +[![Release](https://img.shields.io/github/release/wernerfred/check_synology.svg)](https://github.com/wernerfred/check_synology/releases) ## About From aace1475d95e42826ce4d6f86badec047c30a8dd Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 13 Oct 2022 17:05:45 +0200 Subject: [PATCH 7/8] fix: Installing package in editable mode croaked badly ERROR: For req: check-synology==1.0.0. Invalid script entry point: - A callable suffix is required. Cf https://packaging.python.org/specifications/entry-points/#use-for-scripts for more information. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8e1809d..dd17ccb 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ def read(path): license="AGPL-3.0", keywords="synology, synology-diskstation, snmp, snmpv3, monitoring, monitoring-plugin, nagios, icinga2, icinga2-plugin", py_modules=["check_synology"], - entry_points={"console_scripts": ["check_synology = check_synology"]}, + entry_points={"console_scripts": ["check_synology = check_synology:__main__"]}, python_requires=">=3.6", install_requires=["easysnmp>=0.2.6,<1"], extras_require={ From d2795ad9be8707d3dcf7fff81f2ae6de5e97eff0 Mon Sep 17 00:00:00 2001 From: Andreas Motl Date: Thu, 13 Oct 2022 18:20:25 +0200 Subject: [PATCH 8/8] fix: Adjust license in `setup.py` to "MIT" --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index dd17ccb..6e97c3f 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,7 @@ def read(path): long_description=long_description, long_description_content_type="text/markdown", platforms=["any"], - license="AGPL-3.0", + license="MIT", keywords="synology, synology-diskstation, snmp, snmpv3, monitoring, monitoring-plugin, nagios, icinga2, icinga2-plugin", py_modules=["check_synology"], entry_points={"console_scripts": ["check_synology = check_synology:__main__"]}, @@ -36,7 +36,7 @@ def read(path): }, classifiers=[ "Development Status :: 4 - Beta", - "License :: OSI Approved :: GNU Affero General Public License v3", + "License :: OSI Approved :: MIT License", "Natural Language :: English", "Operating System :: OS Independent", "Environment :: Console",