From ce58c792c734814e40988348276bce4e3a641284 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?F=C3=A9lix=20Pi=C3=A9dallu?= Date: Fri, 21 May 2021 01:10:01 +0200 Subject: [PATCH 1/4] Full revamp of ihatemoney_ynh: * Use example_ynh as a template * Implement change_url * Use the new yunohost permissions system * Use a python3 venv * Use a systemd service + gunicorn * Allow multiple instances (stop installing in /opt/yunohost/ihatemoney) --- README.md | 79 +++-- README_fr.md | 59 ++++ check_process | 29 +- conf/gunicorn.conf.py | 4 +- conf/ihatemoney.cfg | 8 +- conf/nginx.conf | 11 +- conf/supervisord.conf | 6 - conf/systemd.service | 15 + doc/DISCLAIMER.md | 10 + doc/DISCLAIMER_fr.md | 10 + doc/screenshots/screenshot_1_global.webp | Bin 0 -> 13970 bytes .../screenshot_2_new_operation.webp | Bin 0 -> 11896 bytes manifest.json | 38 ++- scripts/_common.sh | 103 +++---- scripts/backup | 88 ++++-- scripts/change_url | 150 +++++++++ scripts/install | 229 ++++++++++---- scripts/remove | 120 ++++++-- scripts/restore | 157 +++++++--- scripts/upgrade | 286 ++++++++++++------ 20 files changed, 1013 insertions(+), 389 deletions(-) create mode 100644 README_fr.md delete mode 100644 conf/supervisord.conf create mode 100644 conf/systemd.service create mode 100644 doc/DISCLAIMER.md create mode 100644 doc/DISCLAIMER_fr.md create mode 100644 doc/screenshots/screenshot_1_global.webp create mode 100644 doc/screenshots/screenshot_2_new_operation.webp mode change 100644 => 100755 scripts/backup create mode 100644 scripts/change_url mode change 100644 => 100755 scripts/restore mode change 100755 => 100644 scripts/upgrade diff --git a/README.md b/README.md index 00a2308..fe1bca7 100644 --- a/README.md +++ b/README.md @@ -1,52 +1,65 @@ -Yunohost app for « I hate money » budget web app -================================================ +--- -[![Install « I hate money » with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=ihatemoney) + -- Supported Yunohost versions : 2.6.x, 2.7.x 3.x -- Tested Yunohost version : 3.3.1 +# I Hate Money for YunoHost -*NB: That means I'll try not to drop support for YunoHost 2.x too soon, and -accept patches to keep retro-compatibility, but I'll not test it myself against -YunoHost 2.x* +[![Integration level](https://dash.yunohost.org/integration/ihatemoney.svg)](https://dash.yunohost.org/appci/app/ihatemoney) ![](https://ci-apps.yunohost.org/ci/badges/ihatemoney.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/ihatemoney.maintain.svg) +[![Install I Hate Money with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=ihatemoney) -Backs on MySQL database, the identifiers are per-project, not per-user, so no -way to do advanced SSO integration with yunohost accounts. +*[Lire ce readme en français.](./README_fr.md)* -The behaviour is either: +> *This package allows you to install I Hate Money 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 + +I hate money is a web application made to ease shared budget management. It keeps track of who bought what, when, and for whom; and helps to settle the bills. + +**Shipped version:** 4.1.5~ynh2 + +## Screenshots + +![](./doc/screenshots/screenshot_1_global.webp) +![](./doc/screenshots/screenshot_2_new_operation.webp) + +## Disclaimers / important information + +* Is LDAP and HTTP authentication supported ? **No** + +The logins are per-project (not per-user) so it's not integrable in the Yunohost login system. + +The app can be public or not. The behaviour is either: - **non-public app**: - yunohost login required - per-project identifiers required - - any yunohost user with authorized access to the app can create a new - project). + - any yunohost user with access to the app can create a new project. - **public app** : - no yunohost login required - per-project identifiers required - any visitor can create a new project. -Update ------- +## Documentation and resources -To update the app, use: +* Official app website: https://github.com/spiral-project/ihatemoney +* Official admin documentation: https://ihatemoney.readthedocs.org/ +* Upstream app code repository: https://github.com/spiral-project/ihatemoney +* YunoHost documentation for this app: https://yunohost.org/app_ihatemoney +* Report a bug: https://github.com/YunoHost-Apps/ihatemoney_ynh/issues -`sudo yunohost app upgrade ihatemoney -u https://github.com/YunoHost-Apps/ihatemoney_ynh` - +## Developer info -Maintainer ----------- +Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing). -I rely on -[ihatemoney official releases](https://github.com/spiral-project/ihatemoney/releases) -(tarballs). Don't hesitate to pull-request this repo if I missed one :-). - - -Ihatemoney license ------------------- - -> The code is distributed under a BSD beerware derivative: if you meet the -> people in person and you want to pay them a craft beer, you are highly -> encouraged to do so. - -[Full license text](https://github.com/spiral-project/ihatemoney/blob/master/LICENSE) +To try the testing branch, please proceed like that. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing --debug +or +sudo yunohost app upgrade ihatemoney -u https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing --debug +``` +**More info regarding app packaging:** https://yunohost.org/packaging_apps diff --git a/README_fr.md b/README_fr.md new file mode 100644 index 0000000..2331937 --- /dev/null +++ b/README_fr.md @@ -0,0 +1,59 @@ +# I Hate Money pour YunoHost + +[![Niveau d'intégration](https://dash.yunohost.org/integration/ihatemoney.svg)](https://dash.yunohost.org/appci/app/ihatemoney) ![](https://ci-apps.yunohost.org/ci/badges/ihatemoney.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/ihatemoney.maintain.svg) +[![Installer I Hate Money avec YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=ihatemoney) + +*[Read this readme in english.](./README.md)* +*[Lire ce readme en français.](./README_fr.md)* + +> *Ce package vous permet d'installer I Hate Money rapidement et simplement sur un serveur YunoHost. +Si vous n'avez pas YunoHost, consultez [le guide](https://yunohost.org/#/install) pour apprendre comment l'installer.* + +## Vue d'ensemble + +Une application web de comptes partagés à plusieurs + +**Version incluse:** 4.1.5~ynh2 + +## Captures d'écran + +![](./doc/screenshots/screenshot_1_global.webp) +![](./doc/screenshots/screenshot_2_new_operation.webp) + +## Avertissements / informations importantes + +* L'authentification LDAP et HTTP est-elle prise en charge ? **Non** + +L'authentification est par projet (et non par utilisateur) donc ce n'est pas intégrable dans le système d'authentification de Yunohost. + +L'application peut néanmoins être configurée en public ou non : + +- **app non publique**: + - authentification Yunohost requise + - identifiants de projets requis + - Tout utilisateur Yunohost avec accès à l'app peut créer un nouveau projet. +- **app publique** : + - authentification Yunohost non requise + - identifiants de projets requis + - Tout visiteur peut créer un nouveau projet. + +## Documentations et ressources + +* Site officiel de l'app : https://github.com/spiral-project/ihatemoney +* Documentation officielle de l'admin: https://ihatemoney.readthedocs.org/ +* Dépôt de code officiel de l'app: https://github.com/spiral-project/ihatemoney +* Documentation YunoHost pour cette app: https://yunohost.org/app_ihatemoney +* Signaler un bug: https://github.com/YunoHost-Apps/ihatemoney_ynh/issues + +## Informations pour les développeurs + +Merci de faire vos pull request sur la [branche testing](https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing). + +Pour essayer la branche testing, procédez comme suit. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing --debug +or +sudo yunohost app upgrade ihatemoney -u https://github.com/YunoHost-Apps/ihatemoney_ynh/tree/testing --debug +``` + +**Plus d'infos sur le packaging d'applications:** https://yunohost.org/packaging_apps \ No newline at end of file diff --git a/check_process b/check_process index 755855f..461d489 100644 --- a/check_process +++ b/check_process @@ -1,8 +1,8 @@ ;; Full test ; Manifest - domain="domain.tld" (DOMAIN) - path="/path" (PATH) - is_public=1 (PUBLIC|public=1|private=0) + domain="domain.tld" + path="/path" + is_public=1 ; Checks pkg_linter=1 setup_sub_dir=1 @@ -11,23 +11,14 @@ setup_private=1 setup_public=1 upgrade=1 + upgrade=1 from_commit=0f904d99367cfec27ec5fe303941fbf1124a7571 backup_restore=1 - multi_instance=0 - incorrect_path=1 + multi_instance=1 port_already_use=0 - change_url=0 -;;; Levels - Level 1=auto - Level 2=auto - Level 3=auto - # There is no user concept in ihatemoney - Level 4=1 - Level 5=auto - Level 6=auto - Level 7=auto - Level 8=0 - Level 9=0 - Level 10=0 + change_url=1 ;;; Options Email= -Notification=change +Notification=none +;;; Upgrade options + ; commit=0f904d99367cfec27ec5fe303941fbf1124a7571 + name=Before refactoring yunohost package diff --git a/conf/gunicorn.conf.py b/conf/gunicorn.conf.py index 60fcdd1..f38b59a 100644 --- a/conf/gunicorn.conf.py +++ b/conf/gunicorn.conf.py @@ -2,6 +2,6 @@ backlog = 2048 daemon = False debug = True workers = 3 -logfile = "/var/log/ihatemoney/budget.gunicorn.log" +logfile = "/var/log/__APP__/budget.gunicorn.log" loglevel = "info" -bind = "unix:/tmp/budget.gunicorn.sock" +bind = "unix:/tmp/budget.gunicorn___APP__.sock" diff --git a/conf/ihatemoney.cfg b/conf/ihatemoney.cfg index f41f6ce..147c189 100644 --- a/conf/ihatemoney.cfg +++ b/conf/ihatemoney.cfg @@ -1,10 +1,10 @@ DEBUG = True -SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://ihatemoney:MY_MYSQL_PW@localhost/ihatemoney' +SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://__DB_USER__:__DB_PWD__@localhost/__DB_NAME__' SQLACHEMY_ECHO = DEBUG -SECRET_KEY = "MY_SECRET_KEY" +SECRET_KEY = "__SECRET_KEY__" -MAIL_DEFAULT_SENDER = ("Budget manager", "MY_EMAIL") -APPLICATION_ROOT='MY_PATH' +MAIL_DEFAULT_SENDER = ("Budget manager", "__MAILS_SENDER__") +__SUB_PATH_ONLY__APPLICATION_ROOT='__PATH__' try: from settings import * diff --git a/conf/nginx.conf b/conf/nginx.conf index 3d13167..e096719 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,8 +1,9 @@ -location PATHTOCHANGE/static/ { - alias /opt/yunohost/ihatemoney/venv/lib/pythonPYTHON_VERSION/site-packages/ihatemoney/static/; +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/static/ { + alias __PYTHON_VENV_SITE_PACKAGES__/ihatemoney/static/; } -location PATHTOCHANGE { - # Force https. +location __PATH__/ { + # Force usage of https if ($scheme = http) { rewrite ^ https://$server_name$request_uri? permanent; } @@ -18,7 +19,7 @@ location PATHTOCHANGE { proxy_busy_buffers_size 32k; proxy_intercept_errors on; if (!-f $request_filename) { - proxy_pass http://unix:/tmp/budget.gunicorn.sock; + proxy_pass http://unix:/tmp/budget.gunicorn___APP__.sock; break; } diff --git a/conf/supervisord.conf b/conf/supervisord.conf deleted file mode 100644 index 86e36b3..0000000 --- a/conf/supervisord.conf +++ /dev/null @@ -1,6 +0,0 @@ -[program:budget] -command=/opt/yunohost/ihatemoney/venv/bin/gunicorn -c /etc/ihatemoney/gunicorn.conf.py ihatemoney.wsgi:application -user=ihatemoney -autostart=true -autorestart=true -redirect_stderr=true diff --git a/conf/systemd.service b/conf/systemd.service new file mode 100644 index 0000000..5803179 --- /dev/null +++ b/conf/systemd.service @@ -0,0 +1,15 @@ +[Unit] +Description=I hate money +Requires=network.target mysql.service +After=network.target mysql.service + +[Service] +Type=simple +User=__APP__ +Environment=IHATEMONEY_SETTINGS_FILE_PATH=__FINALPATH__/ihatemoney.cfg +ExecStart=__FINALPATH__/venv/bin/gunicorn -c __FINALPATH__/gunicorn.conf.py ihatemoney.wsgi:application +Restart=always +RestartSec=2 + +[Install] +WantedBy=multi-user.target diff --git a/doc/DISCLAIMER.md b/doc/DISCLAIMER.md new file mode 100644 index 0000000..37b4fcf --- /dev/null +++ b/doc/DISCLAIMER.md @@ -0,0 +1,10 @@ +* LDAP authentication and Single Sign-on is not supported. The login mechanism in IHateMoney is per-project (not per-user) and therefore can't be integrated in YunoHost. + +- **non-public app**: + - yunohost login required + - per-project identifiers required + - any yunohost user with access to the app can create a new project. +- **public app** : + - no yunohost login required + - per-project identifiers required + - any visitor can create a new project. diff --git a/doc/DISCLAIMER_fr.md b/doc/DISCLAIMER_fr.md new file mode 100644 index 0000000..5403414 --- /dev/null +++ b/doc/DISCLAIMER_fr.md @@ -0,0 +1,10 @@ +* L'authentification LDAP et login unifié (SSO) n'est pas supportée. Le mécanisme de connexion sur IHateMoney se fait par projet et ne peut donc pas être intégrée dans YunoHost + +- **app non publique**: + - authentification Yunohost requise + - identifiants de projets requis + - Tout utilisateur Yunohost avec accès à l'app peut créer un nouveau projet. +- **app publique** : + - authentification Yunohost non requise + - identifiants de projets requis + - Tout visiteur peut créer un nouveau projet. diff --git a/doc/screenshots/screenshot_1_global.webp b/doc/screenshots/screenshot_1_global.webp new file mode 100644 index 0000000000000000000000000000000000000000..e626c28034686bdaf425785f4d17886d85debe7e GIT binary patch literal 13970 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 z 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 U;;jUEZY>+ki;PDu{hKlV4_!O~^#A|> literal 0 HcmV?d00001 diff --git a/doc/screenshots/screenshot_2_new_operation.webp b/doc/screenshots/screenshot_2_new_operation.webp new file mode 100644 index 0000000000000000000000000000000000000000..d76564d4541d8ec7e34d0b3fdcc5bad157fc0555 GIT binary patch literal 11896 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 3.8" + "yunohost": ">= 4.1.0" }, - "multi_instance": false, + "multi_instance": true, "services": ["nginx", "mysql", "postfix"], "arguments": { "install" : [ { "name": "domain", - "type": "domain", - "ask": { - "en": "Choose a domain for ihatemoney", - "fr": "Choisir un domaine pour ihatemoney" - }, - "example": "example.com" + "type": "domain" }, { "name": "path", "type": "path", - "ask": { - "en": "Choose a path for ihatemoney", - "fr": "Choisir un chemin pour ihatemoney" - }, - "example": "/example", + "example": "/ihatemoney", "default": "/ihatemoney" }, { "name": "is_public", "type": "boolean", - "ask": { - "en": "Is it a public website ? (even if service is public, each project is protected by a password)", - "fr": "Le service est-il public ? (même dans ce cas, chaque projet est protégé par un mot de passe)" + "help": { + "en": "Each ihatemoney project is protected by a password anyways", + "fr": "Les projets ihatemoney sont protégés par un mot de passe dans tous les cas" }, "default": true } diff --git a/scripts/_common.sh b/scripts/_common.sh index 8c54da6..7ca836a 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -1,69 +1,56 @@ +#!/bin/bash + +#================================================= +# COMMON VARIABLES +#================================================= + +# dependencies used by the app +pkg_dependencies=( + python3-dev + python3-venv + libffi-dev + libssl-dev +) + +pip_dependencies=( + 'setuptools>=18.5' + 'gunicorn>=19.3.0' + 'PyMySQL>=0.9,<0.10' + 'SQLAlchemy<1.4' + 'ihatemoney>=4,<5' +) + ### Constants -supervisor_conf_path="/etc/supervisor/conf.d/ihatemoney.conf" -gunicorn_conf_path="/etc/ihatemoney/gunicorn.conf.py" -ihatemoney_conf_path="/etc/ihatemoney/ihatemoney.cfg" -INSTALL_DIR="/opt/yunohost/ihatemoney" +#================================================= +# PERSONAL HELPERS +#================================================= +__ynh_python_venv_setup() { + local -A args_array=( [d]=venv_dir= [p]=packages= ) + local venv_dir + local packages + ynh_handle_getopts_args "$@" -### Functions + python3 -m venv --system-site-packages "$venv_dir" - -install_apt_dependencies() { - ynh_install_app_dependencies \ - python3-dev \ - python3-virtualenv \ - libffi-dev \ - libssl-dev \ - supervisor \ - virtualenv + IFS=" " read -r -a pip_packages <<< "$packages" + "$venv_dir/bin/python3" -m pip install --upgrade pip "${pip_packages[@]}" } -create_unix_user() { - mkdir -p /opt/yunohost - useradd ihatemoney -d /opt/yunohost/ihatemoney/ --create-home || ynh_die "User creation failed" +__ynh_python_venv_get_site_packages_dir() { + local -A args_array=( [d]=venv_dir= ) + local venv_dir + ynh_handle_getopts_args "$@" + + "$venv_dir/bin/python3" -c 'import sysconfig; print(sysconfig.get_paths()["purelib"])' } -create_system_dirs() { - install -o ihatemoney -g ihatemoney -m 755 -d \ - /var/log/ihatemoney \ - /etc/ihatemoney - mkdir -p /opt/yunohost -} -init_virtualenv () { - virtualenv /opt/yunohost/ihatemoney/venv --python /usr/bin/python3 +#================================================= +# EXPERIMENTAL HELPERS +#================================================= - # PyMySQL → cryptography → setuptools>=18.5 - # Required on Jessie, Stretch has setuptools>=18.5 - /opt/yunohost/ihatemoney/venv/bin/pip install 'setuptools>=18.5' -} - -pip_install () { - # SQLAlchemy requirement is workaround https://github.com/pallets/flask-sqlalchemy/issues/910 - # Might be removed later when IHM dependency set will no longer prevent working installation. - /opt/yunohost/ihatemoney/venv/bin/pip install --upgrade \ - 'gunicorn>=19.3.0' \ - 'PyMySQL>=0.9,<0.10' \ - 'ihatemoney>=4,<5' \ - 'SQLAlchemy<1.4' \ - -} - -configure_nginx () { - local domain=$1 - local path=$2 - local python_version="$(readlink /usr/bin/python3|sed s/.*python//)" - - ynh_replace_string "PATHTOCHANGE" "$path" ../conf/nginx.conf - ynh_replace_string "PYTHON_VERSION" "$python_version" ../conf/nginx.conf - # Fix double-slash for domain-root install - ynh_replace_string "location //" "location /" ../conf/nginx.conf - install -o root -g root -m644 \ - ../conf/nginx.conf /etc/nginx/conf.d/$domain.d/ihatemoney.conf -} - -configure_supervisor () { - install -o root -g root -m 644 \ - ../conf/supervisord.conf /etc/supervisor/conf.d/ihatemoney.conf -} +#================================================= +# FUTURE OFFICIAL HELPERS +#================================================= diff --git a/scripts/backup b/scripts/backup old mode 100644 new mode 100755 index 7c21e82..030071d --- a/scripts/backup +++ b/scripts/backup @@ -1,34 +1,82 @@ #!/bin/bash -# Source YunoHost helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts source ../settings/scripts/_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 -# Get multi-instances specific variables +#================================================= +# LOAD SETTINGS +#================================================= +ynh_print_info --message="Loading installation settings..." + app=$YNH_APP_INSTANCE_NAME -# Set app specific variables -dbname=$app -dbuser=$app +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +domain=$(ynh_app_setting_get --app=$app --key=domain) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) -INSTALL_DIR=/opt/yunohost/ihatemoney +#================================================= +# DECLARE DATA AND CONF FILES TO BACKUP +#================================================= +ynh_print_info --message="Declaring files to be backed up..." -# Retrieve app settings -domain=$(ynh_app_setting_get "$app" domain) -path=$(ynh_app_setting_get "$app" path) -dbpass=$(ynh_app_setting_get "$app" mysqlpwd) +### N.B. : the following 'ynh_backup' calls are only a *declaration* of what needs +### to be backuped and not an actual copy of any file. The actual backup that +### creates and fill the archive with the files happens in the core after this +### script is called. Hence ynh_backups calls takes basically 0 seconds to run. -# Backup conf files -mkdir ./conf -ynh_backup "/etc/nginx/conf.d/$domain.d/$app.conf" -ynh_backup "$gunicorn_conf_path" -ynh_backup "$supervisor_conf_path" -ynh_backup "$ihatemoney_conf_path" +#================================================= +# BACKUP THE APP MAIN DIR +#================================================= -# Dump the database -mysqldump -u "$dbuser" -p"$dbpass" --no-create-db "$dbname" > ./db.sql +ynh_backup --src_path="$final_path" -# Backup code and venv -ynh_backup "$INSTALL_DIR" "install_dir" +#================================================= +# BACKUP THE NGINX CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# BACKUP FAIL2BAN CONFIGURATION +#================================================= + +# ynh_backup --src_path="/etc/fail2ban/jail.d/$app.conf" +# ynh_backup --src_path="/etc/fail2ban/filter.d/$app.conf" + +#================================================= +# SPECIFIC BACKUP +#================================================= +# BACKUP SYSTEMD +#================================================= + +ynh_backup --src_path="/etc/systemd/system/$app.service" + +#================================================= +# BACKUP THE MYSQL DATABASE +#================================================= +ynh_print_info --message="Backing up the MySQL database..." + +### (However, things like MySQL dumps *do* take some time to run, though the +### copy of the generated dump to the archive still happens later) + +ynh_mysql_dump_db --database="$db_name" > db.sql + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_print_info --message="Backup script completed for $app. (YunoHost will then actually copy those files to the archive)." diff --git a/scripts/change_url b/scripts/change_url new file mode 100644 index 0000000..92e79e2 --- /dev/null +++ b/scripts/change_url @@ -0,0 +1,150 @@ +#!/bin/bash + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +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 + +app=$YNH_APP_INSTANCE_NAME +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name +db_pwd=$(ynh_app_setting_get --app=$app --key=mysqlpwd) + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --time --weight=1 + +# Needed for helper "ynh_add_nginx_config" +final_path=$(ynh_app_setting_get --app=$app --key=final_path) + +python_venv_site_packages=$(__ynh_python_venv_get_site_packages_dir -d "$final_path/venv") + +#================================================= +# BACKUP BEFORE CHANGE URL THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before changing its URL (may take a while)..." --time --weight=1 + +# 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 a systemd service..." --time --weight=1 + +ynh_systemd_action --service_name=$app --action="stop" --log_path=systemd + +#================================================= +# MODIFY URL IN NGINX CONF +#================================================= +ynh_script_progression --message="Updating NGINX web server configuration..." --time --weight=1 + +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 +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 +#================================================= +# Setup ihatemoney.cfg +#================================================= + +path_url="$new_path" +domain="$new_domain" + +# Secret key for cookies encryption. +secret_key=$(ynh_string_random --length 32) +mails_sender="no-reply@$domain" +# Allows to comment some config lines if not using sub path +sub_path_only="$(if [[ "$path_url" == "/" ]]; then echo '# ' ; else echo ''; fi)" + +ynh_backup_if_checksum_is_different --file="$final_path/ihatemoney.cfg" +ynh_add_config --template ../conf/ihatemoney.cfg --destination "$final_path/ihatemoney.cfg" + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" + +#================================================= +# GENERIC FINALISATION +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --time --weight=1 + +ynh_systemd_action --service_name=$app --action="start" --log_path=systemd + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --time --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Change of URL completed for $app" --time --last diff --git a/scripts/install b/scripts/install index 8c1202c..eb05f17 100755 --- a/scripts/install +++ b/scripts/install @@ -1,88 +1,191 @@ #!/bin/bash -# Source YunoHost helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + source _common.sh source /usr/share/yunohost/helpers -# Retrieve arguments -domain=$YNH_APP_ARG_DOMAIN -path=$YNH_APP_ARG_PATH -is_public=$YNH_APP_ARG_IS_PUBLIC -app=ihatemoney - -# Database settings -db_pwd=$(ynh_string_random) -db_name=$app -db_user=$app - -# Constant arguments -secret_key=$(ynh_string_random --length 32) -mails_sender="no-reply@${domain}" - +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= +# Exit if an error occurs during the execution of the script ynh_abort_if_errors -ynh_webpath_register $app $domain $path +#================================================= +# RETRIEVE ARGUMENTS FROM THE MANIFEST +#================================================= -# Configure database -ynh_mysql_create_db "$db_name" "$db_user" "$db_pwd" +domain=$YNH_APP_ARG_DOMAIN +path_url=$YNH_APP_ARG_PATH +is_public=$YNH_APP_ARG_IS_PUBLIC +app=$YNH_APP_INSTANCE_NAME -# Save app settings -ynh_app_setting_set "$app" mysqlpwd "$db_pwd" -ynh_app_setting_set "$app" is_public "$is_public" +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." --weight=1 -install_apt_dependencies +final_path=/var/www/$app +test ! -e "$final_path" || ynh_die --message="This path already contains a folder" -create_unix_user +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url -# Prepare venv -init_virtualenv -pip_install +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." --weight=1 -create_system_dirs +ynh_app_setting_set --app=$app --key=domain --value=$domain +ynh_app_setting_set --app=$app --key=path --value=$path_url -# Configure gunicorn -install -o ihatemoney -g ihatemoney -m 644 \ - ../conf/gunicorn.conf.py /etc/ihatemoney/gunicorn.conf.py +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." --weight=3 -# Configure supervisor -configure_supervisor -# In case it was already installed before, -# so that it picks /etc/supervisor/conf.d/ihatemoney.conf: -supervisorctl update -yunohost service add supervisor +ynh_install_app_dependencies "${pkg_dependencies[@]}" -# Configure ihatemoney -ynh_replace_string "MY_SECRET_KEY" "$secret_key" ../conf/ihatemoney.cfg -ynh_replace_string "MY_EMAIL" "$mails_sender" ../conf/ihatemoney.cfg -ynh_replace_string "MY_MYSQL_PW" "$db_pwd" ../conf/ihatemoney.cfg -ynh_replace_string "MY_PATH" "$path" ../conf/ihatemoney.cfg -# Remove the conf directive if served at root -sed -i "/APPLICATION_ROOT='\/'/d" ../conf/ihatemoney.cfg -install -o ihatemoney -g ihatemoney -m 640 \ - ../conf/ihatemoney.cfg /etc/ihatemoney/ihatemoney.cfg +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." --weight=1 -# If app is public, add url to SSOWat conf as skipped_uris -if [[ "$is_public" -ne 0 ]]; -then - ynh_app_setting_set $app unprotected_uris "/" -fi +# Create a system user +ynh_system_user_create --username=$app --home_dir="$final_path" -# Configure Nginx -configure_nginx "$domain" "$path" +#================================================= +# CREATE A MYSQL DATABASE +#================================================= +ynh_script_progression --message="Creating a MySQL database..." --weight=1 -# Start backend -systemctl start supervisor +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_mysql_setup_db --db_user=$db_user --db_name=$db_name +# defines $db_pwd and setting mysqlpwd -# Wait that gunicorn is ready to consider the install finished, that is to -# avoid HTTP 502 right after installation -for i in `seq 1 120` -do - test -S /tmp/budget.gunicorn.sock && break +#================================================= +# SPECIFIC SETUP +#================================================= +# Init venv +#================================================= + +ynh_script_progression --message="Configuring the app's installation..." --weight=6 + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path + +__ynh_python_venv_setup --venv_dir="$final_path/venv" --packages "${pip_dependencies[*]}" +python_venv_site_packages=$(__ynh_python_venv_get_site_packages_dir -d "$final_path/venv") + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring NGINX web server..." --weight=1 + +# Create a dedicated NGINX config +## Needs $python_venv_site_packages +ynh_add_nginx_config + +#================================================= +# Setup gunicorn +#================================================= + +ynh_add_config --template ../conf/gunicorn.conf.py --destination "$final_path/gunicorn.conf.py" +chmod 644 "$final_path/gunicorn.conf.py" + +#================================================= +# Setup ihatemoney +#================================================= + +# Secret key for cookies encryption. +secret_key=$(ynh_string_random --length 32) +mails_sender="no-reply@$domain" +# Allows to comment some config lines if not using sub path +sub_path_only="$(if [[ "$path_url" == "/" ]]; then echo '# ' ; else echo ''; fi)" + +ynh_add_config --template ../conf/ihatemoney.cfg --destination "$final_path/ihatemoney.cfg" +chmod 640 "$final_path/ihatemoney.cfg" + + +# FIXME: this should be managed by the core in the future +# Here, as a packager, you may have to tweak the ownerhsip/permissions +# such that the appropriate users (e.g. maybe www-data) can access +# files in some cases. +# But FOR THE LOVE OF GOD, do not allow r/x for "others" on the entire folder - +# this will be treated as a security issue. +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" + +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Configuring a systemd service..." --weight=1 + +# Create a dedicated systemd config +ynh_add_systemd_config + +#================================================= +# GENERIC FINALIZATION +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 + +yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --weight=1 + +# Start a systemd service +ynh_systemd_action --service_name=$app --action="start" --log_path="systemd" --line_match="Booting worker" --timeout 30 + +# line_match isn't enough because ihatemoney may stop if database upgrades +for _ in {1..20}; do + test -S /tmp/budget.gunicorn_$app.sock && break sleep 1 done -# If socket not ready after 2 minutes waiting, ihatemoney will not work. -test -S /tmp/budget.gunicorn.sock || ynh_die +#================================================= +# SETUP FAIL2BAN +#================================================= +# ynh_script_progression --message="Configuring Fail2Ban..." --weight=1 -systemctl reload nginx +# Create a dedicated Fail2Ban config +# ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="Regex to match into the log for a failed login" + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring permissions..." --weight=1 + +# Make app public if necessary +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 + +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Installation of $app completed" --last diff --git a/scripts/remove b/scripts/remove index 5620f80..4685e83 100755 --- a/scripts/remove +++ b/scripts/remove @@ -1,37 +1,109 @@ #!/bin/bash -# Source YunoHost helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh source /usr/share/yunohost/helpers -# supervisord and other Debian dependencies remain installed -# there is no way to know if they are used by other programs +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=1 -# Retrieve arguments -app=ihatemoney -domain=$(ynh_app_setting_get $app domain) -db_user=$app -db_name=$app +app=$YNH_APP_INSTANCE_NAME -# Stop service -supervisorctl stop budget +domain=$(ynh_app_setting_get --app=$app --key=domain) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name +final_path=$(ynh_app_setting_get --app=$app --key=final_path) -# Drop database -ynh_mysql_drop_db $db_name -ynh_mysql_drop_user $db_user +#================================================= +# STANDARD REMOVE +#================================================= +# REMOVE SERVICE INTEGRATION IN YUNOHOST +#================================================= -# Remove src and venv -ynh_secure_remove /opt/yunohost/ihatemoney +# Remove the service from the list of services known by YunoHost (added from `yunohost service add`) +if ynh_exec_warn_less yunohost service status $app >/dev/null +then + ynh_script_progression --message="Removing $app service integration..." --weight=1 + yunohost service remove $app +fi -# Remove settings -ynh_secure_remove /etc/ihatemoney -ynh_secure_remove /etc/supervisor/conf.d/ihatemoney.conf -ynh_remove_nginx_config +#================================================= +# STOP AND REMOVE SERVICE +#================================================= +ynh_script_progression --message="Stopping and removing the systemd service..." --weight=1 -# Restart services -systemctl force-reload supervisor +# Remove the dedicated systemd config +ynh_remove_systemd_config -# Remove app dependencies +#================================================= +# REMOVE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Removing the MySQL database..." --weight=1 + +# Remove a database if it exists, along with the associated user +ynh_mysql_remove_db --db_user=$db_user --db_name=$db_name + +#================================================= +# REMOVE DEPENDENCIES +#================================================= +ynh_script_progression --message="Removing dependencies..." --weight=2 + +# Remove metapackage and its dependencies ynh_remove_app_dependencies -# Delete user -userdel ihatemoney +#================================================= +# REMOVE APP MAIN DIR +#================================================= +ynh_script_progression --message="Removing app main directory..." --weight=1 + +# Remove the app directory securely +ynh_secure_remove --file="$final_path" + +#================================================= +# REMOVE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Removing NGINX web server configuration..." --weight=1 + +# Remove the dedicated NGINX config +ynh_remove_nginx_config + +#================================================= +# REMOVE FAIL2BAN CONFIGURATION +#================================================= +# ynh_script_progression --message="Removing Fail2ban configuration..." --weight=1 + +# # Remove the dedicated Fail2Ban config +# ynh_remove_fail2ban_config + +#================================================= +# SPECIFIC REMOVE +#================================================= +# REMOVE VARIOUS FILES +#================================================= +ynh_script_progression --message="Removing configuration files..." --weight=1 + +# Remove the log files +ynh_secure_remove --file="/var/log/$app" + +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= +ynh_script_progression --message="Removing the dedicated system user..." --weight=1 + +# Delete a system user +ynh_system_user_delete --username=$app + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Removal of $app completed" --last diff --git a/scripts/restore b/scripts/restore old mode 100644 new mode 100755 index 0912f3f..ede60f0 --- a/scripts/restore +++ b/scripts/restore @@ -1,58 +1,145 @@ #!/bin/bash -# Source app helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +# Keep this path for calling _common.sh inside the execution's context of backup and restore scripts source ../settings/scripts/_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 -# Get multi-instances specific variables +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=1 + app=$YNH_APP_INSTANCE_NAME -# Set app specific variables -dbname=$app -dbuser=$app +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_path) +db_name=$(ynh_app_setting_get --app=$app --key=db_name) +db_user=$db_name +phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) -# Retrieve old app settings -domain=$(ynh_app_setting_get "$app" domain) -path=$(ynh_app_setting_get "$app" path) -dbpass=$(ynh_app_setting_get "$app" mysqlpwd) +#================================================= +# CHECK IF THE APP CAN BE RESTORED +#================================================= +ynh_script_progression --message="Validating restoration parameters..." --weight=1 +test ! -d $final_path \ + || ynh_die --message="There is already a directory: $final_path " -test -d $INSTALL_DIR && ynh_die \ -"The destination directory '$INSTALL_DIR' already exists.\ - You should safely delete it before restoring this app." +#================================================= +# STANDARD RESTORATION STEPS +#================================================= +# RESTORE THE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the NGINX configuration..." --weight=1 -test -f $supervisor_conf_path && ynh_die \ -"The Supervisor configuration already exists at '${supervisor_conf_path}'. - You should safely delete it before restoring this app." +ynh_restore_file --origin_path="/etc/nginx/conf.d/$domain.d/$app.conf" -test -f $gunicorn_conf_path && ynh_die \ -"The Gunicorn configuration already exists at '${gunicorn_conf_path}'. - You should safely delete it before restoring this app." +#================================================= +# RECREATE THE DEDICATED USER +#================================================= +ynh_script_progression --message="Recreating the dedicated system user..." --weight=1 -install_apt_dependencies +# Create the dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir="$final_path" -create_unix_user +#================================================= +# RESTORE THE APP MAIN DIR +#================================================= +ynh_script_progression --message="Restoring the app main directory..." --weight=1 -create_system_dirs +ynh_restore_file --origin_path="$final_path" -# Restore all backed-up files -ynh_restore +# FIXME: this should be managed by the core in the future +# Here, as a packager, you may have to tweak the ownerhsip/permissions +# such that the appropriate users (e.g. maybe www-data) can access +# files in some cases. +# But FOR THE LOVE OF GOD, do not allow r/x for "others" on the entire folder - +# this will be treated as a security issue. +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" -# Create and restore the database -ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" -ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ./db.sql +#================================================= +# RESTORE FAIL2BAN CONFIGURATION +#================================================= +# ynh_script_progression --message="Restoring the Fail2Ban configuration..." --weight=1 -# Reload -systemctl reload nginx -systemctl restart supervisor -supervisorctl restart budget +# ynh_restore_file "/etc/fail2ban/jail.d/$app.conf" +# ynh_restore_file "/etc/fail2ban/filter.d/$app.conf" +# ynh_systemd_action --action=restart --service_name=fail2ban -# Wait that gunicorn is ready to consider the install finished, that is to -# avoid HTTP 502 right after installation -for i in `seq 1 120` -do - test -S /tmp/budget.gunicorn.sock && break +#================================================= +# SPECIFIC RESTORATION +#================================================= +# REINSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Reinstalling dependencies..." --weight=4 + +# Define and install dependencies +ynh_install_app_dependencies "${pkg_dependencies[@]}" + +#================================================= +# RESTORE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Restoring the MySQL database..." --weight=1 + +db_pwd=$(ynh_app_setting_get --app=$app --key=mysqlpwd) +ynh_mysql_setup_db --db_user=$db_user --db_name=$db_name --db_pwd=$db_pwd +ynh_mysql_connect_as --user=$db_user --password=$db_pwd --database=$db_name < ./db.sql + +#================================================= +# RESTORE SYSTEMD +#================================================= +ynh_script_progression --message="Restoring the systemd configuration..." --weight=1 + +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..." --weight=1 + +yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd + +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --weight=1 + +ynh_systemd_action --service_name=$app --action="start" --log_path="systemd" --line_match="Booting worker" --timeout 30 + +# line_match isn't enough because ihatemoney may stop if database upgrades +for _ in {1..20}; do + test -S /tmp/budget.gunicorn_$app.sock && break sleep 1 done + +#================================================= +# GENERIC FINALIZATION +#================================================= +# RELOAD NGINX AND PHP-FPM +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --weight=1 + +ynh_systemd_action --service_name=nginx --action=reload + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Restoration completed for $app" --last diff --git a/scripts/upgrade b/scripts/upgrade old mode 100755 new mode 100644 index ef98703..855d95f --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,141 +1,227 @@ #!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." --weight=1 + app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) +final_path=$(ynh_app_setting_get --app=$app --key=final_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=mysqlpwd) -# Installation paths -INSTALL_DIR=/opt/yunohost/ihatemoney +#================================================= +# CHECK VERSION +#================================================= -# Source YunoHost helpers -. /usr/share/yunohost/helpers +### This helper will compare the version of the currently installed app and the version of the upstream package. +### $upgrade_type can have 2 different values +### - UPGRADE_APP if the upstream app version has changed +### - UPGRADE_PACKAGE if only the YunoHost package has changed +### ynh_check_app_version_changed will stop the upgrade if the app is up to date. +### UPGRADE_APP should be used to upgrade the core app only if there's an upgrade to do. +upgrade_type=$(ynh_check_app_version_changed) -domain=$(ynh_app_setting_get $app domain) -path=$(ynh_app_setting_get $app path) -is_public=$(ynh_app_setting_get "$app" is_public) +if ynh_compare_current_package_version --comparison le --version "4.1.5~ynh2"; then + upgrade_from_opt=true +else + upgrade_from_opt=false +fi -VENV_PY_VERSION=$(echo ${INSTALL_DIR}/venv/bin/python*.*|sed 's/.*python//') -SYSTEM_PY_VERSION=$(readlink /usr/bin/python3|sed s/.*python//) - -# Source local utils -source _common.sh +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." --weight=3 +# Backup the current version of the app +ynh_backup_before_upgrade ynh_clean_setup () { - if [ -e /opt/yunohost/ihatemoney/venv-old ] - then - mv /opt/yunohost/ihatemoney/venv{-old,} - fi + # 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 +#================================================= -#----------------------------PRE-UPGRADE MIGRATIONS----------------------- +if [[ "$upgrade_from_opt" == "false" ]]; then + ynh_script_progression --message="Stopping a systemd service..." --weight=1 - - -# MIGRATION: upgrade arg to typed boolean form - -if (($is_public != 0)) && (($is_public != 1)) -then - if [ $is_public = "No" ]; - then - is_public=0 - else - is_public=1 - fi - ynh_app_setting_set "$app" is_public "$is_public" + ynh_systemd_action --service_name=$app --action="stop" --log_path=systemd fi +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= +ynh_script_progression --message="Ensuring downward compatibility..." --weight=1 +# Cleaning legacy permissions +if ynh_legacy_permissions_exists; then + ynh_legacy_permissions_delete_all -# MIGRATION: Switch to a python3 venv -if [[ "$VENV_PY_VERSION" == 2.7 ]] -then - install_apt_dependencies - # Trash py2 venv - mv ${INSTALL_DIR}/venv ${INSTALL_DIR}/venv-old - init_virtualenv - - # Clears all cookie-sessions, because py2 & py3 sessions are incompatible - # Relates https://github.com/lepture/flask-wtf/issues/279 (fix unreleased) - new_secret_key=$(ynh_string_random 32) - ynh_replace_string "SECRET_KEY = \".*\"" "SECRET_KEY = \"${new_secret_key}\"" /etc/ihatemoney/ihatemoney.cfg + ynh_app_setting_delete --app=$app --key=is_public fi +# MIGRATION: Remove old code (from pre-4.1.5 versions, not using venv) +if [[ "$upgrade_from_opt" == "true" ]]; then + # Remove legacy install dir + ynh_secure_remove /opt/yunohost/ihatemoney -# MIGRATION: minor Py version has changed ? rebuilt venv + # Remove legacy Supervisor config + rm -f /etc/supervisor/conf.d/ihatemoney.conf -# Useful for Py 3.4 → 3.5, Jessie → Stretch, ynh 2.x → 3.x -if [[ "$VENV_PY_VERSION" != '2.7' ]] && [[ "$VENV_PY_VERSION" != "$SYSTEM_PY_VERSION" ]] -then - mv ${INSTALL_DIR}/venv ${INSTALL_DIR}/venv-old - init_virtualenv + if [ -e /etc/ihatemoney/settings.py ]; then + # Strip out the no longer used part of the settings + python3 -c "d = open('/etc/ihatemoney/settings.py').read().replace('try:\n from settings import *\nexcept ImportError:\n pass\n', ''); open('/etc/ihatemoney/settings.py', 'w').write(d)" + # Rename + mv /etc/ihatemoney/settings.py "/etc/ihatemoney/ihatemoney.cfg" + fi - # the static path changed - configure_nginx "$domain" "$path" + for old_file in "/etc/ihatemoney/ihatemoney.cfg" "/etc/$app/gunicorn.conf.py"; do + ynh_backup_if_checksum_is_different --file="$old_file" + ynh_delete_file_checksum --file="$old_file" + done + + final_path=/var/www/$app + ynh_app_setting_set --app=$app --key=final_path --value=$final_path + + db_name=ihatemoney + db_user=$db_name + ynh_app_setting_set --app=$app --key=db_name --value=$db_name fi -#-------------------------------UPGRADE------------------------- +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." --weight=1 + +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir="$final_path" + +#================================================= +# UPGRADE DEPENDENCIES +#================================================= +ynh_script_progression --message="Upgrading dependencies..." --weight=1 + +ynh_install_app_dependencies "${pkg_dependencies[@]}" + +#================================================= +# SPECIFIC UPGRADE +#================================================= +# Init venv +#================================================= +ynh_script_progression --message="Configuring the app's installation..." --weight=6 + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path + +# MIGRATION: Upgrade venv +python3 -m venv --upgrade "$final_path/venv" +"$final_path/venv/bin/python3" -m pip install --upgrade pip "${pip_dependencies[@]}" +python_venv_site_packages=$(__ynh_python_venv_get_site_packages_dir -d "$final_path/venv") + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading NGINX web server configuration..." --weight=1 + +# Create a dedicated NGINX config +## Needs $python_venv_site_packages +ynh_add_nginx_config "PYTHON_VERSION" + +#================================================= +# Setup gunicorn +#================================================= + +ynh_add_config --template ../conf/gunicorn.conf.py --destination "$final_path/gunicorn.conf.py" +chmod 600 "$final_path/gunicorn.conf.py" + +#================================================= +# Setup ihatemoney +#================================================= + +# Secret key for cookies encryption. +secret_key=$(ynh_string_random --length 32) +mails_sender="no-reply@$domain" +# Allows to comment some config lines if not using sub path +sub_path_only="$(if [[ "$path_url" == "/" ]]; then echo '# ' ; else echo ''; fi)" + +ynh_add_config --template ../conf/ihatemoney.cfg --destination "$final_path/ihatemoney.cfg" +chmod 600 "$final_path/ihatemoney.cfg" -# Upgrade code and dependencies -pip_install +# FIXME: this should be managed by the core in the future +# Here, as a packager, you may have to tweak the ownerhsip/permissions +# such that the appropriate users (e.g. maybe www-data) can access +# files in some cases. +# But FOR THE LOVE OF GOD, do not allow r/x for "others" on the entire folder - +# this will be treated as a security issue. +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:www-data "$final_path" +#================================================= +# SETUP SYSTEMD +#================================================= +ynh_script_progression --message="Upgrading systemd configuration..." --weight=1 -#-----------------------POST-UPGRADE MIGRATIONS----------------- +# Create a dedicated systemd config +ynh_add_systemd_config +#================================================= +# GENERIC FINALIZATION +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 +yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd +#================================================= +# START SYSTEMD SERVICE +#================================================= +ynh_script_progression --message="Starting a systemd service..." --weight=1 -# Python-MySQL is no longer maintained and does not support Py3 -ynh_replace_string "'mysql://" "'mysql+pymysql://" ${ihatemoney_conf_path} +ynh_systemd_action --service_name=$app --action="start" --log_path="systemd" --line_match="Booting worker" --timeout 30 +# line_match isn't enough because ihatemoney may stop if database upgrades +# FIXME: We need to wait for the db to upgrade and gunicorn to restart! +sleep 3 +#================================================= +# UPGRADE FAIL2BAN +#================================================= +# ynh_script_progression --message="Reconfiguring Fail2Ban..." --weight=1 -# MIGRATION: Remove old code (from pre-2.x versions, not using pip) +# # Create a dedicated Fail2Ban config +# ynh_add_fail2ban_config --logpath="/var/log/nginx/${domain}-error.log" --failregex="Regex to match into the log for a failed login" -ynh_secure_remove ${INSTALL_DIR}/src +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." --weight=1 +ynh_systemd_action --service_name=nginx --action=reload +#================================================= +# END OF SCRIPT +#================================================= -# MIGRATION: change the static path (from pre-2.x versions, not using pip) - -if grep -q /opt/yunohost/ihatemoney/src/ /etc/nginx/conf.d/${domain}.d/ihatemoney.conf -then - # the static path changed - configure_nginx "$domain" "$path" - - # Supervisor no longer change its directory to src/ dir - configure_supervisor - supervisorctl update -fi - - -# MIGRATION: new-style settings - -if [ -e /etc/ihatemoney/settings.py ]; then - # Strip out the no longer used part of the settings - python3 -c "d = open('/etc/ihatemoney/settings.py').read().replace('try:\n from settings import *\nexcept ImportError:\n pass\n', ''); open('/etc/ihatemoney/settings.py', 'w').write(d)" - # Rename - mv /etc/ihatemoney/settings.py ${ihatemoney_conf_path} -fi - - - -# MIGRATION: Remove no longer used symlink - -# (ihatemoney now read its conf by default from /etc/ihatemoney/ihatemoney.cfg) -ynh_secure_remove ${INSTALL_DIR}/src/budget/settings.py - - - -#----------------------------FINALIZATION----------------------- - -# Everything went ok ? Let's keep this new venv. -ynh_secure_remove ${INSTALL_DIR}/venv-old - -# Restart backend -supervisorctl restart budget - -# Reload nginx conf -systemctl reload nginx +ynh_script_progression --message="Upgrade of $app completed" --last From c187a39fe1c4cea5a0d028f3d76e4bf83209cd43 Mon Sep 17 00:00:00 2001 From: Salamandar <6552989+Salamandar@users.noreply.github.com> Date: Thu, 16 Dec 2021 19:41:08 +0100 Subject: [PATCH 2/4] Update scripts/install log_type -> log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric Gaspar <46165813+ericgaspar@users.noreply.github.com> --- scripts/install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/install b/scripts/install index eb05f17..6a8713e 100755 --- a/scripts/install +++ b/scripts/install @@ -140,7 +140,7 @@ ynh_add_systemd_config #================================================= ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 -yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd +yunohost service add $app --description="$app daemon for IHateMoney" --log=systemd #================================================= # START SYSTEMD SERVICE From 5344aec40eeb533b64d3d2f202a3c90c40cbefe9 Mon Sep 17 00:00:00 2001 From: Salamandar <6552989+Salamandar@users.noreply.github.com> Date: Thu, 16 Dec 2021 19:41:34 +0100 Subject: [PATCH 3/4] Update scripts/restore log_type -> log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric Gaspar <46165813+ericgaspar@users.noreply.github.com> --- scripts/restore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/restore b/scripts/restore index ede60f0..30d8ce9 100755 --- a/scripts/restore +++ b/scripts/restore @@ -114,7 +114,7 @@ systemctl enable $app.service --quiet #================================================= ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 -yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd +yunohost service add $app --description="$app daemon for IHateMoney" --log=systemd #================================================= # START SYSTEMD SERVICE From 22274b3895def8ef52f27c50fd2f69d514ad0e49 Mon Sep 17 00:00:00 2001 From: Salamandar <6552989+Salamandar@users.noreply.github.com> Date: Thu, 16 Dec 2021 19:41:42 +0100 Subject: [PATCH 4/4] Update scripts/upgrade log_type -> log MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric Gaspar <46165813+ericgaspar@users.noreply.github.com> --- scripts/upgrade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upgrade b/scripts/upgrade index 855d95f..5457ea3 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -192,7 +192,7 @@ ynh_add_systemd_config #================================================= ynh_script_progression --message="Integrating service in YunoHost..." --weight=1 -yunohost service add $app --description="$app daemon for IHateMoney" --log_type=systemd +yunohost service add $app --description="$app daemon for IHateMoney" --log=systemd #================================================= # START SYSTEMD SERVICE