diff --git a/.env.template b/.env.template index 09b602862..60a7295b6 100644 --- a/.env.template +++ b/.env.template @@ -1,42 +1,5 @@ -PYTHONPATH=. - -# for Django -DJANGO_SETTINGS_MODULE=config.settings.dev -POSTGRESQL_ADDON_HOST=localhost -POSTGRESQL_ADDON_DB=communaute -POSTGRESQL_ADDON_USER=communaute -POSTGRESQL_ADDON_PASSWORD=password - -# GITHUB_ACCESS_TOKEN is used to update changelog from last release. -GITHUB_ACCESS_TOKEN=__key_to_be_set__ -GITHUB_REPO=betagouv/itou-communaute-django - -# SENDINBLUE API KEY -SIB_API_KEY=__key_to_be_set__ - -# for Sentry -#SENTRY_DSN=__url_to_be_set__ - -# for Pro Connect -OPENID_CONNECT_BASE_URL=http://127.0.0.1:8080 -OPENID_CONNECT_CLIENT_ID=local_openid_connect -OPENID_CONNECT_CLIENT_SECRET=password - -# parking page -PARKING_PAGE=True - -# Path to the itou-backup project repository. -PATH_TO_BACKUPS=~/path/to/backups - -# bucket for test purpose only -CELLAR_ADDON_KEY_ID=minioadmin -CELLAR_ADDON_KEY_SECRET=minioadmin -CELLAR_ADDON_HOST=localhost:9000 -CELLAR_ADDON_PROTOCOL=http - -# itou-backups -export RCLONE_S3_ACCESS_KEY_ID=ACCESS_KEY_ID -export RCLONE_S3_SECRET_ACCESS_KEY=SECRET_ACCESS_KEY -export RCLONE_CRYPT_PASSWORD=CRYPT-PASSWORD -export RCLONE_CRYPT_PASSWORD2=CRYPT-PASSWORD2 -export RCLONE_REMOTE_NAME=communaute +# for psql +PGDATABASE=communaute +PGHOST=localhost +PGUSER=communaute +PGPASSWORD=password diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 84d9cd75e..6916580c1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,19 +8,12 @@ jobs: runs-on: ubuntu-latest env: PYTHONPATH: . - DJANGO_SETTINGS_MODULE: config.settings.base - DJANGO_SECRET_KEY: ministryofsillywalks + DJANGO_SETTINGS_MODULE: config.settings.test CPUCOUNT: 1 PGPASSWORD: password PGHOST: localhost PGUSER: postgres - POSTGRESQL_ADDON_DB: communaute - POSTGRESQL_ADDON_USER: postgres - POSTGRESQL_ADDON_PASSWORD: password - CELLAR_ADDON_KEY_ID: minioadmin - CELLAR_ADDON_KEY_SECRET: minioadmin - CELLAR_ADDON_PROTOCOL: http - CELLAR_ADDON_HOST: localhost:9000 + services: minio: image: bitnami/minio @@ -54,7 +47,7 @@ jobs: - name: 💂 Install Python uses: actions/setup-python@v4 with: - python-version: '3.11' + python-version: '3.12' cache: pip cache-dependency-path: requirements/dev.txt - name: 📥 Install dependencies diff --git a/.github/workflows/review-app-creation.yml b/.github/workflows/review-app-creation.yml index 61a0b0cb4..c50a6a40b 100644 --- a/.github/workflows/review-app-creation.yml +++ b/.github/workflows/review-app-creation.yml @@ -18,7 +18,7 @@ env: CONFIGURATION_ADDON: ${{ secrets.CLEVER_REVIEW_APPS_CONFIGURATION_ADDON }} S3_ADDON: ${{ secrets.CLEVER_REVIEW_APPS_S3_ADDON }} BRANCH: ${{ github.head_ref }} - PYTHON_VERSION: "3.10" + PYTHON_VERSION: "3.12" jobs: create: diff --git a/config/settings/base.py b/config/settings/base.py index c78061c53..74c7c3556 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -268,14 +268,12 @@ # S3 uploads # ------------------------------------------------------------------------------ -AWS_S3_ACCESS_KEY_ID = os.getenv("CELLAR_ADDON_KEY_ID", "123") -AWS_S3_SECRET_ACCESS_KEY = os.getenv("CELLAR_ADDON_KEY_SECRET", "secret") -AWS_S3_ENDPOINT_URL = ( - f"{os.getenv('CELLAR_ADDON_PROTOCOL', 'https')}://{os.getenv('CELLAR_ADDON_HOST', 'set-var-env.com')}" -) -AWS_STORAGE_BUCKET_NAME = os.getenv("S3_STORAGE_BUCKET_NAME", "private-bucket") -AWS_STORAGE_BUCKET_NAME_PUBLIC = os.getenv("S3_STORAGE_BUCKET_NAME_PUBLIC", "public-bucket") -AWS_S3_STORAGE_BUCKET_REGION = os.getenv("S3_STORAGE_BUCKET_REGION", "eu-west-3") +AWS_S3_ACCESS_KEY_ID = os.getenv("CELLAR_ADDON_KEY_ID") +AWS_S3_SECRET_ACCESS_KEY = os.getenv("CELLAR_ADDON_KEY_SECRET") +AWS_S3_ENDPOINT_URL = f"{os.getenv('CELLAR_ADDON_PROTOCOL')}://{os.getenv('CELLAR_ADDON_HOST')}" +AWS_STORAGE_BUCKET_NAME = os.getenv("S3_STORAGE_BUCKET_NAME") +AWS_STORAGE_BUCKET_NAME_PUBLIC = os.getenv("S3_STORAGE_BUCKET_NAME_PUBLIC") +AWS_S3_STORAGE_BUCKET_REGION = os.getenv("S3_STORAGE_BUCKET_REGION") # MEDIA CONFIGURATION # ------------------------------------------------------------------------------ @@ -359,11 +357,11 @@ # SENDINBLUE # --------------------------------------- -SIB_URL = os.getenv("SIB_URL", "http://test.com") -SIB_SMTP_URL = os.path.join(SIB_URL, "smtp/email") -SIB_CONTACTS_URL = os.path.join(SIB_URL, "contacts/import") +SIB_URL = os.getenv("SIB_URL") +SIB_SMTP_ROUTE = "smtp/email" +SIB_CONTACTS_ROUTE = "contacts/import" -SIB_API_KEY = os.getenv("SIB_API_KEY", "set-sib-api-key") +SIB_API_KEY = os.getenv("SIB_API_KEY") DEFAULT_FROM_EMAIL = os.getenv("DEFAULT_FROM_EMAIL", "noreply@inclusion.gouv.fr") SIB_MAGIC_LINK_TEMPLATE = 31 diff --git a/config/settings/dev.py b/config/settings/dev.py index 94f751e31..34048422b 100644 --- a/config/settings/dev.py +++ b/config/settings/dev.py @@ -3,14 +3,11 @@ from lacommunaute.utils.enums import Environment -from .base import * # pylint: disable=wildcard-import,unused-wildcard-import # noqa: F403 F401 +from .test import * # pylint: disable=wildcard-import,unused-wildcard-import # noqa: F403 F401 # Django settings # --------------- -SECRET_KEY = "foobar" - - DEBUG = True ENVIRONMENT = Environment.DEV diff --git a/config/settings/test.py b/config/settings/test.py new file mode 100644 index 000000000..b44b5ae5f --- /dev/null +++ b/config/settings/test.py @@ -0,0 +1,39 @@ +import os + +from lacommunaute.utils.enums import Environment + +from .base import * # pylint: disable=wildcard-import,unused-wildcard-import # noqa: F403 F401 + + +# Django settings +# --------------- +SECRET_KEY = "v3ry_s3cr3t_k3y" + +ENVIRONMENT = Environment.TEST + +# Database +# ------------------------------------------------------------------------------ +DATABASES["default"]["HOST"] = os.getenv("PGHOST", "localhost") # noqa: F405 +DATABASES["default"]["PORT"] = os.getenv("PGPORT", "5432") # noqa: F405 +DATABASES["default"]["NAME"] = os.getenv("PGDATABASE", "communaute") # noqa: F405 +DATABASES["default"]["USER"] = os.getenv("PGUSER", "postgres") # noqa: F405 +DATABASES["default"]["PASSWORD"] = os.getenv("PGPASSWORD", "password") # noqa: F405 + +# S3 uploads +# ------------------------------------------------------------------------------ + +AWS_S3_ACCESS_KEY_ID = os.getenv("CELLAR_ADDON_KEY_ID", "minioadmin") +AWS_S3_SECRET_ACCESS_KEY = os.getenv("CELLAR_ADDON_KEY_SECRET", "minioadmin") +AWS_S3_ENDPOINT_URL = ( + f"{os.getenv('CELLAR_ADDON_PROTOCOL', 'http')}://{os.getenv('CELLAR_ADDON_HOST', 'localhost:9000')}" +) +AWS_STORAGE_BUCKET_NAME = "private-bucket" +AWS_STORAGE_BUCKET_NAME_PUBLIC = "public-bucket" +AWS_S3_STORAGE_BUCKET_REGION = "eu-west-3" + +MEDIA_URL = f"{AWS_S3_ENDPOINT_URL}/" + +# SENDINBLUE +# --------------------------------------- +SIB_URL = "http://test.com" +SIB_API_KEY = "dummy-sib-api-key" diff --git a/docker/django/entrypoint.sh b/docker/django/entrypoint.sh index 18cd84862..ad3366c4c 100755 --- a/docker/django/entrypoint.sh +++ b/docker/django/entrypoint.sh @@ -1,7 +1,7 @@ #!/bin/sh set -e -while ! pg_isready -h $POSTGRESQL_ADDON_HOST -p 5432; do +while ! pg_isready -h $PGHOST -p 5432; do >&2 echo "Postgres is unavailable - sleeping" sleep 1 done diff --git a/lacommunaute/notification/emails.py b/lacommunaute/notification/emails.py index 415cde026..67ffcc186 100644 --- a/lacommunaute/notification/emails.py +++ b/lacommunaute/notification/emails.py @@ -1,14 +1,19 @@ import logging +from urllib.parse import urljoin import httpx from django.conf import settings from lacommunaute.notification.enums import EmailSentTrackKind from lacommunaute.notification.models import EmailSentTrack +from lacommunaute.utils.enums import Environment logger = logging.getLogger(__name__) +SIB_SMTP_URL = urljoin(settings.SIB_URL, settings.SIB_SMTP_ROUTE) +SIB_CONTACTS_URL = urljoin(settings.SIB_URL, settings.SIB_CONTACTS_ROUTE) + def send_email(to, params, template_id, kind, bcc=None): headers = {"api-key": settings.SIB_API_KEY, "Content-Type": "application/json", "Accept": "application/json"} @@ -21,11 +26,11 @@ def send_email(to, params, template_id, kind, bcc=None): if bcc: payload["bcc"] = bcc - if settings.DEBUG: - # We don't want to send emails in debug mode, payload is saved in the database + if settings.ENVIRONMENT == Environment.DEV: + # We don't want to send emails in DEV mode, payload is saved in the database response = httpx.Response(200, json={"message": "OK"}) else: - response = httpx.post(settings.SIB_SMTP_URL, headers=headers, json=payload) + response = httpx.post(SIB_SMTP_URL, headers=headers, json=payload) EmailSentTrack.objects.create( status_code=response.status_code, @@ -47,7 +52,7 @@ def bulk_send_user_to_list(users, list_id): "emptyContactsAttributes": True, } headers = {"accept": "application/json", "content-type": "application/json", "api-key": settings.SIB_API_KEY} - response = httpx.post(settings.SIB_CONTACTS_URL, headers=headers, json=payload) + response = httpx.post(SIB_CONTACTS_URL, headers=headers, json=payload) EmailSentTrack.objects.create( status_code=response.status_code, response=response.text, datas=payload, kind=EmailSentTrackKind.ONBOARDING diff --git a/lacommunaute/notification/tasks.py b/lacommunaute/notification/tasks.py index c8237e329..5df046a40 100644 --- a/lacommunaute/notification/tasks.py +++ b/lacommunaute/notification/tasks.py @@ -3,7 +3,6 @@ from django.urls import reverse from django.utils import timezone -from config.settings.base import NEW_MESSAGES_EMAIL_MAX_PREVIEW, SIB_NEW_MESSAGES_TEMPLATE from lacommunaute.forum_conversation.models import Topic from lacommunaute.forum_member.shortcuts import get_forum_member_display_name from lacommunaute.notification.emails import bulk_send_user_to_list, send_email @@ -31,13 +30,13 @@ def get_grouped_notifications(): params = { "email_thumbnail": (f"Vous avez {message_count_text} à découvrir sur la communauté de l'inclusion"), - "messages": get_serialized_messages(recipient_notifications[:NEW_MESSAGES_EMAIL_MAX_PREVIEW]), + "messages": get_serialized_messages(recipient_notifications[: settings.NEW_MESSAGES_EMAIL_MAX_PREVIEW]), } send_email( to=[{"email": recipient}], params=params, kind=EmailSentTrackKind.BULK_NOTIFS, - template_id=SIB_NEW_MESSAGES_TEMPLATE, + template_id=settings.SIB_NEW_MESSAGES_TEMPLATE, ) notifications.update(sent_at=timezone.now()) diff --git a/lacommunaute/notification/tests/tests_emails.py b/lacommunaute/notification/tests/tests_emails.py index fc0584766..8d9590473 100644 --- a/lacommunaute/notification/tests/tests_emails.py +++ b/lacommunaute/notification/tests/tests_emails.py @@ -2,11 +2,11 @@ import httpx import respx +from django.conf import settings from django.test import TestCase from faker import Faker -from config.settings.base import DEFAULT_FROM_EMAIL, SIB_CONTACTS_URL, SIB_SMTP_URL -from lacommunaute.notification.emails import bulk_send_user_to_list, send_email +from lacommunaute.notification.emails import SIB_CONTACTS_URL, SIB_SMTP_URL, bulk_send_user_to_list, send_email from lacommunaute.notification.models import EmailSentTrack from lacommunaute.users.factories import UserFactory @@ -23,7 +23,7 @@ def setUpTestData(cls): cls.template_id = faker.random_int() cls.kind = "first_reply" cls.payload = { - "sender": {"name": "La Communauté", "email": DEFAULT_FROM_EMAIL}, + "sender": {"name": "La Communauté", "email": settings.DEFAULT_FROM_EMAIL}, "to": cls.to, "params": cls.params, "templateId": cls.template_id, diff --git a/lacommunaute/notification/tests/tests_tasks.py b/lacommunaute/notification/tests/tests_tasks.py index 3ddee86df..b687300b5 100644 --- a/lacommunaute/notification/tests/tests_tasks.py +++ b/lacommunaute/notification/tests/tests_tasks.py @@ -8,17 +8,11 @@ from django.urls import reverse from faker import Faker -from config.settings.base import ( - DEFAULT_FROM_EMAIL, - SIB_CONTACTS_URL, - SIB_ONBOARDING_LIST, - SIB_SMTP_URL, - SIB_UNANSWERED_QUESTION_TEMPLATE, -) from lacommunaute.forum_conversation.factories import ( TopicFactory, ) from lacommunaute.forum_member.shortcuts import get_forum_member_display_name +from lacommunaute.notification.emails import SIB_CONTACTS_URL, SIB_SMTP_URL from lacommunaute.notification.enums import NotificationDelay from lacommunaute.notification.factories import NotificationFactory from lacommunaute.notification.models import EmailSentTrack @@ -128,7 +122,7 @@ def test_add_user_to_list_when_register(self): ], "emailBlacklist": False, "smsBlacklist": False, - "listIds": [SIB_ONBOARDING_LIST], + "listIds": [settings.SIB_ONBOARDING_LIST], "updateExistingContacts": True, "emptyContactsAttributes": True, } @@ -163,10 +157,10 @@ def payload_for_staff_user_to_notify_on_unanswered_topics_fixture(): ) params = {"count": 1, "link": "".join(url)} payload = { - "sender": {"name": "La Communauté", "email": DEFAULT_FROM_EMAIL}, + "sender": {"name": "La Communauté", "email": settings.DEFAULT_FROM_EMAIL}, "to": to, "params": params, - "templateId": SIB_UNANSWERED_QUESTION_TEMPLATE, + "templateId": settings.SIB_UNANSWERED_QUESTION_TEMPLATE, } yield payload diff --git a/lacommunaute/partner/migrations/0001_initial.py b/lacommunaute/partner/migrations/0001_initial.py index b3d76e2df..755293e94 100644 --- a/lacommunaute/partner/migrations/0001_initial.py +++ b/lacommunaute/partner/migrations/0001_initial.py @@ -2,6 +2,7 @@ import machina.models.fields import storages.backends.s3 +from django.conf import settings from django.db import migrations, models import lacommunaute.utils.validators @@ -35,7 +36,9 @@ class Migration(migrations.Migration): "logo", models.ImageField( help_text="1200x600 recommandé", - storage=storages.backends.s3.S3Storage(bucket_name="private-bucket", file_overwrite=False), + storage=storages.backends.s3.S3Storage( + bucket_name=settings.AWS_STORAGE_BUCKET_NAME, file_overwrite=False + ), upload_to="logos/", validators=[lacommunaute.utils.validators.validate_image_size], ), diff --git a/lacommunaute/users/tests/__snapshots__/tests_views.ambr b/lacommunaute/users/tests/__snapshots__/tests_views.ambr index ae3448457..e53dbb293 100644 --- a/lacommunaute/users/tests/__snapshots__/tests_views.ambr +++ b/lacommunaute/users/tests/__snapshots__/tests_views.ambr @@ -613,9 +613,9 @@ ''' # --- -# name: TestSendMagicLink.test_send_magic_link[DEV-1][send_magic_link_payload] +# name: TestSendMagicLink.test_send_magic[PROD-True-0][send_magic_link_payload] '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}' # --- -# name: TestSendMagicLink.test_send_magic_link[PROD-0][send_magic_link_payload] +# name: TestSendMagicLink.test_send_magic[TEST-True-0][send_magic_link_payload] '{"sender": {"name": "La Communaut\\u00e9", "email": "noreply@inclusion.gouv.fr"}, "to": [{"email": "samuel@jackson.com"}], "params": {"display_name": "Samuel J.", "login_link": "LOGIN_LINK"}, "templateId": 31}' # --- diff --git a/lacommunaute/users/tests/tests_views.py b/lacommunaute/users/tests/tests_views.py index beeb4c7f1..d50fb0b13 100644 --- a/lacommunaute/users/tests/tests_views.py +++ b/lacommunaute/users/tests/tests_views.py @@ -18,6 +18,7 @@ from pytest_django.asserts import assertContains from lacommunaute.forum_member.models import ForumProfile +from lacommunaute.notification.emails import SIB_SMTP_URL from lacommunaute.users.enums import IdentityProvider from lacommunaute.users.factories import UserFactory from lacommunaute.users.models import User @@ -33,7 +34,7 @@ @pytest.fixture(name="mock_respx_post_to_sib_smtp_url") def mock_respx_post_to_sib_smtp_url_fixture(): with respx.mock: - respx.post(settings.SIB_SMTP_URL).mock(return_value=httpx.Response(200, json={"message": "OK"})) + respx.post(SIB_SMTP_URL).mock(return_value=httpx.Response(200, json={"message": "OK"})) yield @@ -85,9 +86,12 @@ def validate_magiclink_payload(payload_as_str, uidb64, token, expected): class TestSendMagicLink: - @pytest.mark.parametrize("env,count_msg", [(Environment.PROD, 0), (Environment.DEV, 1)]) - def test_send_magic_link( - self, db, user, snapshot, mock_token_generator, mock_respx_post_to_sib_smtp_url, env, count_msg + @pytest.mark.parametrize( + "env,sent_mail,count_msg", + [(Environment.PROD, True, 0), (Environment.TEST, True, 0), (Environment.DEV, False, 1)], + ) + def test_send_magic( + self, db, user, snapshot, mock_token_generator, mock_respx_post_to_sib_smtp_url, env, sent_mail, count_msg ): with override_settings(ENVIRONMENT=env): next_url = "/topics/" @@ -102,13 +106,18 @@ def test_send_magic_link( query_params = urlencode({"next": clean_next_url(next_url)}) login_link = f"{settings.COMMU_PROTOCOL}://{settings.COMMU_FQDN}{url}?{query_params}" - payload_as_str = respx.calls[0].request.content.decode() - payload = json.loads(payload_as_str) - assert payload["params"]["login_link"] == login_link - assert payload_as_str.replace(login_link, "LOGIN_LINK") == snapshot(name="send_magic_link_payload") + # testing email + assert (len(respx.calls) == 1) == sent_mail - # we want messages do not appear in the productive environment + if sent_mail: + payload_as_str = respx.calls[0].request.content.decode() + payload = json.loads(payload_as_str) + assert payload["params"]["login_link"] == login_link + assert payload_as_str.replace(login_link, "LOGIN_LINK") == snapshot(name="send_magic_link_payload") + + # testing django message msgs = get_messages(request) + assert len(msgs._queued_messages) == count_msg if msgs._queued_messages: assert str(msgs._queued_messages[0]) == f'{login_link} sent to {user.email}' diff --git a/lacommunaute/users/views.py b/lacommunaute/users/views.py index d3b515db5..7feee8ecb 100644 --- a/lacommunaute/users/views.py +++ b/lacommunaute/users/views.py @@ -20,6 +20,7 @@ from lacommunaute.users.enums import IdentityProvider from lacommunaute.users.forms import CreateUserForm, LoginForm from lacommunaute.users.models import User +from lacommunaute.utils.enums import Environment from lacommunaute.utils.urls import clean_next_url @@ -40,7 +41,7 @@ def send_magic_link(request, user, next_url): template_id=settings.SIB_MAGIC_LINK_TEMPLATE, ) - if settings.ENVIRONMENT == "DEV": + if settings.ENVIRONMENT == Environment.DEV: message = format_html('{0} sent to {1}', login_link, user.email) messages.success(request, message) diff --git a/lacommunaute/utils/enums.py b/lacommunaute/utils/enums.py index 8fc3a6dea..be6095092 100644 --- a/lacommunaute/utils/enums.py +++ b/lacommunaute/utils/enums.py @@ -9,3 +9,4 @@ class PeriodAggregation(models.TextChoices): class Environment(models.TextChoices): DEV = "DEV" PROD = "PROD" + TEST = "TEST" diff --git a/pyproject.toml b/pyproject.toml index 6b5039ead..5f3a156eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -87,3 +87,11 @@ ignore="T002,T003,T027,H006,H023,D018" max_attribute_length=200 format_css = true format_js = false + +[tool.pytest.ini_options] +DJANGO_SETTINGS_MODULE = "config.settings.test" +python_files = ["tests*.py", "test_*.py"] +addopts = "--reuse-db --strict-markers" + +[tool.pytest.ini_options.markers] +no_django_db = "mark tests that should not be marked with django_db." diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index fa1199b23..000000000 --- a/pytest.ini +++ /dev/null @@ -1,9 +0,0 @@ -[pytest] -DJANGO_SETTINGS_MODULE = config.settings.base -python_files = tests*.py test_*.py -addopts = - --reuse-db - --strict-markers -markers = - no_django_db: mark tests that should not be marked with django_db. - diff --git a/scripts/import-latest-db-backup.sh b/scripts/import-latest-db-backup.sh index 5506af41d..2f5219680 100755 --- a/scripts/import-latest-db-backup.sh +++ b/scripts/import-latest-db-backup.sh @@ -23,13 +23,13 @@ echo "Going to inject DB_BACKUP_PATH=$DB_BACKUP_PATH" docker cp $DB_BACKUP_PATH commu_postgres:/backups echo "dropping current db" -dropdb $POSTGRESQL_ADDON_DB -U $POSTGRESQL_ADDON_USER --if-exists --echo +dropdb $PGDATABASE -U $PGUSER --if-exists --echo echo "creating new db" -createdb $POSTGRESQL_ADDON_DB -O $POSTGRESQL_ADDON_USER -U $POSTGRESQL_ADDON_USER --echo +createdb $PGDATABASE -O $PGUSER -U $PGUSER --echo echo "restoring db" -pg_restore -U $POSTGRESQL_ADDON_USER --dbname=$POSTGRESQL_ADDON_DB --format=c --clean --no-owner $DB_BACKUP_PATH +pg_restore -U $PGUSER --dbname=$PGDATABASE --format=c --clean --no-owner $DB_BACKUP_PATH echo "applying new migrations" python manage.py migrate