diff --git a/.codeclimate.yml b/.codeclimate.yml index 978fb5fe3..026ed23f8 100644 --- a/.codeclimate.yml +++ b/.codeclimate.yml @@ -1,3 +1,4 @@ +--- version: "2" # required to adjust maintainability checks checks: argument-count: @@ -26,10 +27,10 @@ checks: threshold: 4 similar-code: config: - threshold: 50 # language-specific defaults. an override will affect all languages. + threshold: 50 # language-specific defaults. an override will affect all languages. identical-code: config: - threshold: # language-specific defaults. an override will affect all languages. + threshold: # language-specific defaults. an override will affect all languages. plugins: bandit: enabled: true diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 88363add0..2d98a9b29 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,3 +1,4 @@ +--- version: 2 updates: - package-ecosystem: "pip" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 81e9153f2..db2f70980 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,9 +1,10 @@ +--- name: Release on: push: tags: - - '*' + - '*' jobs: build: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index ed9d0c95d..e143bd18f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,3 +1,4 @@ +--- name: Test on: [push, pull_request] @@ -14,10 +15,10 @@ jobs: django-version: ['2.2', '3.1', '3.2', 'main'] exclude: - - python-version: '3.6' - django-version: 'main' - - python-version: '3.7' - django-version: 'main' + - python-version: '3.6' + django-version: 'main' + - python-version: '3.7' + django-version: 'main' services: @@ -51,39 +52,39 @@ jobs: options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v2 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 - with: - python-version: ${{ matrix.python-version }} + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} - - name: Get pip cache dir - id: pip-cache - run: | - echo "::set-output name=dir::$(pip cache dir)" + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" - - name: Cache - uses: actions/cache@v2 - with: - path: ${{ steps.pip-cache.outputs.dir }} - key: - ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} - restore-keys: | - ${{ matrix.python-version }}-v1- + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.python-version }}-v1-${{ hashFiles('**/setup.py') }}-${{ hashFiles('**/tox.ini') }} + restore-keys: | + ${{ matrix.python-version }}-v1- - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install -r requirements/tox.txt + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements/tox.txt - - name: Tox tests - run: | - tox -v - env: - DJANGO: ${{ matrix.django-version }} + - name: Tox tests + run: | + tox -v + env: + DJANGO: ${{ matrix.django-version }} - - name: Upload coverage - uses: codecov/codecov-action@v1 - with: - name: Python ${{ matrix.python-version }} + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + name: Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..6c8d914df --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,42 @@ +--- +repos: + - repo: https://github.com/PyCQA/bandit + rev: 1.7.0 + hooks: + - id: bandit + args: + - "-x *test*.py" + + - repo: https://github.com/ambv/black + rev: 21.9b0 + hooks: + - id: black + language_version: python3.8 + + - repo: https://github.com/pycqa/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + args: + - "--config=tox.ini" + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.0.1 + hooks: + - id: requirements-txt-fixer + files: requirements/.*\.txt$ + - id: trailing-whitespace + - id: check-added-large-files + - id: fix-byte-order-marker + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-merge-conflict + - id: debug-statements + - id: detect-private-key + + - repo: https://github.com/adrienverge/yamllint + rev: v1.26.3 + hooks: + - id: yamllint + args: + - "--strict" diff --git a/.yamllint b/.yamllint new file mode 100644 index 000000000..d77347d35 --- /dev/null +++ b/.yamllint @@ -0,0 +1,13 @@ +# allow "on" until yamllint stops checking keys for truthy! +# https://github.com/adrienverge/yamllint/issues/158 +--- +extends: default + +rules: + comments-indentation: disable + braces: disable + line-length: + max: 120 + truthy: + level: error + allowed-values: ['true', 'false', 'on'] diff --git a/CHANGES.rst b/CHANGES.rst index bf20ebca2..74a0f7f48 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -21,6 +21,7 @@ Full list of changes: - Support ``included_fields`` for ``history.diff_against`` (gh-776) - Improve performance of ``history.diff_against`` by reducing number of queries to 0 in most cases (gh-776) - Added Czech translations (gh-885) +- Added pre-commit for better commit quality (gh-896) 3.0.0 (2021-04-16) ------------------ diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 460bfa4cb..1c0c2a248 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -75,9 +75,13 @@ We make use of `black`_ for code formatting. .. _black: https://black.readthedocs.io/en/stable/installation_and_usage.html -Once it is installed you can make sure the code is properly formatted by running:: +You can install and run it along with other linters through pre-commit: - make format + pre-commit install + pre-commit run + +Once you install pre-commit it will sanity check any commit you make. +Additionally, the CI process runs this check as well. Translations ------------ diff --git a/PULL_REQUEST_TEMPLATE.md b/PULL_REQUEST_TEMPLATE.md index 94724990b..b18a4653e 100644 --- a/PULL_REQUEST_TEMPLATE.md +++ b/PULL_REQUEST_TEMPLATE.md @@ -28,7 +28,7 @@ ## Checklist: -- [ ] I have run the `make format` command to format my code +- [ ] I have run the `pre-commit run` command to format and lint. - [ ] My change requires a change to the documentation. - [ ] I have updated the documentation accordingly. - [ ] I have read the **CONTRIBUTING** document. diff --git a/codecov.yml b/codecov.yml index 434bb0f51..856338872 100644 --- a/codecov.yml +++ b/codecov.yml @@ -1,3 +1,4 @@ +--- coverage: status: patch: diff --git a/docs/conf.py b/docs/conf.py index 223bfa0a5..dd0eee686 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -10,7 +10,6 @@ # # All configuration values have a default; values that are commented out # serve to show the default. - import os import sys @@ -176,11 +175,11 @@ latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', + # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', + # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - #'preamble': '', + # 'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples diff --git a/docs/utils.rst b/docs/utils.rst index 4b4217326..1c7bd429a 100644 --- a/docs/utils.rst +++ b/docs/utils.rst @@ -34,7 +34,7 @@ from the duplicate check clean_old_history ----------------------- -You may want to remove historical records that have existed for a certain amount of time. +You may want to remove historical records that have existed for a certain amount of time. If you find yourself with a lot of old history you can schedule the ``clean_old_history`` command @@ -43,9 +43,9 @@ If you find yourself with a lot of old history you can schedule the $ python manage.py clean_old_history --auto -You can use ``--auto`` to remove old historial entries +You can use ``--auto`` to remove old historial entries with ``HistoricalRecords`` or enumerate specific models as args. -You may also specify a ``--days`` parameter, which indicates how many +You may also specify a ``--days`` parameter, which indicates how many days of records you want to keep. The default it 30 days, meaning that all records older than 30 days would be removed. diff --git a/requirements/mysql.txt b/requirements/mysql.txt index 5f213e9a4..fbbfdfeae 100644 --- a/requirements/mysql.txt +++ b/requirements/mysql.txt @@ -1 +1 @@ -mysqlclient==2.0.3 \ No newline at end of file +mysqlclient==2.0.3 diff --git a/requirements/postgres.txt b/requirements/postgres.txt index ee92c0e33..be478bcc3 100644 --- a/requirements/postgres.txt +++ b/requirements/postgres.txt @@ -1 +1 @@ -psycopg2-binary==2.9.1 \ No newline at end of file +psycopg2-binary==2.9.1 diff --git a/runtests.py b/runtests.py index 3a304018f..265e06962 100755 --- a/runtests.py +++ b/runtests.py @@ -99,7 +99,7 @@ def __getitem__(self, item): } -DEFAULT_SETTINGS = dict( +DEFAULT_SETTINGS = dict( # nosec SECRET_KEY="not a secret", ALLOWED_HOSTS=["localhost"], AUTH_USER_MODEL="custom_user.CustomUser", diff --git a/simple_history/__init__.py b/simple_history/__init__.py old mode 100755 new mode 100644 diff --git a/simple_history/manager.py b/simple_history/manager.py old mode 100755 new mode 100644 diff --git a/simple_history/registry_tests/migration_test_app/migrations/0006_alter_historicalmodelwithcustomattronetoonefield_options_and_more.py b/simple_history/registry_tests/migration_test_app/migrations/0006_alter_historicalmodelwithcustomattronetoonefield_options_and_more.py index ae0497c57..5c16f6ab4 100644 --- a/simple_history/registry_tests/migration_test_app/migrations/0006_alter_historicalmodelwithcustomattronetoonefield_options_and_more.py +++ b/simple_history/registry_tests/migration_test_app/migrations/0006_alter_historicalmodelwithcustomattronetoonefield_options_and_more.py @@ -6,17 +6,24 @@ class Migration(migrations.Migration): dependencies = [ - ('migration_test_app', '0005_historicalmodelwithcustomattronetoonefield_modelwithcustomattronetoonefield'), + ( + "migration_test_app", + "0005_historicalmodelwithcustomattronetoonefield_modelwithcustomattronetoonefield", + ), ] operations = [ migrations.AlterModelOptions( - name='historicalmodelwithcustomattronetoonefield', - options={'get_latest_by': ('history_date', 'history_id'), 'ordering': ('-history_date', '-history_id'), 'verbose_name': 'historical model with custom attr one to one field'}, + name="historicalmodelwithcustomattronetoonefield", + options={ + "get_latest_by": ("history_date", "history_id"), + "ordering": ("-history_date", "-history_id"), + "verbose_name": "historical model with custom attr one to one field", + }, ), migrations.AlterField( - model_name='historicalmodelwithcustomattronetoonefield', - name='history_date', + model_name="historicalmodelwithcustomattronetoonefield", + name="history_date", field=models.DateTimeField(db_index=True), ), ] diff --git a/simple_history/registry_tests/tests.py b/simple_history/registry_tests/tests.py index edc30887d..ba8bd3ac5 100644 --- a/simple_history/registry_tests/tests.py +++ b/simple_history/registry_tests/tests.py @@ -51,7 +51,7 @@ def get_history(model): self.assertRaises(AttributeError, get_history, User) self.assertEqual(len(User.histories.all()), 0) - user = User.objects.create(username="bob", password="pass") + user = User.objects.create(username="bob", password="pass") # nosec self.assertEqual(len(User.histories.all()), 1) self.assertEqual(len(user.histories.all()), 1) @@ -80,11 +80,11 @@ def test_register_history_id_field(self): class TestUserAccessor(unittest.TestCase): def test_accessor_default(self): register(UserAccessorDefault) - assert not hasattr(User, "historicaluseraccessordefault_set") + self.assertFalse(hasattr(User, "historicaluseraccessordefault_set")) def test_accessor_override(self): register(UserAccessorOverride, user_related_name="my_history_model_accessor") - assert hasattr(User, "my_history_model_accessor") + self.assertTrue(hasattr(User, "my_history_model_accessor")) class TestInheritedModule(TestCase): diff --git a/simple_history/tests/models.py b/simple_history/tests/models.py index 29d2ee4fd..993ee7a76 100644 --- a/simple_history/tests/models.py +++ b/simple_history/tests/models.py @@ -671,9 +671,9 @@ class CustomManagerNameModel(models.Model): log = HistoricalRecords() -""" -Following classes test the "custom_model_name" option -""" +# +# Following classes test the "custom_model_name" option +# class OverrideModelNameAsString(models.Model): diff --git a/simple_history/tests/tests/test_manager.py b/simple_history/tests/tests/test_manager.py index df9ece1dd..f5f594247 100644 --- a/simple_history/tests/tests/test_manager.py +++ b/simple_history/tests/tests/test_manager.py @@ -111,7 +111,7 @@ def test_simple_bulk_history_create(self): self.assertEqual(Poll.history.count(), 4) @override_settings(SIMPLE_HISTORY_ENABLED=False) - def test_simple_bulk_history_create(self): + def test_simple_bulk_history_create_without_history_enabled(self): Poll.history.bulk_history_create(self.data) self.assertEqual(Poll.history.count(), 0) diff --git a/simple_history/tests/tests/test_models.py b/simple_history/tests/tests/test_models.py index 579cad0f1..78bc08864 100644 --- a/simple_history/tests/tests/test_models.py +++ b/simple_history/tests/tests/test_models.py @@ -276,7 +276,6 @@ def test_save_with_disabled_history(self): anthony.delete() self.assertEqual(Person.history.count(), 0) - def test_save_without_historical_record_for_registered_model(self): model = ExternalModelSpecifiedWithAppParam.objects.create( name="registered model" @@ -1276,7 +1275,7 @@ def test_migrations_include_order(self): if name == "_order": found = True self.assertEqual(type(field), models.IntegerField) - assert found, "_order not in fields " + repr(model_state.fields) + self.assertTrue(found, "_order not in fields " + repr(model_state.fields)) class TestLatest(TestCase): @@ -1298,19 +1297,19 @@ def test_ordered(self): self.write_history( [{"pk": 1, "history_date": yesterday}, {"pk": 2, "history_date": today}] ) - assert HistoricalPoll.objects.latest().pk == 2 + self.assertEqual(HistoricalPoll.objects.latest().pk, 2) def test_jumbled(self): self.write_history( [{"pk": 1, "history_date": today}, {"pk": 2, "history_date": yesterday}] ) - assert HistoricalPoll.objects.latest().pk == 1 + self.assertEqual(HistoricalPoll.objects.latest().pk, 1) def test_sameinstant(self): self.write_history( [{"pk": 1, "history_date": yesterday}, {"pk": 2, "history_date": yesterday}] ) - assert HistoricalPoll.objects.latest().pk == 2 + self.assertEqual(HistoricalPoll.objects.latest().pk, 2) class TestMissingOneToOne(TestCase): @@ -1645,7 +1644,7 @@ class MultiDBExplicitHistoryUserIDTest(TestCase): databases = {"default", "other"} def setUp(self): - self.user = get_user_model().objects.create( + self.user = get_user_model().objects.create( # nosec username="username", email="username@test.com", password="top_secret" ) @@ -1686,10 +1685,10 @@ def test_history_user_does_not_exist(self): class RelatedNameTest(TestCase): def setUp(self): - self.user_one = get_user_model().objects.create( + self.user_one = get_user_model().objects.create( # nosec username="username_one", email="first@user.com", password="top_secret" ) - self.user_two = get_user_model().objects.create( + self.user_two = get_user_model().objects.create( # nosec username="username_two", email="second@user.com", password="top_secret" ) diff --git a/simple_history/tests/tests/test_utils.py b/simple_history/tests/tests/test_utils.py index fc26af6fe..b88a6fcdf 100644 --- a/simple_history/tests/tests/test_utils.py +++ b/simple_history/tests/tests/test_utils.py @@ -299,7 +299,7 @@ def test_bulk_update_history(self): self.assertEqual(Poll.history.filter(history_type="~").count(), 5) @override_settings(SIMPLE_HISTORY_ENABLED=False) - def test_bulk_update_history(self): + def test_bulk_update_history_without_history_enabled(self): self.assertEqual(Poll.history.count(), 5) # because setup called with enabled settings bulk_update_with_history(