From 65451160440d347eef38c52d7d73b72062e1adca Mon Sep 17 00:00:00 2001 From: Charles Coggins Date: Wed, 5 Apr 2023 09:28:34 -0500 Subject: [PATCH] chore: add QA checks to `Test` workflow (#219) This change adds a new `QA` job to the existing `Test` workflow. It is done in a way that the `Test rollup` job can continue to be an enforced status check, but with the addition of this job. A new optional `tox` test environment named `qa` was added and is called by the `QA` job. The environment can also be called by users locally. It relies on simply running `pre-commit` on the existing hooks defined in the repository: * trim trailing whitespace * fix end of files * check yaml * check for added large files * `black` * `pyupgrade` * analyze lockfile with `phylum-ci` * This one is skipped The `phylum-ci` pre-commit hook is skipped in the `QA` job since: * The current GitHub integration expects to *only* run in a PR context * The `Test` workflow also includes `workflow_dispatch` and `push` * The integration will fail even if a `GITHUB_TOKEN` is provided * The `phylum-ci` action will already be run for pull request triggers It is possible this restriction can be lifted in the future if the GitHub integration is updated to account for additional execution contexts beyond pull requests. The `pre-commit` package was added as a dependency in the `qa` group and can therefore be kept under configuration control and used by `poetry`. Documentation was updated to make all the new usage patterns explicit. Additional QA checks and `pre-commit` hooks will be added separately. Additional changes made include: * Fix a missing version check normalization * When no CLI version is specified and no CLI is installed * The version was reported as `latest` * Version is now current latest in "vMajor.Minor.Bugfix" format Closes #14 --- .github/workflows/test.yml | 61 ++++++++++++++++++++++------- CONTRIBUTING.md | 26 +++++++------ poetry.lock | 80 +++++++++++++++++++++++++++++++++++++- pyproject.toml | 2 + src/phylum/ci/cli.py | 2 +- src/phylum/ci/constants.py | 2 +- tox.ini | 14 +++++++ 7 files changed, 158 insertions(+), 29 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8ef166d0..c8b51dc2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -10,7 +10,48 @@ on: branches: - main +defaults: + run: + shell: bash + jobs: + QA: + name: Quality Assurance + runs-on: ubuntu-latest + strategy: + matrix: + # It's only one Python version specified in a "matrix", but on purpose to stay DRY + python-version: ["3.11"] + steps: + - name: Checkout the repo + uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 + + - name: Install poetry + run: pipx install poetry + + - name: Configure poetry + run: poetry config virtualenvs.in-project true + + - name: Set up Python + uses: actions/setup-python@d27e3f3d7c64b4bbf8e4abfb9b63b83e846e0435 # v4.5.0 + with: + python-version: ${{ matrix.python-version }} + cache: 'poetry' + + - name: Install the project with poetry + run: | + poetry env use python${{ matrix.python-version }} + poetry lock --check + poetry install --verbose --no-root --sync --with qa + + - name: Run tox via poetry + env: + # Skip the `phylum-ci` pre-commit hook since: + # * The current GitHub integration expects to *only* be run in a PR context + # * The `phylum-ci` action will already be run for pull request triggers + SKIP: phylum-ci + run: poetry run tox run -e qa + test-matrix: name: Test on Python ${{ matrix.python-version }} runs-on: ubuntu-latest @@ -18,9 +59,6 @@ jobs: fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11"] - defaults: - run: - shell: bash steps: - name: Checkout the repo uses: actions/checkout@8f4b7f84864484a7bf31766abe9204da3cbe65b3 # v3.5.0 @@ -55,9 +93,6 @@ jobs: matrix: # It's only one Python version specified in a "matrix", but on purpose to stay DRY python-version: ["3.11"] - defaults: - run: - shell: bash env: DOCKER_BUILDKIT: 1 steps: @@ -123,18 +158,16 @@ jobs: # the repo settings without needing to update those settings everytime the test jobs are updated. test-rollup: name: Test rollup - if: always() - needs: [test-matrix, docker] runs-on: ubuntu-latest + if: always() + needs: [QA, test-matrix, docker] steps: - name: Check for test jobs failure - if: (needs.test-matrix.result != 'success') || (needs.docker.result != 'success') - shell: bash + if: (needs.QA.result != 'success') || (needs.test-matrix.result != 'success') || (needs.docker.result != 'success') run: | - echo "One or more test matrix jobs was/were not successful" + echo "At least one test job was not successful" exit 1 - name: Confirm test jobs success - if: (needs.test-matrix.result == 'success') && (needs.docker.result == 'success') - shell: bash - run: echo "All test matrix jobs were successful" + if: (needs.QA.result == 'success') && (needs.test-matrix.result == 'success') && (needs.docker.result == 'success') + run: echo "All test jobs were successful" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed24d286..8dbcaa5c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -140,9 +140,10 @@ Here's how to set up `phylum-ci` for local development. # Install the main dependencies only: poetry install --sync - # Alternatively, specific dependency groups can be installed at the same time. - # It makes sense to add the "test" group now if new code is going to be added and tested: - poetry install --sync --with test + # Alternatively, specific dependency groups can be installed at the + # same time. It makes sense to add the "test" and "qa" groups now + # if new code is going to be added and tested: + poetry install --sync --with test,qa ``` 6. Create a branch for local development: @@ -180,12 +181,13 @@ Here's how to set up `phylum-ci` for local development. phylum analyze poetry.lock ``` -8. When you're done making changes, check that your changes pass the tests: +8. When you're done making changes, check that your changes pass QA and the tests: ```sh - # Ensure the "test" dependency group is installed, if not done previously - poetry install --sync --with test - poetry run tox + # Ensure the "test" and "qa" dependency groups are installed, if not done previously + poetry install --sync --with test,qa + poetry run tox run -e qa + poetry run tox run-parallel ``` 9. Commit your changes and push your branch to GitHub: @@ -232,16 +234,16 @@ interact with `pytest` by passing additional positional arguments: ```sh # passing additional options to pytest requires using the double dash # escape twice, once for escaping `poetry` and again for escaping `tox` -poetry run -- tox -e py310 -- --help +poetry run -- tox run -e py310 -- --help -# run a specific test module across all test environments -poetry run -- tox -- tests/unit/test_package_metadata.py +# run a specific test module across all test environments in parallel +poetry run -- tox run-parallel -- tests/unit/test_package_metadata.py # run a specific test module across a specific test environment -poetry run -- tox -e py39 -- tests/unit/test_package_metadata.py +poetry run -- tox run -e py39 -- tests/unit/test_package_metadata.py # run a specific test function within a test module, in a specific test environment -poetry run -- tox -e py310 -- tests/unit/test_package_metadata.py::test_python_version +poetry run -- tox run -e py310 -- tests/unit/test_package_metadata.py::test_python_version ``` To run a script entry point with the local checkout of the code (in develop mode), use `poetry`: diff --git a/poetry.lock b/poetry.lock index 09ed91c3..aa740c0d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -139,6 +139,18 @@ files = [ [package.dependencies] pycparser = "*" +[[package]] +name = "cfgv" +version = "3.3.1" +description = "Validate configuration and produce human readable error messages." +category = "dev" +optional = false +python-versions = ">=3.6.1" +files = [ + {file = "cfgv-3.3.1-py2.py3-none-any.whl", hash = "sha256:c6a0883f3917a037485059700b9e75da2464e6c27051014ad85ba6aaa5884426"}, + {file = "cfgv-3.3.1.tar.gz", hash = "sha256:f5a830efb9ce7a445376bb66ec94c638a9787422f96264c98edc6bdeed8ab736"}, +] + [[package]] name = "chardet" version = "5.1.0" @@ -523,6 +535,21 @@ files = [ [package.dependencies] gitdb = ">=4.0.1,<5" +[[package]] +name = "identify" +version = "2.5.22" +description = "File identification library for Python" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "identify-2.5.22-py2.py3-none-any.whl", hash = "sha256:f0faad595a4687053669c112004178149f6c326db71ee999ae4636685753ad2f"}, + {file = "identify-2.5.22.tar.gz", hash = "sha256:f7a93d6cf98e29bd07663c60728e7a4057615068d7a639d132dc883b2d54d31e"}, +] + +[package.extras] +license = ["ukkonen"] + [[package]] name = "idna" version = "3.4" @@ -844,6 +871,21 @@ files = [ {file = "more_itertools-9.1.0-py3-none-any.whl", hash = "sha256:d2bc7f02446e86a68911e58ded76d6561eea00cddfb2a91e7019bbb586c799f3"}, ] +[[package]] +name = "nodeenv" +version = "1.7.0" +description = "Node.js virtual environment builder" +category = "dev" +optional = false +python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*" +files = [ + {file = "nodeenv-1.7.0-py2.py3-none-any.whl", hash = "sha256:27083a7b96a25f2f5e1d8cb4b6317ee8aeda3bdd121394e5ac54e498028a042e"}, + {file = "nodeenv-1.7.0.tar.gz", hash = "sha256:e0e7f7dfb85fc5394c6fe1e8fa98131a2473e04311a45afb6508f7cf1836fa2b"}, +] + +[package.dependencies] +setuptools = "*" + [[package]] name = "packaging" version = "23.0" @@ -927,6 +969,25 @@ files = [ dev = ["pre-commit", "tox"] testing = ["pytest", "pytest-benchmark"] +[[package]] +name = "pre-commit" +version = "3.2.1" +description = "A framework for managing and maintaining multi-language pre-commit hooks." +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "pre_commit-3.2.1-py2.py3-none-any.whl", hash = "sha256:a06a7fcce7f420047a71213c175714216498b49ebc81fe106f7716ca265f5bb6"}, + {file = "pre_commit-3.2.1.tar.gz", hash = "sha256:b5aee7d75dbba21ee161ba641b01e7ae10c5b91967ebf7b2ab0dfae12d07e1f1"}, +] + +[package.dependencies] +cfgv = ">=2.0.0" +identify = ">=1.0.0" +nodeenv = ">=0.11.1" +pyyaml = ">=5.1" +virtualenv = ">=20.10.0" + [[package]] name = "pycparser" version = "2.21" @@ -1529,6 +1590,23 @@ files = [ {file = "semver-2.13.0.tar.gz", hash = "sha256:fa0fe2722ee1c3f57eac478820c3a5ae2f624af8264cbdf9000c980ff7f75e3f"}, ] +[[package]] +name = "setuptools" +version = "67.6.1" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +category = "dev" +optional = false +python-versions = ">=3.7" +files = [ + {file = "setuptools-67.6.1-py3-none-any.whl", hash = "sha256:e728ca814a823bf7bf60162daf9db95b93d532948c4c0bea762ce62f60189078"}, + {file = "setuptools-67.6.1.tar.gz", hash = "sha256:257de92a9d50a60b8e22abfcbb771571fde0dbf3ec234463212027a4eeecbe9a"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"] +testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-timeout", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"] +testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"] + [[package]] name = "six" version = "1.16.0" @@ -1807,4 +1885,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.8,<3.12" -content-hash = "07266d395f60d5aede8b129bdf22f065538367fbb8b617f52523a8a167a7907d" +content-hash = "ca73dc7c24f70784e26f2ad3ae76f27e31c27a2d74d969e601f3f197a935e9b2" diff --git a/pyproject.toml b/pyproject.toml index 300ec0dd..5884d2d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,8 @@ optional = true [tool.poetry.group.qa.dependencies] types-requests = "*" +pre-commit = "*" +tox = "*" [tool.black] line-length = 120 diff --git a/src/phylum/ci/cli.py b/src/phylum/ci/cli.py index d4185f3e..d9df7a28 100644 --- a/src/phylum/ci/cli.py +++ b/src/phylum/ci/cli.py @@ -252,7 +252,7 @@ def main(args: Optional[Sequence[str]] = None) -> int: parsed_args.version = version_check(parsed_args.version) else: print(" [+] Phylum CLI version not specified") - parsed_args.version = default_phylum_cli_version() + parsed_args.version = version_check(default_phylum_cli_version()) print(f" [*] Using Phylum CLI version: {parsed_args.version}") # Detect which CI environment, if any, we are in diff --git a/src/phylum/ci/constants.py b/src/phylum/ci/constants.py index d6fbf8c9..c67daa14 100644 --- a/src/phylum/ci/constants.py +++ b/src/phylum/ci/constants.py @@ -12,7 +12,7 @@ SUCCESS_DETAILS = "The Phylum risk analysis is complete and did not identify any issues." -# Expandable HTML providing information on why there was a failure +# Background text providing information on why there was a failure FAILURE_DETAILS = """ This repository analyzes risk of new dependencies with Phylum. An administrator of this repository has set score requirements for Phylum's five risk domains. diff --git a/tox.ini b/tox.ini index 90c3d45b..4557431a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,4 +1,6 @@ [tox] +# Sub-commands were introduced in v4. These are used in workflows and docs. +min_version = 4 envlist = py38, py39, py310, py311 isolated_build = true @@ -10,10 +12,22 @@ python = 3.11: py311 [testenv] +description = Test environment for minor Python version passenv = * +# Skip the local package install so that `poetry` can handle installing +# all of the dependencies and do so only from the `poetry.lock` lockfile. +skip_install = true allowlist_externals = poetry commands = poetry lock --check poetry install --verbose --sync --with test poetry run python -m pip list poetry run pytest {posargs} + +[testenv:qa] +description = Quality Assurance (QA) checks +commands = + poetry lock --check + poetry install --verbose --sync --with qa + poetry run python -m pip list + poetry run pre-commit run --all-files --verbose