diff --git a/.github/workflows/pytest.yml b/.github/workflows/pytest.yml index cfa6b69..eab3098 100644 --- a/.github/workflows/pytest.yml +++ b/.github/workflows/pytest.yml @@ -14,7 +14,7 @@ jobs: strategy: max-parallel: 2 matrix: - python-version: [3.9, 3.8, 3.7] + python-version: ["3.10", "3.9", "3.8", "3.7"] steps: - uses: actions/checkout@v2 with: @@ -45,6 +45,10 @@ jobs: run: | make pytest + - name: 'Run Safety check' + run: | + make safety + - name: 'Upload coverage report' uses: codecov/codecov-action@v2 with: diff --git a/Makefile b/Makefile index f174071..03b8150 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,8 @@ local-test: install ## Run local_test.py to run the project locally local-diff-settings: ## Run "manage.py diffsettings" with local test poetry run python3 local_test/opt_yunohost/manage.py diffsettings +safety: ## Run https://github.com/pyupio/safety + poetry run safety check --full-report ############################################################################## diff --git a/conf/gunicorn.conf.py b/conf/gunicorn.conf.py index bcab967..f49a397 100644 --- a/conf/gunicorn.conf.py +++ b/conf/gunicorn.conf.py @@ -17,4 +17,4 @@ accesslog = '__LOG_FILE__' errorlog = '__LOG_FILE__' # https://docs.gunicorn.org/en/latest/settings.html#pidfile -pidfile = '__FINAL_HOME_PATH__/gunicorn.pid' +pidfile = '__FINALPATH__/gunicorn.pid' diff --git a/conf/manage.py b/conf/manage.py index 9962cbd..ec26808 100755 --- a/conf/manage.py +++ b/conf/manage.py @@ -1,4 +1,4 @@ -#!__FINAL_HOME_PATH__/venv/bin/python +#!__FINALPATH__/venv/bin/python import os import sys diff --git a/conf/nginx.conf b/conf/nginx.conf index 729b72d..867d5ef 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,17 +1,11 @@ location __PATH__/static/ { - # Django static files + # Service static files by nginx + # e.g.: /var/www/$app/static alias __PUBLIC_PATH__/static/; expires 30d; } -# TODO: django-sendfile2: -#location __PATH__/media/ { -# # DATA_DIR/media/ -# alias __PUBLIC_PATH__/media/; -# expires 30d; -#} - location __PATH__/ { # https://github.com/benoitc/gunicorn/blob/master/examples/nginx.conf diff --git a/conf/requirements.txt b/conf/requirements.txt index 8a5a240..a33e542 100644 --- a/conf/requirements.txt +++ b/conf/requirements.txt @@ -13,9 +13,9 @@ bx-django-utils==31; python_version >= "3.7" and python_full_version < "4.0.0" \ bx-py-utils==68; python_version >= "3.7" and python_full_version < "4.0.0" \ --hash=sha256:00c3fbc9b5f48626a0a58141aaa90104349bac9723941c64f528f8742b0961db \ --hash=sha256:fe5808c379db7165a51cbf5e4d947ef783d78bdfb5c47665e85e9cfe0b520ff9 -certifi==2022.6.15.2; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" \ - --hash=sha256:0aa1a42fbd57645fabeb6290a7687c21755b0344ecaeaa05f4e9f6207ae2e9a8 \ - --hash=sha256:aa08c101214127b9b0472ca6338315113c9487d45376fd3e669201b477c71003 +certifi==2022.9.14; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" \ + --hash=sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516 \ + --hash=sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5 charset-normalizer==2.1.1; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" and python_full_version >= "3.6.0" \ --hash=sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845 \ --hash=sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f @@ -49,9 +49,9 @@ django-dbbackup==4.0.1; python_version >= "3.7" and python_full_version < "4.0.0 django-debug-toolbar==3.6.0; python_version >= "3.7" and python_full_version < "4.0.0" \ --hash=sha256:95fc2fd29c56cc86678aae9f6919ececefe892f2a78c4004b193a223a8380c3d \ --hash=sha256:fe7fe3f21865218827e2162ecc06eba386dfe8cffe4f3501c49bb4359e06a0e6 -django-import-export==2.8.0; python_version >= "3.7" and python_full_version < "4.0.0" \ - --hash=sha256:33c37b2921ef84e2cd9aa0eb76d04a7c2b538c9d04cb1ed97ac32600876cab30 \ - --hash=sha256:31d7dcfba22251e3ef93accb7da844f58c4d78585db9dd7dae37bb76f939da2c +django-import-export==2.9.0; python_version >= "3.7" and python_full_version < "4.0.0" \ + --hash=sha256:02ce3a8e191992fa7aa1d660877ac6764fa9e32a5ba6394293f2fc761a5bdd19 \ + --hash=sha256:126d32a4410c42b6e1bf060355bf45968f6fe427c3b7ed04c96873bd45d7549a django-ipware==4.0.2; python_version >= "3.7" and python_full_version < "3.0.0" or python_version >= "3.7" and python_full_version < "4.0.0" and python_full_version >= "3.6.0" \ --hash=sha256:602a58325a4808bd19197fef2676a0b2da2df40d0ecf21be414b2ff48c72ad05 \ --hash=sha256:878dbb06a87e25550798e9ef3204ed70a200dd8b15e47dcef848cf08244f04c9 @@ -73,12 +73,12 @@ django-reversion==5.0.2; python_version >= "3.7" and python_full_version < "4.0. django-tagulous==1.3.3; python_version >= "3.7" and python_full_version < "4.0.0" \ --hash=sha256:d445590ae1b5cb9b8c5a425f97bf5f01148a33419c19edeb721ebd9fdd6792fe \ --hash=sha256:ad3bb85f4cce83a47e4c0257143229cb92a294defa02fe661823b0442b35d478 -django-tools==0.53.0; python_version >= "3.7" and python_full_version < "4.0.0" \ - --hash=sha256:5879fd78a75c2820ced74705f0923150db46961b8727b540c13694ff8aacecba \ - --hash=sha256:0dc55eae1f093ec8b04a1539ed1a2847129d85af91b54b690106bd5731a475c0 -django-yunohost-integration==0.3.0; python_version >= "3.7" and python_full_version < "4.0.0" \ - --hash=sha256:f7f8e9d55c50231bb32d3809b240d6ac718aadac1a1f471284baa1bfa6e98dff \ - --hash=sha256:d0e9cc91d241075613cb54b5da0879ad9b8ad3d22b84130cd7a331025650406e +django-tools==0.54.0; python_version >= "3.7" and python_full_version < "4.0.0" \ + --hash=sha256:5040a91282be9d1c9d379b0c65da50bcb3691bff03cee54fd4123ace238c3a43 \ + --hash=sha256:a7b7bfa5b9c5a81966454d17dffb2403cee25a806c858ee0486a08798227598f +django-yunohost-integration==0.4.0; python_version >= "3.7" and python_full_version < "4.0.0" \ + --hash=sha256:560a280d509f8e28ea4b932d6682c91fab5d50f62b907981b9e0376556c8df7b \ + --hash=sha256:11a4add7cac2331e06348ef0c3eae597fe01f37867cde1f315ca707625527cdb django==3.2.15; python_version >= "3.7" and python_full_version < "4.0.0" \ --hash=sha256:115baf5049d5cf4163e43492cdc7139c306ed6d451e7d3571fe9612903903713 \ --hash=sha256:f71934b1a822f14a86c9ac9634053689279cd04ae69cb6ade4a59471b886582b diff --git a/conf/settings.py b/conf/settings.py index b2f60cb..d1e9d99 100644 --- a/conf/settings.py +++ b/conf/settings.py @@ -2,7 +2,7 @@ ################################################################################ # Please do not modify this file, it will be reset at the next update. -# You can edit the file __FINAL_HOME_PATH__/local_settings.py and add/modify the settings you need. +# You can edit the file __FINALPATH__/local_settings.py and add/modify the settings you need. # The parameters you add in local_settings.py will overwrite these, # but you can use the options and documentation in this file to find out what can be done. @@ -11,20 +11,19 @@ from pathlib import Path as __Path -from django_yunohost_integration.base_settings import * # noqa +from django_yunohost_integration.base_settings import * # noqa:F401,F403 from django_yunohost_integration.secret_key import get_or_create_secret as __get_or_create_secret -from inventory_project.settings.base import * # noqa +from inventory_project.settings.base import * # noqa:F401,F403 -DEBUG = False # Don't turn DEBUG on in production! +from django_yunohost_integration.base_settings import LOGGING # noqa:F401 isort:skip -# ----------------------------------------------------------------------------- -FINAL_HOME_PATH = __Path('__FINAL_HOME_PATH__') # /opt/yunohost/$app -assert FINAL_HOME_PATH.is_dir(), f'Directory not exists: {FINAL_HOME_PATH}' +FINALPATH = __Path('__FINALPATH__') # /opt/yunohost/$app +assert FINALPATH.is_dir(), f'Directory not exists: {FINALPATH}' -FINAL_WWW_PATH = __Path('__FINAL_WWW_PATH__') # /var/www/$app -assert FINAL_WWW_PATH.is_dir(), f'Directory not exists: {FINAL_WWW_PATH}' +PUBLIC_PATH = __Path('__PUBLIC_PATH__') # /var/www/$app +assert PUBLIC_PATH.is_dir(), f'Directory not exists: {PUBLIC_PATH}' LOG_FILE = __Path('__LOG_FILE__') # /var/log/$app/pyinventory.log assert LOG_FILE.is_file(), f'File not exists: {LOG_FILE}' @@ -33,13 +32,22 @@ PATH_URL = '__PATH_URL__' # $YNH_APP_ARG_PATH PATH_URL = PATH_URL.strip('/') # ----------------------------------------------------------------------------- +# config_panel.toml settings: -ROOT_URLCONF = 'urls' # /opt/yunohost/pyinventory/ynh_urls.py +DEBUG_ENABLED = '__DEBUG_ENABLED__' +DEBUG = bool(int(DEBUG_ENABLED)) + +LOG_LEVEL = '__LOG_LEVEL__' +ADMIN_EMAIL = '__ADMIN_EMAIL__' +DEFAULT_FROM_EMAIL = '__DEFAULT_FROM_EMAIL__' + + +# ----------------------------------------------------------------------------- # Function that will be called to finalize a user profile: YNH_SETUP_USER = 'setup_user.setup_project_user' -SECRET_KEY = __get_or_create_secret(FINAL_HOME_PATH / 'secret.txt') # /opt/yunohost/$app/secret.txt +SECRET_KEY = __get_or_create_secret(FINALPATH / 'secret.txt') # /opt/yunohost/$app/secret.txt INSTALLED_APPS.append('django_yunohost_integration') @@ -65,10 +73,12 @@ LOGIN_URL = '/yunohost/sso/' LOGOUT_REDIRECT_URL = '/yunohost/sso/' # /yunohost/sso/?action=logout +ROOT_URLCONF = 'urls' # .../conf/urls.py + # ----------------------------------------------------------------------------- -ADMINS = (('__ADMIN__', '__ADMINMAIL__'),) +ADMINS = (('__ADMIN__', ADMIN_EMAIL),) MANAGERS = ADMINS @@ -95,11 +105,10 @@ EMAIL_SUBJECT_PREFIX = f'[{SITE_TITLE}] ' # E-mail address that error messages come from. -SERVER_EMAIL = 'noreply@__DOMAIN__' +SERVER_EMAIL = ADMIN_EMAIL # Default email address to use for various automated correspondence from # the site managers. Used for registration emails. -DEFAULT_FROM_EMAIL = '__ADMINMAIL__' # List of URLs your site is supposed to serve ALLOWED_HOSTS = ['__DOMAIN__'] @@ -131,8 +140,8 @@ else: STATIC_URL = '/static/' MEDIA_URL = '/media/' -STATIC_ROOT = str(FINAL_WWW_PATH / 'static') -MEDIA_ROOT = str(FINAL_WWW_PATH / 'media') +STATIC_ROOT = str(PUBLIC_PATH / 'static') +MEDIA_ROOT = str(PUBLIC_PATH / 'media') # _____________________________________________________________________________ # django-ckeditor @@ -142,46 +151,22 @@ CKEDITOR_BASEPATH = STATIC_URL + 'ckeditor/ckeditor/' # _____________________________________________________________________________ # Django-dbbackup -DBBACKUP_STORAGE_OPTIONS['location'] = str(FINAL_HOME_PATH / 'backups') +DBBACKUP_STORAGE_OPTIONS['location'] = str(FINALPATH / 'backups') # ----------------------------------------------------------------------------- -LOGGING = { - 'version': 1, - 'disable_existing_loggers': True, - 'formatters': { - 'verbose': { - 'format': '{asctime} {levelname} {name} {module}.{funcName} {message}', - 'style': '{', - }, - }, - 'handlers': { - 'mail_admins': { - 'level': 'ERROR', - 'formatter': 'verbose', - 'class': 'django.utils.log.AdminEmailHandler', - 'include_html': True, - }, - 'log_file': { - 'level': 'DEBUG', - 'class': 'logging.handlers.WatchedFileHandler', - 'formatter': 'verbose', - 'filename': str(LOG_FILE), - }, - }, - 'loggers': { - '': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, - 'django': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, - 'axes': {'handlers': ['log_file', 'mail_admins'], 'level': 'WARNING', 'propagate': False}, - 'django_tools': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, - 'django_yunohost_integration': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, - 'inventory': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False}, - }, +# Set log file to e.g.: /var/log/$app/$app.log +LOGGING['handlers']['log_file']['filename'] = str(LOG_FILE) + +LOGGING['loggers']['inventory'] = { + 'handlers': ['syslog', 'log_file', 'mail_admins'], + 'level': 'INFO', + 'propagate': False, } # ----------------------------------------------------------------------------- try: - from local_settings import * # noqa + from local_settings import * # noqa:F401,F403 except ImportError: pass diff --git a/conf/pyinventory.service b/conf/systemd.service similarity index 88% rename from conf/pyinventory.service rename to conf/systemd.service index 67f504b..6b6cc92 100644 --- a/conf/pyinventory.service +++ b/conf/systemd.service @@ -1,5 +1,5 @@ [Unit] -Description=PyInventory application server +Description=__APP__ server After=redis.service postgresql.service [Service] diff --git a/conf/urls.py b/conf/urls.py index 9774f12..68302b4 100644 --- a/conf/urls.py +++ b/conf/urls.py @@ -1,34 +1,18 @@ from django.conf import settings -from django.conf.urls import include, static +from django.conf.urls import include from django.contrib import admin from django.urls import path -# def debug_view(request): -# """ debug request.META """ -# if not request.user.is_authenticated: -# from django.shortcuts import redirect -# return redirect('admin:index') -# -# import pprint -# meta = pprint.pformat(request.META) -# html = f'request.META:
{meta}
' -# from django.http import HttpResponse -# return HttpResponse(html) - - if settings.PATH_URL: # settings.PATH_URL is the $YNH_APP_ARG_PATH # Prefix all urls with "PATH_URL": urlpatterns = [ - # path(f'{settings.PATH_URL}/debug/', debug_view), # MEDIA_URL contains the "PATH_URL" already: path(settings.MEDIA_URL.lstrip('/'), include('django_tools.serve_media_app.urls')), path(f'{settings.PATH_URL}/', admin.site.urls), path(f'{settings.PATH_URL}/ckeditor/', include('ckeditor_uploader.urls')), ] - if settings.SERVE_FILES: - urlpatterns += static.static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) else: # Installed to domain root, without a path prefix # Just use the default project urls.py diff --git a/config_panel.toml b/config_panel.toml new file mode 100644 index 0000000..921a8cb --- /dev/null +++ b/config_panel.toml @@ -0,0 +1,35 @@ +version = "1.0" + +[main] +name = "django_example_ynh configuration" +services = ["__APP__"] + + [main.config] + name = "Configuration Options" + + [main.config.default_from_email] + ask = "from email" + type = "email" + help = "Default email address to use for various automated emails." + bind = "default_from_email:__FINALPATH__/settings.py" + + [main.config.admin_email] + ask = "ADMIN email" + type = "email" + help = "EMail address for error emails." + bind = "admin_email:__FINALPATH__/settings.py" + + [main.config.debug_enabled] + ask = "DEBUG mode" + type = "boolean" + yes = "1" + no = "0" + help = "Should be never enabled in production!" + bind = "debug_enabled:__FINALPATH__/settings.py" + + [main.config.log_level] + type = "string" + ask = "Log Level" + choices = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"] + default = "WARNING" + bind = "log_level:__FINALPATH__/settings.py" diff --git a/local_test.py b/local_test.py index e2664e5..1f857e9 100644 --- a/local_test.py +++ b/local_test.py @@ -22,6 +22,9 @@ def main(): django_settings_path=BASE_PATH / 'conf' / 'settings.py', destination=BASE_PATH / 'local_test', runserver=True, + extra_replacements={ + '__DEBUG_ENABLED__': '1', + }, ) diff --git a/manifest.json b/manifest.json index ef4240e..6eb19a5 100644 --- a/manifest.json +++ b/manifest.json @@ -23,7 +23,7 @@ }, "multi_instance": true, "services": [ - "nginx" + "nginx", "postgresql", "redis" ], "arguments": { "install" : [ diff --git a/poetry.lock b/poetry.lock index 9926dde..b3ac2b8 100644 --- a/poetry.lock +++ b/poetry.lock @@ -107,7 +107,7 @@ python-versions = ">=3.7,<4.0.0" [[package]] name = "certifi" -version = "2022.6.15.2" +version = "2022.9.14" description = "Python package for providing Mozilla's CA Bundle." category = "main" optional = false @@ -322,7 +322,7 @@ sqlparse = ">=0.2.0" [[package]] name = "django-import-export" -version = "2.8.0" +version = "2.9.0" description = "Django application and library for importing and exporting data with included admin integration." category = "main" optional = false @@ -417,7 +417,7 @@ Django = ">=2.2" [[package]] name = "django-tools" -version = "0.53.0" +version = "0.54.0" description = "miscellaneous tools for Django based projects" category = "main" optional = false @@ -432,7 +432,7 @@ pprintpp = "*" [[package]] name = "django-yunohost-integration" -version = "0.3.0" +version = "0.4.0" description = "Glue code to package django projects as yunohost apps." category = "main" optional = false @@ -442,6 +442,7 @@ python-versions = ">=3.7,<4.0.0" django = "*" django-axes = {version = "*", optional = true, markers = "extra == \"ynh\""} django-redis = {version = "*", optional = true, markers = "extra == \"ynh\""} +django-tools = ">=0.54.0" gunicorn = {version = "*", optional = true, markers = "extra == \"ynh\""} psycopg2 = {version = "*", optional = true, markers = "extra == \"ynh\""} @@ -456,6 +457,22 @@ category = "dev" optional = false python-versions = "*" +[[package]] +name = "dparse" +version = "0.6.0" +description = "A parser for Python dependency files" +category = "dev" +optional = false +python-versions = ">=3.5" + +[package.dependencies] +packaging = "*" +toml = "*" + +[package.extras] +conda = ["pyyaml"] +pipenv = ["pipenv"] + [[package]] name = "editorconfig" version = "0.12.3" @@ -909,6 +926,44 @@ urllib3 = ">=1.21.1,<1.27" socks = ["PySocks (>=1.5.6,!=1.5.7)"] use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] +[[package]] +name = "ruamel.yaml" +version = "0.17.21" +description = "ruamel.yaml is a YAML parser/emitter that supports roundtrip preservation of comments, seq/map flow style, and map key order" +category = "dev" +optional = false +python-versions = ">=3" + +[package.dependencies] +"ruamel.yaml.clib" = {version = ">=0.2.6", markers = "platform_python_implementation == \"CPython\" and python_version < \"3.11\""} + +[package.extras] +docs = ["ryd"] +jinja2 = ["ruamel.yaml.jinja2 (>=0.2)"] + +[[package]] +name = "ruamel.yaml.clib" +version = "0.2.6" +description = "C version of reader, parser and emitter for ruamel.yaml derived from libyaml" +category = "dev" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "safety" +version = "2.1.1" +description = "Checks installed dependencies for known vulnerabilities and licenses." +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +Click = ">=8.0.2" +dparse = ">=0.5.1" +packaging = ">=21.0" +requests = "*" +"ruamel.yaml" = ">=0.17.21" + [[package]] name = "six" version = "1.16.0" @@ -1097,7 +1152,7 @@ testing = ["func-timeout", "jaraco.itertools", "pytest (>=6)", "pytest-black (>= [metadata] lock-version = "1.1" python-versions = ">=3.7,<4.0.0" -content-hash = "f1e58401c81b28e096e272818b9f6fd0d87c38924f4b4b766e3c377390ba5ac9" +content-hash = "e2dd35f9bdf5560fa77504e519a7b0a5248a5189c1a26605719366388f78e7d3" [metadata.files] asgiref = [ @@ -1154,8 +1209,8 @@ bx-py-utils = [ {file = "bx_py_utils-68.tar.gz", hash = "sha256:fe5808c379db7165a51cbf5e4d947ef783d78bdfb5c47665e85e9cfe0b520ff9"}, ] certifi = [ - {file = "certifi-2022.6.15.2-py3-none-any.whl", hash = "sha256:0aa1a42fbd57645fabeb6290a7687c21755b0344ecaeaa05f4e9f6207ae2e9a8"}, - {file = "certifi-2022.6.15.2.tar.gz", hash = "sha256:aa08c101214127b9b0472ca6338315113c9487d45376fd3e669201b477c71003"}, + {file = "certifi-2022.9.14-py3-none-any.whl", hash = "sha256:e232343de1ab72c2aa521b625c80f699e356830fd0e2c620b465b304b17b0516"}, + {file = "certifi-2022.9.14.tar.gz", hash = "sha256:36973885b9542e6bd01dea287b2b4b3b21236307c56324fcc3f1160f2d655ed5"}, ] charset-normalizer = [ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, @@ -1274,8 +1329,8 @@ django-debug-toolbar = [ {file = "django_debug_toolbar-3.6.0-py3-none-any.whl", hash = "sha256:fe7fe3f21865218827e2162ecc06eba386dfe8cffe4f3501c49bb4359e06a0e6"}, ] django-import-export = [ - {file = "django-import-export-2.8.0.tar.gz", hash = "sha256:33c37b2921ef84e2cd9aa0eb76d04a7c2b538c9d04cb1ed97ac32600876cab30"}, - {file = "django_import_export-2.8.0-py3-none-any.whl", hash = "sha256:31d7dcfba22251e3ef93accb7da844f58c4d78585db9dd7dae37bb76f939da2c"}, + {file = "django-import-export-2.9.0.tar.gz", hash = "sha256:02ce3a8e191992fa7aa1d660877ac6764fa9e32a5ba6394293f2fc761a5bdd19"}, + {file = "django_import_export-2.9.0-py3-none-any.whl", hash = "sha256:126d32a4410c42b6e1bf060355bf45968f6fe427c3b7ed04c96873bd45d7549a"}, ] django-ipware = [ {file = "django-ipware-4.0.2.tar.gz", hash = "sha256:602a58325a4808bd19197fef2676a0b2da2df40d0ecf21be414b2ff48c72ad05"}, @@ -1306,16 +1361,20 @@ django-tagulous = [ {file = "django_tagulous-1.3.3-py3-none-any.whl", hash = "sha256:ad3bb85f4cce83a47e4c0257143229cb92a294defa02fe661823b0442b35d478"}, ] django-tools = [ - {file = "django-tools-0.53.0.tar.gz", hash = "sha256:5879fd78a75c2820ced74705f0923150db46961b8727b540c13694ff8aacecba"}, - {file = "django_tools-0.53.0-py3-none-any.whl", hash = "sha256:0dc55eae1f093ec8b04a1539ed1a2847129d85af91b54b690106bd5731a475c0"}, + {file = "django-tools-0.54.0.tar.gz", hash = "sha256:5040a91282be9d1c9d379b0c65da50bcb3691bff03cee54fd4123ace238c3a43"}, + {file = "django_tools-0.54.0-py3-none-any.whl", hash = "sha256:a7b7bfa5b9c5a81966454d17dffb2403cee25a806c858ee0486a08798227598f"}, ] django-yunohost-integration = [ - {file = "django_yunohost_integration-0.3.0-py3-none-any.whl", hash = "sha256:f7f8e9d55c50231bb32d3809b240d6ac718aadac1a1f471284baa1bfa6e98dff"}, - {file = "django_yunohost_integration-0.3.0.tar.gz", hash = "sha256:d0e9cc91d241075613cb54b5da0879ad9b8ad3d22b84130cd7a331025650406e"}, + {file = "django_yunohost_integration-0.4.0-py3-none-any.whl", hash = "sha256:560a280d509f8e28ea4b932d6682c91fab5d50f62b907981b9e0376556c8df7b"}, + {file = "django_yunohost_integration-0.4.0.tar.gz", hash = "sha256:11a4add7cac2331e06348ef0c3eae597fe01f37867cde1f315ca707625527cdb"}, ] docopt = [ {file = "docopt-0.6.2.tar.gz", hash = "sha256:49b3a825280bd66b3aa83585ef59c4a8c82f2c8a522dbe754a8bc8d08c85c491"}, ] +dparse = [ + {file = "dparse-0.6.0-py3-none-any.whl", hash = "sha256:3cb489bd06bfa8d285c85f7dec69d9ee8f89c29dd5f4ab48e159746dc13b78b2"}, + {file = "dparse-0.6.0.tar.gz", hash = "sha256:57068bb61859b1676c6beb10f399906eecb41a75b5d3fbc99d0311059cb67213"}, +] editorconfig = [ {file = "EditorConfig-0.12.3-py3-none-any.whl", hash = "sha256:6b0851425aa875b08b16789ee0eeadbd4ab59666e9ebe728e526314c4a2e52c1"}, {file = "EditorConfig-0.12.3.tar.gz", hash = "sha256:57f8ce78afcba15c8b18d46b5170848c88d56fd38f05c2ec60dbbfcb8996e89e"}, @@ -1569,6 +1628,46 @@ requests = [ {file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"}, {file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"}, ] +"ruamel.yaml" = [ + {file = "ruamel.yaml-0.17.21-py3-none-any.whl", hash = "sha256:742b35d3d665023981bd6d16b3d24248ce5df75fdb4e2924e93a05c1f8b61ca7"}, + {file = "ruamel.yaml-0.17.21.tar.gz", hash = "sha256:8b7ce697a2f212752a35c1ac414471dc16c424c9573be4926b56ff3f5d23b7af"}, +] +"ruamel.yaml.clib" = [ + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:6e7be2c5bcb297f5b82fee9c665eb2eb7001d1050deaba8471842979293a80b0"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux2014_aarch64.whl", hash = "sha256:066f886bc90cc2ce44df8b5f7acfc6a7e2b2e672713f027136464492b0c34d7c"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:221eca6f35076c6ae472a531afa1c223b9c29377e62936f61bc8e6e8bdc5f9e7"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win32.whl", hash = "sha256:1070ba9dd7f9370d0513d649420c3b362ac2d687fe78c6e888f5b12bf8bc7bee"}, + {file = "ruamel.yaml.clib-0.2.6-cp310-cp310-win_amd64.whl", hash = "sha256:77df077d32921ad46f34816a9a16e6356d8100374579bc35e15bab5d4e9377de"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:cfdb9389d888c5b74af297e51ce357b800dd844898af9d4a547ffc143fa56751"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7b2927e92feb51d830f531de4ccb11b320255ee95e791022555971c466af4527"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win32.whl", hash = "sha256:ada3f400d9923a190ea8b59c8f60680c4ef8a4b0dfae134d2f2ff68429adfab5"}, + {file = "ruamel.yaml.clib-0.2.6-cp35-cp35m-win_amd64.whl", hash = "sha256:de9c6b8a1ba52919ae919f3ae96abb72b994dd0350226e28f3686cb4f142165c"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d67f273097c368265a7b81e152e07fb90ed395df6e552b9fa858c6d2c9f42502"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:72a2b8b2ff0a627496aad76f37a652bcef400fd861721744201ef1b45199ab78"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:d3c620a54748a3d4cf0bcfe623e388407c8e85a4b06b8188e126302bcab93ea8"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win32.whl", hash = "sha256:9efef4aab5353387b07f6b22ace0867032b900d8e91674b5d8ea9150db5cae94"}, + {file = "ruamel.yaml.clib-0.2.6-cp36-cp36m-win_amd64.whl", hash = "sha256:846fc8336443106fe23f9b6d6b8c14a53d38cef9a375149d61f99d78782ea468"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0847201b767447fc33b9c235780d3aa90357d20dd6108b92be544427bea197dd"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:78988ed190206672da0f5d50c61afef8f67daa718d614377dcd5e3ed85ab4a99"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:210c8fcfeff90514b7133010bf14e3bad652c8efde6b20e00c43854bf94fa5a6"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win32.whl", hash = "sha256:a49e0161897901d1ac9c4a79984b8410f450565bbad64dbfcbf76152743a0cdb"}, + {file = "ruamel.yaml.clib-0.2.6-cp37-cp37m-win_amd64.whl", hash = "sha256:bf75d28fa071645c529b5474a550a44686821decebdd00e21127ef1fd566eabe"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a32f8d81ea0c6173ab1b3da956869114cae53ba1e9f72374032e33ba3118c233"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7f7ecb53ae6848f959db6ae93bdff1740e651809780822270eab111500842a84"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:61bc5e5ca632d95925907c569daa559ea194a4d16084ba86084be98ab1cec1c6"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win32.whl", hash = "sha256:89221ec6d6026f8ae859c09b9718799fea22c0e8da8b766b0b2c9a9ba2db326b"}, + {file = "ruamel.yaml.clib-0.2.6-cp38-cp38-win_amd64.whl", hash = "sha256:31ea73e564a7b5fbbe8188ab8b334393e06d997914a4e184975348f204790277"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:dc6a613d6c74eef5a14a214d433d06291526145431c3b964f5e16529b1842bed"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:1866cf2c284a03b9524a5cc00daca56d80057c5ce3cdc86a52020f4c720856f0"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:1b4139a6ffbca8ef60fdaf9b33dec05143ba746a6f0ae0f9d11d38239211d335"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win32.whl", hash = "sha256:3fb9575a5acd13031c57a62cc7823e5d2ff8bc3835ba4d94b921b4e6ee664104"}, + {file = "ruamel.yaml.clib-0.2.6-cp39-cp39-win_amd64.whl", hash = "sha256:825d5fccef6da42f3c8eccd4281af399f21c02b32d98e113dbc631ea6a6ecbc7"}, + {file = "ruamel.yaml.clib-0.2.6.tar.gz", hash = "sha256:4ff604ce439abb20794f05613c374759ce10e3595d1867764dd1ae675b85acbd"}, +] +safety = [ + {file = "safety-2.1.1-py3-none-any.whl", hash = "sha256:05ba551fb61ef24c864835d21089f75bc8b37292680047b9f29693a6552e2fc7"}, + {file = "safety-2.1.1.tar.gz", hash = "sha256:dbc5dffa2e47da76cc43dfe8cbbbfca99d29118d0c6c54dfcfa11c2bd349dff6"}, +] six = [ {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, diff --git a/pyproject.toml b/pyproject.toml index 9d4143e..0ff3a8d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ coveralls = "*" flynt = "*" pyupgrade = "*" EditorConfig = "*" # https://github.com/editorconfig/editorconfig-core-py +safety = "*" # https://github.com/pyupio/safety [build-system] requires = ["poetry-core"] @@ -59,15 +60,15 @@ line_length = 120 atomic=true profile='black' line_length=120 -skip_glob=["*/htmlcov/*","*/migrations/*"] known_first_party=["inventory"] +skip_glob=["*/htmlcov/*","*/migrations/*","*/local_test/*"] lines_after_imports=2 [tool.pytest.ini_options] # https://docs.pytest.org/en/latest/customize.html#pyproject-toml minversion = "6.0" -norecursedirs = ".* .git __pycache__ conf coverage* dist htmlcov" +norecursedirs = ".* .git __pycache__ conf local_test coverage* dist htmlcov" # sometimes helpfull "addopts" arguments: # -vv # --verbose @@ -76,7 +77,6 @@ norecursedirs = ".* .git __pycache__ conf coverage* dist htmlcov" # --full-trace # -p no:warnings addopts = """ - --import-mode=importlib --reuse-db --nomigrations --cov=. @@ -88,17 +88,20 @@ addopts = """ --darker --doctest-modules --failed-first - --last-failed-no-failures all --new-first """ +[tool.coverage.run] +omit = [".*"] + + [tool.tox] # https://tox.readthedocs.io/en/latest/example/basic.html#pyproject-toml-tox-legacy-ini legacy_tox_ini = """ [tox] isolated_build = True -envlist = px310,py39,py38,py37 +envlist = py{37,38,39,310} skip_missing_interpreters = True [testenv] diff --git a/scripts/_common.sh b/scripts/_common.sh index 25ee365..0abf6a7 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -13,6 +13,22 @@ app=$YNH_APP_INSTANCE_NAME # Currently not used: PyInventory has no public pages, yet! is_public=$YNH_APP_ARG_IS_PUBLIC +#================================================= +# ARGUMENTS FROM CONFIG PANEL +#================================================= + +# 'debug_enabled' -> '__DEBUG_ENABLED__' -> settings.DEBUG +debug_enabled="0" + +# 'log_level' -> '__LOG_LEVEL__' -> settings.LOG_LEVEL +log_level="WARNING" + +# 'admin_email' -> '__ADMIN_EMAIL__' add in settings.ADMINS +admin_email="${admin}@${domain}" + +# 'default_from_email' -> '__DEFAULT_FROM_EMAIL__' -> settings.DEFAULT_FROM_EMAIL +default_from_email="${app}@${domain}" + #================================================= # SET CONSTANTS #================================================= @@ -20,7 +36,7 @@ is_public=$YNH_APP_ARG_IS_PUBLIC public_path=/var/www/$app final_path=/opt/yunohost/$app log_path=/var/log/$app -log_file="${log_path}/pyinventory.log" +log_file="${log_path}/${app}.log" #================================================= # COMMON VARIABLES @@ -71,17 +87,3 @@ ynh_redis_remove_db() { redis-cli -n "$db" flushall } -#================================================= - -# Execute a command as another user -# usage: ynh_exec_as USER COMMAND [ARG ...] -ynh_exec_as() { - local USER=$1 - shift 1 - - if [[ $USER = $(whoami) ]]; then - eval "$@" - else - sudo -u "$USER" "$@" - fi -} diff --git a/scripts/change_url b/scripts/change_url index 56e02c2..ed3d59b 100644 --- a/scripts/change_url +++ b/scripts/change_url @@ -30,10 +30,21 @@ final_path=$(ynh_app_setting_get --app="$app" --key=final_path) log_path=$(ynh_app_setting_get --app="$app" --key=log_path) port=$(ynh_app_setting_get --app="$app" --key=port) + db_pwd=$(ynh_app_setting_get --app="$app" --key=psqlpwd) -admin_mail=$(ynh_user_get_info "$admin" mail) +db_name=$(ynh_sanitize_dbid --db_name="$app") +db_user=$db_name + redis_db=$(ynh_app_setting_get --app="$app" --key=redis_db) +#------------------------------------------------- +# config_panel.toml settings: + +debug_enabled=$(ynh_app_setting_get --app="$app" --key=debug_enabled) +log_level=$(ynh_app_setting_get --app="$app" --key=log_level) +admin_email=$(ynh_app_setting_get --app="$app" --key=admin_email) +default_from_email=$(ynh_app_setting_get --app="$app" --key=default_from_email) + #================================================= # BACKUP BEFORE UPGRADE THEN ACTIVE TRAP #================================================= @@ -72,7 +83,7 @@ fi #================================================= # STOP SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Stopping systemd services..." +ynh_script_progression --message="Stopping systemd service '$app'..." ynh_systemd_action --service_name="$app" --action="stop" @@ -112,36 +123,16 @@ fi #================================================= # MODIFY SETTINGS #================================================= -ynh_script_progression --message="Modify PyInventory's config file..." +ynh_script_progression --message="Modify $app config file..." -# save old settings file -settings="$final_path/settings.py" -ynh_backup_if_checksum_is_different --file="$settings" - -cp "../conf/settings.py" "$settings" - -ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings" -ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings" -ynh_replace_string --match_string="__ADMIN__" --replace_string="$admin" --target_file="$settings" -ynh_replace_string --match_string="__ADMINMAIL__" --replace_string="$admin_mail" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_WWW_PATH__" --replace_string="$public_path" --target_file="$settings" -ynh_replace_string --match_string="__LOG_FILE__" --replace_string="$log_file" --target_file="$settings" -ynh_replace_string --match_string="__REDIS_DB__" --replace_string="$redis_db" --target_file="$settings" - -# Difference to install/upgrade scripts: Use $new_domain and $new_path here: -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$new_domain" --target_file="$settings" -ynh_replace_string --match_string="__PATH_URL__" --replace_string="$new_path" --target_file="$settings" - -# Recalculate and store the config file checksum into the app settings -ynh_store_file_checksum --file="$settings" +ynh_add_config --template="settings.py" --destination="$final_path/settings.py" #================================================= # GENERIC FINALISATION #================================================= # START SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Starting systemd services..." --weight=5 +ynh_script_progression --message="Starting systemd service '$app'..." --weight=5 ynh_systemd_action --service_name="$app" --action="start" diff --git a/scripts/install b/scripts/install index d568634..b56bb3b 100755 --- a/scripts/install +++ b/scripts/install @@ -50,19 +50,29 @@ ynh_app_setting_set --app="$app" --key=path --value="$path_url" # Find a free port port=$(ynh_find_port --port=8000) # Set port as application setting -# https://github.com/YunoHost/yunohost/blob/dev/data/helpers.d/setting +# https://yunohost.org/en/contribute/packaging_apps/helpers +# https://github.com/YunoHost/yunohost/blob/dev/helpers/setting ynh_app_setting_set --app="$app" --key=port --value="$port" db_pwd=$(ynh_app_setting_get --app="$app" --key=psqlpwd) -admin_mail=$(ynh_user_get_info --username="$admin" --key=mail) + redis_db=$(ynh_redis_get_free_db) +ynh_app_setting_set --app="$app" --key=redis_db --value="$redis_db" + +#------------------------------------------------- +# config_panel.toml settings: + +ynh_app_setting_set --app="$app" --key=debug_enabled --value="$debug_enabled" +ynh_app_setting_set --app="$app" --key=log_level --value="$log_level" +ynh_app_setting_set --app="$app" --key=admin_email --value="$admin_email" +ynh_app_setting_set --app="$app" --key=default_from_email --value="$default_from_email" #================================================= # STANDARD MODIFICATIONS #================================================= # INSTALL DEPENDENCIES #================================================= -ynh_script_progression --message="Installing dependencies..." --weight=20 +ynh_script_progression --message="Installing $app dependencies..." --weight=20 ynh_exec_warn_less ynh_install_app_dependencies "$pkg_dependencies" @@ -86,21 +96,22 @@ ynh_psql_setup_db --db_user="$db_user" --db_name="$db_name" ynh_script_progression --message="Configuring nginx web server..." # Create a dedicated nginx config -# https://github.com/YunoHost/yunohost/blob/dev/data/helpers.d/nginx +# https://yunohost.org/en/contribute/packaging_apps/helpers +# https://github.com/YunoHost/yunohost/blob/dev/helpers/nginx ynh_add_nginx_config "public_path" "port" #================================================= # CREATE DEDICATED USER #================================================= -ynh_script_progression --message="Configuring system user..." +ynh_script_progression --message="Configuring system user '$app'..." # A home directory for venv and settings etc. ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell #================================================= -# PIP INSTALLATION +# PYTHON VIRTUALENV #================================================= -ynh_script_progression --message="Install project via pip..." --weight=50 +ynh_script_progression --message="Create Python virtualenv..." --weight=5 # Always recreate everything fresh with current python version ynh_secure_remove "${final_path}/venv" @@ -111,6 +122,11 @@ python3 -m venv --without-pip "${final_path}/venv" cp ../conf/requirements.txt "$final_path/requirements.txt" chown -R "$app:" "$final_path" +#================================================= +# PIP INSTALLATION +#================================================= +ynh_script_progression --message="Install project via pip..." --weight=45 + #run source in a 'sub shell' ( set +o nounset @@ -124,45 +140,17 @@ chown -R "$app:" "$final_path" #================================================= # copy config files # ================================================ -ynh_script_progression --message="Create project configuration files..." +ynh_script_progression --message="Create $app configuration files..." -gunicorn_conf="$final_path/gunicorn.conf.py" -cp "../conf/gunicorn.conf.py" "$gunicorn_conf" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$gunicorn_conf" -ynh_replace_string --match_string="__LOG_FILE__" --replace_string="$log_file" --target_file="$gunicorn_conf" -ynh_replace_string --match_string="__PORT__" --replace_string="$port" --target_file="$gunicorn_conf" -ynh_store_file_checksum --file="$gunicorn_conf" +ynh_add_config --template="gunicorn.conf.py" --destination="$final_path/gunicorn.conf.py" -cp ../conf/manage.py "$final_path/manage.py" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$final_path/manage.py" -ynh_store_file_checksum --file="$final_path/manage.py" +ynh_add_config --template="manage.py" --destination="$final_path/manage.py" chmod +x "$final_path/manage.py" -settings="$final_path/settings.py" -cp "../conf/settings.py" "$settings" - -ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings" -ynh_replace_string --match_string="__DB_NAME__" --replace_string="$db_name" --target_file="$settings" -ynh_replace_string --match_string="__DB_USER__" --replace_string="$db_user" --target_file="$settings" -ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings" -ynh_replace_string --match_string="__ADMIN__" --replace_string="$admin" --target_file="$settings" -ynh_replace_string --match_string="__ADMINMAIL__" --replace_string="$admin_mail" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_WWW_PATH__" --replace_string="$public_path" --target_file="$settings" -ynh_replace_string --match_string="__LOG_FILE__" --replace_string="$log_file" --target_file="$settings" -ynh_replace_string --match_string="__REDIS_DB__" --replace_string="$redis_db" --target_file="$settings" - -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$settings" -ynh_replace_string --match_string="__PATH_URL__" --replace_string="$path_url" --target_file="$settings" - -# Calculate and store the config file checksum into the app settings -ynh_store_file_checksum --file="$settings" - -ynh_app_setting_set --app="$app" --key=redis_db --value="$redis_db" - -cp ../conf/setup_user.py "$final_path/setup_user.py" -cp ../conf/urls.py "$final_path/urls.py" -cp ../conf/wsgi.py "$final_path/wsgi.py" +ynh_add_config --template="settings.py" --destination="$final_path/settings.py" +ynh_add_config --template="setup_user.py" --destination="$final_path/setup_user.py" +ynh_add_config --template="urls.py" --destination="$final_path/urls.py" +ynh_add_config --template="wsgi.py" --destination="$final_path/wsgi.py" touch "$final_path/local_settings.py" @@ -180,7 +168,7 @@ cd "$final_path" || exit ./manage.py collectstatic --no-input # Create/update Django superuser (set unusable password, because auth done via SSOwat): -./manage.py create_superuser --username="$admin" --email="$admin_mail" +./manage.py create_superuser --username="$admin" --email="$(ynh_user_get_info "$admin" mail)" # Check the configuration # This may fail in some cases with errors, etc., but the app works and the user can fix issues later. @@ -200,7 +188,7 @@ ynh_use_logrotate "$log_file" #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $app --description="Web based management to catalog things" --log="${log_file}" +yunohost service add $app --log="${log_file}" #================================================= # GENERIC FINALIZATION @@ -220,15 +208,29 @@ chmod o-rwx "$final_path" #================================================= # SETUP SYSTEMD #================================================= -ynh_script_progression --message="Configuring a systemd service..." +ynh_script_progression --message="Configuring systemd service '$app'..." --weight=5 -# https://github.com/YunoHost/yunohost/blob/dev/data/helpers.d/systemd -ynh_add_systemd_config --service="$app" --template="pyinventory.service" +# https://yunohost.org/en/contribute/packaging_apps/helpers +# https://github.com/YunoHost/yunohost/blob/dev/helpers/systemd +ynh_add_systemd_config --service="$app" --template="systemd.service" #================================================= -# Start pyinventory via systemd +# SETUP SSOWAT #================================================= -ynh_script_progression --message="Starting PyInventory's services..." --weight=5 +ynh_script_progression --message="Configuring SSOwat..." + +# Make app public if necessary or protect it +if [ $is_public -eq 1 ] +then + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + ynh_permission_update --permission "main" --add "visitors" +fi + +#================================================= +# Start the app server via systemd +#================================================= +ynh_script_progression --message="Starting systemd service '$app'..." --weight=5 ynh_systemd_action --service_name="$app" --action="start" diff --git a/scripts/remove b/scripts/remove index 5e630a4..dc573b8 100755 --- a/scripts/remove +++ b/scripts/remove @@ -36,7 +36,7 @@ fi #================================================= # STOP PYINVENTORY'S SERVICES #================================================= -ynh_script_progression --message="Stopping and removing systemd services..." --weight=5 +ynh_script_progression --message="Stopping and removing systemd service '$app'..." --weight=5 ynh_remove_systemd_config --service="$app" diff --git a/scripts/restore b/scripts/restore index 0cbf548..a72837e 100755 --- a/scripts/restore +++ b/scripts/restore @@ -20,8 +20,8 @@ ynh_abort_if_errors #================================================= ynh_script_progression --message="Loading settings..." -public_path=$(ynh_app_setting_get --app="$app" --key=public_path) final_path=$(ynh_app_setting_get --app="$app" --key=final_path) +public_path=$(ynh_app_setting_get --app="$app" --key=public_path) db_name=$(ynh_app_setting_get --app="$app" --key=db_name) db_user=$db_name db_pwd=$(ynh_app_setting_get --app="$app" --key=psqlpwd) @@ -34,8 +34,6 @@ path_url=$(ynh_app_setting_get --app="$app" --key=path) #================================================= ynh_script_progression --message="Validating restoration parameters..." -ynh_webpath_available --domain=$domain --path_url=$path_url \ - || ynh_die --message="Path not available: ${domain}${path_url}" test ! -d $final_path \ || ynh_die --message="There is already a directory: $final_path " @@ -50,12 +48,10 @@ ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" #================================================= # RESTORE THE APP MAIN DIR #================================================= -ynh_script_progression --message="Restoring the app main directory..." +ynh_script_progression --message="Restoring $app main directory..." -ynh_restore_file --origin_path="$public_path" ynh_restore_file --origin_path="$final_path" - -touch "$final_path/local_settings.py" +ynh_restore_file --origin_path="$public_path" #================================================= # RECREATE THE DEDICATED USER @@ -83,10 +79,10 @@ ynh_script_progression --message="Reinstalling dependencies..." --weight=20 ynh_exec_warn_less ynh_install_app_dependencies "$pkg_dependencies" #================================================= -# REINSTALL PYTHON VIRTUALENV +# PYTHON VIRTUALENV # Maybe the backup contains a other Python version #================================================= -ynh_script_progression --message="Upgrade Python virtualenv..." --weight=50 +ynh_script_progression --message="Recreate Python virtualenv..." --weight=5 # Always recreate everything fresh with current python version ynh_secure_remove "${final_path}/venv" @@ -95,6 +91,10 @@ ynh_secure_remove "${final_path}/venv" python3 -m venv --without-pip "${final_path}/venv" chown -R "$app:" "$final_path" +#================================================= +# PIP INSTALLATION +#================================================= +ynh_script_progression --message="Install project via pip..." --weight=45 #run source in a 'sub shell' ( set +o nounset @@ -127,7 +127,7 @@ systemctl enable $app.service --quiet #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $app --description="Web based management to catalog things" --log="${log_file}" +yunohost service add $app --log="${log_file}" #================================================= # RESTORE THE LOGROTATE CONFIGURATION @@ -158,7 +158,7 @@ chmod o-rwx "$final_path" #================================================= # START PYINVENTORY #================================================= -ynh_script_progression --message="Starting a systemd service..." --weight=5 +ynh_script_progression --message="Starting systemd service '$app'..." --weight=5 ynh_systemd_action --service_name="$app" --action="start" diff --git a/scripts/upgrade b/scripts/upgrade index 4bd17b5..9bdc8f1 100755 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -26,13 +26,39 @@ db_pwd=$(ynh_app_setting_get --app="$app" --key=psqlpwd) db_name=$(ynh_sanitize_dbid --db_name="$app") db_user=$db_name -admin_mail=$(ynh_user_get_info "$admin" mail) redis_db=$(ynh_app_setting_get --app="$app" --key=redis_db) +#------------------------------------------------- +# config_panel.toml settings: + +debug_enabled=$(ynh_app_setting_get --app="$app" --key=debug_enabled) +if [ -z "$debug_enabled" ]; then + debug_enabled="0" + ynh_app_setting_set --app="$app" --key=debug_enabled --value="$debug_enabled" +fi + +log_level=$(ynh_app_setting_get --app="$app" --key=log_level) +if [ -z "$log_level" ]; then + log_level="WARNING" + ynh_app_setting_set --app="$app" --key=log_level --value="$log_level" +fi + +admin_email=$(ynh_app_setting_get --app="$app" --key=admin_email) +if [ -z "$admin_email" ]; then + admin_email="${admin}@${domain}" + ynh_app_setting_set --app="$app" --key=admin_email --value="$admin_email" +fi + +default_from_email=$(ynh_app_setting_get --app="$app" --key=default_from_email) +if [ -z "$default_from_email" ]; then + default_from_email="${app}@${domain}" + ynh_app_setting_set --app="$app" --key=default_from_email --value="$default_from_email" +fi + #================================================= # BACKUP BEFORE UPGRADE THEN ACTIVE TRAP #================================================= -ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --weight=40 +ynh_script_progression --message="Backing up $app before upgrading (may take a while)..." --weight=40 # Backup the current version of the app ynh_backup_before_upgrade @@ -48,7 +74,7 @@ ynh_abort_if_errors #================================================= # STOP SYSTEMD SERVICE #================================================= -ynh_script_progression --message="Stopping systemd services..." --weight=5 +ynh_script_progression --message="Stopping systemd service '$app'..." --weight=5 ynh_systemd_action --service_name="$app" --action="stop" @@ -58,7 +84,8 @@ ynh_systemd_action --service_name="$app" --action="stop" ynh_script_progression --message="Upgrading nginx web server configuration..." # Create a dedicated nginx config -# https://github.com/YunoHost/yunohost/blob/dev/data/helpers.d/nginx +# https://yunohost.org/en/contribute/packaging_apps/helpers +# https://github.com/YunoHost/yunohost/blob/dev/helpers/nginx ynh_add_nginx_config "public_path" "port" #================================================= @@ -81,14 +108,14 @@ ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell #================================================= # SETUP SYSTEMD #================================================= -ynh_script_progression --message="Configuring a systemd service..." +ynh_script_progression --message="Configuring systemd service '$app'..." --weight=5 -ynh_add_systemd_config --service="$app" --template="pyinventory.service" +ynh_add_systemd_config --service="$app" --template="systemd.service" #================================================= -# UPGRADE VENV +# PYTHON VIRTUALENV #================================================= -ynh_script_progression --message="Upgrade project via pip..." --weight=50 +ynh_script_progression --message="Recreate Python virtualenv..." --weight=5 # Always recreate everything fresh with current python version ynh_secure_remove "${final_path}/venv" @@ -99,6 +126,10 @@ python3 -m venv --without-pip "${final_path}/venv" cp ../conf/requirements.txt "$final_path/requirements.txt" chown -R "$app:" "$final_path" +#================================================= +# PIP INSTALLATION +#================================================= +ynh_script_progression --message="Install project via pip..." --weight=45 #run source in a 'sub shell' ( set +o nounset @@ -114,52 +145,15 @@ chown -R "$app:" "$final_path" # ================================================ ynh_script_progression --message="Create project configuration files..." -gunicorn_conf="$final_path/gunicorn.conf.py" -ynh_backup_if_checksum_is_different --file="$gunicorn_conf" -cp "../conf/gunicorn.conf.py" "$gunicorn_conf" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$gunicorn_conf" -ynh_replace_string --match_string="__LOG_FILE__" --replace_string="$log_file" --target_file="$gunicorn_conf" -ynh_replace_string --match_string="__PORT__" --replace_string="$port" --target_file="$gunicorn_conf" -ynh_store_file_checksum --file="$gunicorn_conf" +ynh_add_config --template="gunicorn.conf.py" --destination="$final_path/gunicorn.conf.py" -cp ../conf/manage.py "$final_path/manage.py" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$final_path/manage.py" -ynh_store_file_checksum --file="$final_path/manage.py" +ynh_add_config --template="manage.py" --destination="$final_path/manage.py" chmod +x "$final_path/manage.py" -# save old settings file -settings="$final_path/settings.py" -ynh_backup_if_checksum_is_different --file="$settings" - -cp "../conf/settings.py" "$settings" - -ynh_replace_string --match_string="__APP__" --replace_string="$app" --target_file="$settings" -ynh_replace_string --match_string="__DB_NAME__" --replace_string="$db_name" --target_file="$settings" -ynh_replace_string --match_string="__DB_USER__" --replace_string="$db_user" --target_file="$settings" -ynh_replace_string --match_string="__DB_PWD__" --replace_string="$db_pwd" --target_file="$settings" -ynh_replace_string --match_string="__ADMIN__" --replace_string="$admin" --target_file="$settings" -ynh_replace_string --match_string="__ADMINMAIL__" --replace_string="$admin_mail" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_HOME_PATH__" --replace_string="$final_path" --target_file="$settings" -ynh_replace_string --match_string="__FINAL_WWW_PATH__" --replace_string="$public_path" --target_file="$settings" -ynh_replace_string --match_string="__LOG_FILE__" --replace_string="$log_file" --target_file="$settings" -ynh_replace_string --match_string="__REDIS_DB__" --replace_string="$redis_db" --target_file="$settings" - -ynh_replace_string --match_string="__DOMAIN__" --replace_string="$domain" --target_file="$settings" -ynh_replace_string --match_string="__PATH_URL__" --replace_string="$path_url" --target_file="$settings" - -# Recalculate and store the config file checksum into the app settings -ynh_store_file_checksum --file="$settings" - -ynh_backup_if_checksum_is_different --file="$final_path/setup_user.py" -cp ../conf/setup_user.py "$final_path/setup_user.py" - -ynh_backup_if_checksum_is_different --file="$final_path/urls.py" -cp ../conf/urls.py "$final_path/urls.py" - -ynh_backup_if_checksum_is_different --file="$final_path/wsgi.py" -cp ../conf/wsgi.py "$final_path/wsgi.py" - -touch "$final_path/local_settings.py" +ynh_add_config --template="settings.py" --destination="$final_path/settings.py" +ynh_add_config --template="setup_user.py" --destination="$final_path/setup_user.py" +ynh_add_config --template="urls.py" --destination="$final_path/urls.py" +ynh_add_config --template="wsgi.py" --destination="$final_path/wsgi.py" #================================================= # MIGRATE PYINVENTORY @@ -175,7 +169,7 @@ cd "$final_path" || exit ./manage.py collectstatic --no-input # Create/update Django superuser (set unusable password, because auth done via SSOwat): -./manage.py create_superuser --username="$admin" --email="$admin_mail" +./manage.py create_superuser --username="$admin" --email="$(ynh_user_get_info "$admin" mail)" # Check the configuration # This may fail in some cases with errors, etc., but the app works and the user can fix issues later. @@ -195,7 +189,7 @@ ynh_use_logrotate --non-append #================================================= ynh_script_progression --message="Integrating service in YunoHost..." -yunohost service add $app --description="Web based management to catalog things" --log="${log_file}" +yunohost service add $app --log="${log_file}" #================================================= # GENERIC FINALIZATION @@ -213,9 +207,9 @@ chmod o-rwx "$public_path" chmod o-rwx "$final_path" #================================================= -# Start pyinventory via systemd +# Start the app server via systemd #================================================= -ynh_script_progression --message="Starting PyInventory's services..." --weight=5 +ynh_script_progression --message="Starting systemd service '$app'..." --weight=5 ynh_systemd_action --service_name="$app" --action="start" diff --git a/test_requirements.sh b/test_requirements.sh deleted file mode 100755 index e7ececf..0000000 --- a/test_requirements.sh +++ /dev/null @@ -1,17 +0,0 @@ -#!/bin/bash - -# Test to create the python virtual env and install all requirements. -# Note: Maybe you didn't have all OS packages installed ;) - -set -e - -final_path="./local_test" - -set -x - -mkdir -p "${final_path}/" -python3 -m venv "${final_path}/venv" -source "${final_path}/venv/bin/activate" - -$final_path/venv/bin/pip install --upgrade wheel pip -$final_path/venv/bin/pip install --no-deps -r "./conf/requirements.txt" diff --git a/tests/test_django_project.py b/tests/test_django_project.py index 7f1372b..d3e45ca 100644 --- a/tests/test_django_project.py +++ b/tests/test_django_project.py @@ -27,17 +27,24 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): assert settings.configured is True assert settings.PATH_URL == 'app_path' + assert settings.ROOT_URLCONF == 'urls' + assert reverse('admin:index') == '/app_path/' def assert_path(path, end_text): assert isinstance(path, Path) path = str(path) assert path.endswith(end_text) - assert_path(settings.FINAL_HOME_PATH, '/local_test/opt_yunohost') - assert_path(settings.FINAL_WWW_PATH, '/local_test/var_www') + assert_path(settings.FINALPATH, '/local_test/opt_yunohost') + assert_path(settings.PUBLIC_PATH, '/local_test/var_www') assert_path(settings.LOG_FILE, '/local_test/var_log_pyinventory.log') - assert settings.ROOT_URLCONF == 'urls' + def test_config_panel_settings(self): + # config_panel.toml settings, set via tests.conftest.pytest_configure(): + assert settings.DEBUG_ENABLED == '0' and settings.DEBUG is False + assert settings.LOG_LEVEL == 'INFO' + assert settings.ADMIN_EMAIL == 'foo-bar@test.tld' + assert settings.DEFAULT_FROM_EMAIL == 'django_app@test.tld' def test_urls(self): assert reverse('admin:index') == '/app_path/' @@ -66,7 +73,6 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): response = self.client.get('/app_path/', secure=True) self.assertRedirects(response, expected_url='/app_path/login/?next=/app_path/', fetch_redirect_response=False) - @override_settings(SECURE_SSL_REDIRECT=False) def test_create_unknown_user(self): assert User.objects.count() == 0 @@ -77,6 +83,7 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): HTTP_REMOTE_USER='test', HTTP_AUTH_USER='test', HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz', + secure=True, ) assert User.objects.count() == 1 @@ -95,7 +102,6 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): ), ) - @override_settings(SECURE_SSL_REDIRECT=False) def test_wrong_auth_user(self): assert User.objects.count() == 0 assert AccessLog.objects.count() == 0 @@ -107,6 +113,7 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): HTTP_REMOTE_USER='test', HTTP_AUTH_USER='foobar', # <<< wrong user name HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz', + secure=True, ) assert User.objects.count() == 1 @@ -120,7 +127,6 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): assert response.status_code == 403 # Forbidden - @override_settings(SECURE_SSL_REDIRECT=False) def test_wrong_cookie(self): assert User.objects.count() == 0 assert AccessLog.objects.count() == 0 @@ -132,6 +138,7 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): HTTP_REMOTE_USER='test', HTTP_AUTH_USER='test', HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz', + secure=True, ) assert User.objects.count() == 1 @@ -145,7 +152,6 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): assert response.status_code == 403 # Forbidden - @override_settings(SECURE_SSL_REDIRECT=False) def test_wrong_authorization_user(self): assert User.objects.count() == 0 @@ -155,7 +161,11 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase): path='/app_path/', HTTP_REMOTE_USER='test', HTTP_AUTH_USER='test', - HTTP_AUTHORIZATION=generate_basic_auth(username='foobar', password='test123'), # <<< wrong user name + HTTP_AUTHORIZATION=generate_basic_auth( + username='foobar', # <<< wrong user name + password='test123', + ), + secure=True, ) assert User.objects.count() == 1 diff --git a/tests/test_project_setup.py b/tests/test_project_setup.py index 2278693..7f1fe91 100644 --- a/tests/test_project_setup.py +++ b/tests/test_project_setup.py @@ -6,6 +6,7 @@ from pathlib import Path from django_tools.serve_media_app.utils import clean_filename from django_tools.unittest_utils.assertments import assert_is_dir, assert_is_file +from django_tools.unittest_utils.project_setup import check_editor_config import inventory @@ -83,3 +84,7 @@ def test_screenshot_filenames(): file_path.rename(new_path) renamed.append(f'{file_name!r} renamed to {cleaned_name!r}') assert not renamed, f'Bad screenshots file names found: {", ".join(renamed)}' + + +def test_check_editor_config(): + check_editor_config(package_root=PACKAGE_ROOT)