1
0
Fork 0
mirror of https://github.com/YunoHost-Apps/scovie_ynh.git synced 2024-09-03 20:16:29 +02:00

first commit

This commit is contained in:
André Théo LAURET 2023-05-26 21:09:08 +04:00
commit 4b0275e7f7
42 changed files with 4266 additions and 0 deletions

20
.editorconfig Normal file
View file

@ -0,0 +1,20 @@
# see http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.py]
max_line_length = 100
[{Makefile,**.mk}]
indent_style = tab
insert_final_newline = false
[*.yml]
indent_style = tab

7
.flake8 Normal file
View file

@ -0,0 +1,7 @@
#
# Move to pyproject.toml after: https://gitlab.com/pycqa/flake8/-/issues/428
#
[flake8]
exclude = .pytest_cache, .tox, dist, htmlcov, local_test
ignore = F405
max-line-length = 119

55
.github/ISSUE_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,55 @@
---
name: Bug report
about: When creating a bug report, please use the following template to provide all the relevant information and help debugging efficiently.
---
**How to post a meaningful bug report**
1. *Read this whole template first.*
2. *Determine if you are on the right place:*
- *If you were performing an action on the app from the webadmin or the CLI (install, update, backup, restore, change_url...), you are on the right place!*
- *Otherwise, the issue may be due to the app itself. Refer to its documentation or repository for help.*
- *When in doubt, post here and we will figure it out together.*
3. *Delete the italic comments as you write over them below, and remove this guide.*
---
### Describe the bug
*A clear and concise description of what the bug is.*
### Context
- Hardware: *VPS bought online / Old laptop or computer / Raspberry Pi at home / Internet Cube with VPN / Other ARM board / ...*
- YunoHost version: x.x.x
- I have access to my server: *Through SSH | through the webadmin | direct access via keyboard / screen | ...*
- Are you in a special context or did you perform some particular tweaking on your YunoHost instance?: *no / yes*
- If yes, please explain:
- Using, or trying to install package version/branch:
- If upgrading, current package version: *can be found in the admin, or with `yunohost app info $app_id`*
### Steps to reproduce
- *If you performed a command from the CLI, the command itself is enough. For example:*
```sh
sudo yunohost app install the_app
```
- *If you used the webadmin, please perform the equivalent command from the CLI first.*
- *If the error occurs in your browser, explain what you did:*
1. *Go to '...'*
2. *Click on '...'*
3. *Scroll down to '...'*
4. *See error*
### Expected behavior
*A clear and concise description of what you expected to happen. You can remove this section if the command above is enough to understand your intent.*
### Logs
*When an operation fails, YunoHost provides a simple way to share the logs.*
- *In the webadmin, the error message contains a link to the relevant log page. On that page, you will be able to 'Share with Yunopaste'. If you missed it, the logs of previous operations are also available under Tools > Logs.*
- *In command line, the command to share the logs is displayed at the end of the operation and looks like `yunohost log display [log name] --share`. If you missed it, you can find the log ID of a previous operation using `yunohost log list`.*
*After sharing the log, please copypaste directly the link provided by YunoHost (to help readability, no need to copypaste the entire content of the log here, just the link is enough...)*
*If applicable and useful, add screenshots to help explain your problem.*

16
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View file

@ -0,0 +1,16 @@
## Problem
- *Description of why you made this PR*
## Solution
- *And how do you fix that problem*
## PR Status
- [ ] Code finished and ready to be reviewed/tested
- [ ] The fix/enhancement were manually tested (if applicable)
## Automatic tests
Automatic tests can be triggered on https://ci-apps-dev.yunohost.org/ *after creating the PR*, by commenting "!testme", "!gogogadgetoci" or "By the power of systemd, I invoke The Great App CI to test this Pull Request!". (N.B. : for this to work you need to be a member of the Yunohost-Apps organization)

23
.github/workflows/package_linter.yml vendored Normal file
View file

@ -0,0 +1,23 @@
name: YunoHost apps package linter
on:
push:
branches:
- main
pull_request:
schedule:
- cron: '0 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: 'Clone YunoHost apps package linter'
run: |
git clone --depth=1 https://github.com/YunoHost/package_linter ~/package_linter
- name: 'Run linter'
run: |
~/package_linter/package_linter.py .

56
.github/workflows/pytest.yml vendored Normal file
View file

@ -0,0 +1,56 @@
name: pytest
on:
push:
branches:
- master
pull_request:
schedule:
- cron: '0 8 * * *'
jobs:
test:
runs-on: ubuntu-latest
strategy:
max-parallel: 2
matrix:
python-version: ["3.10", "3.9"]
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
- name: 'fetch master'
run: |
git fetch origin master
- name: 'Set up Python ${{ matrix.python-version }}'
uses: actions/setup-python@v2
with:
python-version: '${{ matrix.python-version }}'
- uses: actions/cache@v2
with:
path: ~/.cache/
key: dot-cache-files
- name: 'Install package'
run: |
pip3 install poetry
make install
- name: 'List installed packages'
run: |
poetry run pip freeze
- name: 'Run tests with Python v${{ matrix.python-version }}'
run: |
make pytest
- name: 'Run Safety check'
run: |
make safety
- name: 'Upload coverage report'
uses: codecov/codecov-action@v2
with:
fail_ci_if_error: false
verbose: true

11
.gitignore vendored Normal file
View file

@ -0,0 +1,11 @@
.*
!.github
!.editorconfig
!.flake8
!.gitignore
!/doc/screenshots/.gitkeep
__pycache__
secret.txt
/local_test/
/coverage.xml
/htmlcov/

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 André Théo LAURET
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

59
Makefile Normal file
View file

@ -0,0 +1,59 @@
SHELL := /bin/bash
MAX_LINE_LENGTH := 100
all: help
help:
@awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z0-9 -]+:.*?## / {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST)
check-poetry:
@if [[ "$(shell poetry --version 2>/dev/null)" == *"Poetry"* ]] ; \
then \
echo "Poetry found, ok." ; \
else \
echo 'Please install poetry first, with e.g.:' ; \
echo 'make install-poetry' ; \
exit 1 ; \
fi
install-poetry: ## install or update poetry
curl -sSL https://install.python-poetry.org | python3 -
install: check-poetry ## install project via poetry
poetry install
update: check-poetry ## update the sources and installation and generate "conf/requirements.txt"
poetry self update
poetry update -v
poetry install
poetry export -f requirements.txt --output conf/requirements.txt
lint: ## Run code formatters and linter
poetry run isort --check-only .
poetry run flake8 .
fix-code-style: ## Fix code formatting
poetry run black --verbose --safe --line-length=${MAX_LINE_LENGTH} --skip-string-normalization .
poetry run isort .
tox-listenvs: check-poetry ## List all tox test environments
poetry run tox --listenvs
tox: check-poetry ## Run pytest via tox with all environments
poetry run tox
pytest: install ## Run pytest
poetry run pytest
local-test: install ## Run local_test.py to run the project locally
poetry run python3 ./local_test.py
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
##############################################################################
.PHONY: help check-poetry install-poetry install update local-test

208
README.md Normal file
View file

