From 5aa4080d2193269aa5b0f0c2ed2b3e2c09e09c65 Mon Sep 17 00:00:00 2001 From: Michael Chouinard <46358556+chouinar@users.noreply.github.com> Date: Wed, 21 Sep 2022 10:20:28 -0400 Subject: [PATCH] WMDP-195: Automatically run linting/formatting/tests in CI for PRs and merges to main (#21) WMDP-195: Automatically run linting/formatting/tests in CI for PRs and merges to main --- .github/workflows/api-checks.yml | 41 +++++++++++----- app/Makefile | 20 +++++--- app/poetry.lock | 82 ++++++++++++++++++++++++++++++-- app/pyproject.toml | 3 ++ 4 files changed, 124 insertions(+), 22 deletions(-) diff --git a/.github/workflows/api-checks.yml b/.github/workflows/api-checks.yml index 23c2978..aa8aea8 100644 --- a/.github/workflows/api-checks.yml +++ b/.github/workflows/api-checks.yml @@ -6,33 +6,50 @@ on: - main paths: - app/** -# this workflow should be updated/merged when WMDP-123. etc are merged + - .github/workflows/api-checks.yml + pull_request: + paths: + - app/** + - .github/workflows/api-checks.yml + +defaults: + run: + working-directory: ./app + +env: + # Some commands in the Makefile check if this is set + # and add additional parameters to the commands called + CI: true + jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - - name: Run Linting - run: | - echo "Placeholder until linting is installed" - exit 0 + - name: build container + run: make build + + - name: Run format check + run: make format-check + + - name: Run linting + run: make lint security: - runs-on: ubutntu-latest + runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Security Check - run: | - echo "Placeholder until security tools are installed" - exit 0 + run: make lint-security test: needs: [lint, security] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + - name: test for multi-headed migration situation + run: make test args="-x tests/api/db/test_migrations.py::test_only_single_head_revision_in_migrations" + - name: Start tests - run: | - echo "Placeholder until tests are written" - exit 0 \ No newline at end of file + run: make test-coverage \ No newline at end of file diff --git a/app/Makefile b/app/Makefile index bdeb50a..6fa58da 100644 --- a/app/Makefile +++ b/app/Makefile @@ -119,6 +119,14 @@ db-migrate-heads: ## Show migrations marked as a head test: $(PY_RUN_CMD) pytest $(args) +test-coverage: + $(PY_RUN_CMD) coverage run --branch --source=api -m pytest $(args) + $(PY_RUN_CMD) coverage report + +test-coverage-report: ## Open HTML test coverage report + $(PY_RUN_CMD) coverage html --directory .coverage_report + open .coverage_report/index.html + ################################################## # Formatting and linting ################################################## @@ -127,6 +135,10 @@ format: $(PY_RUN_CMD) isort --atomic api tests $(PY_RUN_CMD) black api tests +format-check: + $(PY_RUN_CMD) isort --atomic --check-only api tests + $(PY_RUN_CMD) black --check api tests + lint: lint-spectral lint-py lint-py: lint-flake lint-mypy lint-poetry-version @@ -144,13 +156,9 @@ lint-spectral: docker run --rm --tty --cap-drop=ALL --network=none --read-only --volume=$(PWD):/tmp:ro \ stoplight/spectral:6 lint /tmp/openapi.yml --ruleset /tmp/.spectral.yaml $(SPECTRAL_POSTPROC) -test-coverage: - $(PY_RUN_CMD) coverage run --branch --source=api -m pytest $(args) - $(PY_RUN_CMD) coverage report +lint-security: # https://bandit.readthedocs.io/en/latest/index.html + $(PY_RUN_CMD) bandit -r . --number 3 --skip B101 -ll -x ./.venv -test-coverage-report: ## Open HTML test coverage report - $(PY_RUN_CMD) coverage html --directory .coverage_report - open .coverage_report/index.html ################################################## # Scripts diff --git a/app/poetry.lock b/app/poetry.lock index 7b47ce5..112e669 100644 --- a/app/poetry.lock +++ b/app/poetry.lock @@ -35,6 +35,25 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] +[[package]] +name = "bandit" +version = "1.7.4" +description = "Security oriented static analyser for python code." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = ">=0.3.9", markers = "platform_system == \"Windows\""} +GitPython = ">=1.0.1" +PyYAML = ">=5.3.1" +stevedore = ">=1.20.0" + +[package.extras] +yaml = ["pyyaml"] +toml = ["toml"] +test = ["pylint (==1.9.4)", "beautifulsoup4 (>=4.8.0)", "toml", "testtools (>=2.3.0)", "testscenarios (>=0.5.0)", "stestr (>=2.5.0)", "flake8 (>=4.0.0)", "fixtures (>=3.0.0)", "coverage (>=4.5.4)"] + [[package]] name = "black" version = "22.8.0" @@ -289,6 +308,28 @@ Werkzeug = ">=2.0" async = ["asgiref (>=3.2)"] dotenv = ["python-dotenv"] +[[package]] +name = "gitdb" +version = "4.0.9" +description = "Git Object Database" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +smmap = ">=3.0.1,<6" + +[[package]] +name = "gitpython" +version = "3.1.27" +description = "GitPython is a python library used to interact with Git repositories" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +gitdb = ">=4.0.1,<5" + [[package]] name = "greenlet" version = "1.1.3" @@ -418,7 +459,7 @@ python-versions = ">=3.6" [[package]] name = "moto" -version = "4.0.2" +version = "4.0.3" description = "A library that allows your python tests to easily mock out the boto library" category = "dev" optional = false @@ -507,6 +548,14 @@ category = "dev" optional = false python-versions = ">=3.7" +[[package]] +name = "pbr" +version = "5.10.0" +description = "Python Build Reasonableness" +category = "dev" +optional = false +python-versions = ">=2.6" + [[package]] name = "platformdirs" version = "2.5.2" @@ -736,6 +785,14 @@ s3 = ["boto3"] test = ["boto3", "google-cloud-storage (>=1.31.0)", "azure-storage-blob", "azure-common", "azure-core", "requests", "moto", "pathlib2", "responses", "paramiko", "pytest", "pytest-rerunfailures"] webhdfs = ["requests"] +[[package]] +name = "smmap" +version = "5.0.0" +description = "A pure Python implementation of a sliding window memory map manager" +category = "dev" +optional = false +python-versions = ">=3.6" + [[package]] name = "sqlalchemy" version = "1.4.41" @@ -781,6 +838,17 @@ python-versions = ">=3.6" [package.dependencies] typing-extensions = ">=3.7.4" +[[package]] +name = "stevedore" +version = "4.0.0" +description = "Manage dynamic plugins for Python applications" +category = "dev" +optional = false +python-versions = ">=3.8" + +[package.dependencies] +pbr = ">=2.0.0,<2.1.0 || >2.1.0" + [[package]] name = "swagger-ui-bundle" version = "0.0.9" @@ -859,12 +927,13 @@ python-versions = ">=3.4" [metadata] lock-version = "1.1" python-versions = "^3.10" -content-hash = "5e94f4df6090616ceb9c9b8e01cd06e8df5e614a3a2e55c951bbd3c76c49ac5d" +content-hash = "209fe2645a946ce0995a92074be9be8e21bc932fea655dbb4f7b002dabe5523a" [metadata.files] alembic = [] atomicwrites = [] attrs = [] +bandit = [] black = [ {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, @@ -1006,6 +1075,8 @@ flake8-bugbear = [ {file = "flake8_bugbear-22.8.23-py3-none-any.whl", hash = "sha256:1b0ebe0873d1cd55bf9f1588bfcb930db339018ef44a3981a26532daa9fd14a8"}, ] flask = [] +gitdb = [] +gitpython = [] greenlet = [ {file = "greenlet-1.1.3-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:8c287ae7ac921dfde88b1c125bd9590b7ec3c900c2d3db5197f1286e144e712b"}, {file = "greenlet-1.1.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:870a48007872d12e95a996fca3c03a64290d3ea2e61076aa35d3b253cf34cd32"}, @@ -1089,8 +1160,8 @@ mccabe = [ {file = "mccabe-0.7.0.tar.gz", hash = "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325"}, ] moto = [ - {file = "moto-4.0.2-py3-none-any.whl", hash = "sha256:befb70c7364e141ef122e9e85f8b2304946ee3a457aeeb2424f80db5409745a1"}, - {file = "moto-4.0.2.tar.gz", hash = "sha256:231836b76ceb1786f4e91dae77e9d34e037380764edd9fd55dffa42781c8e4e7"}, + {file = "moto-4.0.3-py3-none-any.whl", hash = "sha256:749f98f52110faa124258113c7b1980edc1551d073c0a425fe46d2480f088bf6"}, + {file = "moto-4.0.3.tar.gz", hash = "sha256:8aeb56757e686af3e4a63f90afbc973cb231adaad87c5ce89e68936c109b0be9"}, ] mypy = [ {file = "mypy-0.971-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:f2899a3cbd394da157194f913a931edfd4be5f274a88041c9dc2d9cdcb1c315c"}, @@ -1123,6 +1194,7 @@ pathspec = [ {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, ] +pbr = [] platformdirs = [] pluggy = [] psycopg2-binary = [] @@ -1229,6 +1301,7 @@ smart-open = [ {file = "smart_open-6.1.0-py3-none-any.whl", hash = "sha256:1a5315659844085a1ba7ec40d080c1d084ba8535ec113160d692fe8a0e032417"}, {file = "smart_open-6.1.0.tar.gz", hash = "sha256:c8fd9e9f90f0e285f1346481b0ae0fb57bd04b1138826c5e826ee98b2029d7f3"}, ] +smmap = [] sqlalchemy = [ {file = "SQLAlchemy-1.4.41-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:13e397a9371ecd25573a7b90bd037db604331cf403f5318038c46ee44908c44d"}, {file = "SQLAlchemy-1.4.41-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:2d6495f84c4fd11584f34e62f9feec81bf373787b3942270487074e35cbe5330"}, @@ -1275,6 +1348,7 @@ sqlalchemy2-stubs = [ {file = "sqlalchemy2-stubs-0.0.2a27.tar.gz", hash = "sha256:f79bce50b7837a2c2374ef4480b41e2b8a8226f313f347dc2a70526a4191db93"}, {file = "sqlalchemy2_stubs-0.0.2a27-py3-none-any.whl", hash = "sha256:6cea12fec3c261f6e0e14a95d2cc4914e373095e68ec4fc2eb473183ac2b17a2"}, ] +stevedore = [] swagger-ui-bundle = [] toml = [] tomli = [] diff --git a/app/pyproject.toml b/app/pyproject.toml index 9d82615..a18b652 100644 --- a/app/pyproject.toml +++ b/app/pyproject.toml @@ -31,6 +31,7 @@ mypy = "^0.971" moto = {extras = ["s3"], version = "^4.0.2"} types-pytz = "^2022.2.1" coverage = "^6.4.4" +bandit = "^1.7.4" [build-system] requires = ["poetry-core>=1.0.0"] @@ -77,6 +78,8 @@ warn_unused_ignores = true plugins = ["sqlalchemy.ext.mypy.plugin"] + + [[tool.mypy.overrides]] # Migrations are generated without "-> None" # for the returns. Rather than require manually