modenize YunoHost project setup

This commit is contained in:
JensDiemer 2022-08-16 09:47:54 +02:00
parent 4ea25646c7
commit 67cfbc87ec
28 changed files with 657 additions and 699 deletions

View file

@ -10,7 +10,7 @@ trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
line_length = 119
max_line_length = 100
[{Makefile,**.mk}]
indent_style = tab
@ -18,4 +18,3 @@ insert_final_newline = false
[*.yml]
indent_style = tab
indent_size = 4

View file

@ -41,4 +41,7 @@ jobs:
make pytest
- name: 'Upload coverage report'
run: bash <(curl -s https://codecov.io/bash)
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: false
verbose: true

1
.gitignore vendored
View file

@ -3,6 +3,7 @@
!.editorconfig
!.flake8
!.gitignore
!/doc/screenshots/.gitkeep
__pycache__
secret.txt
/local_test/

View file

@ -1,5 +1,5 @@
SHELL := /bin/bash
MAX_LINE_LENGTH := 119
MAX_LINE_LENGTH := 100
all: help
@ -45,7 +45,7 @@ tox: check-poetry ## Run pytest via tox with all environments
poetry run tox
pytest: install ## Run pytest
poetry run python3 ./run_pytest.py
poetry run pytest
local-test: install ## Run local_test.py to run the project locally
poetry run python3 ./local_test.py

View file

@ -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'

View file

@ -1,8 +0,0 @@
"""
Here it's possible to overwrite everything in settings.py
Note:
Used for YunoHost config and will be **overwritten** on every config change!
"""
DEBUG = __DJANGO_DEBUG__ # Don't turn DEBUG on in production!

View file

@ -1,4 +1,4 @@
#!__FINAL_HOME_PATH__/venv/bin/python
#!__FINALPATH__/venv/bin/python
import os
import sys

View file

@ -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

View file

@ -1,27 +1,27 @@
asgiref==3.5.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:45a429524fba18aba9d512498b19d220c4d628e75b40cf5c627524dbaebc5cc1 \
--hash=sha256:fddeea3c53fa99d0cdb613c3941cc6e52d822491fc2753fba25768fb5bf4e865
asgiref==3.5.2; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4 \
--hash=sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424
async-timeout==4.0.2; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15 \
--hash=sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c
bleach==5.0.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:08a1fe86d253b5c88c92cc3d810fd8048a16d15762e1e5b74d502256e5926aa1 \
--hash=sha256:c6d6cc054bdc9c83b48b8083e236e5f00f238428666d2ce2e083eaa5fd568565
bx-django-utils==21; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:414ada254464b6db851a4eed20460e3d6d78c749459419cbadc7341019d69379 \
--hash=sha256:ccd90e09c5f334d848508b9ba0fe1857291548cfb49a41cc56d3c78744773951
bx-py-utils==61; python_version >= "3.6" and python_full_version < "4.0.0" \
--hash=sha256:8af6c2ab7396452f689441c4cf2ea2b26a7d3cc1349041e333ce441ca1c8261b \
--hash=sha256:34be9906ad6629d28e2d22290de575a4a600a49625455ec758c8b02adadb44f2
certifi==2021.10.8; 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:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569 \
--hash=sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872
charset-normalizer==2.0.12; python_version >= "3.7" and python_full_version < "4.0.0" and python_full_version >= "3.6.0" \
--hash=sha256:2857e29ff0d34db842cd7ca3230549d1a697f96ee6d3fb071cfa6c7393832597 \
--hash=sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df
colorama==0.4.4; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "3.7" and python_full_version < "4.0.0" and sys_platform == "win32" and python_full_version >= "3.5.0" \
--hash=sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2 \
--hash=sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b
bleach==5.0.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a \
--hash=sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c
bx-django-utils==26; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:c04776c4c6b275ddcadae79fd1578e6ae9ba92d8cf752a9ee4f2b70a22f4236e \
--hash=sha256:19349d0a8fa165d87b4fd544efb61418f7d6c41cfabaa46d0153bb6d844262a1
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; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" \
--hash=sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412 \
--hash=sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d
charset-normalizer==2.1.0; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" and python_full_version >= "3.6.0" \
--hash=sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413 \
--hash=sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5
colorama==0.4.5; python_version >= "3.7" and python_full_version < "3.0.0" and sys_platform == "win32" or python_version >= "3.7" and python_full_version < "4.0.0" and sys_platform == "win32" and python_full_version >= "3.5.0" \
--hash=sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da \
--hash=sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4
colorlog==6.6.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:351c51e866c86c3217f08e4b067a7974a678be78f07f85fc2d55b8babde6d94e \
--hash=sha256:344f73204009e4c83c5b6beb00b3c45dc70fcdae3c80db919e0a4171d006fde8
@ -34,12 +34,12 @@ diff-match-patch==20200713; python_version >= "3.7" and python_full_version < "4
django-admin-sortable2==1.0.4; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:f96044003176c6684c5f969792ca833a505d654fa0f7b24232a0a610e4332a53 \
--hash=sha256:e22956889533b48a35a7f02859ae3a939753fa9a7d7d532cefc2835b41bdcebb
django-axes==5.32.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:e42c9a9c83489d795970784681ac221f60609ec22079f9def9990b8258d3db3b \
--hash=sha256:02793a3b007ee1d6c71a0dfc2e165471d50fdf806bb29451bea85b4d70d6d099
django-debug-toolbar==3.4.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:ae6bec2c1ce0e6900b0ab0443e1427eb233d8e6f57a84a0b2705eeecb8874e22 \
--hash=sha256:42c1c2e9dc05bb57b53d641e3a6d131fc031b92377b34ae32e604a1fe439bb83
django-axes==5.36.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:bac08a7047fde26ffb54813c971fd40eeadb4ecb8d342a6e47d53de666d1a792 \
--hash=sha256:466e6ed1affd0866c78f245ee658d2619f74250aca5856852d86e61dba400eda
django-debug-toolbar==3.5.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:97965f2630692de316ea0c1ca5bfa81660d7ba13146dbc6be2059cf55b35d0e5 \
--hash=sha256:89a52128309eb4da12738801ff0c202d2ff8730d1c3225fac6acf630c303e661
django-fritzconnection==0.2.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:5573ef7497fbd339e54c6067d9d7e223d820785d581cd5e6593af46c828a6425 \
--hash=sha256:4dbc96661da17cfa0f57ee6e6cc0956574d47479aa688eedf136475bf96f870e
@ -52,21 +52,21 @@ django-redis==5.2.0; python_version >= "3.7" and python_full_version < "4.0.0" \
django-reversion-compare==0.15.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:ed0264a2852d9d867023f1874948b8234dad9c2d2fa22ea18cfd5f28f304d7a0 \
--hash=sha256:d6f37b106aec287ae17a076bb7db1184ab02ab1898f0e8693f2779fbdaf71697
django-reversion==5.0.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:0bade3c399387451087f0c67835e0d3d186d767de6a5c5ba53a88eafc3f271c8 \
--hash=sha256:7bea725de7f56769d89a5a712cf7b7f1b02abc27655432f2eb1a703264986d99
django-reversion==5.0.2; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:243a12ee4e04c1611c0f076fbfc3074f1ad40afc45adc64de5ba2414cc4eaf29 \
--hash=sha256:5ae3f0a529530bc24afd64b084b690c96d615b70a45bad3a68dc12fcf50ed8c9
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.49.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:9da6d5610576a34219be3dea9c7c2207669e5c916d89987011843ded772ece0a \
--hash=sha256:a27c32cff98cd82dde23ab2f05fb9e06a32aaeb8357e96f1ec02a634b6619170
django-yunohost-integration==0.2.4; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:f5cdb5480025e90de0221d2b1c6282f517fd0c903702563cccb771cb3e1d9417 \
--hash=sha256:a4b3617a3b39225d6162fa88827e9fe8b65388e26a0bbc23ea665c62aa8cb044
django==3.2.13; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf \
--hash=sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6
django-tools==0.51.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:ebe69065ae5504e66667125858a7edd1cf6627e2b96cb03aeaab9e0ee3b8b98b \
--hash=sha256:4fe3768d4d863e7f3fa8bd7a3f014c4320ab320776218641fe760202c08f2d5a
django-yunohost-integration==0.3.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:f7f8e9d55c50231bb32d3809b240d6ac718aadac1a1f471284baa1bfa6e98dff \
--hash=sha256:d0e9cc91d241075613cb54b5da0879ad9b8ad3d22b84130cd7a331025650406e
django==3.2.15; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:115baf5049d5cf4163e43492cdc7139c306ed6d451e7d3571fe9612903903713 \
--hash=sha256:f71934b1a822f14a86c9ac9634053689279cd04ae69cb6ade4a59471b886582b
fritzconnection==1.9.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:9daa9a6c8366a77a2617b34b1db9b2191bb21ba638be35d691541a0892363a16 \
--hash=sha256:244f74d62e426465108654d422d3a9e484c857b5bfaf088b623860f1874dd715
@ -75,7 +75,7 @@ gunicorn==20.1.0; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
icdiff==2.0.5; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:35d24b728e48b7e0a12bdb69386d3bfc7eef4fe922d0ac1cd70d6e5c11630bae
idna==3.3; 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" \
idna==3.3; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" \
--hash=sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff \
--hash=sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d
importlib-metadata==4.2.0; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "3.8" \
@ -105,27 +105,27 @@ pyparsing==3.0.9; python_version >= "3.7" and python_full_version < "4.0.0" and
python-stdnum==1.17; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:374e2b5e13912ccdbf50b0b23fca2c3e0531174805c32d74e145f37756328340 \
--hash=sha256:a46e6cf9652807314d369b654b255c86a59f93d18be2834f3d567ed1a346c547
pytz==2022.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c \
--hash=sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7
redis==4.3.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:84316970995a7adb907a56754d2b92d88fc2d252963dc5ac34c88f0f1a22c25d \
--hash=sha256:94b617b4cd296e94991146f66fc5559756fbefe9493604f0312e4d3298ac63e9
requests==2.27.1; 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:f22fa1e554c9ddfd16e6e41ac79759e17be9e492b3587efa038054674760e72d \
--hash=sha256:68d7c56fd5a8999887728ef304a6d12edc7be74f1cfa47714fc8b414525c9a61
pytz==2022.2.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:220f481bdafa09c3955dfbdddb7b57780e9a94f5127e35456a48589b9e0c0197 \
--hash=sha256:cea221417204f2d1a2aa03ddae3e867921971d0d76f14d87abb4414415bbdcf5
redis==4.3.4; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:a52d5694c9eb4292770084fa8c863f79367ca19884b329ab574d5cb2036b3e54 \
--hash=sha256:ddf27071df4adf3821c4f2ca59d67525c3a82e5f268bed97b813cb4fabf87880
requests==2.28.1; python_version >= "3.7" and python_version < "4" and python_full_version < "4.0.0" \
--hash=sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349 \
--hash=sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983
six==1.16.0; 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.3.0" \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254 \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926
sqlparse==0.4.2; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:48719e356bb8b42991bdbb1e8b83223757b93789c00910a616a071910ca4a64d \
--hash=sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae
typing-extensions==4.2.0; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "3.8" \
--hash=sha256:6657594ee297170d19f67d55c05852a874e7eb634f4f753dbd667855e07c1708 \
--hash=sha256:f1c24655a0da0d1b67f07e17a5e6b2a105894e6824b92096378bb3668ef02376
urllib3==1.26.9; python_version >= "3.7" and python_full_version < "3.0.0" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14 \
--hash=sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e
typing-extensions==4.3.0; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "3.8" \
--hash=sha256:25642c956049920a5aa49edcdd6ab1e06d7e5d467fc00e0506c44ac86fbfca02 \
--hash=sha256:e6d2677a32f47fc7eb2795db1dd15c1f34eff616bcaf2cfb5e997f854fa1c4a6
urllib3==1.26.11; python_version >= "3.7" and python_full_version < "3.0.0" and python_version < "4" or python_full_version >= "3.6.0" and python_version < "4" and python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:c33ccba33c819596124764c23a97d25f32b28433ba0dedeb77d873a38722c9bc \
--hash=sha256:ea6e8fb210b19d950fab93b60c9009226c63a28808bc8386e05301e25883ac0a
webencodings==0.5.1; python_version >= "3.7" and python_full_version < "4.0.0" \
--hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \
--hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923
@ -194,6 +194,6 @@ wrapt==1.14.1; python_version >= "3.7" and python_full_version < "3.0.0" or pyth
--hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
--hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
--hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d
zipp==3.8.0; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "3.8" \
--hash=sha256:c4f6e5bbf48e74f7a38e7cc5b0480ff42b0ae5178957d564d18932525d5cf099 \
--hash=sha256:56bf8aadb83c24db6c4b577e13de374ccfb67da2078beba1d037c17980bf43ad
zipp==3.8.1; python_version >= "3.7" and python_full_version < "4.0.0" and python_version < "3.8" \
--hash=sha256:47c40d7fe183a6f21403a199b3e4192cca5774656965b0a4988ad2f8feb5f009 \
--hash=sha256:05b45f1ee8f807d0cc928485ca40a07cb491cf092ff587c0df9cb1fd154848d2

View file

@ -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,45 +11,77 @@
from pathlib import Path as __Path
from django.core.validators import EmailValidator as __EmailValidator
from django_yunohost_integration.base_settings import * # noqa
from django_yunohost_integration.secret_key import get_or_create_secret as __get_or_create_secret
from djfritz_project.settings.base import * # noqa
# -----------------------------------------------------------------------------
FINALPATH = __Path('__FINALPATH__') # /opt/yunohost/$app
assert FINALPATH.is_dir(), f'Directory not exists: {FINALPATH}'
FINAL_HOME_PATH = __Path('__FINAL_HOME_PATH__') # /opt/yunohost/$app
assert FINAL_HOME_PATH.is_dir(), f'Directory not exists: {FINAL_HOME_PATH}'
PUBLIC_PATH = __Path('__PUBLIC_PATH__') # /var/www/$app
assert PUBLIC_PATH.is_dir(), f'Directory not exists: {PUBLIC_PATH}'
FINAL_WWW_PATH = __Path('__FINAL_WWW_PATH__') # /var/www/$app
assert FINAL_WWW_PATH.is_dir(), f'Directory not exists: {FINAL_WWW_PATH}'
LOG_FILE = __Path('__LOG_FILE__') # /var/log/$app/django-fritzconnection.log
LOG_FILE = __Path('__LOG_FILE__') # /var/log/$app/django_example_ynh.log
assert LOG_FILE.is_file(), f'File not exists: {LOG_FILE}'
PATH_URL = '__PATH_URL__' # $YNH_APP_ARG_PATH
PATH_URL = PATH_URL.strip('/')
# -----------------------------------------------------------------------------
# config_panel.toml settings:
ROOT_URLCONF = 'urls' # /opt/yunohost/django-fritzconnection/ynh_urls.py
DEBUG_ENABLED = '__DEBUG_ENABLED__'
LOG_LEVEL = '__LOG_LEVEL__'
ADMIN_EMAIL = '__ADMIN_EMAIL__'
DEFAULT_FROM_EMAIL = '__DEFAULT_FROM_EMAIL__'
# -----------------------------------------------------------------------------
# Use/convert/validate config_panel.toml settings:
DEBUG = bool(int(DEBUG_ENABLED))
assert LOG_LEVEL in (
'DEBUG',
'INFO',
'WARNING',
'ERROR',
'CRITICAL',
), f'Invalid LOG_LEVEL: {LOG_LEVEL!r}'
__EmailValidator(
message='ADMIN_EMAIL %(value)r from config panel is not valid!',
)(ADMIN_EMAIL)
__EmailValidator(
message='DEFAULT_FROM_EMAIL %(value)r from config panel is not valid!',
)(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')
INSTALLED_APPS += [
'axes', # https://github.com/jazzband/django-axes
'django_yunohost_integration', # https://github.com/YunoHost-Apps/django_yunohost_integration
]
MIDDLEWARE.insert(
MIDDLEWARE.index('django.contrib.auth.middleware.AuthenticationMiddleware') + 1,
# login a user via HTTP_REMOTE_USER header from SSOwat:
'django_yunohost_integration.sso_auth.auth_middleware.SSOwatRemoteUserMiddleware',
)
# AxesMiddleware should be the last middleware:
MIDDLEWARE.append('axes.middleware.AxesMiddleware')
# Keep ModelBackend around for per-user permissions and superuser
AUTHENTICATION_BACKENDS = (
'axes.backends.AxesBackend', # AxesBackend should be the first backend!
#
# Authenticate via SSO and nginx 'HTTP_REMOTE_USER' header:
'django_yunohost_integration.sso_auth.auth_backend.SSOwatUserBackend',
#
@ -60,12 +92,13 @@ AUTHENTICATION_BACKENDS = (
LOGIN_REDIRECT_URL = None
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
@ -96,7 +129,6 @@ SERVER_EMAIL = 'noreply@__DOMAIN__'
# 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__']
@ -107,9 +139,6 @@ CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/__REDIS_DB__',
# If redis is running on same host as django-fritzconnection, you might
# want to use unix sockets instead:
# 'LOCATION': 'unix:///var/run/redis/redis.sock?db=1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
},
@ -128,8 +157,9 @@ 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')
# -----------------------------------------------------------------------------
@ -157,12 +187,24 @@ LOGGING = {
},
},
'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},
'djfritz': {'handlers': ['log_file', 'mail_admins'], 'level': 'INFO', 'propagate': False},
'': {'handlers': ['log_file', 'mail_admins'], 'level': LOG_LEVEL, 'propagate': False},
'django': {'handlers': ['log_file', 'mail_admins'], 'level': LOG_LEVEL, 'propagate': False},
'axes': {'handlers': ['log_file', 'mail_admins'], 'level': LOG_LEVEL, 'propagate': False},
'django_tools': {
'handlers': ['log_file', 'mail_admins'],
'level': LOG_LEVEL,
'propagate': False,
},
'django_yunohost_integration': {
'handlers': ['log_file', 'mail_admins'],
'level': LOG_LEVEL,
'propagate': False,
},
'djfritz': {
'handlers': ['log_file', 'mail_admins'],
'level': LOG_LEVEL,
'propagate': False,
},
},
}

