mirror of
https://github.com/jazzband/django-fernet-encrypted-fields.git
synced 2026-03-16 22:40:27 +00:00
setup github actions with black, flake8, testing and coverage
This commit is contained in:
parent
04625a8e40
commit
a20d77e256
9 changed files with 238 additions and 73 deletions
53
.github/lint-and-test.yml
vendored
Normal file
53
.github/lint-and-test.yml
vendored
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Lint & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.10
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -q Django==${{ matrix.django_version }}
|
||||||
|
pip install flake8 coverage black
|
||||||
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
|
||||||
|
- name: Lint with flake8
|
||||||
|
run: |
|
||||||
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
|
||||||
|
- name: Lint with Black
|
||||||
|
run: |
|
||||||
|
black --check .
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
coverage3 run --source='./encrypted_fields' manage.py test
|
||||||
|
coverage xml
|
||||||
|
|
||||||
|
- name: "Upload coverage to Codecov"
|
||||||
|
uses: codecov/codecov-action@v2
|
||||||
|
with:
|
||||||
|
fail_ci_if_error: true
|
||||||
|
|
@ -12,15 +12,25 @@ class EncryptedFieldMixin(object):
|
||||||
@cached_property
|
@cached_property
|
||||||
def keys(self):
|
def keys(self):
|
||||||
keys = []
|
keys = []
|
||||||
salt_keys = settings.SALT_KEY if isinstance(settings.SALT_KEY, list) else [settings.SALT_KEY]
|
salt_keys = (
|
||||||
|
settings.SALT_KEY
|
||||||
|
if isinstance(settings.SALT_KEY, list)
|
||||||
|
else [settings.SALT_KEY]
|
||||||
|
)
|
||||||
for salt_key in salt_keys:
|
for salt_key in salt_keys:
|
||||||
salt = bytes(salt_key, 'utf-8')
|
salt = bytes(salt_key, "utf-8")
|
||||||
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(),
|
kdf = PBKDF2HMAC(
|
||||||
|
algorithm=hashes.SHA256(),
|
||||||
length=32,
|
length=32,
|
||||||
salt=salt,
|
salt=salt,
|
||||||
iterations=100000,
|
iterations=100000,
|
||||||
backend=default_backend())
|
backend=default_backend(),
|
||||||
keys.append(base64.urlsafe_b64encode(kdf.derive(settings.SECRET_KEY.encode('utf-8'))))
|
)
|
||||||
|
keys.append(
|
||||||
|
base64.urlsafe_b64encode(
|
||||||
|
kdf.derive(settings.SECRET_KEY.encode("utf-8"))
|
||||||
|
)
|
||||||
|
)
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
@cached_property
|
@cached_property
|
||||||
|
|
@ -33,13 +43,13 @@ class EncryptedFieldMixin(object):
|
||||||
"""
|
"""
|
||||||
To treat everything as text
|
To treat everything as text
|
||||||
"""
|
"""
|
||||||
return 'TextField'
|
return "TextField"
|
||||||
|
|
||||||
def get_prep_value(self, value):
|
def get_prep_value(self, value):
|
||||||
if value:
|
if value:
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
value = str(value)
|
value = str(value)
|
||||||
return self.f.encrypt(bytes(value, 'utf-8')).decode('utf-8')
|
return self.f.encrypt(bytes(value, "utf-8")).decode("utf-8")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_db_prep_value(self, value, connection, prepared=False):
|
def get_db_prep_value(self, value, connection, prepared=False):
|
||||||
|
|
@ -51,9 +61,13 @@ class EncryptedFieldMixin(object):
|
||||||
return self.to_python(value)
|
return self.to_python(value)
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
if value is None or not isinstance(value, str) or hasattr(self, '_already_decrypted'):
|
if (
|
||||||
|
value is None
|
||||||
|
or not isinstance(value, str)
|
||||||
|
or hasattr(self, "_already_decrypted")
|
||||||
|
):
|
||||||
return value
|
return value
|
||||||
value = self.f.decrypt(bytes(value, 'utf-8')).decode('utf-8')
|
value = self.f.decrypt(bytes(value, "utf-8")).decode("utf-8")
|
||||||
return super(EncryptedFieldMixin, self).to_python(value)
|
return super(EncryptedFieldMixin, self).to_python(value)
|
||||||
|
|
||||||
def clean(self, value, model_instance):
|
def clean(self, value, model_instance):
|
||||||
|
|
|
||||||
53
lint-and-test.yml
Normal file
53
lint-and-test.yml
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Lint & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.10
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -q Django==${{ matrix.django_version }}
|
||||||
|
pip install flake8 coverage black
|
||||||
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
|
||||||
|
- name: Lint with flake8
|
||||||
|
run: |
|
||||||
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
|
||||||
|
- name: Lint with Black
|
||||||
|
run: |
|
||||||
|
black --check .
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
coverage3 run --source='./encrypted_fields' manage.py test
|
||||||
|
coverage xml
|
||||||
|
|
||||||
|
- name: "Upload coverage to Codecov"
|
||||||
|
uses: codecov/codecov-action@v2
|
||||||
|
with:
|
||||||
|
fail_ci_if_error: true
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == "__main__":
|
||||||
os.environ['DJANGO_SETTINGS_MODULE'] = 'package_test.settings'
|
os.environ["DJANGO_SETTINGS_MODULE"] = "package_test.settings"
|
||||||
|
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,14 @@
|
||||||
DATABASES = {
|
DATABASES = {
|
||||||
'default': {
|
"default": {
|
||||||
'ENGINE': 'django.db.backends.sqlite3',
|
"ENGINE": "django.db.backends.sqlite3",
|
||||||
'NAME': ':memory:',
|
"NAME": ":memory:",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
SECRET_KEY = 'abc'
|
SECRET_KEY = "abc"
|
||||||
SALT_KEY = 'xyz'
|
SALT_KEY = "xyz"
|
||||||
|
|
||||||
INSTALLED_APPS = (
|
INSTALLED_APPS = ("encrypted_fields", "package_test")
|
||||||
'encrypted_fields',
|
|
||||||
'package_test'
|
|
||||||
)
|
|
||||||
|
|
||||||
MIDDLEWARE_CLASSES = []
|
MIDDLEWARE_CLASSES = []
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
DEFAULT_AUTO_FIELD = "django.db.models.AutoField"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,38 +11,38 @@ class FieldTest(TestCase):
|
||||||
def get_db_value(self, field, model_id):
|
def get_db_value(self, field, model_id):
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
'select {0} '
|
"select {0} "
|
||||||
'from package_test_testmodel '
|
"from package_test_testmodel "
|
||||||
'where id = {1};'.format(field, model_id)
|
"where id = {1};".format(field, model_id)
|
||||||
)
|
)
|
||||||
return cursor.fetchone()[0]
|
return cursor.fetchone()[0]
|
||||||
|
|
||||||
def test_char_field_encrypted(self):
|
def test_char_field_encrypted(self):
|
||||||
plaintext = 'Oh hi, test reader!'
|
plaintext = "Oh hi, test reader!"
|
||||||
|
|
||||||
model = TestModel()
|
model = TestModel()
|
||||||
model.char = plaintext
|
model.char = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('char', model.id)
|
ciphertext = self.get_db_value("char", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertTrue('test' not in ciphertext)
|
self.assertTrue("test" not in ciphertext)
|
||||||
|
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
self.assertEqual(fresh_model.char, plaintext)
|
self.assertEqual(fresh_model.char, plaintext)
|
||||||
|
|
||||||
def test_text_field_encrypted(self):
|
def test_text_field_encrypted(self):
|
||||||
plaintext = 'Oh hi, test reader!' * 10
|
plaintext = "Oh hi, test reader!" * 10
|
||||||
|
|
||||||
model = TestModel()
|
model = TestModel()
|
||||||
model.text = plaintext
|
model.text = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('text', model.id)
|
ciphertext = self.get_db_value("text", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertTrue('test' not in ciphertext)
|
self.assertTrue("test" not in ciphertext)
|
||||||
|
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
self.assertEqual(fresh_model.text, plaintext)
|
self.assertEqual(fresh_model.text, plaintext)
|
||||||
|
|
@ -54,10 +54,10 @@ class FieldTest(TestCase):
|
||||||
model.datetime = plaintext
|
model.datetime = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('datetime', model.id)
|
ciphertext = self.get_db_value("datetime", model.id)
|
||||||
|
|
||||||
# Django's normal date serialization format
|
# Django's normal date serialization format
|
||||||
self.assertTrue(re.search('^\d\d\d\d-\d\d-\d\d', ciphertext) is None)
|
self.assertTrue(re.search("^\d\d\d\d-\d\d-\d\d", ciphertext) is None)
|
||||||
|
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
self.assertEqual(fresh_model.datetime, plaintext)
|
self.assertEqual(fresh_model.datetime, plaintext)
|
||||||
|
|
@ -69,7 +69,7 @@ class FieldTest(TestCase):
|
||||||
model.integer = plaintext
|
model.integer = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('integer', model.id)
|
ciphertext = self.get_db_value("integer", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertNotEqual(plaintext, str(ciphertext))
|
self.assertNotEqual(plaintext, str(ciphertext))
|
||||||
|
|
@ -84,7 +84,7 @@ class FieldTest(TestCase):
|
||||||
model.date = plaintext
|
model.date = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('date', model.id)
|
ciphertext = self.get_db_value("date", model.id)
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
|
|
||||||
self.assertNotEqual(ciphertext, plaintext.isoformat())
|
self.assertNotEqual(ciphertext, plaintext.isoformat())
|
||||||
|
|
@ -97,7 +97,7 @@ class FieldTest(TestCase):
|
||||||
model.floating = plaintext
|
model.floating = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('floating', model.id)
|
ciphertext = self.get_db_value("floating", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertNotEqual(plaintext, str(ciphertext))
|
self.assertNotEqual(plaintext, str(ciphertext))
|
||||||
|
|
@ -106,16 +106,16 @@ class FieldTest(TestCase):
|
||||||
self.assertEqual(fresh_model.floating, plaintext)
|
self.assertEqual(fresh_model.floating, plaintext)
|
||||||
|
|
||||||
def test_email_field_encrypted(self):
|
def test_email_field_encrypted(self):
|
||||||
plaintext = 'test@gmail.com'
|
plaintext = "test@gmail.com"
|
||||||
|
|
||||||
model = TestModel()
|
model = TestModel()
|
||||||
model.email = plaintext
|
model.email = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('email', model.id)
|
ciphertext = self.get_db_value("email", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertTrue('aron' not in ciphertext)
|
self.assertTrue("aron" not in ciphertext)
|
||||||
|
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
self.assertEqual(fresh_model.email, plaintext)
|
self.assertEqual(fresh_model.email, plaintext)
|
||||||
|
|
@ -127,13 +127,13 @@ class FieldTest(TestCase):
|
||||||
model.boolean = plaintext
|
model.boolean = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = self.get_db_value('boolean', model.id)
|
ciphertext = self.get_db_value("boolean", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertNotEqual(True, ciphertext)
|
self.assertNotEqual(True, ciphertext)
|
||||||
self.assertNotEqual('True', ciphertext)
|
self.assertNotEqual("True", ciphertext)
|
||||||
self.assertNotEqual('true', ciphertext)
|
self.assertNotEqual("true", ciphertext)
|
||||||
self.assertNotEqual('1', ciphertext)
|
self.assertNotEqual("1", ciphertext)
|
||||||
self.assertNotEqual(1, ciphertext)
|
self.assertNotEqual(1, ciphertext)
|
||||||
self.assertTrue(not isinstance(ciphertext, bool))
|
self.assertTrue(not isinstance(ciphertext, bool))
|
||||||
|
|
||||||
|
|
@ -142,27 +142,24 @@ class FieldTest(TestCase):
|
||||||
|
|
||||||
|
|
||||||
class RotatedSaltTestCase(TestCase):
|
class RotatedSaltTestCase(TestCase):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@override_settings(SALT_KEY=['abcdefghijklmnopqrstuvwxyz0123456789'])
|
@override_settings(SALT_KEY=["abcdefghijklmnopqrstuvwxyz0123456789"])
|
||||||
def setUpTestData(cls):
|
def setUpTestData(cls):
|
||||||
"""Create the initial record using the old salt"""
|
"""Create the initial record using the old salt"""
|
||||||
cls.original = TestModel.objects.create(
|
cls.original = TestModel.objects.create(text="Oh hi test reader")
|
||||||
text="Oh hi test reader"
|
|
||||||
)
|
|
||||||
|
|
||||||
@override_settings(SALT_KEY=['newkeyhere', 'abcdefghijklmnopqrstuvwxyz0123456789'])
|
@override_settings(SALT_KEY=["newkeyhere", "abcdefghijklmnopqrstuvwxyz0123456789"])
|
||||||
def test_rotated_salt(self):
|
def test_rotated_salt(self):
|
||||||
"""Chage the salt, keep the old one as the last in the list for reading"""
|
"""Change the salt, keep the old one as the last in the list for reading"""
|
||||||
plaintext = "Oh hi test reader"
|
plaintext = "Oh hi test reader"
|
||||||
model = TestModel()
|
model = TestModel()
|
||||||
model.text = plaintext
|
model.text = plaintext
|
||||||
model.save()
|
model.save()
|
||||||
|
|
||||||
ciphertext = FieldTest.get_db_value(self, 'text', model.id)
|
ciphertext = FieldTest.get_db_value(self, "text", model.id)
|
||||||
|
|
||||||
self.assertNotEqual(plaintext, ciphertext)
|
self.assertNotEqual(plaintext, ciphertext)
|
||||||
self.assertTrue('test' not in ciphertext)
|
self.assertTrue("test" not in ciphertext)
|
||||||
|
|
||||||
fresh_model = TestModel.objects.get(id=model.id)
|
fresh_model = TestModel.objects.get(id=model.id)
|
||||||
self.assertEqual(fresh_model.text, plaintext)
|
self.assertEqual(fresh_model.text, plaintext)
|
||||||
|
|
@ -170,5 +167,6 @@ class RotatedSaltTestCase(TestCase):
|
||||||
old_record = TestModel.objects.get(id=self.original.id)
|
old_record = TestModel.objects.get(id=self.original.id)
|
||||||
self.assertEqual(fresh_model.text, old_record.text)
|
self.assertEqual(fresh_model.text, old_record.text)
|
||||||
|
|
||||||
self.assertNotEqual(ciphertext, FieldTest.get_db_value(self, 'text', self.original.pk))
|
self.assertNotEqual(
|
||||||
|
ciphertext, FieldTest.get_db_value(self, "text", self.original.pk)
|
||||||
|
)
|
||||||
|
|
|
||||||
26
setup.py
26
setup.py
|
|
@ -2,20 +2,18 @@ from __future__ import print_function
|
||||||
from setuptools import setup
|
from setuptools import setup
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-fernet-encrypted-fields',
|
name="django-fernet-encrypted-fields",
|
||||||
description=(
|
description=("This is inspired by django-encrypted-fields."),
|
||||||
'This is inspired by django-encrypted-fields.'
|
long_description=open("README.md").read(),
|
||||||
),
|
long_description_content_type="text/markdown",
|
||||||
long_description=open('README.md').read(),
|
url="http://github.com/frgmt/django-fernet-encrypted-fields/",
|
||||||
long_description_content_type='text/markdown',
|
license="MIT",
|
||||||
url='http://github.com/frgmt/django-fernet-encrypted-fields/',
|
author="fragment.co.jp",
|
||||||
license='MIT',
|
author_email="info@fragment.co.jp",
|
||||||
author='fragment.co.jp',
|
packages=["encrypted_fields"],
|
||||||
author_email='info@fragment.co.jp',
|
version="0.1.1",
|
||||||
packages=['encrypted_fields'],
|
|
||||||
version='0.1.1',
|
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'Django>=2.2',
|
"Django>=2.2",
|
||||||
'cryptography>=35.0.0',
|
"cryptography>=35.0.0",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
||||||
53
workflows/lint-and-test.yml
Normal file
53
workflows/lint-and-test.yml
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
name: Lint & Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main ]
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
django_version: [2.2, 3.0, 3.1, 3.2, 4.0a1]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Set up Python 3.10
|
||||||
|
uses: actions/setup-python@v3
|
||||||
|
with:
|
||||||
|
python-version: "3.10"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -q Django==${{ matrix.django_version }}
|
||||||
|
pip install flake8 coverage black
|
||||||
|
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
|
||||||
|
|
||||||
|
- name: Lint with flake8
|
||||||
|
run: |
|
||||||
|
# stop the build if there are Python syntax errors or undefined names
|
||||||
|
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||||
|
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
|
||||||
|
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||||
|
|
||||||
|
- name: Lint with Black
|
||||||
|
run: |
|
||||||
|
black --check .
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: |
|
||||||
|
coverage3 run --source='./encrypted_fields' manage.py test
|
||||||
|
coverage xml
|
||||||
|
|
||||||
|
- name: "Upload coverage to Codecov"
|
||||||
|
uses: codecov/codecov-action@v2
|
||||||
|
with:
|
||||||
|
fail_ci_if_error: true
|
||||||
Loading…
Reference in a new issue