@ -0,0 +1,208 @@
<!--
N.B.: This README was automatically generated by https://github.com/YunoHost/apps/tree/master/tools/README-generator
It shall NOT be edited by hand.
-->
# Django Example for YunoHost
[![Integration level](https://dash.yunohost.org/integration/scovie_ynh.svg)](https://dash.yunohost.org/appci/app/scovie_ynh) ![Working status](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.status.svg) ![Maintenance status](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.maintain.svg)
[![Install Django Example with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=scovie_ynh)
*[Lire ce readme en français.](./README_fr.md)*
> *This package allows you to install Django Example quickly and simply on a YunoHost server.
If you don't have YunoHost, please consult [the guide](https://yunohost.org/#/install) to learn how to install it.*
## Overview
[![pytest](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/pytest.yml/badge.svg)](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/pytest.yml) [![YunoHost apps package linter](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/package_linter.yml/badge.svg)](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/package_linter.yml)
Demo [YunoHost Application](https://install-app.yunohost.org/?app=scovie_ynh) to demonstrate the integration of a [Python](https://www.python.org/)/[Django](https://www.djangoproject.com/) project under YunoHost using [django_yunohost_integration](https://github.com/YunoHost-Apps/django_yunohost_integration).
To demonstrate the functionality the small [django-example](https://github.com/jedie/django-example) app will be installed.
[![Integration level](https://dash.yunohost.org/integration/scovie_ynh.svg)](https://dash.yunohost.org/appci/app/scovie_ynh) ![](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.maintain.svg)
[![Install scovie_ynh with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=scovie_ynh)
Pull requests welcome ;)
**Shipped version:** 0.5.0rc1~ynh1
## Disclaimers / important information
## local test
For quicker developing of scovie_ynh in the context of YunoHost app,
it's possible to run the Django developer server with the settings
and urls made for YunoHost installation.
e.g.:
```bash
~$ git clone https://github.com/YunoHost-Apps/scovie_ynh.git
~$ cd scovie_ynh/
~/scovie_ynh$ make
install-poetry install or update poetry
install install project via poetry
update update the sources and installation and generate "conf/requirements.txt"
lint Run code formatters and linter
fix-code-style Fix code formatting
tox-listenvs List all tox test environments
tox Run pytest via tox with all environments
pytest Run pytest
publish Release new version to PyPi
local-test Run local_test.py to run the project locally
local-diff-settings Run "manage.py diffsettings" with local test
~/scovie_ynh$ make install-poetry
~/scovie_ynh$ make install
~/scovie_ynh$ make local-test
```
Notes:
* SQlite database will be used
* A super user with username `test` and password `test` is created
* The page is available under `http://127.0.0.1:8000/app_path/`
## history
* [compare v0.1.5...master](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.2.0...master) **dev**
* tbc
* [v0.2.0 - 15.09.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.5...v0.2.0)
* rename/split `scovie_ynh` into:
* [django_yunohost_integration](https://github.com/jedie/django_yunohost_integration) - Python package with the glue code to integrate a Django project with YunoHost
* [scovie_ynh](https://github.com/YunoHost-Apps/scovie_ynh) - Demo YunoHost App to demonstrate the integration of a Django project under YunoHost
* [v0.1.5 - 19.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.4...v0.1.5)
* Make some deps `gunicorn`, `psycopg2-binary`, `django-redis`, `django-axes` optional
* [v0.1.4 - 08.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.3...v0.1.4)
* Bugfix [CSRF verification failed on POST requests #7](https://github.com/YunoHost-Apps/scovie_ynh/issues/7)
* [v0.1.3 - 08.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.2...v0.1.3)
* set "DEBUG = True" in local_test (so static files are served and auth works)
* Bugfixes and cleanups
* [v0.1.2 - 29.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.1...v0.1.2)
* Bugfixes
* [v0.1.1 - 29.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.0...v0.1.1)
* Refactor "create_superuser" to a manage command, useable via "scovie_ynh" in `INSTALLED_APPS`
* Generate "conf/requirements.txt" and use this file for install
* rename own settings and urls (in `/conf/`)
* [v0.1.0 - 28.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/f578f14...v0.1.0)
* first working state
* [23.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/commit/f578f144a3a6d11d7044597c37d550d29c247773)
* init the project
## Links
* Report a bug about this package: https://github.com/YunoHost-Apps/scovie_ynh
* YunoHost website: https://yunohost.org/
* PyPi package: https://pypi.org/project/django-ynh/
These projects used `scovie_ynh`:
* https://github.com/YunoHost-Apps/scovie_ynh
* https://github.com/YunoHost-Apps/django-for-runners_ynh
---
# Developer info
The App project will be stored under `__FINALPATH__` (e.g.: `/opt/yunohost/$app`) that's Django's `settings.FINALPATH`
"static" / "media" files to serve via nginx are under `__PUBLIC_PATH__` (e.g.: `/var/www/$app`) that's `settings.PUBLIC_PATH`
## package installation / debugging
This app is not in YunoHost app catalog. Test install, e.g.:
```bash
~# git clone https://github.com/YunoHost-Apps/scovie_ynh.git
~# yunohost app install scovie_ynh/ -f
```
To update:
```bash
~# cd scovie_ynh
~/scovie_ynh# git fetch && git reset --hard origin/testing
~/scovie_ynh# yunohost app upgrade scovie_ynh -u . -F
```
To remove call e.g.:
```bash
sudo yunohost app remove scovie_ynh
```
Backup / remove / restore cycle, e.g.:
```bash
yunohost backup create --apps scovie_ynh
yunohost backup list
archives:
- scovie_ynh-pre-upgrade1
- 20201223-163434
yunohost app remove scovie_ynh
yunohost backup restore 20201223-163434 --apps scovie_ynh
```
Debug the installation, e.g.:
```bash
root@yunohost:~# cat /etc/yunohost/apps/scovie_ynh/settings.yml
...
root@yunohost:~# ls -la /var/www/scovie_ynh/
total 18
drwxr-xr-x 4 root root 4 Dec 8 08:36 .
drwxr-xr-x 6 root root 6 Dec 8 08:36 ..
drwxr-xr-x 2 root root 2 Dec 8 08:36 media
drwxr-xr-x 7 root root 8 Dec 8 08:40 static
root@yunohost:~# ls -la /opt/yunohost/scovie_ynh/
total 58
drwxr-xr-x 5 scovie_ynh scovie_ynh 11 Dec 8 08:39 .
drwxr-xr-x 3 root root 3 Dec 8 08:36 ..
-rw-r--r-- 1 scovie_ynh scovie_ynh 460 Dec 8 08:39 gunicorn.conf.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 0 Dec 8 08:39 local_settings.py
-rwxr-xr-x 1 scovie_ynh scovie_ynh 274 Dec 8 08:39 manage.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 171 Dec 8 08:39 secret.txt
drwxr-xr-x 6 scovie_ynh scovie_ynh 6 Dec 8 08:37 venv
-rw-r--r-- 1 scovie_ynh scovie_ynh 115 Dec 8 08:39 wsgi.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 4737 Dec 8 08:39 scovie_ynh_demo_settings.py
root@yunohost:~# cd /opt/yunohost/scovie_ynh/
root@yunohost:/opt/yunohost/scovie_ynh# source venv/bin/activate
(venv) root@yunohost:/opt/yunohost/scovie_ynh# ./manage.py check
scovie_ynh v0.8.2 (Django v2.2.17)
DJANGO_SETTINGS_MODULE='scovie_ynh_demo_settings'
PROJECT_PATH:/opt/yunohost/scovie_ynh/venv/lib/python3.7/site-packages
BASE_PATH:/opt/yunohost/scovie_ynh
System check identified no issues (0 silenced).
root@yunohost:~# tail -f /var/log/scovie_ynh/scovie_ynh.log
root@yunohost:~# cat /etc/systemd/system/systemd.service
...
root@yunohost:~# systemctl reload-or-restart scovie_ynh
root@yunohost:~# journalctl --unit=scovie_ynh --follow
```
## Documentation and resources
* Official app website: <https://github.com/YunoHost-Apps/scovie_ynh>
* Official user documentation: <https://github.com/YunoHost-Apps/scovie_ynh>
* Official admin documentation: <https://github.com/YunoHost-Apps/scovie_ynh>
* Upstream app code repository: <https://github.com/YunoHost-Apps/scovie_ynh>
* YunoHost documentation for this app: <https://yunohost.org/app_scovie_ynh>
* Report a bug: <https://github.com/YunoHost-Apps/scovie_ynh_ynh/issues>
## Developer info
Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/scovie_ynh_ynh/tree/testing).
To try the testing branch, please proceed like that.
``` bash
sudo yunohost app install https://github.com/YunoHost-Apps/scovie_ynh_ynh/tree/testing --debug
or
sudo yunohost app upgrade scovie_ynh -u https://github.com/YunoHost-Apps/scovie_ynh_ynh/tree/testing --debug
```
**More info regarding app packaging:** <https://yunohost.org/packaging_apps>

33
check_process Normal file
View file

@ -0,0 +1,33 @@
# See here for more information
# https://github.com/YunoHost/package_check#syntax-check_process-file
# Move this file from check_process.default to check_process when you have filled it.
;; Test complet
; Manifest
domain="domain.tld" (DOMAIN)
path="/path" (PATH)
admin="john" (USER)
is_public=1 (PUBLIC|public=1|private=0)
password="pass"
port="666" (PORT)
; Checks
pkg_linter=1
setup_sub_dir=1
setup_root=1
setup_nourl=0
setup_private=0
setup_public=1
upgrade=1
backup_restore=1
multi_instance=1
port_already_use=0
change_url=1
;;; Options
Email=
Notification=none
;;; Upgrade options
; commit=CommitHash
name=Name and date of the commit.
manifest_arg=domain=DOMAIN&path=PATH&admin=USER&language=fr&is_public=1&password=pass&port=666&

20
conf/gunicorn.conf.py Normal file
View file

@ -0,0 +1,20 @@
"""
Configuration for Gunicorn
"""
import multiprocessing
bind = '127.0.0.1:__PORT__'
# https://docs.gunicorn.org/en/latest/settings.html#workers
workers = multiprocessing.cpu_count() * 2 + 1
# https://docs.gunicorn.org/en/latest/settings.html#logging
loglevel = 'info'
# https://docs.gunicorn.org/en/latest/settings.html#logging
accesslog = '__LOG_FILE__'
errorlog = '__LOG_FILE__'
# https://docs.gunicorn.org/en/latest/settings.html#pidfile
pidfile = '__FINALPATH__/gunicorn.pid'

15
conf/manage.py Normal file
View file

@ -0,0 +1,15 @@
#!__FINALPATH__/venv/bin/python
import os
import sys
def main():
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()

28
conf/nginx.conf Normal file
View file

@ -0,0 +1,28 @@
location __PATH__/static/ {
# Service static files by nginx
# e.g.: /var/www/$app/static
alias __PUBLIC_PATH__/static/;
expires 30d;
}
location __PATH__/ {
# https://github.com/benoitc/gunicorn/blob/master/examples/nginx.conf
# this is needed if you have file import via upload enabled
client_max_body_size 100M;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_read_timeout 30;
proxy_send_timeout 30;
proxy_connect_timeout 30;
proxy_redirect off;
proxy_pass http://127.0.0.1:__PORT__;
}

157
conf/requirements.txt Normal file
View file

@ -0,0 +1,157 @@
asgiref==3.5.2 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:1d2880b792ae8757289136f1db2b7b99100ce959b2aa57fd69dab783d05afac4 \
--hash=sha256:4a29362a6acebe09bf1d6640db38c1dc3d9217c68e6f9f6204d72667fc19a424
async-timeout==4.0.2 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:2163e1640ddb52b7a8c80d0a67a08587e5d245cc9c553a74a847056bc2976b15 \
--hash=sha256:8ca1e4fcf50d07413d66d1a5e416e42cfdf5851c981d679a09851a6853383b3c
bleach==5.0.1 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:085f7f33c15bd408dd9b17a4ad77c577db66d76203e5984b1bd59baeee948b2a \
--hash=sha256:0d03255c47eb9bd2f26aa9bb7f2107732e7e8fe195ca2f64709fcf3b0a4a085c
bx-django-utils==36 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:98f900da91e3cdb22d2f386863fe05e58cfc18ff2d7c0ee656e7d551a135f529 \
--hash=sha256:badb8d7fb04ce449cac7896ee435e69d2786eebc3e8c756e99e9379be816cd5f
bx-py-utils==69 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:728fd575c4d5048e114b502a97d19679f9abcda90889a6896534c48348320460 \
--hash=sha256:b25419e020c9c5ea16938a45cf5120086a5ac29648be78a8eb98ae202515fee1
colorama==0.4.5 ; python_version >= "3.9" and python_full_version < "4.0.0" and sys_platform == "win32" \
--hash=sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da \
--hash=sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4
colorlog==6.7.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:0d33ca236784a1ba3ff9c532d4964126d8a2c44f1f0cb1d2b0728196f512f662 \
--hash=sha256:bd94bd21c1e13fac7bd3153f4bc3a7dc0eb0974b8bc2fdf1a989e474f6e582e5
deprecated==1.2.13 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d \
--hash=sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d
django-axes==5.39.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:8f039f8e98f050f13f654efca599d8a04d0b57d330c590cf89ec2bf731c9a7fb \
--hash=sha256:97702552f7939c81db5bba2ef855ae43f20df92fa261cb79fd4c8633ba3b3955
django-example==0.1.0rc0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:9bf31725f539d6c3489fd29a743f496fce1080164f5a62b87a6af2be04ca81c0 \
--hash=sha256:de4460c2175506dcb528ac4f98df8c436c2d102f8d08c77b766cf406038eef53
django-ipware==4.0.2 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:602a58325a4808bd19197fef2676a0b2da2df40d0ecf21be414b2ff48c72ad05 \
--hash=sha256:878dbb06a87e25550798e9ef3204ed70a200dd8b15e47dcef848cf08244f04c9
django-redis==5.2.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:1d037dc02b11ad7aa11f655d26dac3fb1af32630f61ef4428860a2e29ff92026 \
--hash=sha256:8a99e5582c79f894168f5865c52bd921213253b7fd64d16733ae4591564465de
django-tools==0.54.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:5040a91282be9d1c9d379b0c65da50bcb3691bff03cee54fd4123ace238c3a43 \
--hash=sha256:a7b7bfa5b9c5a81966454d17dffb2403cee25a806c858ee0486a08798227598f
django-yunohost-integration[ynh]==0.5.0rc1 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:0e6d8ec12d48d9897c9dc02fc60c35bb3c7cc5f9446961fc0be61bb6f5586197 \
--hash=sha256:5d8823acb83a668a5126e1324d2c3c7b239595a77a03d8c9bdaa8446154c64e4
django==4.1.2 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:26dc24f99c8956374a054bcbf58aab8dc0cad2e6ac82b0fe036b752c00eee793 \
--hash=sha256:b8d843714810ab88d59344507d4447be8b2cf12a49031363b6eed9f1b9b2280f
gunicorn==20.1.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e \
--hash=sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8
icdiff==2.0.5 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:35d24b728e48b7e0a12bdb69386d3bfc7eef4fe922d0ac1cd70d6e5c11630bae
packaging==21.3 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb \
--hash=sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522
pprintpp==0.4.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:b6b4dcdd0c0c0d75e4d7b2f21a9e933e5b2ce62b26e1a54537f9651ae5a5c01d \
--hash=sha256:ea826108e2c7f49dc6d66c752973c3fc9749142a798d6b254e1e301cfdbc6403
psycopg2==2.9.3 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:06f32425949bd5fe8f625c49f17ebb9784e1e4fe928b7cce72edc36fb68e4c0c \
--hash=sha256:0762c27d018edbcb2d34d51596e4346c983bd27c330218c56c4dc25ef7e819bf \
--hash=sha256:083707a696e5e1c330af2508d8fab36f9700b26621ccbcb538abe22e15485362 \
--hash=sha256:34b33e0162cfcaad151f249c2649fd1030010c16f4bbc40a604c1cb77173dcf7 \
--hash=sha256:4295093a6ae3434d33ec6baab4ca5512a5082cc43c0505293087b8a46d108461 \
--hash=sha256:8cf3878353cc04b053822896bc4922b194792df9df2f1ad8da01fb3043602126 \
--hash=sha256:8e841d1bf3434da985cc5ef13e6f75c8981ced601fd70cc6bf33351b91562981 \
--hash=sha256:9572e08b50aed176ef6d66f15a21d823bb6f6d23152d35e8451d7d2d18fdac56 \
--hash=sha256:a81e3866f99382dfe8c15a151f1ca5fde5815fde879348fe5a9884a7c092a305 \
--hash=sha256:cb10d44e6694d763fa1078a26f7f6137d69f555a78ec85dc2ef716c37447e4b2 \
--hash=sha256:d3ca6421b942f60c008f81a3541e8faf6865a28d5a9b48544b0ee4f40cac7fca
pyparsing==3.0.9 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb \
--hash=sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc
python-stdnum==1.17 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:374e2b5e13912ccdbf50b0b23fca2c3e0531174805c32d74e145f37756328340 \
--hash=sha256:a46e6cf9652807314d369b654b255c86a59f93d18be2834f3d567ed1a346c547
redis==4.3.4 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:a52d5694c9eb4292770084fa8c863f79367ca19884b329ab574d5cb2036b3e54 \
--hash=sha256:ddf27071df4adf3821c4f2ca59d67525c3a82e5f268bed97b813cb4fabf87880
setuptools==65.4.1 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012 \
--hash=sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e
six==1.16.0 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926 \
--hash=sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254
sqlparse==0.4.3 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:0323c0ec29cd52bceabc1b4d9d579e311f3e4961b98d174201d5622a23b85e34 \
--hash=sha256:69ca804846bb114d2ec380e4360a8a340db83f0ccf3afceeb1404df028f57268
tzdata==2022.4 ; python_version >= "3.9" and python_full_version < "4.0.0" and sys_platform == "win32" \
--hash=sha256:74da81ecf2b3887c94e53fc1d466d4362aaf8b26fc87cda18f22004544694583 \
--hash=sha256:ada9133fbd561e6ec3d1674d3fba50251636e918aa97bd59d63735bef5a513bb
webencodings==0.5.1 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78 \
--hash=sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923
wrapt==1.14.1 ; python_version >= "3.9" and python_full_version < "4.0.0" \
--hash=sha256:00b6d4ea20a906c0ca56d84f93065b398ab74b927a7a3dbd470f6fc503f95dc3 \
--hash=sha256:01c205616a89d09827986bc4e859bcabd64f5a0662a7fe95e0d359424e0e071b \
--hash=sha256:02b41b633c6261feff8ddd8d11c711df6842aba629fdd3da10249a53211a72c4 \
--hash=sha256:07f7a7d0f388028b2df1d916e94bbb40624c59b48ecc6cbc232546706fac74c2 \
--hash=sha256:11871514607b15cfeb87c547a49bca19fde402f32e2b1c24a632506c0a756656 \
--hash=sha256:1b376b3f4896e7930f1f772ac4b064ac12598d1c38d04907e696cc4d794b43d3 \
--hash=sha256:21ac0156c4b089b330b7666db40feee30a5d52634cc4560e1905d6529a3897ff \
--hash=sha256:257fd78c513e0fb5cdbe058c27a0624c9884e735bbd131935fd49e9fe719d310 \
--hash=sha256:2b39d38039a1fdad98c87279b48bc5dce2c0ca0d73483b12cb72aa9609278e8a \
--hash=sha256:2cf71233a0ed05ccdabe209c606fe0bac7379fdcf687f39b944420d2a09fdb57 \
--hash=sha256:2fe803deacd09a233e4762a1adcea5db5d31e6be577a43352936179d14d90069 \
--hash=sha256:3232822c7d98d23895ccc443bbdf57c7412c5a65996c30442ebe6ed3df335383 \
--hash=sha256:34aa51c45f28ba7f12accd624225e2b1e5a3a45206aa191f6f9aac931d9d56fe \
--hash=sha256:36f582d0c6bc99d5f39cd3ac2a9062e57f3cf606ade29a0a0d6b323462f4dd87 \
--hash=sha256:380a85cf89e0e69b7cfbe2ea9f765f004ff419f34194018a6827ac0e3edfed4d \
--hash=sha256:40e7bc81c9e2b2734ea4bc1aceb8a8f0ceaac7c5299bc5d69e37c44d9081d43b \
--hash=sha256:43ca3bbbe97af00f49efb06e352eae40434ca9d915906f77def219b88e85d907 \
--hash=sha256:4fcc4649dc762cddacd193e6b55bc02edca674067f5f98166d7713b193932b7f \
--hash=sha256:5a0f54ce2c092aaf439813735584b9537cad479575a09892b8352fea5e988dc0 \
--hash=sha256:5a9a0d155deafd9448baff28c08e150d9b24ff010e899311ddd63c45c2445e28 \
--hash=sha256:5b02d65b9ccf0ef6c34cba6cf5bf2aab1bb2f49c6090bafeecc9cd81ad4ea1c1 \
--hash=sha256:60db23fa423575eeb65ea430cee741acb7c26a1365d103f7b0f6ec412b893853 \
--hash=sha256:642c2e7a804fcf18c222e1060df25fc210b9c58db7c91416fb055897fc27e8cc \
--hash=sha256:6a9a25751acb379b466ff6be78a315e2b439d4c94c1e99cb7266d40a537995d3 \
--hash=sha256:6b1a564e6cb69922c7fe3a678b9f9a3c54e72b469875aa8018f18b4d1dd1adf3 \
--hash=sha256:6d323e1554b3d22cfc03cd3243b5bb815a51f5249fdcbb86fda4bf62bab9e164 \
--hash=sha256:6e743de5e9c3d1b7185870f480587b75b1cb604832e380d64f9504a0535912d1 \
--hash=sha256:709fe01086a55cf79d20f741f39325018f4df051ef39fe921b1ebe780a66184c \
--hash=sha256:7b7c050ae976e286906dd3f26009e117eb000fb2cf3533398c5ad9ccc86867b1 \
--hash=sha256:7d2872609603cb35ca513d7404a94d6d608fc13211563571117046c9d2bcc3d7 \
--hash=sha256:7ef58fb89674095bfc57c4069e95d7a31cfdc0939e2a579882ac7d55aadfd2a1 \
--hash=sha256:80bb5c256f1415f747011dc3604b59bc1f91c6e7150bd7db03b19170ee06b320 \
--hash=sha256:81b19725065dcb43df02b37e03278c011a09e49757287dca60c5aecdd5a0b8ed \
--hash=sha256:833b58d5d0b7e5b9832869f039203389ac7cbf01765639c7309fd50ef619e0b1 \
--hash=sha256:88bd7b6bd70a5b6803c1abf6bca012f7ed963e58c68d76ee20b9d751c74a3248 \
--hash=sha256:8ad85f7f4e20964db4daadcab70b47ab05c7c1cf2a7c1e51087bfaa83831854c \
--hash=sha256:8c0ce1e99116d5ab21355d8ebe53d9460366704ea38ae4d9f6933188f327b456 \
--hash=sha256:8d649d616e5c6a678b26d15ece345354f7c2286acd6db868e65fcc5ff7c24a77 \
--hash=sha256:903500616422a40a98a5a3c4ff4ed9d0066f3b4c951fa286018ecdf0750194ef \
--hash=sha256:9736af4641846491aedb3c3f56b9bc5568d92b0692303b5a305301a95dfd38b1 \
--hash=sha256:988635d122aaf2bdcef9e795435662bcd65b02f4f4c1ae37fbee7401c440b3a7 \
--hash=sha256:9cca3c2cdadb362116235fdbd411735de4328c61425b0aa9f872fd76d02c4e86 \
--hash=sha256:9e0fd32e0148dd5dea6af5fee42beb949098564cc23211a88d799e434255a1f4 \
--hash=sha256:9f3e6f9e05148ff90002b884fbc2a86bd303ae847e472f44ecc06c2cd2fcdb2d \
--hash=sha256:a85d2b46be66a71bedde836d9e41859879cc54a2a04fad1191eb50c2066f6e9d \
--hash=sha256:a9a52172be0b5aae932bef82a79ec0a0ce87288c7d132946d645eba03f0ad8a8 \
--hash=sha256:aa31fdcc33fef9eb2552cbcbfee7773d5a6792c137b359e82879c101e98584c5 \
--hash=sha256:b014c23646a467558be7da3d6b9fa409b2c567d2110599b7cf9a0c5992b3b471 \
--hash=sha256:b21bb4c09ffabfa0e85e3a6b623e19b80e7acd709b9f91452b8297ace2a8ab00 \
--hash=sha256:b5901a312f4d14c59918c221323068fad0540e34324925c8475263841dbdfe68 \
--hash=sha256:b9b7a708dd92306328117d8c4b62e2194d00c365f18eff11a9b53c6f923b01e3 \
--hash=sha256:d1967f46ea8f2db647c786e78d8cc7e4313dbd1b0aca360592d8027b8508e24d \
--hash=sha256:d52a25136894c63de15a35bc0bdc5adb4b0e173b9c0d07a2be9d3ca64a332735 \
--hash=sha256:d77c85fedff92cf788face9bfa3ebaa364448ebb1d765302e9af11bf449ca36d \
--hash=sha256:d79d7d5dc8a32b7093e81e97dad755127ff77bcc899e845f41bf71747af0c569 \
--hash=sha256:dbcda74c67263139358f4d188ae5faae95c30929281bc6866d00573783c422b7 \
--hash=sha256:ddaea91abf8b0d13443f6dac52e89051a5063c7d014710dcb4d4abb2ff811a59 \
--hash=sha256:dee0ce50c6a2dd9056c20db781e9c1cfd33e77d2d569f5d1d9321c641bb903d5 \
--hash=sha256:dee60e1de1898bde3b238f18340eec6148986da0455d8ba7848d50470a7a32fb \
--hash=sha256:e2f83e18fe2f4c9e7db597e988f72712c0c3676d337d8b101f6758107c42425b \
--hash=sha256:e3fb1677c720409d5f671e39bac6c9e0e422584e5f518bfd50aa4cbbea02433f \
--hash=sha256:ee2b1b1769f6707a8a445162ea16dddf74285c3964f605877a20e38545c3c462 \
--hash=sha256:ee6acae74a2b91865910eef5e7de37dc6895ad96fa23603d1d27ea69df545015 \
--hash=sha256:ef3f72c9666bba2bab70d2a8b79f2c6d2c1a42a7f7e2b0ec83bb2f9e383950af

174
conf/settings.py Normal file
View file

@ -0,0 +1,174 @@
################################################################################
################################################################################
# Please do not modify this file, it will be reset at the next update.
# 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.
################################################################################
################################################################################
from pathlib import Path as __Path
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
# https://github.com/jedie/django-example/
from django_example.settings.prod import * # noqa:F401,F403 isort:skip
from django_yunohost_integration.base_settings import LOGGING # noqa:F401 isort:skip
FINALPATH = __Path('__FINALPATH__') # /opt/yunohost/$app
assert FINALPATH.is_dir(), f'Directory not exists: {FINALPATH}'
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/scovie_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('/')
YNH_CURRENT_HOST = '__YNH_CURRENT_HOST__' # YunoHost main domain from: /etc/yunohost/current_host
# -----------------------------------------------------------------------------
# config_panel.toml settings:
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(FINALPATH / 'secret.txt') # /opt/yunohost/$app/secret.txt
INSTALLED_APPS += [
'axes', # https://github.com/jazzband/django-axes
'django_yunohost_integration.apps.YunohostIntegrationConfig',
]
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',
#
# Fallback to normal Django model backend:
'django.contrib.auth.backends.ModelBackend',
)
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__', ADMIN_EMAIL),)
MANAGERS = ADMINS
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': '__DB_NAME__',
'USER': '__DB_USER__',
'PASSWORD': '__DB_PWD__',
'HOST': '127.0.0.1',
'PORT': '5432', # Default Postgres Port
'CONN_MAX_AGE': 600,
}
}
# Title of site to use
SITE_TITLE = '__APP__'
# Site domain
SITE_DOMAIN = '__DOMAIN__'
# Subject of emails includes site title
EMAIL_SUBJECT_PREFIX = f'[{SITE_TITLE}] '
# E-mail address that error messages come from.
SERVER_EMAIL = ADMIN_EMAIL
# Default email address to use for various automated correspondence from
# the site managers. Used for registration emails.
# List of URLs your site is supposed to serve
ALLOWED_HOSTS = ['__DOMAIN__']
# _____________________________________________________________________________
# Configuration for caching
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/__REDIS_DB__',
# If redis is running on same host as PyInventory, you might
# want to use unix sockets instead:
# 'LOCATION': 'unix:///var/run/redis/redis.sock?db=1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
},
'KEY_PREFIX': '__APP__',
},
}
# _____________________________________________________________________________
# Static files (CSS, JavaScript, Images)
if PATH_URL:
STATIC_URL = f'/{PATH_URL}/static/'
MEDIA_URL = f'/{PATH_URL}/media/'
else:
# Installed to domain root, without a path prefix?
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
STATIC_ROOT = str(PUBLIC_PATH / 'static')
MEDIA_ROOT = str(PUBLIC_PATH / 'media')
# -----------------------------------------------------------------------------
# Set log file to e.g.: /var/log/$app/$app.log
LOGGING['handlers']['log_file']['filename'] = str(LOG_FILE)
# Example how to add logging to own app:
LOGGING['loggers']['django_example'] = {
'handlers': ['syslog', 'log_file', 'mail_admins'],
'level': 'INFO',
'propagate': False,
}
# -----------------------------------------------------------------------------
try:
from local_settings import * # noqa:F401,F403
except ImportError:
pass

8
conf/setup_user.py Normal file
View file

@ -0,0 +1,8 @@
def setup_project_user(user):
"""
All users used the Django admin, so we need to set the "staff" user flag.
Called from django_yunohost_integration.sso_auth
"""
user.is_staff = True
user.save()
return user

17
conf/systemd.service Normal file
View file

@ -0,0 +1,17 @@
[Unit]
Description=__APP__ server
After=redis.service postgresql.service
[Service]
User=__APP__
Group=__APP__
WorkingDirectory=__FINALPATH__/
ExecStart=__FINALPATH__/venv/bin/gunicorn --config __FINALPATH__/gunicorn.conf.py wsgi
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=__APP__-server
[Install]
WantedBy=multi-user.target

29
conf/urls.py Normal file
View file

@ -0,0 +1,29 @@
"""
urls.py
~~~~~~~
Look at real examples, here:
* https://github.com/YunoHost-Apps/django-fritzconnection_ynh/blob/master/conf/urls.py
* https://github.com/YunoHost-Apps/django-for-runners_ynh/blob/testing/conf/urls.py
* https://github.com/YunoHost-Apps/pyinventory_ynh/blob/testing/conf/urls.py
"""
from django.conf import settings
from django.urls import include, path
from django.views.generic import RedirectView
if settings.PATH_URL:
# settings.PATH_URL is the $YNH_APP_ARG_PATH
# Prefix all urls with "PATH_URL":
urlpatterns = [
path('', RedirectView.as_view(url=f'{settings.PATH_URL}/')),
path(f'{settings.PATH_URL}/', include('django_example.urls')),
]
else:
# Installed to domain root, without a path prefix
# Just use the default project urls.py
from django_example.urls import urlpatterns # noqa

12
conf/wsgi.py Normal file
View file

@ -0,0 +1,12 @@
"""
WSGI config
"""
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from django.core.wsgi import get_wsgi_application # noqa
application = get_wsgi_application()

38
config_panel.toml Normal file
View file

@ -0,0 +1,38 @@
# https://github.com/YunoHost/example_ynh/blob/master/config_panel.toml.example
version = "1.0"
[main]
name.en = "Main configuration"
name.fr = "Configuration principale"
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"

11
doc/DESCRIPTION.md Normal file
View file

@ -0,0 +1,11 @@
[![pytest](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/pytest.yml/badge.svg)](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/pytest.yml) [![YunoHost apps package linter](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/package_linter.yml/badge.svg)](https://github.com/YunoHost-Apps/scovie_ynh/actions/workflows/package_linter.yml)
Demo [YunoHost Application](https://install-app.yunohost.org/?app=scovie_ynh) to demonstrate the integration of a [Python](https://www.python.org/)/[Django](https://www.djangoproject.com/) project under YunoHost using [django_yunohost_integration](https://github.com/YunoHost-Apps/django_yunohost_integration).
To demonstrate the functionality the small [django-example](https://github.com/jedie/django-example) app will be installed.
[![Integration level](https://dash.yunohost.org/integration/scovie_ynh.svg)](https://dash.yunohost.org/appci/app/scovie_ynh) ![](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/scovie_ynh.maintain.svg)
[![Install scovie_ynh with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=scovie_ynh)
Pull requests welcome ;)

152
doc/DISCLAIMER.md Normal file
View file

@ -0,0 +1,152 @@
## local test
For quicker developing of scovie_ynh in the context of YunoHost app,
it's possible to run the Django developer server with the settings
and urls made for YunoHost installation.
e.g.:
```bash
~$ git clone https://github.com/YunoHost-Apps/scovie_ynh.git
~$ cd scovie_ynh/
~/scovie_ynh$ make
install-poetry install or update poetry
install install project via poetry
update update the sources and installation and generate "conf/requirements.txt"
lint Run code formatters and linter
fix-code-style Fix code formatting
tox-listenvs List all tox test environments
tox Run pytest via tox with all environments
pytest Run pytest
publish Release new version to PyPi
local-test Run local_test.py to run the project locally
local-diff-settings Run "manage.py diffsettings" with local test
~/scovie_ynh$ make install-poetry
~/scovie_ynh$ make install
~/scovie_ynh$ make local-test
```
Notes:
* SQlite database will be used
* A super user with username `test` and password `test` is created
* The page is available under `http://127.0.0.1:8000/app_path/`
## history
* [compare v0.1.5...master](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.2.0...master) **dev**
* tbc
* [v0.2.0 - 15.09.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.5...v0.2.0)
* rename/split `scovie_ynh` into:
* [django_yunohost_integration](https://github.com/jedie/django_yunohost_integration) - Python package with the glue code to integrate a Django project with YunoHost
* [scovie_ynh](https://github.com/YunoHost-Apps/scovie_ynh) - Demo YunoHost App to demonstrate the integration of a Django project under YunoHost
* [v0.1.5 - 19.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.4...v0.1.5)
* Make some deps `gunicorn`, `psycopg2-binary`, `django-redis`, `django-axes` optional
* [v0.1.4 - 08.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.3...v0.1.4)
* Bugfix [CSRF verification failed on POST requests #7](https://github.com/YunoHost-Apps/scovie_ynh/issues/7)
* [v0.1.3 - 08.01.2021](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.2...v0.1.3)
* set "DEBUG = True" in local_test (so static files are served and auth works)
* Bugfixes and cleanups
* [v0.1.2 - 29.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.1...v0.1.2)
* Bugfixes
* [v0.1.1 - 29.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/v0.1.0...v0.1.1)
* Refactor "create_superuser" to a manage command, useable via "scovie_ynh" in `INSTALLED_APPS`
* Generate "conf/requirements.txt" and use this file for install
* rename own settings and urls (in `/conf/`)
* [v0.1.0 - 28.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/compare/f578f14...v0.1.0)
* first working state
* [23.12.2020](https://github.com/YunoHost-Apps/scovie_ynh/commit/f578f144a3a6d11d7044597c37d550d29c247773)
* init the project
## Links
* Report a bug about this package: https://github.com/YunoHost-Apps/scovie_ynh
* YunoHost website: https://yunohost.org/
* PyPi package: https://pypi.org/project/django-ynh/
These projects used `scovie_ynh`:
* https://github.com/YunoHost-Apps/scovie_ynh
* https://github.com/YunoHost-Apps/django-for-runners_ynh
---
# Developer info
The App project will be stored under `__FINALPATH__` (e.g.: `/opt/yunohost/$app`) that's Django's `settings.FINALPATH`
"static" / "media" files to serve via nginx are under `__PUBLIC_PATH__` (e.g.: `/var/www/$app`) that's `settings.PUBLIC_PATH`
## package installation / debugging
This app is not in YunoHost app catalog. Test install, e.g.:
```bash
~# git clone https://github.com/YunoHost-Apps/scovie_ynh.git
~# yunohost app install scovie_ynh/ -f
```
To update:
```bash
~# cd scovie_ynh
~/scovie_ynh# git fetch && git reset --hard origin/testing
~/scovie_ynh# yunohost app upgrade scovie_ynh -u . -F
```
To remove call e.g.:
```bash
sudo yunohost app remove scovie_ynh
```
Backup / remove / restore cycle, e.g.:
```bash
yunohost backup create --apps scovie_ynh
yunohost backup list
archives:
- scovie_ynh-pre-upgrade1
- 20201223-163434
yunohost app remove scovie_ynh
yunohost backup restore 20201223-163434 --apps scovie_ynh
```
Debug the installation, e.g.:
```bash
root@yunohost:~# cat /etc/yunohost/apps/scovie_ynh/settings.yml
...
root@yunohost:~# ls -la /var/www/scovie_ynh/
total 18
drwxr-xr-x 4 root root 4 Dec 8 08:36 .
drwxr-xr-x 6 root root 6 Dec 8 08:36 ..
drwxr-xr-x 2 root root 2 Dec 8 08:36 media
drwxr-xr-x 7 root root 8 Dec 8 08:40 static
root@yunohost:~# ls -la /opt/yunohost/scovie_ynh/
total 58
drwxr-xr-x 5 scovie_ynh scovie_ynh 11 Dec 8 08:39 .
drwxr-xr-x 3 root root 3 Dec 8 08:36 ..
-rw-r--r-- 1 scovie_ynh scovie_ynh 460 Dec 8 08:39 gunicorn.conf.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 0 Dec 8 08:39 local_settings.py
-rwxr-xr-x 1 scovie_ynh scovie_ynh 274 Dec 8 08:39 manage.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 171 Dec 8 08:39 secret.txt
drwxr-xr-x 6 scovie_ynh scovie_ynh 6 Dec 8 08:37 venv
-rw-r--r-- 1 scovie_ynh scovie_ynh 115 Dec 8 08:39 wsgi.py
-rw-r--r-- 1 scovie_ynh scovie_ynh 4737 Dec 8 08:39 scovie_ynh_demo_settings.py
root@yunohost:~# cd /opt/yunohost/scovie_ynh/
root@yunohost:/opt/yunohost/scovie_ynh# source venv/bin/activate
(venv) root@yunohost:/opt/yunohost/scovie_ynh# ./manage.py check
scovie_ynh v0.8.2 (Django v2.2.17)
DJANGO_SETTINGS_MODULE='scovie_ynh_demo_settings'
PROJECT_PATH:/opt/yunohost/scovie_ynh/venv/lib/python3.7/site-packages
BASE_PATH:/opt/yunohost/scovie_ynh
System check identified no issues (0 silenced).
root@yunohost:~# tail -f /var/log/scovie_ynh/scovie_ynh.log
root@yunohost:~# cat /etc/systemd/system/systemd.service
...
root@yunohost:~# systemctl reload-or-restart scovie_ynh
root@yunohost:~# journalctl --unit=scovie_ynh --follow
```

0
doc/screenshots/.gitkeep Normal file
View file

32
local_test.py Normal file
View file

@ -0,0 +1,32 @@
"""
Build a "local_test" YunoHost installation and start the Django dev. server against it.
Run via:
make local-test
see README for details ;)
"""
from pathlib import Path
try:
from django_yunohost_integration.local_test import create_local_test
except ImportError as err:
raise ImportError('Did you forget to activate a virtual environment?') from err
BASE_PATH = Path(__file__).parent
def main():
create_local_test(
django_settings_path=BASE_PATH / 'conf' / 'settings.py',
destination=BASE_PATH / 'local_test',
runserver=True,
extra_replacements={
'__DEBUG_ENABLED__': '1',
},
)
if __name__ == '__main__':
main()

55
manifest.json Normal file
View file

@ -0,0 +1,55 @@
{
"name": "Scovie",
"id": "scovie_ynh",
"packaging_format": 1,
"description": {
"en": "Scovie is an open-source digital signage system for high schools."
},
"version": "0.0.1",
"url": "https://github.com/eldertek/scovie_ynh",
"upstream": {
"license": "MIT",
"website": "https://github.com/eldertek/scovie",
"code": "https://github.com/YunoHost-Apps/scovie_ynh"
},
"license": "GPL-3.0",
"maintainer": {
"name": "André Théo LAURET",
"email": "andrelauret@eclipse-technology.eu"
},
"previous_maintainers": [],
"requirements": {
"yunohost": ">=11"
},
"multi_instance": true,
"services": [
"nginx", "postgresql", "redis"
],
"arguments": {
"install" : [
{
"name": "domain",
"type": "domain"
},
{
"name": "path",
"type": "path",
"example": "/scovie",
"default": "/scovie"
},
{
"name": "admin",
"type": "user"
},
{
"name": "is_public",
"type": "boolean",
"help": {
"en": "Any YunoHost user and anonymous people from the web will be able to access the application",
"fr": "Tout utilisateur YunoHost et les personnes anonymes pourront accéder à l'application"
},
"default": true
}
]
}
}

1294
poetry.lock generated Normal file

File diff suppressed because it is too large Load diff

115
pyproject.toml Normal file
View file

@ -0,0 +1,115 @@
[tool.poetry]
name = "scovie_ynh"
version = "0.0.1"
description = "Scovie is an open-source digital signage system for high schools."
authors = ["André Théo LAURET <andrelauret@eclipse-technology.eu>"]
license = "MIT"
homepage = "https://github.com/eldertek/scovie_ynh"
[tool.poetry.urls]
"Bug Tracker" = "https://github.com/eldertek/scovie_ynh/issues"
[tool.poetry.dependencies]
python = ">=3.9,<4.0.0" # Stay with 3.9 until YunoHost used >=Debian 11 (Bullseye)
scovie = ">=0.1.0rc0" # https://github.com/eldertek/scovie
# extras "ynh" will install: gunicorn, psycopg2, django-redis and django-axes
# see: https://github.com/YunoHost-Apps/django_yunohost_integration/blob/main/pyproject.toml
django_yunohost_integration = {version = ">=0.5.0rc1", extras = ["ynh"]} # https://github.com/YunoHost-Apps/django_yunohost_integration
[tool.poetry.dev-dependencies]
bx_py_utils = "*" # https://github.com/boxine/bx_py_utils
bx_django_utils = "*" # https://github.com/boxine/bx_django_utils
tox = "*"
pytest = "*"
pytest-cov = "*"
pytest-django = "*"
pytest-darker = "*" # https://github.com/akaihola/pytest-darker
coveralls = "*"
isort = "*"
flake8 = "*"
EditorConfig = "*" # https://github.com/editorconfig/editorconfig-core-py
safety = "*" # https://github.com/pyupio/safety
requests = "*" # https://github.com/psf/requests
packaging = "*" # https://github.com/pypa/packagi
beautifulsoup4 = "*" # https://pypi.org/project/beautifulsoup4/
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
[tool.darker]
src = ['.']
revision = "origin/master..."
line_length = 100
verbose = true
skip_string_normalization = true
diff = false
check = false
stdout = false
isort = true
lint = [
"flake8",
]
log_level = "INFO"
[tool.isort]
# https://pycqa.github.io/isort/docs/configuration/config_files/#pyprojecttoml-preferred-format
atomic=true
profile='black'
skip_glob=["*/htmlcov/*","*/migrations/*","*/local_test/*"]
known_first_party=['django_yunohost_integration']
line_length=100
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 local_test coverage* dist htmlcov"
# sometimes helpfull "addopts" arguments:
# -vv
# --verbose
# --capture=no
# --trace-config
# --full-trace
# -p no:warnings
addopts = """
--reuse-db
--nomigrations
--cov=.
--cov-report term-missing
--cov-report html
--cov-report xml
--no-cov-on-fail
--showlocals
--darker
--doctest-modules
--failed-first
--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 = py{39,310}
skip_missing_interpreters = True
[testenv]
passenv = *
whitelist_externals = make
commands =
make pytest
"""

91
scripts/_common.sh Normal file
View file

@ -0,0 +1,91 @@
#!/bin/bash
#=================================================
# RETRIEVE ARGUMENTS FROM THE MANIFEST
#=================================================
domain=$YNH_APP_ARG_DOMAIN
path_url=$YNH_APP_ARG_PATH
admin=$YNH_APP_ARG_ADMIN
is_public=$YNH_APP_ARG_IS_PUBLIC
app=$YNH_APP_INSTANCE_NAME
# Transfer the main SSO domain to the App:
ynh_current_host=$(cat /etc/yunohost/current_host)
__YNH_CURRENT_HOST__=${ynh_current_host}
#=================================================
# 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
#=================================================
public_path=/var/www/$app
final_path=/opt/yunohost/$app
log_path=/var/log/$app
log_file="${log_path}/${app}.log"
#=================================================
# COMMON VARIABLES
#=================================================
# dependencies used by the app
pkg_dependencies="build-essential python3-dev python3-pip python3-venv git libpq-dev postgresql postgresql-contrib"
#=================================================
# Redis HELPERS
#=================================================
# get the first available redis database
#
# usage: ynh_redis_get_free_db
# | returns: the database number to use
ynh_redis_get_free_db() {
local result max db
result=$(redis-cli INFO keyspace)
# get the num
max=$(cat /etc/redis/redis.conf | grep ^databases | grep -Eow "[0-9]+")
db=0
# default Debian setting is 15 databases
for i in $(seq 0 "$max")
do
if ! echo "$result" | grep -q "db$i"
then
db=$i
break 1
fi
db=-1
done
test "$db" -eq -1 && ynh_die "No available Redis databases..."
echo "$db"
}
# Create a master password and set up global settings
# Please always call this script in install and restore scripts
#
# usage: ynh_redis_remove_db database
# | arg: database - the database to erase
ynh_redis_remove_db() {
local db=$1
redis-cli -n "$db" flushall
}

69
scripts/backup Normal file
View file

@ -0,0 +1,69 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source ../settings/scripts/_common.sh
source /usr/share/yunohost/helpers
ynh_abort_if_errors
#=================================================
# LOAD SETTINGS
#=================================================
ynh_print_info --message="Loading installation settings..."
app=$YNH_APP_INSTANCE_NAME
public_path=$(ynh_app_setting_get --app="$app" --key=public_path)
final_path=$(ynh_app_setting_get --app="$app" --key=final_path)
db_name=$(ynh_app_setting_get --app="$app" --key=db_name)
domain=$(ynh_app_setting_get --app="$app" --key=domain)
#=================================================
# DECLARE DATA AND CONF FILES TO BACKUP
#=================================================
ynh_print_info --message="Declaring files to be backed up..."
#=================================================
# BACKUP THE APP MAIN DIR
#=================================================
ynh_backup --src_path="$final_path"
ynh_backup --src_path="$public_path"
#=================================================
# BACKUP THE NGINX CONFIGURATION
#=================================================
ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf"
#=================================================
# BACKUP THE PostgreSQL DATABASE
#=================================================
ynh_psql_dump_db --database="$db_name" > db.sql
#=================================================
# SPECIFIC BACKUP
#=================================================
# BACKUP LOGROTATE
#=================================================
ynh_backup --src_path="/etc/logrotate.d/$app"
#=================================================
# BACKUP SYSTEMD
#=================================================
ynh_backup --src_path="/etc/systemd/system/$app.service"
#=================================================
# END OF SCRIPT
#=================================================
ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)."

156
scripts/change_url Normal file
View file

@ -0,0 +1,156 @@
#!/bin/bash
#=================================================
# GENERIC STARTING
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
YNH_APP_ARG_DOMAIN=$YNH_APP_NEW_DOMAIN
YNH_APP_ARG_PATH=$YNH_APP_NEW_PATH
source _common.sh
source /usr/share/yunohost/helpers
#=================================================
# RETRIEVE ARGUMENTS
#=================================================
old_domain=$YNH_APP_OLD_DOMAIN
old_path=$YNH_APP_OLD_PATH
new_domain=$YNH_APP_NEW_DOMAIN
new_path=$YNH_APP_NEW_PATH
#=================================================
# LOAD SETTINGS
#=================================================
ynh_script_progression --message="Loading installation settings..."
admin=$(ynh_app_setting_get --app="$app" --key=admin)
public_path=$(ynh_app_setting_get --app="$app" --key=public_path)
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)
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
#=================================================
ynh_script_progression --message="Backing up the app before changing its URL (may take a while)..." --weight=40
# Backup the current version of the app
ynh_backup_before_upgrade
ynh_clean_setup () {
# Remove the new domain config file, the remove script won't do it as it doesn't know yet its location.
ynh_secure_remove --file="/etc/nginx/conf.d/$new_domain.d/$app.conf"
# restore it if the upgrade fails
ynh_restore_upgradebackup
}
# Exit if an error occurs during the execution of the script
ynh_abort_if_errors
#=================================================
# CHECK WHICH PARTS SHOULD BE CHANGED
#=================================================
change_domain=0
if [ "$old_domain" != "$new_domain" ]
then
change_domain=1
fi
change_path=0
if [ "$old_path" != "$new_path" ]
then
change_path=1
fi
#=================================================
# STANDARD MODIFICATIONS
#=================================================
# STOP SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Stopping systemd service '$app'..."
ynh_systemd_action --service_name="$app" --action="stop"
#=================================================
# STANDARD MODIFICATIONS
#=================================================
# MODIFY URL IN NGINX CONF
#=================================================
ynh_script_progression --message="Updating nginx web server configuration..."
nginx_conf_path=/etc/nginx/conf.d/$old_domain.d/$app.conf
# Change the path in the nginx config file
if [ $change_path -eq 1 ]
then
# Make a backup of the original nginx config file if modified
ynh_backup_if_checksum_is_different --file="$nginx_conf_path"
# Set global variables for nginx helper
domain="$old_domain"
path_url="$new_path"
# Create a dedicated nginx config
ynh_add_nginx_config "public_path" "port"
fi
# Change the domain for nginx
if [ $change_domain -eq 1 ]
then
# Delete file checksum for the old conf file location
ynh_delete_file_checksum --file="$nginx_conf_path"
mv $nginx_conf_path /etc/nginx/conf.d/$new_domain.d/$app.conf
# Store file checksum for the new config file location
ynh_store_file_checksum --file="/etc/nginx/conf.d/$new_domain.d/$app.conf"
fi
#=================================================
# SPECIFIC MODIFICATIONS
#=================================================
# MODIFY SETTINGS
#=================================================
ynh_script_progression --message="Modify $app config file..."
domain=$YNH_APP_NEW_DOMAIN
path_url=$YNH_APP_NEW_PATH
ynh_add_config --template="settings.py" --destination="$final_path/settings.py"
#=================================================
# GENERIC FINALISATION
#=================================================
# START SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Starting systemd service '$app'..." --weight=5
ynh_systemd_action --service_name="$app" --action="start"
#=================================================
# RELOAD NGINX
#=================================================
ynh_script_progression --message="Reloading nginx web server..."
ynh_systemd_action --service_name=nginx --action=reload
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Change of URL completed for $app" --last

248
scripts/install Normal file
View file

@ -0,0 +1,248 @@
#!/bin/bash
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
#=================================================
# MANAGE SCRIPT FAILURE
#=================================================
# Exit if an error occurs during the execution of the script
ynh_abort_if_errors
#=================================================
# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS
#=================================================
ynh_script_progression --message="Validating installation parameters..."
# Path for e.g. "static" files, served by nginx:
test ! -e "$public_path" || ynh_die --message="This path already contains a folder"
# Path for own config files, e.g.: Django's settings.py:
test ! -e "$final_path" || ynh_die --message="This path already contains a folder"
# Register (book) web path
ynh_webpath_register --app="$app" --domain="$domain" --path_url="$path_url"
mkdir -p "$public_path/media" "$public_path/static"
mkdir -p "$final_path"
mkdir -p "$log_path"
touch "${log_file}"
#=================================================
# STORE SETTINGS FROM MANIFEST
#=================================================
ynh_script_progression --message="Storing installation settings..."
ynh_app_setting_set --app="$app" --key=admin --value="$admin"
ynh_app_setting_set --app="$app" --key=public_path --value="$public_path"
ynh_app_setting_set --app="$app" --key=final_path --value="$final_path"
ynh_app_setting_set --app="$app" --key=log_path --value="$log_file"
ynh_app_setting_set --app="$app" --key=domain --value="$domain"
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://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)
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 $app dependencies..." --weight=20
ynh_exec_warn_less ynh_install_app_dependencies "$pkg_dependencies"
#=================================================
# CREATE A PostgreSQL DATABASE
#=================================================
ynh_script_progression --message="Creating a PostgreSQL database..."
db_name=$(ynh_sanitize_dbid --db_name="$app")
db_user=$db_name
ynh_app_setting_set --app="$app" --key=db_name --value="$db_name"
ynh_psql_test_if_first_run
# Initialize database and store postgres password for upgrade
ynh_psql_setup_db --db_user="$db_user" --db_name="$db_name"
#=================================================
# NGINX CONFIGURATION
#=================================================
ynh_script_progression --message="Configuring nginx web server..."
# Create a dedicated nginx config
# 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 '$app'..."
# A home directory for venv and settings etc.
ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell
#=================================================
# PYTHON VIRTUALENV
#=================================================
ynh_script_progression --message="Create Python virtualenv..." --weight=5
# Always recreate everything fresh with current python version
ynh_secure_remove "${final_path}/venv"
# Skip pip because of: https://github.com/YunoHost/issues/issues/1960
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
source "${final_path}/venv/bin/activate"
set -o nounset
ynh_exec_as $app $final_path/venv/bin/python3 -m ensurepip
ynh_exec_as $app $final_path/venv/bin/pip3 install --upgrade wheel pip setuptools
ynh_exec_as $app $final_path/venv/bin/pip3 install --no-deps -r "$final_path/requirements.txt"
)
#=================================================
# copy config files
# ================================================
ynh_script_progression --message="Create $app configuration files..."
ynh_add_config --template="gunicorn.conf.py" --destination="$final_path/gunicorn.conf.py"
ynh_add_config --template="manage.py" --destination="$final_path/manage.py"
chmod +x "$final_path/manage.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"
#=================================================
# MIGRATE / COLLECTSTATIC / CREATEADMIN
#=================================================
ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight=10
cd "$final_path" || exit
# Just for debugging:
./manage.py diffsettings
./manage.py migrate --no-input
./manage.py collectstatic --no-input
# Create/update Django superuser (set unusable password, because auth done via SSOwat):
./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.
./manage.py check --deploy || true
#=================================================
# SETUP LOGROTATE
#=================================================
ynh_script_progression --message="Configuring log rotation..."
# Use logrotate to manage app-specific logfile(s)
ynh_use_logrotate "$log_file"
#=================================================
# INTEGRATE SERVICE IN YUNOHOST
#=================================================
ynh_script_progression --message="Integrating service in YunoHost..."
yunohost service add $app --log="${log_file}"
#=================================================
# GENERIC FINALIZATION
#=================================================
# SECURE FILES AND DIRECTORIES
#=================================================
# Set permissions to app files
chown -R "$app:" "$log_path"
chown -R "$app:www-data" "$public_path"
chown -R "$app:" "$final_path"
chmod o-rwx "$log_path"
chmod o-rwx "$public_path"
chmod o-rwx "$final_path"
#=================================================
# SETUP SYSTEMD
#=================================================
ynh_script_progression --message="Configuring systemd service '$app'..." --weight=5
# 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"
#=================================================
# SETUP SSOWAT
#=================================================
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"
#=================================================
# RELOAD NGINX
#=================================================
ynh_script_progression --message="Reloading nginx web server..."
ynh_systemd_action --service_name="nginx" --action="reload"
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Installation of $app completed" --last

103
scripts/remove Normal file
View file

@ -0,0 +1,103 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
#=================================================
# LOAD SETTINGS
#=================================================
ynh_script_progression --message="Loading installation settings..."
domain=$(ynh_app_setting_get --app="$app" --key=domain)
db_name=$(ynh_app_setting_get --app="$app" --key=db_name)
db_user=$db_name
public_path=$(ynh_app_setting_get --app="$app" --key=public_path)
final_path=$(ynh_app_setting_get --app="$app" --key=final_path)
#=================================================
# STANDARD REMOVE
#=================================================
# REMOVE SERVICE FROM ADMIN PANEL
#=================================================
# Remove a service from the admin panel, added by `yunohost service add`
if yunohost service status "$app" >/dev/null 2>&1
then
ynh_script_progression --message="Removing $app service integration..."
yunohost service remove "$app"
fi
#=================================================
# STOP PYINVENTORY'S SERVICES
#=================================================
ynh_script_progression --message="Stopping and removing systemd service '$app'..." --weight=5
ynh_remove_systemd_config --service="$app"
#=================================================
# REMOVE THE PostgreSQL DATABASE
#=================================================
ynh_script_progression --message="Removing the PostgreSQL database..."
# Remove a database if it exists, along with the associated user
ynh_psql_remove_db --db_user=$db_user --db_name=$db_name
##=================================================
## REMOVE REDIS DB
##=================================================
ynh_redis_remove_db
#=================================================
# REMOVE DEPENDENCIES
#=================================================
ynh_script_progression --message="Removing dependencies..." --weight=10
# Remove metapackage and its dependencies
ynh_exec_warn_less ynh_remove_app_dependencies
#=================================================
# REMOVE APP MAIN DIR
#=================================================
ynh_script_progression --message="Removing app main directory..."
# Remove the app directory securely
ynh_secure_remove --file="$public_path"
ynh_secure_remove --file="$final_path"
#=================================================
# REMOVE NGINX CONFIGURATION
#=================================================
ynh_script_progression --message="Removing nginx web server configuration..."
# Remove the dedicated nginx config
ynh_remove_nginx_config
#=================================================
# REMOVE LOGROTATE CONFIGURATION
#=================================================
ynh_script_progression --message="Removing logrotate configuration..."
# Remove the app-specific logrotate config
ynh_remove_logrotate
#=================================================
# GENERIC FINALIZATION
#=================================================
# REMOVE DEDICATED USER
#=================================================
ynh_script_progression --message="Removing the dedicated system user..."
# Delete a system user
ynh_system_user_delete --username="$app"
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Removal of $app completed" --last

175
scripts/restore Normal file
View file

@ -0,0 +1,175 @@
#!/bin/bash
#=================================================
# GENERIC START
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source ../settings/scripts/_common.sh
source /usr/share/yunohost/helpers
#=================================================
# MANAGE SCRIPT FAILURE
#=================================================
ynh_abort_if_errors
#=================================================
# LOAD SETTINGS
#=================================================
ynh_script_progression --message="Loading settings..."
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)
domain=$(ynh_app_setting_get --app="$app" --key=domain)
path_url=$(ynh_app_setting_get --app="$app" --key=path)
#=================================================
# CHECK IF THE APP CAN BE RESTORED
#=================================================
ynh_script_progression --message="Validating restoration parameters..."
test ! -d $final_path \
|| ynh_die --message="There is already a directory: $final_path "
#=================================================
# STANDARD RESTORATION STEPS
#=================================================
# RESTORE THE NGINX CONFIGURATION
#=================================================
ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf"
#=================================================
# RESTORE THE APP MAIN DIR
#=================================================
ynh_script_progression --message="Restoring $app main directory..."
ynh_restore_file --origin_path="$final_path"
ynh_restore_file --origin_path="$public_path"
#=================================================
# RECREATE THE DEDICATED USER
#=================================================
ynh_script_progression --message="Recreating the dedicated system user..."
# Create the dedicated user (if not existing)
ynh_system_user_create --username=$app --home_dir="$final_path" --use_shell
#=================================================
# RESTORE USER RIGHTS
#=================================================
# Restore permissions on app files
chown -R "$app:www-data" "$public_path"
chown -R "$app:" "$final_path"
#=================================================
# SPECIFIC RESTORATION
#=================================================
# REINSTALL DEPENDENCIES
#=================================================
ynh_script_progression --message="Reinstalling dependencies..." --weight=20
ynh_exec_warn_less ynh_install_app_dependencies "$pkg_dependencies"
#=================================================
# PYTHON VIRTUALENV
# Maybe the backup contains a other Python version
#=================================================
ynh_script_progression --message="Recreate Python virtualenv..." --weight=5
# Always recreate everything fresh with current python version
ynh_secure_remove "${final_path}/venv"
# Skip pip because of: https://github.com/YunoHost/issues/issues/1960
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
source "${final_path}/venv/bin/activate"
set -o nounset
ynh_exec_as $app $final_path/venv/bin/python3 -m ensurepip
ynh_exec_as $app $final_path/venv/bin/pip3 install --upgrade wheel pip setuptools
ynh_exec_as $app $final_path/venv/bin/pip3 install --no-deps -r "$final_path/requirements.txt"
)
#=================================================
# RESTORE THE PostgreSQL DATABASE
#=================================================
ynh_script_progression --message="Restoring the PostgreSQL database..." --weight=5
ynh_psql_test_if_first_run
ynh_psql_setup_db --db_user="$db_user" --db_name="$db_name" --db_pwd="$db_pwd"
ynh_psql_connect_as --user=$db_user --password=$db_pwd --database=$db_name < ./db.sql
#=================================================
# RESTORE SYSTEMD
#=================================================
ynh_script_progression --message="Restoring the systemd configuration..."
ynh_restore_file --origin_path="/etc/systemd/system/$app.service"
systemctl enable $app.service --quiet
#=================================================
# INTEGRATE SERVICE IN YUNOHOST
#=================================================
ynh_script_progression --message="Integrating service in YunoHost..."
yunohost service add $app --log="${log_file}"
#=================================================
# RESTORE THE LOGROTATE CONFIGURATION
#=================================================
mkdir -p "$log_path"
touch "${log_file}"
chown -R "$app:" "$log_path"
ynh_restore_file --origin_path="/etc/logrotate.d/$app"
#=================================================
# GENERIC FINALIZATION
#=================================================
# SECURE FILES AND DIRECTORIES
#=================================================
# Set permissions to app files
chown -R "$app:" "$log_path"
chown -R "$app:www-data" "$public_path"
chown -R "$app:" "$final_path"
chmod o-rwx "$log_path"
chmod o-rwx "$public_path"
chmod o-rwx "$final_path"
#=================================================
# GENERIC FINALIZATION
#=================================================
# START PYINVENTORY
#=================================================
ynh_script_progression --message="Starting systemd service '$app'..." --weight=5
ynh_systemd_action --service_name="$app" --action="start"
#=================================================
# RELOAD NGINX
#=================================================
ynh_script_progression --message="Reloading nginx web server..."
ynh_systemd_action --service_name="nginx" --action="reload"
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Restoration completed for $app" --last

227
scripts/upgrade Normal file
View file

@ -0,0 +1,227 @@
#!/bin/bash
#=================================================
# IMPORT GENERIC HELPERS
#=================================================
source _common.sh
source /usr/share/yunohost/helpers
#=================================================
# LOAD SETTINGS
#=================================================
ynh_script_progression --message="Loading installation settings..."
admin=$(ynh_app_setting_get --app="$app" --key=admin)
public_path=$(ynh_app_setting_get --app="$app" --key=public_path)
final_path=$(ynh_app_setting_get --app="$app" --key=final_path)
log_path=$(ynh_app_setting_get --app="$app" --key=log_path)
domain=$(ynh_app_setting_get --app="$app" --key=domain)
path_url=$(ynh_app_setting_get --app="$app" --key=path)
port=$(ynh_app_setting_get --app="$app" --key=port)
db_pwd=$(ynh_app_setting_get --app="$app" --key=psqlpwd)
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)
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 $app before upgrading (may take a while)..." --weight=40
# Backup the current version of the app
ynh_backup_before_upgrade
ynh_clean_setup () {
# restore it if the upgrade fails
ynh_restore_upgradebackup
}
# Exit if an error occurs during the execution of the script
ynh_abort_if_errors
#=================================================
# STANDARD UPGRADE STEPS
#=================================================
# STOP SYSTEMD SERVICE
#=================================================
ynh_script_progression --message="Stopping systemd service '$app'..." --weight=5
ynh_systemd_action --service_name="$app" --action="stop"
#=================================================
# NGINX CONFIGURATION
#=================================================
ynh_script_progression --message="Upgrading nginx web server configuration..."
# Create a dedicated nginx config
# https://yunohost.org/en/contribute/packaging_apps/helpers
# https://github.com/YunoHost/yunohost/blob/dev/helpers/nginx
ynh_add_nginx_config "public_path" "port"
#=================================================
# SPECIFIC UPGRADE
#=================================================
# Update dependencies
#=================================================
ynh_script_progression --message="Upgrading dependencies..." --weight=20
ynh_exec_warn_less ynh_install_app_dependencies "$pkg_dependencies"
#=================================================
# CREATE DEDICATED USER
#=================================================
ynh_script_progression --message="Making sure dedicated system user exists..."
# Create a system user
ynh_system_user_create --username="$app" --home_dir="$final_path" --use_shell
#=================================================
# SETUP SYSTEMD
#=================================================
ynh_script_progression --message="Configuring systemd service '$app'..." --weight=5
ynh_add_systemd_config --service="$app" --template="systemd.service"
#=================================================
# PYTHON VIRTUALENV
#=================================================
ynh_script_progression --message="Recreate Python virtualenv..." --weight=5
# Always recreate everything fresh with current python version
ynh_secure_remove "${final_path}/venv"
# Skip pip because of: https://github.com/YunoHost/issues/issues/1960
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
source "${final_path}/venv/bin/activate"
set -o nounset
ynh_exec_as $app $final_path/venv/bin/python3 -m ensurepip
ynh_exec_as $app $final_path/venv/bin/pip3 install --upgrade wheel pip setuptools
ynh_exec_as $app $final_path/venv/bin/pip3 install --no-deps -r "$final_path/requirements.txt"
)
#=================================================
# copy config files
# ================================================
ynh_script_progression --message="Create project configuration files..."
ynh_add_config --template="gunicorn.conf.py" --destination="$final_path/gunicorn.conf.py"
ynh_add_config --template="manage.py" --destination="$final_path/manage.py"
chmod +x "$final_path/manage.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
#=================================================
ynh_script_progression --message="migrate/collectstatic/createadmin..." --weight=10
cd "$final_path" || exit
# Just for debugging:
./manage.py diffsettings
./manage.py migrate --no-input
./manage.py collectstatic --no-input
# Create/update Django superuser (set unusable password, because auth done via SSOwat):
./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.
./manage.py check --deploy || true
#=================================================
# SETUP LOGROTATE
#=================================================
ynh_script_progression --message="Upgrading logrotate configuration..."
# Use logrotate to manage app-specific logfile(s)
ynh_use_logrotate --non-append
#=================================================
# INTEGRATE SERVICE IN YUNOHOST
#=================================================
ynh_script_progression --message="Integrating service in YunoHost..."
yunohost service add $app --log="${log_file}"
#=================================================
# GENERIC FINALIZATION
#=================================================
# SECURE FILES AND DIRECTORIES
#=================================================
# Set permissions to app files
chown -R "$app:" "$log_path"
chown -R "$app:www-data" "$public_path"
chown -R "$app:" "$final_path"
chmod o-rwx "$log_path"
chmod o-rwx "$public_path"
chmod o-rwx "$final_path"
#=================================================
# Start the app server via systemd
#=================================================
ynh_script_progression --message="Starting systemd service '$app'..." --weight=5
ynh_systemd_action --service_name="$app" --action="start"
#=================================================
# RELOAD NGINX
#=================================================
ynh_script_progression --message="Reloading nginx web server..."
ynh_systemd_action --service_name=nginx --action=reload
#=================================================
# END OF SCRIPT
#=================================================
ynh_script_progression --message="Upgrade of $app completed" --last

0
tests/__init__.py Normal file
View file

44
tests/conftest.py Normal file
View file

@ -0,0 +1,44 @@
"""
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

@ -0,0 +1,160 @@
from axes.models import AccessLog
from bx_django_utils.test_utils.html_assertion import HtmlAssertionMixin
from django.conf import settings
from django.contrib.auth.models import User
from django.test import override_settings
from django.test.testcases import TestCase
from django.urls.base import reverse
from django_yunohost_integration.test_utils import generate_basic_auth
@override_settings(DEBUG=False)
class DjangoYnhTestCase(HtmlAssertionMixin, TestCase):
def setUp(self):
super().setUp()
# Always start a fresh session:
self.client = self.client_class()
def test_settings(self):
assert settings.PATH_URL == 'app_path'
assert str(settings.FINALPATH).endswith('/local_test/opt_yunohost')
assert str(settings.PUBLIC_PATH).endswith('/local_test/var_www')
assert str(settings.LOG_FILE).endswith('/local_test/var_log_scovie_ynh.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_auth(self):
assert settings.PATH_URL == 'app_path'
assert reverse('admin:index') == '/app_path/admin/'
# SecurityMiddleware should redirects all non-HTTPS requests to HTTPS:
assert settings.SECURE_SSL_REDIRECT is True
response = self.client.get('/app_path/admin/', secure=False)
self.assertRedirects(
response,
status_code=301, # permanent redirect
expected_url='https://testserver/app_path/admin/',
fetch_redirect_response=False,
)
response = self.client.get('/app_path/admin/', secure=True)
self.assertRedirects(
response,
expected_url='/app_path/admin/login/?next=%2Fapp_path%2Fadmin%2F',
fetch_redirect_response=False,
)
def test_create_unknown_user(self):
assert User.objects.count() == 0
self.client.cookies['SSOwAuthUser'] = 'test'
response = self.client.get(
path='/app_path/admin/',
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: conf.setup_user.setup_project_user
assert user.is_superuser is False
self.assert_html_parts(
response,
parts=(
'<h1 id="site-name"><a href="/app_path/admin/">Django administration</a></h1>',
'<strong>test</strong>',
),
)
def test_wrong_auth_user(self):
assert User.objects.count() == 0
assert AccessLog.objects.count() == 0
self.client.cookies['SSOwAuthUser'] = 'test'
response = self.client.get(
path='/app_path/admin/',
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: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert AccessLog.objects.count() == 1
assert response.status_code == 403 # Forbidden
def test_wrong_cookie(self):
assert User.objects.count() == 0
assert AccessLog.objects.count() == 0
self.client.cookies['SSOwAuthUser'] = 'foobar' # <<< wrong user name
response = self.client.get(
path='/app_path/',
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: conf.setup_user.setup_project_user
assert user.is_superuser is False
assert AccessLog.objects.count() == 1
assert response.status_code == 403 # Forbidden
def test_wrong_authorization_user(self):
assert User.objects.count() == 0
self.client.cookies['SSOwAuthUser'] = 'test'
response = self.client.get(
path='/app_path/',
HTTP_REMOTE_USER='test',
HTTP_AUTH_USER='test',
HTTP_AUTHORIZATION=generate_basic_auth(
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: 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

@ -0,0 +1,66 @@
import os
from bx_django_utils.test_utils.html_assertion import (
HtmlAssertionMixin,
assert_html_response_snapshot,
)
from django.conf import settings
from django.test.testcases import TestCase
from django.urls.base import reverse
from django_example import __version__
class ExampleProjectTestCase(HtmlAssertionMixin, TestCase):
def test_urls(self):
assert settings.PATH_URL == 'app_path'
assert reverse('admin:index') == '/app_path/admin/'
assert reverse('debug-view') == '/app_path/'
###############################################################################
# Test as anonymous user
with self.assertLogs('django_example') as logs:
response = self.client.get(
path='/app_path/',
secure=True,
)
self.assert_html_parts(
response,
parts=(
f'<h2>YunoHost Django Example Project v{__version__}</h2>',
'<p>Go to <a href="/app_path/admin/">Django Admin</a>.</p>',
'<p>Log in to see more information</p>',
'<tr><td>User:</td><td>AnonymousUser</td></tr>',
'<tr><td>META:</td><td></td></tr>',
),
)
self.assertEqual(
logs.output, ['INFO:django_example.views:DebugView request from user: AnonymousUser']
)
assert_html_response_snapshot(response, query_selector='#container', validate=False)
###############################################################################
# Test as SSO user
self.client.cookies['SSOwAuthUser'] = 'test'
with self.assertLogs('django_example') as logs:
response = self.client.get(
path='/app_path/',
HTTP_REMOTE_USER='test',
HTTP_AUTH_USER='test',
HTTP_AUTHORIZATION='basic dGVzdDp0ZXN0MTIz',
secure=True,
)
self.assert_html_parts(
response,
parts=(
f'<h2>YunoHost Django Example Project v{__version__}</h2>',
'<p>Go to <a href="/app_path/admin/">Django Admin</a>.</p>',
'<tr><td>User:</td><td>test</td></tr>',
f'<tr><td>Process ID:</td><td>{os.getpid()}</td></tr>',
),
)
self.assertEqual(
logs.output, ['INFO:django_example.views:DebugView request from user: test']
)

View file

@ -0,0 +1,143 @@
<div id="container">
<!-- Header -->
<div id="header">
<div id="branding">
<h1 id="site-name">
<a href="/">
Debug View
</a>
</h1>
</div>
</div>
<!-- END Header -->
<div class="breadcrumbs">
<a href="/app_path/admin/">
Home
</a>
</div>
<div class="main" id="main">
<div class="content">
<!-- Content -->
<div class="colM" id="content">
<h2>
YunoHost Django Example Project v0.1.0rc0
</h2>
<p>
Go to
<a href="/app_path/admin/">
Django Admin
</a>
.
</p>
<p>
Log in to see more information
</p>
<table>
<tr>
<td>
<code>
settings.SETTINGS_MODULE
</code>
:
</td>
<td>
<code>
settings
</code>
</td>
</tr>
<tr>
<td>
Env. type:
</td>
<td>
<code>
None
</code>
</td>
</tr>
<tr>
<td>
User:
</td>
<td>
AnonymousUser
</td>
</tr>
<tr>
<td>
Process User:
</td>
<td>
(ID: :)
</td>
</tr>
<tr>
<td>
Executable:
</td>
<td>
</td>
</tr>
<tr>
<td>
Process ID:
</td>
<td>
</td>
</tr>
<tr>
<td>
Python Version:
</td>
<td>
</td>
</tr>
<tr>
<td>
Sys prefix:
</td>
<td>
</td>
</tr>
<tr>
<td>
Current work dir:
</td>
<td>
</td>
</tr>
<tr>
<td>
OS uname:
</td>
<td>
</td>
</tr>
<tr>
<td>
OS Environment:
</td>
<td>
</td>
</tr>
<tr>
<td>
META:
</td>
<td>
</td>
</tr>
</table>
<br class="clear"/>
</div>
<!-- END Content -->
<hr/>
<p>
<a href="https://github.com/jedie/django-example">
github.com/jedie/django-example
</a>
</p>
</div>
</div>
</div>

110
tests/test_project_setup.py Normal file
View file

@ -0,0 +1,110 @@
import difflib
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
from django_tools.unittest_utils.project_setup import check_editor_config
import django_yunohost_integration
from django_yunohost_integration.test_utils import assert_project_version
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 test_version():
upstream_version = django_yunohost_integration.__version__
assert_project_version(
current_version=upstream_version,
github_project_url='https://github.com/YunoHost-Apps/django_yunohost_integration',
)
pyproject_toml_path = Path(PACKAGE_ROOT, 'pyproject.toml')
pyproject_toml = tomli.loads(pyproject_toml_path.read_text(encoding='UTF-8'))
pyproject_version = pyproject_toml['tool']['poetry']['version']
assert pyproject_version.startswith(f'{upstream_version}+ynh')
# pyproject.toml needs a PEP 440 conform version and used "+ynh"
# the YunoHost syntax is: "~ynh", just "convert this:
manifest_version = pyproject_version.replace('+', '~')
assert_file_contains_string(
file_path=Path(PACKAGE_ROOT, 'manifest.json'),
string=f'"version": "{manifest_version}"',
)
def poetry_check_output(*args):
poerty_bin = shutil.which('poetry')
output = subprocess.check_output(
(poerty_bin,) + args,
text=True,
env=os.environ,
stderr=subprocess.STDOUT,
cwd=str(PACKAGE_ROOT),
)
print(output)
return output
def test_poetry_check():
output = poetry_check_output('check')
assert output == 'All set!\n'
def test_requirements_txt():
requirements_txt = PACKAGE_ROOT / 'conf' / 'requirements.txt'
assert_is_file(requirements_txt)
output = poetry_check_output('export', '-f', 'requirements.txt')
assert 'Warning' not in output
current_content = requirements_txt.read_text()
diff = '\n'.join(
difflib.unified_diff(
current_content.splitlines(),
output.splitlines(),
fromfile=str(requirements_txt),
tofile='FRESH EXPORT',
)
)
print(diff)
assert diff == '', f'{requirements_txt} is not up-to-date! (Hint: call: "make update")'
def test_screenshot_filenames():
"""
https://forum.yunohost.org/t/yunohost-bot-cant-handle-spaces-in-screenshots/19483
"""
screenshot_path = PACKAGE_ROOT / 'doc' / 'screenshots'
assert_is_dir(screenshot_path)
renamed = []
for file_path in screenshot_path.iterdir():
file_name = file_path.name
if file_name.startswith('.'):
continue
cleaned_name = clean_filename(file_name)
if cleaned_name != file_name:
new_path = file_path.with_name(cleaned_name)
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)

8
tests/test_utils.py Normal file
View file

@ -0,0 +1,8 @@
from unittest.case import TestCase
from django_yunohost_integration.test_utils import generate_basic_auth
class TestUtilsTestCase(TestCase):
def test_generate_basic_auth(self):
assert generate_basic_auth(username='test', password='test123') == 'basic dGVzdDp0ZXN0MTIz'