diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml new file mode 100644 index 0000000..dc98f3e --- /dev/null +++ b/.github/workflows/pipeline.yml @@ -0,0 +1,94 @@ +name: CI pipeline + +on: + pull_request: + branches: + - main + push: + branches: + - main + +jobs: + test: + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [macos-latest, ubuntu-latest] + python-version: ["3.8"] + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Install wkhtmltopdf + run: | + if [ "$RUNNER_OS" == "Linux" ]; then + sudo apt-get install xvfb libfontconfig wkhtmltopdf + elif [ "$RUNNER_OS" == "macOS" ]; then + brew install wkhtmltopdf + fi + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Install tox + run: python -m pip install --upgrade pip tox tox-gh-actions + + - name: Write example configuration file + run: cp configuration.example.py configuration.py + + - name: Run tests + run: tox + + - name: Upload coverage artifact + if: matrix.os == 'ubuntu-latest' + uses: actions/upload-artifact@v3.1.2 + with: + name: coverage-${{ matrix.python-version }} + path: reports/.coverage.*test + + coverage: + runs-on: ubuntu-latest + timeout-minutes: 5 + needs: test + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Download all coverage artifacts + uses: actions/download-artifact@v3 + + - name: Copy coverage reports to reports folder + run: mkdir reports && find . -type f -path "./coverage-3.*/*" -exec cp {} reports/ \; + + - name: Install tox + run: python -m pip install --upgrade pip tox + + - name: Combine coverage results + run: tox run -e combine-test-reports + + lint: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - uses: actions/setup-python@v4 + with: + python-version: 3.8 + + - name: Install tox + run: python -m pip install --upgrade tox + + - name: Run static checks + run: tox -e lint diff --git a/Manager.py b/Manager.py index 731fb89..a512574 100755 --- a/Manager.py +++ b/Manager.py @@ -5,9 +5,10 @@ from flask_migrate import Migrate, MigrateCommand from flask_script import Manager -import configuration as config from shop_db2.api import app, db, set_app +import configuration as config # isort: skip + set_app(config.ProductiveConfig) migrate = Migrate(app, db) manager = Manager(app) diff --git a/backup.py b/backup.py index f8a3966..0ede9ea 100755 --- a/backup.py +++ b/backup.py @@ -6,7 +6,7 @@ import sqlite3 import sys -from configuration import ProductiveConfig +from configuration import ProductiveConfig # isort: skip if __name__ == "__main__": _currentDate = datetime.datetime.now() diff --git a/setupdb.py b/setupdb.py index 5be5881..7792599 100644 --- a/setupdb.py +++ b/setupdb.py @@ -9,12 +9,13 @@ from sqlalchemy.exc import IntegrityError -import configuration as config import shop_db2.exceptions as exc from shop_db2.api import app, db, set_app from shop_db2.helpers.users import insert_user from shop_db2.models import Rank, User +import configuration as config # isort: skip + def _get_password(): """Ask the user for a password and repeat it until both passwords match and diff --git a/shopdb_entry.py b/shopdb_entry.py index 9aa443f..be7ca39 100755 --- a/shopdb_entry.py +++ b/shopdb_entry.py @@ -5,7 +5,7 @@ import sys try: - import configuration as config + import configuration as config # isort: skip except ModuleNotFoundError: sys.exit( "No configuration file was found. Please make sure, " diff --git a/src/shop_db2/api.py b/src/shop_db2/api.py index af2b3e7..4de750f 100644 --- a/src/shop_db2/api.py +++ b/src/shop_db2/api.py @@ -5,9 +5,10 @@ from flask import Flask, jsonify from flask_bcrypt import Bcrypt -import configuration as config from shop_db2.shared import db +import configuration as config # isort: skip + app = Flask(__name__) # Default app settings (to suppress unittest warnings) will be overwritten. diff --git a/src/shop_db2/helpers/uploads.py b/src/shop_db2/helpers/uploads.py index a724c18..7b36f14 100644 --- a/src/shop_db2/helpers/uploads.py +++ b/src/shop_db2/helpers/uploads.py @@ -11,9 +11,10 @@ from PIL import Image -import configuration as config import shop_db2.exceptions as exc +import configuration as config # isort: skip + def insert_image(file: dict) -> str: if not file: diff --git a/src/shop_db2/routes/maintenance.py b/src/shop_db2/routes/maintenance.py index 70115e5..58b4608 100644 --- a/src/shop_db2/routes/maintenance.py +++ b/src/shop_db2/routes/maintenance.py @@ -8,7 +8,6 @@ from flask import jsonify import shop_db2.exceptions as exc -from configuration import PATH from shop_db2.api import app from shop_db2.helpers.decorators import adminRequired from shop_db2.helpers.utils import json_body diff --git a/tests/base.py b/tests/base.py index a051209..8cf63ae 100644 --- a/tests/base.py +++ b/tests/base.py @@ -4,7 +4,6 @@ from flask_testing import TestCase -import configuration as config from shop_db2.api import app, bcrypt, db, set_app from shop_db2.models import ( AdminUpdate, @@ -20,6 +19,8 @@ User, ) +import configuration as config # isort: skip + # Global password storage. Hashing the passwords for each unit test # would take too long. For this reason, the passwords are created once # and then stored in this array. diff --git a/tests/unit/api/test_api_get_stocktaking_print_template.py b/tests/unit/api/test_api_get_stocktaking_print_template.py index 2b4aa24..1a0abaa 100644 --- a/tests/unit/api/test_api_get_stocktaking_print_template.py +++ b/tests/unit/api/test_api_get_stocktaking_print_template.py @@ -2,6 +2,7 @@ # -*- coding: utf-8 -*- __author__ = "g3n35i5" + import shop_db2.exceptions as exc from shop_db2.api import db from shop_db2.models import Product diff --git a/tests/unit/api/test_api_toggle_maintenance.py b/tests/unit/api/test_api_toggle_maintenance.py index f2e0414..0c2eb24 100644 --- a/tests/unit/api/test_api_toggle_maintenance.py +++ b/tests/unit/api/test_api_toggle_maintenance.py @@ -9,10 +9,11 @@ from flask import json import shop_db2.exceptions as exc -from configuration import PATH from shop_db2.api import app from tests.base_api import BaseAPITestCase +from configuration import PATH # isort: skip + class ToggleMaintenanceAPITestCase(BaseAPITestCase): @staticmethod diff --git a/tox.ini b/tox.ini index fa52f29..3133624 100644 --- a/tox.ini +++ b/tox.ini @@ -5,15 +5,19 @@ envlist = combine-test-reports isolated_build = True +[gh-actions] +python = + 3.8: py38-test [testenv:lint] description = Run static checkers. basepython = py38 extras = lint -passenv = CODEMETER_HOST +passenv = + RUNNER_OS commands = # Check import ordering - isort . --check + isort . --check --diff # Check formatting black . --check # Check type hinting @@ -32,7 +36,8 @@ extras = test setenv = PY_IGNORE_IMPORTMISMATCH=1 COVERAGE_FILE = reports{/}.coverage.{envname} -passenv = CODEMETER_HOST +passenv = + RUNNER_OS commands = # Run tests and doctests from .py files pytest --junitxml=reports/pytest.xml.{envname} {posargs} src/ tests/ @@ -56,7 +61,8 @@ commands = [testenv:build] description = Build the package. extras = build -passenv = CODEMETER_HOST +passenv = + RUNNER_OS commands = # Clean up build directories python -c 'from shutil import rmtree; rmtree("build", True); rmtree("dist", True)' diff --git a/wsgi.py b/wsgi.py index 53097b4..318a280 100755 --- a/wsgi.py +++ b/wsgi.py @@ -11,9 +11,10 @@ import gunicorn.app.base from gunicorn.six import iteritems -import configuration as config from shop_db2.api import app, set_app +import configuration as config # isort: skip + def number_of_workers(): return (multiprocessing.cpu_count() * 2) + 1