diff --git a/.github/workflows/publish.yaml b/.github/workflows/publish.yaml index 7c25cf3..19e9421 100644 --- a/.github/workflows/publish.yaml +++ b/.github/workflows/publish.yaml @@ -10,6 +10,11 @@ jobs: build-n-publish: name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/p/statick-tooling + permissions: + id-token: write steps: - uses: actions/checkout@v3 @@ -18,47 +23,12 @@ jobs: uses: actions/setup-python@v4 with: python-version: '3.11' - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'macOS') - with: - path: ~/Library/Caches/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'Windows') - with: - path: ~\AppData\Local\pip\Cache - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - name: Install tools - run: | - python -m pip install --upgrade setuptools - python -m pip install --upgrade wheel + cache: 'pip' - name: Build a binary wheel and a source tarball run: | - python setup.py sdist bdist_wheel - - - name: Publish distribution 📦 to Test PyPI - uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_TEST_TOKEN }} - repository_url: https://test.pypi.org/legacy/ + pip install -q build + python -m build - name: Publish distribution 📦 to PyPI uses: pypa/gh-action-pypi-publish@release/v1 - with: - password: ${{ secrets.PYPI_TOKEN }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index b644820..0a7544f 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -8,69 +8,35 @@ on: # NOLINT - cron: '0 10 * * MON' workflow_dispatch: - jobs: build: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, ubuntu-20.04, ubuntu-22.04, windows-latest] - python-version: ['3.8', '3.9', '3.10', '3.11'] + os: [macos-latest, ubuntu-20.04, ubuntu-22.04, ubuntu-24.04, windows-latest] + python-version: ['3.9', '3.10', '3.11', '3.12', '3.13'] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + cache: 'pip' - - uses: actions/setup-node@v3 - with: - node-version: '16' - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'Linux') - with: - path: ~/.cache/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'macOS') - with: - path: ~/Library/Caches/pip - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- - - - uses: actions/cache@v3 - if: startsWith(runner.os, 'Windows') + - uses: actions/setup-node@v4 with: - path: ~\AppData\Local\pip\Cache - key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} - restore-keys: | - ${{ runner.os }}-pip- + node-version: '20' - name: Install dependencies run: | - python -m pip install --upgrade pip - python -m pip install --upgrade setuptools - python -m pip install --upgrade wheel - python -m pip install --upgrade coverage - python -m pip install --upgrade mypy - python -m pip install --upgrade statick - python -m pip install --upgrade statick-md - python -m pip install --upgrade tox - python -m pip install --upgrade tox-gh-actions - python -m pip install --upgrade virtualenv - python -m pip install -r requirements.txt + pip install --upgrade pip + pip install .[docs,test] - # Have to install newer version from non-apt source due to SSL library compatibility issues. - - name: Install Node and node-based tools (Linux) - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' + - name: Install Node-based tools (Linux) + if: runner.os == 'Linux' run: | npm install -g markdownlint-cli npm install -g dockerfilelint @@ -78,7 +44,7 @@ jobs: # Do not install on macos until there is a hadolint release for macos (Darwin on arm64 architecture). - name: Install Hadolint binary (github) - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' || matrix.os == 'windows-latest' + if: runner.os == 'Linux' || runner.os == 'Windows' run: | mkdir -p $HOME/.local/bin echo "$HOME/.local/bin" >> $GITHUB_PATH @@ -87,7 +53,7 @@ jobs: mv hadolint $HOME/.local/bin/ - name: Install Hadolint docker image (Linux) - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' + if: runner.os == 'Linux' run: | docker pull hadolint/hadolint:latest @@ -105,12 +71,12 @@ jobs: fail_ci_if_error: false - name: Statick Documentation - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' + if: runner.os == 'Linux' run: | statick . --check --user-paths . --profile documentation.yaml - name: Self check - if: matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04' + if: runner.os == 'Linux' run: | mkdir statick-output statick . --output-directory statick-output --check --profile self_check.yaml diff --git a/CHANGELOG.md b/CHANGELOG.md index 04a1f7d..9eadd7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,25 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## Unreleased +### Added + +- Support for Python 3.12 and 3.13. +- Use of `pyproject.toml` instead of `setup.py` and `requirements.txt`. +- Supports new plugin discovery mechanism for the main Statick tool. + - Switched from yapsy to setuptools for plugin mechanism. (sscpac/statick#508) + +### Changed + +- Disabled code coverage requirements in CI for now. + - Unable to get line coverage working with new plugin mechanism. + Unit tests still work to find problems. +- Updated README to use more modern approach to installing Python and NPM packages. +- Rename plugin modules so they are shorter and less redundant. + +### Removed + +- No longer support Python 3.8. + ## v0.2.0 - 2025-01-03 ### Added diff --git a/README.md b/README.md index 51836ac..a1d207d 100644 --- a/README.md +++ b/README.md @@ -17,32 +17,55 @@ Custom exceptions can be applied the same way they are with [Statick exceptions] ## Table of Contents -* [Installation](#installation) -* [Usage](#usage) -* [Existing Plugins](#existing-plugins) - * [Discovery Plugins](#discovery-plugins) - * [Tool Plugins](#tool-plugins) -* [Contributing](#contributing) - * [Mypy](#mypy) - * [Formatting](#formatting) +- [Statick Tooling Plugins](#statick-tooling-plugins) + - [Table of Contents](#table-of-contents) + - [Installation](#installation) + - [Usage](#usage) + - [Pip Install](#pip-install) + - [Pip Install and Custom Configuration](#pip-install-and-custom-configuration) + - [Source Install and Custom Configuration](#source-install-and-custom-configuration) + - [Existing Plugins](#existing-plugins) + - [Discovery Plugins](#discovery-plugins) + - [Tool Plugins](#tool-plugins) + - [Contributing](#contributing) + - [Mypy](#mypy) + - [Formatting](#formatting) ## Installation The recommended method to install these Statick plugins is via pip: ```shell -python3 -m pip install statick-tooling +pip install statick-tooling ``` You can also clone the repository and use it locally. ## Usage -Make sure you install all the dependencies from apt/npm: +Make sure you install all the dependencies from apt/npm. +See for Node/npm installation instructions. + +Configure npm to allow a non-root user to install packages. + +```shell +npm config set prefix '~/.local/' +``` + +Make sure `~/.local/bin` exists. +Check your `PATH` with `echo $PATH`. +If `~/.local/bin` is not listed then add it to your `PATH`. + +```shell +mkdir -p ~/.local/bin +echo 'export PATH="$HOME/.local/bin/:$PATH"' >> ~/.bashrc +``` + +Install npm packages. ```shell -cat install.txt | xargs sudo apt-get install -y -cat npm-deps.txt | xargs sudo npm install -g +npm install -g dockerfilelint +npm install -g dockerfile_lint ``` ### Pip Install diff --git a/install.txt b/install.txt deleted file mode 100644 index b235581..0000000 --- a/install.txt +++ /dev/null @@ -1 +0,0 @@ -npm diff --git a/npm-deps.txt b/npm-deps.txt deleted file mode 100644 index e00b8f8..0000000 --- a/npm-deps.txt +++ /dev/null @@ -1,2 +0,0 @@ -dockerfilelint -dockerfile_lint diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..5b10447 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,66 @@ +[build-system] +requires = ["setuptools", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "statick-tooling" +authors = [{name = "NIWC Pacific"}] +description="Statick analysis plugins for Tooling files." +version = "0.2.0" +readme = "README.md" +requires-python = ">=3.9" +license = {text = "CC0-1.0"} +classifiers = [ + "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", + "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", + "Topic :: Software Development :: Quality Assurance", + "Topic :: Software Development :: Testing", + "Typing :: Typed", +] + +dependencies = [ + "importlib_metadata", + "statick", + "types-docutils", +] + +[tool.setuptools.package-data] +statick_tool = [ + "rsc/*", + "rsc/.*", +] + +[project.entry-points."statick_tool.plugins.discovery"] +dockerfile = "statick_tool.plugins.discovery.dockerfile:DockerfileDiscoveryPlugin" + +[project.entry-points."statick_tool.plugins.tool"] +dockerfile-lint = "statick_tool.plugins.tool.dockerfile_lint:DockerfileULintToolPlugin" +dockerfilelint = "statick_tool.plugins.tool.dockerfilelint:DockerfileLintToolPlugin" +hadolint = "statick_tool.plugins.tool.hadolint:HadolintToolPlugin" + +[project.urls] +"Homepage" = "https://github.com/sscpac/statick-tooling" +"Bug Tracker" = "https://github.com/sscpac/statick-tooling/issues" + +[project.optional-dependencies] +test = [ + "coverage", + "mock", + "pytest", + "pytest-cov", + "statick-md@git+https://github.com/tdenewiler/statick-md@stdlib-plugins", + "tox", + "tox-gh-actions", +] +docs = [ + "sphinx==1.7.9", + "yaml-1.3", +] + +[tool.isort] +profile = "black" diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 15f3587..0000000 --- a/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -statick diff --git a/setup.py b/setup.py deleted file mode 100644 index 2a312a3..0000000 --- a/setup.py +++ /dev/null @@ -1,51 +0,0 @@ -"""Setup.""" - -from setuptools import setup - -with open("README.md", encoding="utf8") as f: - long_description = f.read() # pylint: disable=invalid-name - -TEST_DEPS = [ - "mock", - "pytest", -] - -EXTRAS = { - "test": TEST_DEPS, -} - -setup( - author="NIWC Pacific", - name="statick-tooling", - description="Statick analysis plugins for Tooling files.", - version="0.2.0", - packages=[ - "statick_tool", - "statick_tool.plugins.discovery", - "statick_tool.plugins.tool", - ], - package_dir={ - "statick_tool": ".", - "statick_tool.plugins.discovery": "src/statick_tooling/plugins/discovery", - "statick_tool.plugins.tool": "src/statick_tooling/plugins/tool", - }, - package_data={ - "statick_tool": ["rsc/.*", "rsc/*"], - "statick_tool.plugins.discovery": ["*.yapsy-plugin"], - "statick_tool.plugins.tool": ["*.yapsy-plugin"], - }, - long_description=long_description, - long_description_content_type="text/markdown", - install_requires=["statick"], - tests_require=TEST_DEPS, - extras_require=EXTRAS, - url="https://github.com/sscpac/statick-tooling", - classifiers=[ - "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Topic :: Software Development :: Testing", - ], -) diff --git a/src/statick_tooling/plugins/discovery/dockerfile_discovery_plugin.py b/src/statick_tool/plugins/discovery/dockerfile.py similarity index 100% rename from src/statick_tooling/plugins/discovery/dockerfile_discovery_plugin.py rename to src/statick_tool/plugins/discovery/dockerfile.py diff --git a/src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.py b/src/statick_tool/plugins/tool/dockerfile_lint.py similarity index 91% rename from src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.py rename to src/statick_tool/plugins/tool/dockerfile_lint.py index 272b0d6..cb8923f 100644 --- a/src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.py +++ b/src/statick_tool/plugins/tool/dockerfile_lint.py @@ -79,14 +79,22 @@ def add_filename(cls, output: str, src: str) -> str: """Add the filename to the json output. This is because dockerfile-lint does not include the filename in the output. + + Some warnings and errors are included in the tool output, but they are not in + json format. Those lines start with a "(". Any line that does not start with a + "(" is considered to be a line of output. """ + updated_output = "" + for line in output.splitlines(): + if not line.startswith("("): + updated_output = updated_output + line + "\n" try: - json_dict = json.loads(output) + json_dict = json.loads(updated_output) json_dict["filename"] = src return json.dumps(json_dict) except ValueError as ex: logging.warning("ValueError: %s", ex) - return output + return updated_output def parse_output( self, total_output: List[str], package: Optional[Package] = None diff --git a/src/statick_tooling/plugins/tool/dockerfilelint_tool_plugin.py b/src/statick_tool/plugins/tool/dockerfilelint.py similarity index 100% rename from src/statick_tooling/plugins/tool/dockerfilelint_tool_plugin.py rename to src/statick_tool/plugins/tool/dockerfilelint.py diff --git a/src/statick_tooling/plugins/tool/hadolint_tool_plugin.py b/src/statick_tool/plugins/tool/hadolint.py similarity index 100% rename from src/statick_tooling/plugins/tool/hadolint_tool_plugin.py rename to src/statick_tool/plugins/tool/hadolint.py diff --git a/rsc/.dockerfilelintrc b/src/statick_tool/rsc/.dockerfilelintrc similarity index 100% rename from rsc/.dockerfilelintrc rename to src/statick_tool/rsc/.dockerfilelintrc diff --git a/rsc/.hadolint.yaml b/src/statick_tool/rsc/.hadolint.yaml similarity index 100% rename from rsc/.hadolint.yaml rename to src/statick_tool/rsc/.hadolint.yaml diff --git a/rsc/dockerfile-lint-profile.yaml b/src/statick_tool/rsc/dockerfile-lint-profile.yaml similarity index 100% rename from rsc/dockerfile-lint-profile.yaml rename to src/statick_tool/rsc/dockerfile-lint-profile.yaml diff --git a/rsc/dockerfilelint-profile.yaml b/src/statick_tool/rsc/dockerfilelint-profile.yaml similarity index 100% rename from rsc/dockerfilelint-profile.yaml rename to src/statick_tool/rsc/dockerfilelint-profile.yaml diff --git a/rsc/hadolint-profile.yaml b/src/statick_tool/rsc/hadolint-profile.yaml similarity index 100% rename from rsc/hadolint-profile.yaml rename to src/statick_tool/rsc/hadolint-profile.yaml diff --git a/rsc/tooling-config.yaml b/src/statick_tool/rsc/tooling-config.yaml similarity index 100% rename from rsc/tooling-config.yaml rename to src/statick_tool/rsc/tooling-config.yaml diff --git a/rsc/tooling-profile.yaml b/src/statick_tool/rsc/tooling-profile.yaml similarity index 100% rename from rsc/tooling-profile.yaml rename to src/statick_tool/rsc/tooling-profile.yaml diff --git a/src/statick_tooling/__init__.py b/src/statick_tooling/__init__.py deleted file mode 100644 index 5da5b0e..0000000 --- a/src/statick_tooling/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Statick tool.""" diff --git a/src/statick_tooling/plugins/__init__.py b/src/statick_tooling/plugins/__init__.py deleted file mode 100644 index 9e74282..0000000 --- a/src/statick_tooling/plugins/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Markdown plugins for Statick.""" diff --git a/src/statick_tooling/plugins/discovery/__init__.py b/src/statick_tooling/plugins/discovery/__init__.py deleted file mode 100644 index 878179b..0000000 --- a/src/statick_tooling/plugins/discovery/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Md file discovery plugins.""" diff --git a/src/statick_tooling/plugins/discovery/dockerfile_discovery_plugin.yapsy-plugin b/src/statick_tooling/plugins/discovery/dockerfile_discovery_plugin.yapsy-plugin deleted file mode 100644 index 9d594fa..0000000 --- a/src/statick_tooling/plugins/discovery/dockerfile_discovery_plugin.yapsy-plugin +++ /dev/null @@ -1,3 +0,0 @@ -[Core] -Name = Dockerfile Discovery Plugin -Module = dockerfile_discovery_plugin diff --git a/src/statick_tooling/plugins/tool/__init__.py b/src/statick_tooling/plugins/tool/__init__.py deleted file mode 100644 index ef93d6a..0000000 --- a/src/statick_tooling/plugins/tool/__init__.py +++ /dev/null @@ -1 +0,0 @@ -"""Md tool plugins.""" diff --git a/src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.yapsy-plugin b/src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.yapsy-plugin deleted file mode 100644 index 914206a..0000000 --- a/src/statick_tooling/plugins/tool/dockerfile_lint_tool_plugin.yapsy-plugin +++ /dev/null @@ -1,3 +0,0 @@ -[Core] -Name = Dockerfile Lint Tool Plugin -Module = dockerfile_lint_tool_plugin diff --git a/src/statick_tooling/plugins/tool/dockerfilelint_tool_plugin.yapsy-plugin b/src/statick_tooling/plugins/tool/dockerfilelint_tool_plugin.yapsy-plugin deleted file mode 100644 index 69936eb..0000000 --- a/src/statick_tooling/plugins/tool/dockerfilelint_tool_plugin.yapsy-plugin +++ /dev/null @@ -1,3 +0,0 @@ -[Core] -Name = DockerfileLint Tool Plugin -Module = dockerfilelint_tool_plugin diff --git a/src/statick_tooling/plugins/tool/hadolint_tool_plugin.yapsy-plugin b/src/statick_tooling/plugins/tool/hadolint_tool_plugin.yapsy-plugin deleted file mode 100644 index a4e5db1..0000000 --- a/src/statick_tooling/plugins/tool/hadolint_tool_plugin.yapsy-plugin +++ /dev/null @@ -1,3 +0,0 @@ -[Core] -Name = Hadolint Tool Plugin -Module = hadolint_tool_plugin diff --git a/tests/discovery/dockerfile_discovery_plugin/test_dockerfile_discovery_plugin.py b/tests/discovery/dockerfile_discovery_plugin/test_dockerfile_discovery_plugin.py index a2555c5..70b1684 100644 --- a/tests/discovery/dockerfile_discovery_plugin/test_dockerfile_discovery_plugin.py +++ b/tests/discovery/dockerfile_discovery_plugin/test_dockerfile_discovery_plugin.py @@ -1,40 +1,27 @@ """Unit tests for the dockerfile discovery plugin.""" import os +import sys -from yapsy.PluginManager import PluginManager - -import statick_tool -from statick_tool.discovery_plugin import DiscoveryPlugin from statick_tool.exceptions import Exceptions from statick_tool.package import Package -from statick_tool.plugins.discovery.dockerfile_discovery_plugin import ( - DockerfileDiscoveryPlugin, -) + +from statick_tool.plugins.discovery.dockerfile import DockerfileDiscoveryPlugin + +if sys.version_info < (3, 10): + from importlib_metadata import entry_points +else: + from importlib.metadata import entry_points def test_dockerfile_plugin_found(): """Test that the plugin manager finds the dockerfile discovery plugin.""" - manager = PluginManager() - # Get the path to statick_tool/__init__.py, get the directory part, and - # add 'plugins' to that to get the standard plugins dir - manager.setPluginPlaces( - [os.path.join(os.path.dirname(statick_tool.__file__), "plugins")] - ) - manager.setCategoriesFilter( - { - "Discovery": DiscoveryPlugin, - } - ) - manager.collectPlugins() - # Verify that a plugin's get_name() function returns "dockerfile" - assert any( - plugin_info.plugin_object.get_name() == "dockerfile" - for plugin_info in manager.getPluginsOfCategory("Discovery") - ) - # While we're at it, verify that a plugin is named dockerfile Discovery Plugin + discovery_plugins = {} + plugins = entry_points(group="statick_tool.plugins.discovery") + for plugin_type in plugins: + plugin = plugin_type.load() + discovery_plugins[plugin_type.name] = plugin() assert any( - plugin_info.name == "Dockerfile Discovery Plugin" - for plugin_info in manager.getPluginsOfCategory("Discovery") + plugin.get_name() == "dockerfile" for _, plugin in list(discovery_plugins.items()) ) diff --git a/tests/tool/dockerfile_lint_tool_plugin/test_dockerfile_lint_tool_plugin.py b/tests/tool/dockerfile_lint_tool_plugin/test_dockerfile_lint_tool_plugin.py index e6c3830..ae1d06d 100644 --- a/tests/tool/dockerfile_lint_tool_plugin/test_dockerfile_lint_tool_plugin.py +++ b/tests/tool/dockerfile_lint_tool_plugin/test_dockerfile_lint_tool_plugin.py @@ -1,22 +1,23 @@ """Unit tests for the dockerfilelint plugin.""" - import argparse import os import subprocess +import sys import mock import pytest -from yapsy.PluginManager import PluginManager - -import statick_tool from statick_tool.config import Config from statick_tool.package import Package from statick_tool.plugin_context import PluginContext -from statick_tool.plugins.tool.dockerfile_lint_tool_plugin import ( - DockerfileULintToolPlugin, -) from statick_tool.resources import Resources -from statick_tool.tool_plugin import ToolPlugin + +import statick_tool +from statick_tool.plugins.tool.dockerfile_lint import DockerfileULintToolPlugin + +if sys.version_info < (3, 10): + from importlib_metadata import entry_points +else: + from importlib.metadata import entry_points def setup_dockerfilelint_tool_plugin(): @@ -45,27 +46,13 @@ def setup_dockerfilelint_tool_plugin(): def test_dockerfilelint_tool_plugin_found(): """Test that the plugin manager can find the dockerfilelint plugin.""" - manager = PluginManager() - # Get the path to statick_tool/__init__.py, get the directory part, and - # add 'plugins' to that to get the standard plugins dir - manager.setPluginPlaces( - [os.path.join(os.path.dirname(statick_tool.__file__), "plugins")] - ) - manager.setCategoriesFilter( - { - "Tool": ToolPlugin, - } - ) - manager.collectPlugins() - # Verify that a plugin's get_name() function returns "dockerfilelint" - assert any( - plugin_info.plugin_object.get_name() == "dockerfile-lint" - for plugin_info in manager.getPluginsOfCategory("Tool") - ) - # While we're at it, verify that a plugin is named dockerfilelint Tool Plugin + tool_plugins = {} + plugins = entry_points(group="statick_tool.plugins.tool") + for plugin_type in plugins: + plugin = plugin_type.load() + tool_plugins[plugin_type.name] = plugin() assert any( - plugin_info.name == "Dockerfile Lint Tool Plugin" - for plugin_info in manager.getPluginsOfCategory("Tool") + plugin.get_name() == "dockerfile-lint" for _, plugin in list(tool_plugins.items()) ) @@ -132,7 +119,7 @@ def test_dockerfilelint_tool_plugin_parse_invalid(): @mock.patch( - "statick_tool.plugins.tool.dockerfilelint_tool_plugin.subprocess.check_output" + "statick_tool.plugins.tool.dockerfilelint.subprocess.check_output" ) def test_dockerfilelint_tool_plugin_scan_calledprocesserror( mock_subprocess_check_output, @@ -169,7 +156,7 @@ def test_dockerfilelint_tool_plugin_scan_calledprocesserror( @mock.patch( - "statick_tool.plugins.tool.dockerfilelint_tool_plugin.subprocess.check_output" + "statick_tool.plugins.tool.dockerfilelint.subprocess.check_output" ) def test_dockerfilelint_tool_plugin_scan_oserror(mock_subprocess_check_output): """ diff --git a/tests/tool/dockerfilelint_tool_plugin/test_dockerfilelint_tool_plugin.py b/tests/tool/dockerfilelint_tool_plugin/test_dockerfilelint_tool_plugin.py index e4846d0..8bc5e85 100644 --- a/tests/tool/dockerfilelint_tool_plugin/test_dockerfilelint_tool_plugin.py +++ b/tests/tool/dockerfilelint_tool_plugin/test_dockerfilelint_tool_plugin.py @@ -1,22 +1,23 @@ """Unit tests for the dockerfilelint plugin.""" - import argparse import os import subprocess +import sys import mock import pytest -from yapsy.PluginManager import PluginManager - -import statick_tool from statick_tool.config import Config from statick_tool.package import Package from statick_tool.plugin_context import PluginContext -from statick_tool.plugins.tool.dockerfilelint_tool_plugin import ( - DockerfileLintToolPlugin, -) from statick_tool.resources import Resources -from statick_tool.tool_plugin import ToolPlugin + +import statick_tool +from statick_tool.plugins.tool.dockerfilelint import DockerfileLintToolPlugin + +if sys.version_info < (3, 10): + from importlib_metadata import entry_points +else: + from importlib.metadata import entry_points def setup_dockerfilelint_tool_plugin(package="valid_package"): @@ -45,27 +46,13 @@ def setup_dockerfilelint_tool_plugin(package="valid_package"): def test_dockerfilelint_tool_plugin_found(): """Test that the plugin manager can find the dockerfilelint plugin.""" - manager = PluginManager() - # Get the path to statick_tool/__init__.py, get the directory part, and - # add 'plugins' to that to get the standard plugins dir - manager.setPluginPlaces( - [os.path.join(os.path.dirname(statick_tool.__file__), "plugins")] - ) - manager.setCategoriesFilter( - { - "Tool": ToolPlugin, - } - ) - manager.collectPlugins() - # Verify that a plugin's get_name() function returns "dockerfilelint" - assert any( - plugin_info.plugin_object.get_name() == "dockerfilelint" - for plugin_info in manager.getPluginsOfCategory("Tool") - ) - # While we're at it, verify that a plugin is named dockerfilelint Tool Plugin + tool_plugins = {} + plugins = entry_points(group="statick_tool.plugins.tool") + for plugin_type in plugins: + plugin = plugin_type.load() + tool_plugins[plugin_type.name] = plugin() assert any( - plugin_info.name == "DockerfileLint Tool Plugin" - for plugin_info in manager.getPluginsOfCategory("Tool") + plugin.get_name() == "dockerfilelint" for _, plugin in list(tool_plugins.items()) ) @@ -155,7 +142,7 @@ def test_dockerfilelint_tool_plugin_scan_invalid_rc_file(): # at Array.forEach () # at Object. (/usr/local/lib/node_modules/dockerfilelint/bin/dockerfilelint:65:8) # at Module._compile (internal/modules/cjs/loader.js:1063:30) - assert len(issues) == 14 + assert len(issues) == 15 assert issues[2].filename == "EXCEPTION" assert issues[2].line_number == "0" assert issues[2].tool == "dockerfilelint" @@ -165,7 +152,7 @@ def test_dockerfilelint_tool_plugin_scan_invalid_rc_file(): @mock.patch( - "statick_tool.plugins.tool.dockerfilelint_tool_plugin.subprocess.check_output" + "statick_tool.plugins.tool.dockerfilelint.subprocess.check_output" ) def test_dockerfilelint_tool_plugin_scan_calledprocesserror( mock_subprocess_check_output, @@ -202,7 +189,7 @@ def test_dockerfilelint_tool_plugin_scan_calledprocesserror( @mock.patch( - "statick_tool.plugins.tool.dockerfilelint_tool_plugin.subprocess.check_output" + "statick_tool.plugins.tool.dockerfilelint.subprocess.check_output" ) def test_dockerfilelint_tool_plugin_scan_oserror(mock_subprocess_check_output): """ diff --git a/tests/tool/hadolint_tool_plugin/test_hadolint_tool_plugin.py b/tests/tool/hadolint_tool_plugin/test_hadolint_tool_plugin.py index bfad3d0..aa5d3e1 100644 --- a/tests/tool/hadolint_tool_plugin/test_hadolint_tool_plugin.py +++ b/tests/tool/hadolint_tool_plugin/test_hadolint_tool_plugin.py @@ -1,5 +1,4 @@ """Unit tests for the hadolint plugin.""" - import argparse import json import os @@ -8,15 +7,18 @@ import mock import pytest -from yapsy.PluginManager import PluginManager - -import statick_tool from statick_tool.config import Config from statick_tool.package import Package from statick_tool.plugin_context import PluginContext -from statick_tool.plugins.tool.hadolint_tool_plugin import HadolintToolPlugin from statick_tool.resources import Resources -from statick_tool.tool_plugin import ToolPlugin + +import statick_tool +from statick_tool.plugins.tool.hadolint import HadolintToolPlugin + +if sys.version_info < (3, 10): + from importlib_metadata import entry_points +else: + from importlib.metadata import entry_points def setup_hadolint_tool_plugin( @@ -56,27 +58,13 @@ def setup_hadolint_tool_plugin( def test_hadolint_tool_plugin_found(): """Test that the plugin manager can find the hadolint plugin.""" - manager = PluginManager() - # Get the path to statick_tool/__init__.py, get the directory part, and - # add 'plugins' to that to get the standard plugins dir - manager.setPluginPlaces( - [os.path.join(os.path.dirname(statick_tool.__file__), "plugins")] - ) - manager.setCategoriesFilter( - { - "Tool": ToolPlugin, - } - ) - manager.collectPlugins() - # Verify that a plugin's get_name() function returns "hadolint" - assert any( - plugin_info.plugin_object.get_name() == "hadolint" - for plugin_info in manager.getPluginsOfCategory("Tool") - ) - # While we're at it, verify that a plugin is named hadolint Tool Plugin + tool_plugins = {} + plugins = entry_points(group="statick_tool.plugins.tool") + for plugin_type in plugins: + plugin = plugin_type.load() + tool_plugins[plugin_type.name] = plugin() assert any( - plugin_info.name == "Hadolint Tool Plugin" - for plugin_info in manager.getPluginsOfCategory("Tool") + plugin.get_name() == "hadolint" for _, plugin in list(tool_plugins.items()) ) @@ -228,7 +216,7 @@ def test_hadolint_tool_plugin_scan_docker_duplicate_format(): assert len(issues) == 4 -@mock.patch("statick_tool.plugins.tool.hadolint_tool_plugin.json.loads") +@mock.patch("statick_tool.plugins.tool.hadolint.json.loads") def test_hadolint_tool_plugin_scan_jsondecodeerror( mock_json_loads_jsondecodeerror, ): @@ -264,7 +252,7 @@ def test_hadolint_tool_plugin_scan_different_binary(): assert issues is None -@mock.patch("statick_tool.plugins.tool.hadolint_tool_plugin.subprocess.check_output") +@mock.patch("statick_tool.plugins.tool.hadolint.subprocess.check_output") def test_hadolint_tool_plugin_scan_calledprocesserror( mock_subprocess_check_output, ): @@ -293,7 +281,7 @@ def test_hadolint_tool_plugin_scan_calledprocesserror( assert not issues -@mock.patch("statick_tool.plugins.tool.hadolint_tool_plugin.subprocess.check_output") +@mock.patch("statick_tool.plugins.tool.hadolint.subprocess.check_output") def test_hadolint_tool_plugin_scan_oserror(mock_subprocess_check_output): """ Test what happens when an OSError is raised (usually means hadolint doesn't exist). @@ -312,7 +300,7 @@ def test_hadolint_tool_plugin_scan_oserror(mock_subprocess_check_output): assert issues is None -@mock.patch("statick_tool.plugins.tool.hadolint_tool_plugin.subprocess.check_output") +@mock.patch("statick_tool.plugins.tool.hadolint.subprocess.check_output") def test_hadolint_tool_plugin_scan_calledprocesserror_with_docker( mock_subprocess_check_output, ): @@ -342,7 +330,7 @@ def test_hadolint_tool_plugin_scan_calledprocesserror_with_docker( assert not issues -@mock.patch("statick_tool.plugins.tool.hadolint_tool_plugin.subprocess.check_output") +@mock.patch("statick_tool.plugins.tool.hadolint.subprocess.check_output") def test_hadolint_tool_plugin_scan_oserror_with_docker(mock_subprocess_check_output): """ Test what happens when an OSError is raised by scan_docker. diff --git a/tox.ini b/tox.ini index c1d41c9..14ff8b9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,48 +1,28 @@ [tox] -envlist = py38, py39, py310, py311 +envlist = py39, py310, py311, py312, py313 skip_missing_interpreters = true [pytest] -norecursedirs = .tox - -# To work with black a specific configuration is required. -# https://github.com/psf/black#how-black-wraps-lines -[isort] -known_first_party = statick_tool -multi_line_output = 3 -include_trailing_comma = True -force_grid_wrap = 0 -use_parentheses = True -line_length = 88 +norecursedirs = .tox build [gh-actions] python = - 3.8: py38 3.9: py39 3.10: py310 3.11: py311 + 3.12: py312 + 3.13: py313 [testenv] changedir = {toxinidir}/output-{envname} -passenv = CI -setenv = PY_IGNORE_IMPORTMISMATCH = 1 deps = - codecov - pycodestyle - pydocstyle - pytest - pytest-cov - pytest-isort .[test] commands = - pydocstyle ../src/ - pycodestyle --ignore=E203,E501,W503 ../src/ - pytest -rs --isort \ - --cov=statick_tool.plugins.discovery.dockerfile_discovery_plugin \ - --cov=statick_tool.plugins.tool.dockerfilelint_tool_plugin \ - --cov=statick_tool.plugins.tool.dockerfile_lint_tool_plugin \ - --cov=statick_tool.plugins.tool.hadolint_tool_plugin \ + pytest \ + --cov={toxinidir}/src/statick_tool \ --cov-report term-missing \ --doctest-modules \ + --junit-xml=statick-{envname}-junit.xml \ + --junit-prefix={envname} \ {toxinidir} {posargs} coverage xml