View file

@ -1,5 +1,5 @@
[Unit]
Description=django-fritzconnection application server
Description=__APP__ server
After=redis.service postgresql.service
[Service]

View file

@ -1,21 +1,13 @@
from django.conf import settings
from django.conf.urls import static
from django.urls import include, path
# from django_yunohost_integration.views import request_media_debug_view
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/', request_media_debug_view),
path(f'{settings.PATH_URL}/', include('djfritz_project.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

View file

@ -3,18 +3,35 @@
version = "1.0"
[main]
name = "Settings"
[main.djfritz_config]
name = "django-fritzconnection configuration"
# Trigger a service reload-or-restart after the user change a question in this panel
name = "django_example_ynh configuration"
services = ["__APP__"]
[main.djfritz_config.django_debug]
ask = "debug mode"
help = "Important: Never activate this settings.DEBUG on production!"
[main.config]
name = "Configuration Options"
[main.config.default_from_email]
ask = "from email"
type = "string"
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 = "string"
help = "EMail address for error emails."
bind = "admin_email:__FINALPATH__/settings.py"
[main.config.debug_enabled]
ask = "DEBUG mode"
type = "boolean"
yes = "True"
no = "False"
bind = "DJANGO_DEBUG:/opt/yunohost/__APP__/local_settings.py"
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"

0
doc/screenshots/gitkeep Normal file
View file

View file

@ -22,6 +22,12 @@ def main():
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
destination=BASE_PATH / 'local_test',
runserver=True,
extra_replacements={
'__DEBUG_ENABLED__': '1',
'__LOG_LEVEL__': 'DEBUG',
'__ADMIN_EMAIL__': 'foo-bar@test.tld',
'__DEFAULT_FROM_EMAIL__': 'django_app@test.tld',
},
)

View file

@ -5,8 +5,8 @@
"description": {
"en": "Web based FritzBox management using Python/Django."
},
"version": "0.2.0~ynh1",
"url": "https://github.com/jedie/django-fritzconnection",
"version": "0.2.0~ynh2",
"url": "https://github.com/YunoHost-Apps/django-fritzconnection_ynh",
"upstream": {
"license": "GPL-3.0",
"website": "https://github.com/jedie/django-fritzconnection",
@ -23,7 +23,7 @@
},
"multi_instance": true,
"services": [
"nginx"
"nginx", "postgresql", "redis"
],
"arguments": {
"install" : [

580
poetry.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,6 @@
[tool.poetry]
name = "django-fritzconnection_ynh"
version = "0.2.0~ynh1"
version = "0.2.0~ynh2"
description = "Test django-fritzconnection_ynh via local_test.py"
authors = ["JensDiemer <git@jensdiemer.de>"]
license = "GPL"
@ -11,25 +11,20 @@ python = ">=3.7,<4.0.0"
django-fritzconnection = ">=0.2.0"
# Note: "ynh" extras will install gunicorn, psycopg2, django-redis and django-axes
django_yunohost_integration = {version = ">=v0.2.0", extras = ["ynh"]}
django_yunohost_integration = {version = ">=v0.3.0", extras = ["ynh"]}
psycopg2 = "*" # https://www.psycopg.org/docs/install.html#build-prerequisites
[tool.poetry.dev-dependencies]
bx_py_utils = "*"
bx_py_utils = "*" # https://github.com/boxine/bx_py_utils
tox = "*"
pytest = "*"
pytest-cov = "*"
pytest-django = "*"
pytest-darker = "*" # https://github.com/akaihola/pytest-darker
pytest-flake8 = "*"
pytest-isort = "*"
coveralls = "*"
isort = "*"
flake8 = "*"
flynt = "*"
darker = "*" # https://github.com/akaihola/darker
pyupgrade = "*"
[build-system]
requires = ["poetry-core>=1.0.0"]
@ -69,7 +64,7 @@ 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
@ -87,26 +82,28 @@ addopts = """
--no-cov-on-fail
--showlocals
--darker
--flake8
--isort
--doctest-modules
--failed-first
--last-failed-no-failures all
--new-first
"""
# TODO: --mypy
[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]
passenv = *
whitelist_externals = make
commands =
pytest djfritz djfritz_project
make pytest
"""

View file

@ -1,25 +0,0 @@
"""
Run pytest against local test creation
"""
from pathlib import Path
try:
from django_yunohost_integration.pytest_helper import run_pytest
except ImportError as err:
raise ImportError('Did you forget to activate a virtual environment?') from err
BASE_PATH = Path(__file__).parent
def main():
run_pytest(
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
destination=BASE_PATH / 'local_test',
)
if __name__ == '__main__':
main()

View file

@ -13,6 +13,22 @@ app=$YNH_APP_INSTANCE_NAME
# Currently not used: django-fritzconnection 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,10 +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}/django-fritzconnection.log"
# Default: settings.DEBUG=False
django_debug="False"
log_file="${log_path}/${app}.log"
#=================================================
# COMMON VARIABLES
@ -73,18 +86,3 @@ ynh_redis_remove_db() {
local db=$1
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
}

View file

@ -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
#=================================================
@ -112,29 +123,9 @@ fi
#=================================================
# MODIFY SETTINGS
#=================================================
ynh_script_progression --message="Modify django-fritzconnection's config file..."
ynh_script_progression --message="Modify django-fmd's 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

View file

@ -50,15 +50,22 @@ 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)
# Always deactivate settings.DEBUG:
ynh_app_setting_set --app=$app --key=django_debug --value="0"
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
@ -89,7 +96,8 @@ 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"
#=================================================
@ -101,9 +109,9 @@ ynh_script_progression --message="Configuring system user..."
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"
@ -114,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
@ -129,45 +142,17 @@ chown -R "$app:" "$final_path"
# ================================================
ynh_script_progression --message="Create project 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_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"
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="local_settings.py" --destination="$final_path/local_settings.py"
touch "$final_path/local_settings.py"
#=================================================
# MIGRATE / COLLECTSTATIC / CREATEADMIN
@ -183,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.
@ -225,13 +210,29 @@ chmod o-rwx "$final_path"
#=================================================
ynh_script_progression --message="Configuring a systemd service..."
# https://github.com/YunoHost/yunohost/blob/dev/data/helpers.d/systemd
ynh_add_systemd_config --service="$app" --template="django-fritzconnection.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 django-fritzconnection via systemd
# SETUP SSOWAT
#=================================================
ynh_script_progression --message="Starting django-fritzconnection's services..." --weight=5
ynh_script_progression --message="Configuring SSOwat..."
# Currently not used: django-fritzconnection has no public pages, yet!
#
## 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 the application..." --weight=5
ynh_systemd_action --service_name="$app" --action="start"

View file

@ -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 "
@ -52,8 +50,8 @@ ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf"
#=================================================
ynh_script_progression --message="Restoring the app main directory..."
ynh_restore_file --origin_path="$public_path"
ynh_restore_file --origin_path="$final_path"
ynh_restore_file --origin_path="$public_path"
#=================================================
# RECREATE THE DEDICATED USER
@ -81,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"
@ -93,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

View file

@ -26,10 +26,34 @@ 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)
django_debug=$(ynh_app_setting_get --app="$app" --key=django_debug)
#-------------------------------------------------
# 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
@ -54,20 +78,14 @@ ynh_script_progression --message="Stopping systemd services..." --weight=5
ynh_systemd_action --service_name="$app" --action="stop"
#=================================================
# Manage old settings
#=================================================
# Always deactivate settings.DEBUG:
ynh_app_setting_set --app=$app --key=django_debug --value="0"
#=================================================
# NGINX CONFIGURATION
#=================================================
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"
#=================================================
@ -92,12 +110,12 @@ ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell
#=================================================
ynh_script_progression --message="Configuring a systemd service..."
ynh_add_systemd_config --service="$app" --template="django-fritzconnection.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"
@ -108,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
@ -123,54 +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"
# Regenerate a fresh local_settings:
ynh_backup_if_checksum_is_different --file="$final_path/local_settings.py"
ynh_add_config --template="local_settings.py" --destination="$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
@ -186,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.
@ -224,9 +207,9 @@ chmod o-rwx "$public_path"
chmod o-rwx "$final_path"
#=================================================
# Start django-fritzconnection via systemd
# Start the app server via systemd
#=================================================
ynh_script_progression --message="Starting django-fritzconnection's services..." --weight=5
ynh_script_progression --message="Starting the application..." --weight=5
ynh_systemd_action --service_name="$app" --action="start"

View file

@ -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"

43
tests/conftest.py Normal file
View file

@ -0,0 +1,43 @@
"""
Special pytest init:
- Build a "local_test" YunoHost installation
- init Django with this local test installation
So the pytests will run against this local test installation
"""
import os
import sys
from pathlib import Path
import django
from django_yunohost_integration.local_test import create_local_test
BASE_PATH = Path(__file__).parent.parent
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
def pytest_configure():
print('Compile YunoHost files...')
final_path = create_local_test(
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
destination=BASE_PATH / 'local_test',
runserver=False,
extra_replacements={
'__DEBUG_ENABLED__': '0',
'__LOG_LEVEL__': 'INFO',
'__ADMIN_EMAIL__': 'foo-bar@test.tld',
'__DEFAULT_FROM_EMAIL__': 'django_app@test.tld',
},
)
print('Local test files created here:')
print(f'"{final_path}"')
os.chdir(final_path)
final_home_str = str(final_path)
if final_home_str not in sys.path:
sys.path.insert(0, final_home_str)
django.setup()

View file

@ -1,5 +1,6 @@
from pathlib import Path
from axes.models import AccessLog
from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin
from django.conf import LazySettings, settings
from django.contrib.auth.models import User
@ -32,12 +33,19 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
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_django-fritzconnection.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/admin/'
@ -86,13 +94,14 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
HTTP_REMOTE_USER='test',
HTTP_AUTH_USER='test',
HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
secure=True,
)
assert User.objects.count() == 1
user = User.objects.first()
assert user.username == 'test'
assert user.is_active is True
assert user.is_staff is True # Set by: django_yunohost_integration
assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert response.status_code == 200
@ -110,6 +119,7 @@ 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
self.client.cookies['SSOwAuthUser'] = 'test'
@ -118,20 +128,24 @@ 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
user = User.objects.first()
assert user.username == 'test'
assert user.is_active is True
assert user.is_staff is True # Set by: django_yunohost_integration
assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert AccessLog.objects.count() == 1
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
self.client.cookies['SSOwAuthUser'] = 'foobar' # <<< wrong user name
@ -140,15 +154,18 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
HTTP_REMOTE_USER='test',
HTTP_AUTH_USER='test',
HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
secure=True,
)
assert User.objects.count() == 1
user = User.objects.first()
assert user.username == 'test'
assert user.is_active is True
assert user.is_staff is True # Set by: django_yunohost_integration
assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert AccessLog.objects.count() == 1
assert response.status_code == 403 # Forbidden
@override_settings(SECURE_SSL_REDIRECT=False)
@ -162,15 +179,19 @@ class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
HTTP_REMOTE_USER='test',
HTTP_AUTH_USER='test',
HTTP_AUTHORIZATION=generate_basic_auth(
username='foobar', password='test123'
), # <<< wrong user name
username='foobar', # <<< wrong user name
password='test123',
),
secure=True,
)
assert User.objects.count() == 1
user = User.objects.first()
assert user.username == 'test'
assert user.is_active is True
assert user.is_staff is True # Set by: django_yunohost_integration
assert user.is_staff is True # Set by: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert AccessLog.objects.count() == 1
assert response.status_code == 403 # Forbidden

View file

@ -1,9 +1,11 @@
import difflib
import json
import os
import shutil
import subprocess
from pathlib import Path
import tomli
from bx_django_utils.filename import clean_filename
from bx_py_utils.path import assert_is_dir, assert_is_file
@ -13,31 +15,37 @@ import djfritz
PACKAGE_ROOT = Path(__file__).parent.parent
def assert_file_contains_string(file_path, string):
with file_path.open('r') as f:
for line in f:
if string in line:
return
raise AssertionError(f'File {file_path} does not contain {string!r} !')
def assert_is_ynh_version(version: str, package_version: str):
assert '~ynh' in version
assert version[0].isdigit()
assert version.startswith(package_version)
assert version[-1].isdigit()
def test_version():
version = djfritz.__version__
package_version = djfritz.__version__
assert package_version[0].isdigit()
assert '~ynh' not in package_version
assert_file_contains_string(
file_path=Path(PACKAGE_ROOT, 'pyproject.toml'), string=f'version = "{version}~ynh'
)
assert_file_contains_string(
file_path=Path(PACKAGE_ROOT, 'manifest.json'), string=f'"version": "{version}~ynh'
pyproject_toml_path = Path(PACKAGE_ROOT, 'pyproject.toml')
assert_is_file(pyproject_toml_path)
pyproject_toml = tomli.loads(pyproject_toml_path.read_text(encoding='UTF-8'))
assert_is_ynh_version(
version=pyproject_toml['tool']['poetry']['version'], package_version=package_version
)
manifest_json_path = Path(PACKAGE_ROOT, 'manifest.json')
assert_is_file(manifest_json_path)
manifest = json.loads(manifest_json_path.read_text(encoding='utf-8'))
assert_is_ynh_version(version=manifest['version'], package_version=package_version)
def poetry_check_output(*args):
poerty_bin = shutil.which('poetry')
output = subprocess.check_output(
(poerty_bin,) + args,
universal_newlines=True,
text=True,
env=os.environ,
stderr=subprocess.STDOUT,
cwd=str(PACKAGE_ROOT),
@ -52,7 +60,7 @@ def test_poetry_check():
def test_requirements_txt():
requirements_txt = Path('conf', 'requirements.txt')
requirements_txt = PACKAGE_ROOT / 'conf' / 'requirements.txt'
assert_is_file(requirements_txt)
output = poetry_check_output('export', '-f', 'requirements.txt')