mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2024-12-15 04:36:34 +00:00
Merge branch 'jim/cli-tooling' of https://github.com/jimfingal/bookwyrm into main
This commit is contained in:
commit
00752232ff
12 changed files with 182 additions and 112 deletions
2
.coveragerc
Normal file
2
.coveragerc
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[run]
|
||||||
|
omit = */test*,celerywyrm*,bookwyrm/migrations/*
|
7
.dockerignore
Normal file
7
.dockerignore
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
__pycache__
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.git
|
||||||
|
.github
|
||||||
|
.pytest*
|
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -13,3 +13,6 @@
|
||||||
# BookWyrm
|
# BookWyrm
|
||||||
.env
|
.env
|
||||||
/images/
|
/images/
|
||||||
|
|
||||||
|
# Testing
|
||||||
|
.coverage
|
|
@ -65,9 +65,8 @@ You'll have to install the Docker and docker-compose. When you're ready, run:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker-compose build
|
docker-compose build
|
||||||
docker-compose up
|
docker-compose run --rm web python manage.py migrate
|
||||||
docker-compose exec web python manage.py migrate
|
docker-compose run --rm web python manage.py initdb
|
||||||
docker-compose exec web python manage.py shell -c 'import init_db'
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Without Docker
|
### Without Docker
|
||||||
|
|
100
bookwyrm/management/commands/initdb.py
Normal file
100
bookwyrm/management/commands/initdb.py
Normal file
|
@ -0,0 +1,100 @@
|
||||||
|
from django.core.management.base import BaseCommand, CommandError
|
||||||
|
from django.contrib.auth.models import Group, Permission
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
|
from bookwyrm.models import Connector, User
|
||||||
|
from bookwyrm.settings import DOMAIN
|
||||||
|
|
||||||
|
def init_groups():
|
||||||
|
groups = ['admin', 'moderator', 'editor']
|
||||||
|
for group in groups:
|
||||||
|
Group.objects.create(name=group)
|
||||||
|
|
||||||
|
def init_permissions():
|
||||||
|
permissions = [{
|
||||||
|
'codename': 'edit_instance_settings',
|
||||||
|
'name': 'change the instance info',
|
||||||
|
'groups': ['admin',]
|
||||||
|
}, {
|
||||||
|
'codename': 'set_user_group',
|
||||||
|
'name': 'change what group a user is in',
|
||||||
|
'groups': ['admin', 'moderator']
|
||||||
|
}, {
|
||||||
|
'codename': 'control_federation',
|
||||||
|
'name': 'control who to federate with',
|
||||||
|
'groups': ['admin', 'moderator']
|
||||||
|
}, {
|
||||||
|
'codename': 'create_invites',
|
||||||
|
'name': 'issue invitations to join',
|
||||||
|
'groups': ['admin', 'moderator']
|
||||||
|
}, {
|
||||||
|
'codename': 'moderate_user',
|
||||||
|
'name': 'deactivate or silence a user',
|
||||||
|
'groups': ['admin', 'moderator']
|
||||||
|
}, {
|
||||||
|
'codename': 'moderate_post',
|
||||||
|
'name': 'delete other users\' posts',
|
||||||
|
'groups': ['admin', 'moderator']
|
||||||
|
}, {
|
||||||
|
'codename': 'edit_book',
|
||||||
|
'name': 'edit book info',
|
||||||
|
'groups': ['admin', 'moderator', 'editor']
|
||||||
|
}]
|
||||||
|
|
||||||
|
content_type = ContentType.objects.get_for_model(User)
|
||||||
|
for permission in permissions:
|
||||||
|
permission_obj = Permission.objects.create(
|
||||||
|
codename=permission['codename'],
|
||||||
|
name=permission['name'],
|
||||||
|
content_type=content_type,
|
||||||
|
)
|
||||||
|
# add the permission to the appropriate groups
|
||||||
|
for group_name in permission['groups']:
|
||||||
|
Group.objects.get(name=group_name).permissions.add(permission_obj)
|
||||||
|
|
||||||
|
# while the groups and permissions shouldn't be changed because the code
|
||||||
|
# depends on them, what permissions go with what groups should be editable
|
||||||
|
|
||||||
|
|
||||||
|
def init_connectors():
|
||||||
|
Connector.objects.create(
|
||||||
|
identifier=DOMAIN,
|
||||||
|
name='Local',
|
||||||
|
local=True,
|
||||||
|
connector_file='self_connector',
|
||||||
|
base_url='https://%s' % DOMAIN,
|
||||||
|
books_url='https://%s/book' % DOMAIN,
|
||||||
|
covers_url='https://%s/images/covers' % DOMAIN,
|
||||||
|
search_url='https://%s/search?q=' % DOMAIN,
|
||||||
|
priority=1,
|
||||||
|
)
|
||||||
|
|
||||||
|
Connector.objects.create(
|
||||||
|
identifier='bookwyrm.social',
|
||||||
|
name='BookWyrm dot Social',
|
||||||
|
connector_file='bookwyrm_connector',
|
||||||
|
base_url='https://bookwyrm.social' ,
|
||||||
|
books_url='https://bookwyrm.social/book',
|
||||||
|
covers_url='https://bookwyrm.social/images/covers',
|
||||||
|
search_url='https://bookwyrm.social/search?q=',
|
||||||
|
priority=2,
|
||||||
|
)
|
||||||
|
|
||||||
|
Connector.objects.create(
|
||||||
|
identifier='openlibrary.org',
|
||||||
|
name='OpenLibrary',
|
||||||
|
connector_file='openlibrary',
|
||||||
|
base_url='https://openlibrary.org',
|
||||||
|
books_url='https://openlibrary.org',
|
||||||
|
covers_url='https://covers.openlibrary.org',
|
||||||
|
search_url='https://openlibrary.org/search?q=',
|
||||||
|
priority=3,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Initializes the database with starter data'
|
||||||
|
|
||||||
|
def handle(self, *args, **options):
|
||||||
|
init_groups()
|
||||||
|
init_permissions()
|
||||||
|
init_connectors()
|
|
@ -6,6 +6,8 @@ import pathlib
|
||||||
import json
|
import json
|
||||||
import responses
|
import responses
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
from django.test import TestCase, Client
|
from django.test import TestCase, Client
|
||||||
from django.utils.http import http_date
|
from django.utils.http import http_date
|
||||||
|
|
||||||
|
@ -167,6 +169,7 @@ class Signature(TestCase):
|
||||||
response = self.send_test_request(sender=self.fake_remote)
|
response = self.send_test_request(sender=self.fake_remote)
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
def test_changed_data(self):
|
def test_changed_data(self):
|
||||||
'''Message data must match the digest header.'''
|
'''Message data must match the digest header.'''
|
||||||
response = self.send_test_request(
|
response = self.send_test_request(
|
||||||
|
@ -174,12 +177,14 @@ class Signature(TestCase):
|
||||||
send_data=get_follow_data(self.mouse, self.cat))
|
send_data=get_follow_data(self.mouse, self.cat))
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
def test_invalid_digest(self):
|
def test_invalid_digest(self):
|
||||||
response = self.send_test_request(
|
response = self.send_test_request(
|
||||||
self.mouse,
|
self.mouse,
|
||||||
digest='SHA-256=AAAAAAAAAAAAAAAAAA')
|
digest='SHA-256=AAAAAAAAAAAAAAAAAA')
|
||||||
self.assertEqual(response.status_code, 401)
|
self.assertEqual(response.status_code, 401)
|
||||||
|
|
||||||
|
@pytest.mark.integration
|
||||||
def test_old_message(self):
|
def test_old_message(self):
|
||||||
'''Old messages should be rejected to prevent replay attacks.'''
|
'''Old messages should be rejected to prevent replay attacks.'''
|
||||||
response = self.send_test_request(
|
response = self.send_test_request(
|
||||||
|
|
|
@ -22,6 +22,7 @@ services:
|
||||||
- main
|
- main
|
||||||
web:
|
web:
|
||||||
build: .
|
build: .
|
||||||
|
env_file: .env
|
||||||
command: python manage.py runserver 0.0.0.0:8000
|
command: python manage.py runserver 0.0.0.0:8000
|
||||||
volumes:
|
volumes:
|
||||||
- .:/app
|
- .:/app
|
||||||
|
|
69
fr-dev
69
fr-dev
|
@ -3,51 +3,86 @@
|
||||||
set -e
|
set -e
|
||||||
set -x
|
set -x
|
||||||
|
|
||||||
|
function clean {
|
||||||
|
docker-compose stop
|
||||||
|
docker-compose rm -f
|
||||||
|
}
|
||||||
|
|
||||||
|
function runweb {
|
||||||
|
docker-compose run --rm web "$@"
|
||||||
|
clean
|
||||||
|
}
|
||||||
|
|
||||||
|
function execdb {
|
||||||
|
docker-compose exec db $@
|
||||||
|
}
|
||||||
|
|
||||||
|
function execweb {
|
||||||
|
docker-compose exec web "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
function initdb {
|
||||||
|
execweb python manage.py migrate
|
||||||
|
execweb python manage.py initdb
|
||||||
|
}
|
||||||
|
|
||||||
case "$1" in
|
case "$1" in
|
||||||
up)
|
up)
|
||||||
docker-compose up --build
|
docker-compose up --build
|
||||||
;;
|
;;
|
||||||
run)
|
run)
|
||||||
docker-compose run --service-ports web
|
docker-compose run --rm --service-ports web
|
||||||
;;
|
;;
|
||||||
initdb)
|
initdb)
|
||||||
docker-compose exec web python manage.py migrate
|
initdb
|
||||||
docker-compose exec web python manage.py shell -c 'import init_db'
|
|
||||||
;;
|
;;
|
||||||
resetdb)
|
resetdb)
|
||||||
docker-compose stop web
|
clean
|
||||||
docker-compose exec db dropdb -U fedireads fedireads
|
docker-compose up --build -d
|
||||||
docker-compose exec db createdb -U fedireads fedireads
|
execdb dropdb -U fedireads fedireads
|
||||||
docker-compose start web
|
execdb createdb -U fedireads fedireads
|
||||||
docker-compose exec web python manage.py migrate
|
initdb
|
||||||
docker-compose exec web python manage.py shell -c 'import init_db'
|
clean
|
||||||
;;
|
;;
|
||||||
makemigrations)
|
makemigrations)
|
||||||
docker-compose exec web python manage.py makemigrations
|
execweb python manage.py makemigrations
|
||||||
;;
|
;;
|
||||||
migrate)
|
migrate)
|
||||||
docker-compose exec web python manage.py migrate
|
execweb python manage.py migrate
|
||||||
|
;;
|
||||||
|
bash)
|
||||||
|
execweb bash
|
||||||
;;
|
;;
|
||||||
shell)
|
shell)
|
||||||
docker-compose exec web python manage.py shell
|
execweb python manage.py shell
|
||||||
;;
|
;;
|
||||||
dbshell)
|
dbshell)
|
||||||
docker-compose exec db psql -U fedireads fedireads
|
execdb psql -U fedireads fedireads
|
||||||
;;
|
;;
|
||||||
restart_celery)
|
restart_celery)
|
||||||
docker-compose restart celery_worker
|
docker-compose restart celery_worker
|
||||||
;;
|
;;
|
||||||
test)
|
test)
|
||||||
shift 1
|
shift 1
|
||||||
docker-compose exec web coverage run --source='.' --omit="*/test*,celerywyrm*,bookwyrm/migrations/*" manage.py test "$@"
|
execweb coverage run --source='.' --omit="*/test*,celerywyrm*,bookwyrm/migrations/*" manage.py test "$@"
|
||||||
|
;;
|
||||||
|
pytest)
|
||||||
|
shift 1
|
||||||
|
execweb pytest "$@"
|
||||||
;;
|
;;
|
||||||
test_report)
|
test_report)
|
||||||
docker-compose exec web coverage report
|
execweb coverage report
|
||||||
;;
|
;;
|
||||||
collectstatic)
|
collectstatic)
|
||||||
docker-compose exec web python manage.py collectstatic --no-input
|
execweb python manage.py collectstatic --no-input
|
||||||
|
;;
|
||||||
|
build)
|
||||||
|
docker-compose build
|
||||||
|
;;
|
||||||
|
clean)
|
||||||
|
clean
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "Unrecognised command. Try: up, initdb, resetdb, makemigrations, migrate, shell, dbshell, restart_celery, test, test_report"
|
echo "Unrecognised command. Try: build, clean, up, initdb, resetdb, makemigrations, migrate, bash, shell, dbshell, restart_celery, test, pytest, test_report"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
91
init_db.py
91
init_db.py
|
@ -1,91 +0,0 @@
|
||||||
''' starter data '''
|
|
||||||
from django.contrib.auth.models import Group, Permission
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
|
||||||
|
|
||||||
from bookwyrm.models import Connector, User
|
|
||||||
from bookwyrm.settings import DOMAIN
|
|
||||||
|
|
||||||
|
|
||||||
groups = ['admin', 'moderator', 'editor']
|
|
||||||
for group in groups:
|
|
||||||
Group.objects.create(name=group)
|
|
||||||
|
|
||||||
permissions = [{
|
|
||||||
'codename': 'edit_instance_settings',
|
|
||||||
'name': 'change the instance info',
|
|
||||||
'groups': ['admin',]
|
|
||||||
}, {
|
|
||||||
'codename': 'set_user_group',
|
|
||||||
'name': 'change what group a user is in',
|
|
||||||
'groups': ['admin', 'moderator']
|
|
||||||
}, {
|
|
||||||
'codename': 'control_federation',
|
|
||||||
'name': 'control who to federate with',
|
|
||||||
'groups': ['admin', 'moderator']
|
|
||||||
}, {
|
|
||||||
'codename': 'create_invites',
|
|
||||||
'name': 'issue invitations to join',
|
|
||||||
'groups': ['admin', 'moderator']
|
|
||||||
}, {
|
|
||||||
'codename': 'moderate_user',
|
|
||||||
'name': 'deactivate or silence a user',
|
|
||||||
'groups': ['admin', 'moderator']
|
|
||||||
}, {
|
|
||||||
'codename': 'moderate_post',
|
|
||||||
'name': 'delete other users\' posts',
|
|
||||||
'groups': ['admin', 'moderator']
|
|
||||||
}, {
|
|
||||||
'codename': 'edit_book',
|
|
||||||
'name': 'edit book info',
|
|
||||||
'groups': ['admin', 'moderator', 'editor']
|
|
||||||
}]
|
|
||||||
|
|
||||||
content_type = ContentType.objects.get_for_model(User)
|
|
||||||
for permission in permissions:
|
|
||||||
permission_obj = Permission.objects.create(
|
|
||||||
codename=permission['codename'],
|
|
||||||
name=permission['name'],
|
|
||||||
content_type=content_type,
|
|
||||||
)
|
|
||||||
# add the permission to the appropriate groups
|
|
||||||
for group_name in permission['groups']:
|
|
||||||
Group.objects.get(name=group_name).permissions.add(permission_obj)
|
|
||||||
|
|
||||||
# while the groups and permissions shouldn't be changed because the code
|
|
||||||
# depends on them, what permissions go with what groups should be editable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Connector.objects.create(
|
|
||||||
identifier=DOMAIN,
|
|
||||||
name='Local',
|
|
||||||
local=True,
|
|
||||||
connector_file='self_connector',
|
|
||||||
base_url='https://%s' % DOMAIN,
|
|
||||||
books_url='https://%s/book' % DOMAIN,
|
|
||||||
covers_url='https://%s/images/covers' % DOMAIN,
|
|
||||||
search_url='https://%s/search?q=' % DOMAIN,
|
|
||||||
priority=1,
|
|
||||||
)
|
|
||||||
|
|
||||||
Connector.objects.create(
|
|
||||||
identifier='bookwyrm.social',
|
|
||||||
name='BookWyrm dot Social',
|
|
||||||
connector_file='bookwyrm_connector',
|
|
||||||
base_url='https://bookwyrm.social' ,
|
|
||||||
books_url='https://bookwyrm.social/book',
|
|
||||||
covers_url='https://bookwyrm.social/images/covers',
|
|
||||||
search_url='https://bookwyrm.social/search?q=',
|
|
||||||
priority=2,
|
|
||||||
)
|
|
||||||
|
|
||||||
Connector.objects.create(
|
|
||||||
identifier='openlibrary.org',
|
|
||||||
name='OpenLibrary',
|
|
||||||
connector_file='openlibrary',
|
|
||||||
base_url='https://openlibrary.org',
|
|
||||||
books_url='https://openlibrary.org',
|
|
||||||
covers_url='https://covers.openlibrary.org',
|
|
||||||
search_url='https://openlibrary.org/search?q=',
|
|
||||||
priority=3,
|
|
||||||
)
|
|
6
pytest.ini
Normal file
6
pytest.ini
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE = bookwyrm.settings
|
||||||
|
python_files = tests.py test_*.py *_tests.py
|
||||||
|
addopts = --cov=bookwyrm --cov-config=.coveragerc
|
||||||
|
markers =
|
||||||
|
integration: marks tests as requiring external resources (deselect with '-m "not integration"')
|
|
@ -21,5 +21,5 @@ fi
|
||||||
python manage.py makemigrations fedireads
|
python manage.py makemigrations fedireads
|
||||||
python manage.py migrate
|
python manage.py migrate
|
||||||
|
|
||||||
python manage.py shell < init_db.py
|
python manage.py initdb
|
||||||
python manage.py runserver
|
python manage.py runserver
|
||||||
|
|
|
@ -7,6 +7,9 @@ flower==0.9.4
|
||||||
Pillow>=7.1.0
|
Pillow>=7.1.0
|
||||||
psycopg2==2.8.4
|
psycopg2==2.8.4
|
||||||
pycryptodome==3.9.4
|
pycryptodome==3.9.4
|
||||||
|
pytest-django==4.1.0
|
||||||
|
pytest==6.1.2
|
||||||
|
pytest-cov==2.10.1
|
||||||
python-dateutil==2.8.1
|
python-dateutil==2.8.1
|
||||||
redis==3.4.1
|
redis==3.4.1
|
||||||
requests==2.22.0
|
requests==2.22.0
|
||||||
|
|
Loading…
Reference in a new issue