diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b8f6dd2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,26 @@ +# Dotfiles +**/.* + +# Docker +**/Dockerfile* + +# Requirements +**/requirements.in +**/requirements_dev.in +**/requirements_dev.txt + +# Documentation +docs +LICENSE +README.md +README.rst + +# Configuration +pyproject.toml + +# Testing +**/tests +pytest.ini + +# Generated files +**/node_modules diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 713d465..cca922e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,13 +8,14 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' cache: pip cache-dependency-path: '**/requirements*.txt' - run: pip install -r requirements.txt # Check requirements.txt contains production requirements. - run: ./manage.py --help - run: pip install -r requirements_dev.txt + - run: ./manage.py collectstatic --noinput -v2 - name: Run checks and tests env: PYTHONWARNINGS: error @@ -23,5 +24,5 @@ jobs: ./manage.py migrate ./manage.py makemigrations --check --dry-run ./manage.py check --fail-level WARNING - coverage run --source=cove_oc4ids,cove_project -m pytest -W error + coverage run --source=core,cove_oc4ids -m pytest -W error - uses: coverallsapp/github-action@v2 diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..508d90d --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,38 @@ +name: Deploy +on: + workflow_run: + workflows: ["CI"] + branches: [main] + types: + - completed +jobs: + docker: + if: ${{ github.event.workflow_run.conclusion == 'success' }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + # https://github.com/docker/login-action#github-container-registry + - uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + # https://github.com/docker/setup-buildx-action#usage + - uses: docker/setup-buildx-action@v3 + # https://github.com/docker/build-push-action#usage + - uses: docker/build-push-action@v6 + with: + push: true + file: Dockerfile_django + tags: | + ghcr.io/${{ github.repository }}-django:latest + cache-from: type=gha + cache-to: type=gha,mode=max + - uses: docker/build-push-action@v6 + with: + push: true + file: Dockerfile_static + tags: | + ghcr.io/${{ github.repository }}-static:latest + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/.github/workflows/i18n.yml b/.github/workflows/i18n.yml index b6b6040..a8d4f53 100644 --- a/.github/workflows/i18n.yml +++ b/.github/workflows/i18n.yml @@ -10,7 +10,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' cache: pip cache-dependency-path: '**/requirements*.txt' - name: Install translate-toolkit diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 2fc37d9..6a60c4c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: token: ${{ secrets.PAT || github.token }} - uses: actions/setup-python@v5 with: - python-version: '3.10' + python-version: '3.11' cache: pip cache-dependency-path: '**/requirements*.txt' - id: changed-files diff --git a/.gitignore b/.gitignore index 97162bf..4b3fbd7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ __pycache__ .coverage .ve /media +/static diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d828bc..54e08b8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,7 +2,7 @@ ci: autoupdate_schedule: quarterly skip: [pip-compile] default_language_version: - python: python3.10 + python: python3.11 repos: - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.6.3 diff --git a/.python-version b/.python-version index c8cfe39..2c07333 100644 --- a/.python-version +++ b/.python-version @@ -1 +1 @@ -3.10 +3.11 diff --git a/Dockerfile_django b/Dockerfile_django new file mode 100644 index 0000000..96255db --- /dev/null +++ b/Dockerfile_django @@ -0,0 +1,43 @@ +FROM python:3.11 as build-stage + +COPY requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +WORKDIR /workdir + +COPY . . + +ENV DJANGO_ENV=production + +RUN python manage.py collectstatic --noinput -v2 + +FROM python:3.11 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + gettext \ + && rm -rf /var/lib/apt/lists/* + +RUN groupadd -r runner && useradd --no-log-init -r -g runner runner + +# Must match the settings.DATABASES default value. +RUN mkdir -p /data/db && chown -R runner:runner /data/db +# Must match the settings.MEDIA_ROOT default value. +RUN mkdir -p /data/media && chown -R runner:runner /data/media + +COPY requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +WORKDIR /workdir +USER runner:runner +COPY --chown=runner:runner . . + +# Django needs a copy of the staticfiles.json manifest file. +COPY --from=build-stage --chown=runner:runner /workdir/static/staticfiles.json /workdir/static/staticfiles.json + +ENV DJANGO_ENV=production +ENV WEB_CONCURRENCY=2 + +RUN python manage.py compilemessages + +EXPOSE 8000 +CMD ["gunicorn", "core.wsgi", "--bind", "0.0.0.0:8000", "--worker-tmp-dir", "/dev/shm", "--threads", "2", "--name", "cove"] diff --git a/Dockerfile_static b/Dockerfile_static new file mode 100644 index 0000000..a6360e8 --- /dev/null +++ b/Dockerfile_static @@ -0,0 +1,17 @@ +FROM python:{{ cookiecutter.python_version }} as build-stage + +COPY requirements.txt /tmp/requirements.txt +RUN pip install --no-cache-dir -r /tmp/requirements.txt + +WORKDIR /workdir +COPY . . + +ENV DJANGO_ENV=production + +RUN python manage.py collectstatic --noinput -v2 + +FROM nginxinc/nginx-unprivileged:latest as production-stage +USER root +COPY --from=build-stage --chown=nginx:root /workdir/static /usr/share/nginx/html/static +COPY --chown=nginx:root default.conf /etc/nginx/conf.d/default.conf +USER nginx diff --git a/cove_project/__init__.py b/core/__init__.py similarity index 100% rename from cove_project/__init__.py rename to core/__init__.py diff --git a/core/asgi.py b/core/asgi.py new file mode 100644 index 0000000..66c1231 --- /dev/null +++ b/core/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for core project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") + +application = get_asgi_application() diff --git a/cove_project/context_processors.py b/core/context_processors.py similarity index 100% rename from cove_project/context_processors.py rename to core/context_processors.py diff --git a/core/settings.py b/core/settings.py new file mode 100644 index 0000000..cff3cc9 --- /dev/null +++ b/core/settings.py @@ -0,0 +1,260 @@ +""" +Django settings for the project. + +Generated by 'django-admin startproject'. + +For more information on this file, see +https://docs.djangoproject.com/en/4.2/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/4.2/ref/settings/ +""" + +import os +from glob import glob +from pathlib import Path + +import sentry_sdk +from sentry_sdk.integrations.django import DjangoIntegration +from sentry_sdk.integrations.logging import ignore_logger + +production = os.getenv("DJANGO_ENV") == "production" +local_access = "LOCAL_ACCESS" in os.environ or "ALLOWED_HOSTS" not in os.environ + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = os.getenv("SECRET_KEY", "2n5k63x#a(xc@-!tpxisd)bd!3bimfr1prj-*t7tnl(*j+#$0k") + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = not production + +ALLOWED_HOSTS = [".localhost", "127.0.0.1", "[::1]", "0.0.0.0"] # noqa: S104 # Docker +if "ALLOWED_HOSTS" in os.environ: + ALLOWED_HOSTS.extend(os.getenv("ALLOWED_HOSTS").split(",")) + + +# Application definition + +INSTALLED_APPS = [ + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "bootstrap3", + "cove", + "cove.input", + "cove_oc4ids", +] + + +MIDDLEWARE = ( + "django.middleware.cache.UpdateCacheMiddleware", + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.locale.LocaleMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "cove.middleware.CoveConfigCurrentApp", + "django.middleware.cache.FetchFromCacheMiddleware", +) + +ROOT_URLCONF = "core.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [BASE_DIR / "core" / "templates"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.template.context_processors.i18n", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "cove.context_processors.from_settings", + "core.context_processors.from_settings", + ], + }, + }, +] + +WSGI_APPLICATION = "core.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/4.2/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": os.getenv("DATABASE_PATH", "/data/db/db.sqlite3" if production else str(BASE_DIR / "db.sqlite3")), + } +} + + +# Password validation +# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/4.2/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/4.2/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + + +# Project-specific Django configuration + +LOCALE_PATHS = glob(str(BASE_DIR / "**" / "locale")) + +STATIC_ROOT = BASE_DIR / "static" + +STORAGES = { + "default": { + "BACKEND": "django.core.files.storage.FileSystemStorage", + }, + "staticfiles": { + "BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage", + }, +} + +# https://docs.djangoproject.com/en/4.2/topics/logging/#django-security +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "console": { + "format": "%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(message)s", + }, + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "formatter": "console", + }, + "null": { + "class": "logging.NullHandler", + }, + }, + "loggers": { + "": { + "handlers": ["console"], + "level": "INFO", + }, + "django.security.DisallowedHost": { + "handlers": ["null"], + "propagate": False, + }, + }, +} + +# https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ +if production and not local_access: + # Run: env DJANGO_ENV=production SECURE_HSTS_SECONDS=1 ./manage.py check --deploy + CSRF_COOKIE_SECURE = True + SESSION_COOKIE_SECURE = True + SECURE_SSL_REDIRECT = True + SECURE_REFERRER_POLICY = "same-origin" # default in Django >= 3.1 + + # https://docs.djangoproject.com/en/4.2/ref/middleware/#http-strict-transport-security + if "SECURE_HSTS_SECONDS" in os.environ: + SECURE_HSTS_SECONDS = int(os.getenv("SECURE_HSTS_SECONDS")) + SECURE_HSTS_INCLUDE_SUBDOMAINS = True + SECURE_HSTS_PRELOAD = True + +# https://docs.djangoproject.com/en/4.2/ref/settings/#secure-proxy-ssl-header +if "DJANGO_PROXY" in os.environ: + USE_X_FORWARDED_HOST = True + SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") + +LANGUAGES = ( + ("en", "English"), + ("es", "Spanish"), +) + +MEDIA_ROOT = os.getenv("MEDIA_ROOT", "/data/media/" if production else BASE_DIR / "media/") +MEDIA_URL = "media/" + +# https://docs.djangoproject.com/en/4.2/ref/settings/#data-upload-max-memory-size +DATA_UPLOAD_MAX_MEMORY_SIZE = 52428800 # 5 MB + +if production: + CACHES = { + "default": { + "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", + "LOCATION": "127.0.0.1:11211", + } + } + + +# Dependency configuration + +if "SENTRY_DSN" in os.environ: + # https://docs.sentry.io/platforms/python/logging/#ignoring-a-logger + ignore_logger("django.security.DisallowedHost") + sentry_sdk.init( + dsn=os.getenv("SENTRY_DSN"), + integrations=[DjangoIntegration()], + traces_sample_rate=1.0, + ) + +COVE_CONFIG = { + # lib-cove-web options + "app_name": "cove_oc4ids", + "app_base_template": "cove_oc4ids/base.html", + "app_verbose_name": "Open Contracting for Infrastructure Data Standards Review Tool", + "app_strapline": "Review your OC4IDS data.", + "input_methods": ["upload", "url", "text"], + "support_email": "data@open-contracting.org", +} + + +# Project configuration + +FATHOM = { + "domain": os.getenv("FATHOM_ANALYTICS_DOMAIN") or "cdn.usefathom.com", + "id": os.getenv("FATHOM_ANALYTICS_ID"), +} diff --git a/cove_project/templates/terms.html b/core/templates/terms.html similarity index 100% rename from cove_project/templates/terms.html rename to core/templates/terms.html diff --git a/cove_project/urls.py b/core/urls.py similarity index 100% rename from cove_project/urls.py rename to core/urls.py diff --git a/cove_project/wsgi.py b/core/wsgi.py similarity index 72% rename from cove_project/wsgi.py rename to core/wsgi.py index bc339f7..a08c895 100644 --- a/cove_project/wsgi.py +++ b/core/wsgi.py @@ -1,5 +1,5 @@ """ -WSGI config for cove_project project. +WSGI config for core project. It exposes the WSGI callable as a module-level variable named ``application``. @@ -11,6 +11,6 @@ from django.core.wsgi import get_wsgi_application -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cove_project.settings") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") application = get_wsgi_application() diff --git a/cove_oc4ids/templates/cove_oc4ids/base.html b/cove_oc4ids/templates/cove_oc4ids/base.html index 493ba47..f832163 100644 --- a/cove_oc4ids/templates/cove_oc4ids/base.html +++ b/cove_oc4ids/templates/cove_oc4ids/base.html @@ -3,7 +3,6 @@ {% load static %} {% block after_head %} - {% include "cove_oc4ids/fathom.html" %} diff --git a/cove_project/settings.py b/cove_project/settings.py deleted file mode 100644 index d367f9d..0000000 --- a/cove_project/settings.py +++ /dev/null @@ -1,180 +0,0 @@ -""" -Django settings for cove_project project. - -Generated by 'django-admin startproject'. - -For more information on this file, see -https://docs.djangoproject.com/en/4.2/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/4.2/ref/settings/ -""" - -import os -from pathlib import Path - -from cove import settings - -# Build paths inside the project like this: BASE_DIR / "subdir". -BASE_DIR = Path(__file__).resolve().parent.parent - -# We use the setting to choose whether to show the section about Sentry in the -# terms and conditions -SENTRY_DSN = os.getenv("SENTRY_DSN", "") - -if SENTRY_DSN: - import sentry_sdk - from sentry_sdk.integrations.django import DjangoIntegration - from sentry_sdk.integrations.logging import ignore_logger - - ignore_logger("django.security.DisallowedHost") - sentry_sdk.init(dsn=SENTRY_DSN, integrations=[DjangoIntegration()]) - -FATHOM = { - "domain": os.getenv("FATHOM_ANALYTICS_DOMAIN", "cdn.usefathom.com"), - "id": os.getenv("FATHOM_ANALYTICS_ID", ""), -} -VALIDATION_ERROR_LOCATIONS_LENGTH = settings.VALIDATION_ERROR_LOCATIONS_LENGTH -VALIDATION_ERROR_LOCATIONS_SAMPLE = settings.VALIDATION_ERROR_LOCATIONS_SAMPLE -DELETE_FILES_AFTER_DAYS = int(os.getenv("DELETE_FILES_AFTER_DAYS", "90")) - -# We can't take MEDIA_ROOT and MEDIA_URL from cove settings, -# ... otherwise the files appear under the BASE_DIR that is the Cove library install. -# That could get messy. We want them to appear in our directory. -MEDIA_ROOT = BASE_DIR / "media" -MEDIA_URL = "/infrastructure/media/" - -SECRET_KEY = os.getenv("SECRET_KEY", "2n5k63x#a(xc@-!tpxisd)bd!3bimfr1prj-*t7tnl(*j+#$0k") -DEBUG = settings.DEBUG -ALLOWED_HOSTS = settings.ALLOWED_HOSTS - -# Application definition - -INSTALLED_APPS = [ - "django.contrib.admin", - "django.contrib.auth", - "django.contrib.contenttypes", - "django.contrib.sessions", - "django.contrib.messages", - "django.contrib.staticfiles", - "bootstrap3", - "cove", - "cove.input", - "cove_oc4ids", -] - - -MIDDLEWARE = ( - "django.middleware.cache.UpdateCacheMiddleware", - "django.contrib.sessions.middleware.SessionMiddleware", - "django.middleware.locale.LocaleMiddleware", - "django.middleware.common.CommonMiddleware", - "django.middleware.csrf.CsrfViewMiddleware", - "django.contrib.auth.middleware.AuthenticationMiddleware", - "django.contrib.messages.middleware.MessageMiddleware", - "django.middleware.clickjacking.XFrameOptionsMiddleware", - "django.middleware.security.SecurityMiddleware", - "cove.middleware.CoveConfigCurrentApp", - "django.middleware.cache.FetchFromCacheMiddleware", -) - - -ROOT_URLCONF = "cove_project.urls" - -TEMPLATES = settings.TEMPLATES -TEMPLATES[0]["DIRS"] = [BASE_DIR / "cove_project" / "templates"] -TEMPLATES[0]["OPTIONS"]["context_processors"].append( - "cove_project.context_processors.from_settings", -) - -WSGI_APPLICATION = "cove_project.wsgi.application" - -# We can't take DATABASES from cove settings, -# ... otherwise the files appear under the BASE_DIR that is the Cove library install. -# That could get messy. We want them to appear in our directory. -DATABASES = { - "default": { - "ENGINE": "django.db.backends.sqlite3", - "NAME": os.getenv("DB_NAME", str(BASE_DIR / "db.sqlite3")), - } -} - -# Password validation -# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", - }, - { - "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", - }, -] - - -# Internationalization -# https://docs.djangoproject.com/en/4.2/topics/i18n/ - -LANGUAGE_CODE = settings.LANGUAGE_CODE -TIME_ZONE = settings.TIME_ZONE -USE_I18N = settings.USE_I18N -USE_TZ = settings.USE_TZ - -LANGUAGES = settings.LANGUAGES - -LOCALE_PATHS = (BASE_DIR / "cove_oc4ids" / "locale",) - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/4.2/howto/static-files/ - -# We can't take STATIC_URL and STATIC_ROOT from cove settings, -# ... otherwise the files appear under the BASE_DIR that is the Cove library install. -# and that doesn't work with our standard Apache setup. -STATIC_URL = "infrastructure/static/" -STATIC_ROOT = BASE_DIR / "static" - -# Misc - -LOGGING = settings.LOGGING -LOGGING["handlers"]["null"] = { - "class": "logging.NullHandler", -} -LOGGING["loggers"]["django.security.DisallowedHost"] = { - "handlers": ["null"], - "propagate": False, -} - -# OC4IDS Config - -COVE_CONFIG = { - # lib-cove-web options - "app_name": "cove_oc4ids", - "app_base_template": "cove_oc4ids/base.html", - "app_verbose_name": "Open Contracting for Infrastructure Data Standards Review Tool", - "app_strapline": "Review your OC4IDS data.", - "input_methods": ["upload", "url", "text"], - "support_email": "data@open-contracting.org", -} - -# Because of how the standard site proxies traffic, we want to use this -USE_X_FORWARDED_HOST = True - -# This Cove is served from the same domain as another Django app. -# To make sure sessions and CSRF tokens don't clash, use different names. -# https://github.com/open-contracting/deploy/issues/188 -CSRF_COOKIE_NAME = "oc4idscsrftoken" -SESSION_COOKIE_NAME = "oc4idssessionid" - -if not DEBUG: - CACHES = { - "default": { - "BACKEND": "django.core.cache.backends.memcached.PyMemcacheCache", - "LOCATION": "127.0.0.1:11211", - } - } diff --git a/default.conf b/default.conf new file mode 100644 index 0000000..d4f4a75 --- /dev/null +++ b/default.conf @@ -0,0 +1,56 @@ +server { + listen 8080; + listen [::]:8080; + server_name localhost; + server_tokens off; + + #access_log /var/log/nginx/host.access.log main; + + location /media/ { + root /data; + } + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + + location /static/ { + expires max; + } + location = /static/staticfiles.json { + expires -1; + } + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} diff --git a/manage.py b/manage.py index 0c1fd3e..bd9928d 100755 --- a/manage.py +++ b/manage.py @@ -1,10 +1,18 @@ #!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" + import os import sys -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "cove_project.settings") + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "core.settings") from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/pyproject.toml b/pyproject.toml index 13707c2..2ab63ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "0.0.0" [tool.ruff] line-length = 119 -target-version = "py310" +target-version = "py311" [tool.ruff.lint] select = ["ALL"] @@ -25,7 +25,8 @@ ignore-variadic-names = true "*/migrations/*" = ["E501"] "tests/*" = [ "D", "FBT003", "INP001", "PLR2004", "PT", "S", "TRY003", + "ARG001", # fixtures ] [tool.pytest.ini_options] -DJANGO_SETTINGS_MODULE = 'cove_project.settings' +DJANGO_SETTINGS_MODULE = 'core.settings'