From b000bd18c6e6945486aa86af49c74b1f3797d552 Mon Sep 17 00:00:00 2001 From: yalh76 Date: Sun, 16 May 2021 13:47:02 +0200 Subject: [PATCH] Apply exampe_ynh --- README.md | 38 ++++--- conf/gunicorn.conf.py | 4 +- conf/ihatemoney.cfg | 8 +- conf/ihatemoney.conf | 6 + conf/nginx.conf | 46 ++++---- conf/supervisord.conf | 6 - manifest.json | 25 ++--- scripts/_common.sh | 79 +++---------- scripts/backup | 82 ++++++++++---- scripts/install | 235 ++++++++++++++++++++++++++++----------- scripts/remove | 115 +++++++++++++++---- scripts/restore | 152 ++++++++++++++++++------- scripts/upgrade | 246 +++++++++++++++++++++++++++-------------- scripts/ynh_supervisor | 167 ++++++++++++++++++++++++++++ 14 files changed, 855 insertions(+), 354 deletions(-) create mode 100644 conf/ihatemoney.conf delete mode 100644 conf/supervisord.conf create mode 100644 scripts/ynh_supervisor diff --git a/README.md b/README.md index 00a2308..fb8892f 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,15 @@ -Yunohost app for « I hate money » budget web app +« I hate money » budget web app for YunoHost ================================================ -[![Install « I hate money » with YunoHost](https://install-app.yunohost.org/install-with-yunohost.png)](https://install-app.yunohost.org/?app=ihatemoney) +[![Integration level](https://dash.yunohost.org/integration/example.svg)](https://dash.yunohost.org/appci/app/example) ![](https://ci-apps.yunohost.org/ci/badges/example.status.svg) ![](https://ci-apps.yunohost.org/ci/badges/example.maintain.svg) +[![Install example with YunoHost](https://install-app.yunohost.org/install-with-yunohost.svg)](https://install-app.yunohost.org/?app=example) -- Supported Yunohost versions : 2.6.x, 2.7.x 3.x -- Tested Yunohost version : 3.3.1 +> *This package allows you to install 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.* -*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* - -Backs on MySQL database, the identifiers are per-project, not per-user, so no -way to do advanced SSO integration with yunohost accounts. +## 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. The behaviour is either: - **non-public app**: @@ -25,13 +22,9 @@ The behaviour is either: - per-project identifiers required - any visitor can create a new project. -Update ------- +**Shipped version:** 4.1.5~ynh2 -To update the app, use: - -`sudo yunohost app upgrade ihatemoney -u https://github.com/YunoHost-Apps/ihatemoney_ynh` - +**Demo:** https://demo.example.com Maintainer ---------- @@ -50,3 +43,16 @@ Ihatemoney license [Full license text](https://github.com/spiral-project/ihatemoney/blob/master/LICENSE) + +## Developer info + +Please send your pull request to the [testing branch](https://github.com/YunoHost-Apps/example_ynh/tree/testing). + +To try the testing branch, please proceed like that. +``` +sudo yunohost app install https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +or +sudo yunohost app upgrade example -u https://github.com/YunoHost-Apps/example_ynh/tree/testing --debug +``` + +**More info regarding app packaging:** https://yunohost.org/packaging_apps \ No newline at end of file diff --git a/conf/gunicorn.conf.py b/conf/gunicorn.conf.py index 60fcdd1..470520f 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__/__APP__.gunicorn.log" loglevel = "info" -bind = "unix:/tmp/budget.gunicorn.sock" +bind = "unix:/tmp/__APP__.gunicorn.sock" diff --git a/conf/ihatemoney.cfg b/conf/ihatemoney.cfg index f41f6ce..dbce12d 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 = ("__APP__ manager", "__SENDER_MAIL__") +APPLICATION_ROOT='__PATH__' try: from settings import * diff --git a/conf/ihatemoney.conf b/conf/ihatemoney.conf new file mode 100644 index 0000000..914455d --- /dev/null +++ b/conf/ihatemoney.conf @@ -0,0 +1,6 @@ +[program:__APP__] +command=__FINALPATH__/venv/bin/gunicorn -c /etc/__APP__/gunicorn.conf.py __APP__.wsgi:application +user=__APP__ +autostart=true +autorestart=true +redirect_stderr=true diff --git a/conf/nginx.conf b/conf/nginx.conf index 3d13167..aa29ec7 100644 --- a/conf/nginx.conf +++ b/conf/nginx.conf @@ -1,27 +1,27 @@ -location PATHTOCHANGE/static/ { - alias /opt/yunohost/ihatemoney/venv/lib/pythonPYTHON_VERSION/site-packages/ihatemoney/static/; +location __PATH__/static/ { + alias __FINALPATH__/venv/lib/pythonPYTHON_VERSION/site-packages/ihatemoney/static/; } -location PATHTOCHANGE { - # Force https. - if ($scheme = http) { - rewrite ^ https://$server_name$request_uri? permanent; - } +location __PATH__/ { + # Force usage of https + if ($scheme = http) { + rewrite ^ https://$server_name$request_uri? permanent; + } - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_redirect off; - proxy_connect_timeout 90; - proxy_send_timeout 180; - proxy_read_timeout 180; - proxy_buffer_size 16k; - proxy_buffers 8 16k; - proxy_busy_buffers_size 32k; - proxy_intercept_errors on; - if (!-f $request_filename) { - proxy_pass http://unix:/tmp/budget.gunicorn.sock; - break; - } + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header Host $http_host; + proxy_redirect off; + proxy_connect_timeout 90; + proxy_send_timeout 180; + proxy_read_timeout 180; + proxy_buffer_size 16k; + proxy_buffers 8 16k; + proxy_busy_buffers_size 32k; + proxy_intercept_errors on; + if (!-f $request_filename) { + proxy_pass http://unix:/tmp/__APP__.gunicorn.sock; + break; + } - # Include SSOWAT user panel. - include conf.d/yunohost_panel.conf.inc; + # Include SSOWAT user panel. + include conf.d/yunohost_panel.conf.inc; } 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/manifest.json b/manifest.json index 5e1bd73..9801709 100644 --- a/manifest.json +++ b/manifest.json @@ -6,8 +6,14 @@ "en": "A simple shared budget manager web application", "fr": "Une application web de comptes partagés à plusieurs" }, - "url": "http://ihatemoney.org/", "version": "4.1.5~ynh2", + "url": "http://ihatemoney.org/", + "upstream": { + "license": "free", + "demo": "https://ihatemoney.org/", + "admindoc": "https://ihatemoney.readthedocs.io/", + "code": "https://github.com/YunoHost-Apps/ihatemoney_ynh" + }, "license": "free", "maintainer": { "name": "Jocelyn Delalande", @@ -18,35 +24,26 @@ "yunohost": ">= 3.8" }, "multi_instance": false, - "services": ["nginx", "mysql", "postfix"], + "services": [ + "nginx", + "mysql" + ], "arguments": { "install" : [ { "name": "domain", "type": "domain", - "ask": { - "en": "Choose a domain for ihatemoney", - "fr": "Choisir un domaine pour ihatemoney" - }, "example": "example.com" }, { "name": "path", "type": "path", - "ask": { - "en": "Choose a path for ihatemoney", - "fr": "Choisir un chemin pour ihatemoney" - }, "example": "/example", "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)" - }, "default": true } ] diff --git a/scripts/_common.sh b/scripts/_common.sh index 8c54da6..3080111 100644 --- a/scripts/_common.sh +++ b/scripts/_common.sh @@ -1,69 +1,20 @@ -### Constants +#!/bin/bash -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" +#================================================= +# COMMON VARIABLES +#================================================= +# dependencies used by the app +pkg_dependencies="python3-venv supervisor" -### Functions +#================================================= +# PERSONAL HELPERS +#================================================= +#================================================= +# EXPERIMENTAL HELPERS +#================================================= -install_apt_dependencies() { - ynh_install_app_dependencies \ - python3-dev \ - python3-virtualenv \ - libffi-dev \ - libssl-dev \ - supervisor \ - virtualenv -} - -create_unix_user() { - mkdir -p /opt/yunohost - useradd ihatemoney -d /opt/yunohost/ihatemoney/ --create-home || ynh_die "User creation failed" -} - -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 - - # 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 index 7c21e82..222c413 100644 --- a/scripts/backup +++ b/scripts/backup @@ -1,34 +1,76 @@ #!/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 +#================================================= + +ynh_clean_setup () { + ynh_clean_check_starting +} +# 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) +#================================================= +# BACKUP THE APP MAIN DIR +#================================================= -# 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" +ynh_backup --src_path="$final_path" -# Dump the database -mysqldump -u "$dbuser" -p"$dbpass" --no-create-db "$dbname" > ./db.sql +#================================================= +# BACKUP THE NGINX CONFIGURATION +#================================================= -# Backup code and venv -ynh_backup "$INSTALL_DIR" "install_dir" +ynh_backup --src_path="/etc/nginx/conf.d/$domain.d/$app.conf" + +#================================================= +# SPECIFIC BACKUP +#================================================= +# BACKUP SUPERVISOR CONFIGURATION +#================================================= + +ynh_backup --src_path="/etc/supervisor/conf.d/$app.conf" + +#================================================= +# BACKUP VARIOUS FILES +#================================================= + +ynh_backup --src_path="/etc/$app/" + +#================================================= +# BACKUP THE MYSQL DATABASE +#================================================= +ynh_print_info --message="Backing up the MySQL database..." + +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/install b/scripts/install index 8c1202c..82a26ac 100755 --- a/scripts/install +++ b/scripts/install @@ -1,88 +1,195 @@ #!/bin/bash -# Source YunoHost helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + source _common.sh +source ynh_supervisor 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 +#================================================= +ynh_clean_setup () { + ynh_clean_check_starting +} +# 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 -# Save app settings -ynh_app_setting_set "$app" mysqlpwd "$db_pwd" -ynh_app_setting_set "$app" is_public "$is_public" +# Constant arguments +secret_key=$(ynh_string_random --length=32 | base64) +sender_mail="no-reply@${domain}" -install_apt_dependencies +app=$YNH_APP_INSTANCE_NAME -create_unix_user +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." -# Prepare venv -init_virtualenv -pip_install +final_path=/opt/yunohost/$app +test ! -e "$final_path" || ynh_die --message="This path already contains a folder" -create_system_dirs +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url -# Configure gunicorn -install -o ihatemoney -g ihatemoney -m 644 \ - ../conf/gunicorn.conf.py /etc/ihatemoney/gunicorn.conf.py +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." -# 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_app_setting_set --app=$app --key=domain --value=$domain +ynh_app_setting_set --app=$app --key=path --value=$path_url +ynh_app_setting_set --app=$app --key=path --value=$secret_key +ynh_app_setting_set --app=$app --key=path --value=$sender_mail -# 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 +#================================================= +# STANDARD MODIFICATIONS +#================================================= +# INSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Installing dependencies..." -# If app is public, add url to SSOWat conf as skipped_uris -if [[ "$is_public" -ne 0 ]]; +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Configuring system user..." + +# Create a system user +ynh_system_user_create --username=$app --home_dir=$final_path + +#================================================= +# CREATE A MYSQL DATABASE +#================================================= +ynh_script_progression --message="Creating a MySQL 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_mysql_setup_db --db_user=$db_user --db_name=$db_name + +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +ynh_script_progression --message="Setting up source files..." + +ynh_app_setting_set --app=$app --key=final_path --value=$final_path +# Download, check integrity, uncompress and patch the source from app.src +mkdir -p $final_path + +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" + +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Configuring NGINX web server..." + +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# SPECIFIC SETUP +#================================================= +# BUILD IHATEMONEY +#================================================= +ynh_script_progression --message="Building $app..." + +pushd $final_path + # Prepare venv + python3 -m venv $final_path/venv + source $final_path/venv/bin/activate + + # PyMySQL ? cryptography ? setuptools>=18.5 + # Required on Jessie, Stretch has setuptools>=18.5 + $final_path/venv/bin/pip install 'setuptools>=18.5' + + # 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. + $final_path/venv/bin/pip install --upgrade \ + 'gunicorn>=19.3.0' \ + 'PyMySQL>=0.9,<0.10' \ + 'ihatemoney>=4,<5' \ + 'SQLAlchemy<1.4' +popd + +#================================================= +# ADD A CONFIGURATION +#================================================= +ynh_script_progression --message="Adding a configuration file..." + +install -o $app -g $app -m 755 -d /var/log/$app /etc/$app + +ynh_add_config --template="../conf/gunicorn.conf.py" --destination="/etc/$app/gunicorn.conf.py" + +chmod 644 "/etc/$app/gunicorn.conf.py" +chown $app:$app "/etc/$app/gunicorn.conf.py" + +ynh_add_config --template="../conf/ihatemoney.cfg" --destination="/etc/$app/ihatemoney.cfg" + +chmod 400 "/etc/$app/ihatemoney.cfg" +chown $app:$app "/etc/$app/ihatemoney.cfg" + +#================================================= +# SETUP SUPERVISOR +#================================================= +ynh_script_progression --message="Configuring a supervisor service..." + +# Create a dedicated supervisor config +ynh_add_supervisor_config --service="$app" --template=ihatemoney.conf + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add "supervisor" --description="Supervisor daemon for $app" --log="/var/log/$app/${app}-horizon.log" + +#================================================= +# START SUPERVISOR SERVICE +#================================================= +ynh_script_progression --message="Starting a supervisor service..." + +# Start a supervisor service +ynh_supervisor_action --service_name=$app --action="reload" --log_path="systemd" --line_match="success: $app" + +#================================================= +# SETUP SSOWAT +#================================================= +ynh_script_progression --message="Configuring permissions..." + +# Make app public if necessary +if [ $is_public -eq 1 ] then - ynh_app_setting_set $app unprotected_uris "/" + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + ynh_permission_update --permission="main" --add="visitors" fi -# Configure Nginx -configure_nginx "$domain" "$path" +#================================================= +# RELOAD NGINX +#================================================= +ynh_script_progression --message="Reloading NGINX web server..." -# Start backend -systemctl start supervisor +ynh_systemd_action --service_name=nginx --action=reload -# 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 - sleep 1 -done +#================================================= +# END OF SCRIPT +#================================================= -# If socket not ready after 2 minutes waiting, ihatemoney will not work. -test -S /tmp/budget.gunicorn.sock || ynh_die - -systemctl reload nginx +ynh_script_progression --message="Installation of $app completed" diff --git a/scripts/remove b/scripts/remove index 5620f80..f454c4c 100755 --- a/scripts/remove +++ b/scripts/remove @@ -1,37 +1,106 @@ #!/bin/bash -# Source YunoHost helpers +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source ynh_supervisor 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..." -# 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 "supervisor" >/dev/null +then + ynh_script_progression --message="Removing supervisor service..." + yunohost service remove "supervisor" +fi + +#================================================= +# STOP AND REMOVE SERVICE +#================================================= +ynh_script_progression --message="Stopping and removing the supervisor service..." + +# Remove the dedicated supervisor config +ynh_remove_supervisor_config --service="$app" + +#================================================= +# REMOVE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Removing the MySQL database..." + +# 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..." + +# Remove metapackage and its dependencies +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="$final_path" + +#================================================= +# REMOVE NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Removing NGINX web server configuration..." + +# Remove the dedicated NGINX config +ynh_remove_nginx_config + + +#================================================= +# SPECIFIC REMOVE +#================================================= +# REMOVE VARIOUS FILES +#================================================= +ynh_script_progression --message="Removing various files..." # Remove settings ynh_secure_remove /etc/ihatemoney -ynh_secure_remove /etc/supervisor/conf.d/ihatemoney.conf -ynh_remove_nginx_config -# Restart services -systemctl force-reload supervisor +# Remove the log files +ynh_secure_remove --file="/var/log/$app" -# Remove app dependencies -ynh_remove_app_dependencies +#================================================= +# GENERIC FINALIZATION +#================================================= +# REMOVE DEDICATED USER +#================================================= +ynh_script_progression --message="Removing the dedicated system user..." -# Delete user -userdel ihatemoney +# Delete a system user +ynh_system_user_delete --username=$app + +#================================================= +# END OF SCRIPT +#================================================= + +ynh_script_progression --message="Removal of $app completed" diff --git a/scripts/restore b/scripts/restore index 0912f3f..7d1fba5 100644 --- a/scripts/restore +++ b/scripts/restore @@ -1,58 +1,136 @@ #!/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 ../settings/scripts/ynh_supervisor source /usr/share/yunohost/helpers +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +ynh_clean_setup () { + ynh_clean_check_starting +} +# 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..." + 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 -# 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..." +ynh_webpath_available --domain=$domain --path_url=$path_url \ + || ynh_die --message="Path not available: ${domain}${path_url}" +test ! -d $final_path \ + || ynh_die --message="There is already a directory: $final_path " -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..." -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..." -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..." -create_system_dirs +ynh_restore_file --origin_path="$final_path" -# Restore all backed-up files -ynh_restore +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" -# Create and restore the database -ynh_mysql_create_db "$dbname" "$dbuser" "$dbpass" -ynh_mysql_connect_as "$dbuser" "$dbpass" "$dbname" < ./db.sql +#================================================= +# SPECIFIC RESTORATION +#================================================= +# REINSTALL DEPENDENCIES +#================================================= +ynh_script_progression --message="Reinstalling dependencies..." -# Reload -systemctl reload nginx -systemctl restart supervisor -supervisorctl restart budget +# Define and install dependencies +ynh_install_app_dependencies $pkg_dependencies -# 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 - sleep 1 -done +#================================================= +# RESTORE THE MYSQL DATABASE +#================================================= +ynh_script_progression --message="Restoring the MySQL database..." + +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 SUPERVISOR CONFIGURATION +#================================================= +ynh_script_progression --message="Restoring the supervisor configuration..." + +ynh_restore_file --origin_path="/etc/supervisor/conf.d/$app" +supervisorctl reread && supervisorctl update + +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add "supervisor" --description="Supervisor daemon for $app" --log="/var/log/$app/${app}-horizon.log" + +#================================================= +# START SUPERVISOR SERVICE +#================================================= +ynh_script_progression --message="Starting a supervisor service..." + +ynh_supervisor_action --service_name="$app" --action="reload" --log_path="systemd" --line_match="success: ${app}-horizon" + +#================================================= +# RESTORE VARIOUS FILES +#================================================= +ynh_script_progression --message="Restoring various files..." + +install -o $app -g $app -m 755 -d /var/log/ihatemoney + +#================================================= +# GENERIC FINALIZATION +#================================================= +# RELOAD NGINX AND PHP-FPM +#================================================= +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" diff --git a/scripts/upgrade b/scripts/upgrade index ef98703..bd234c2 100755 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -1,54 +1,65 @@ #!/bin/bash + +#================================================= +# GENERIC START +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source _common.sh +source ynh_supervisor +source /usr/share/yunohost/helpers + +#================================================= +# LOAD SETTINGS +#================================================= +ynh_script_progression --message="Loading installation settings..." + app=$YNH_APP_INSTANCE_NAME +domain=$(ynh_app_setting_get --app=$app --key=domain) +path_url=$(ynh_app_setting_get --app=$app --key=path) -# Installation paths -INSTALL_DIR=/opt/yunohost/ihatemoney -# Source YunoHost helpers -. /usr/share/yunohost/helpers -domain=$(ynh_app_setting_get $app domain) -path=$(ynh_app_setting_get $app path) -is_public=$(ynh_app_setting_get "$app" is_public) -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 +#================================================= +# CHECK VERSION +#================================================= +ynh_script_progression --message="Checking version..." +upgrade_type=$(ynh_check_app_version_changed) + +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= +ynh_script_progression --message="Backing up the app before upgrading (may take a while)..." + +# 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 + ynh_clean_check_starting + # Restore it if the upgrade fails + ynh_restore_upgradebackup } - +# Exit if an error occurs during the execution of the script ynh_abort_if_errors +#================================================= +# ENSURE DOWNWARD COMPATIBILITY +#================================================= +ynh_script_progression --message="Ensuring downward compatibility..." -#----------------------------PRE-UPGRADE MIGRATIONS----------------------- +# Cleaning legacy permissions +if ynh_legacy_permissions_exists; then + ynh_legacy_permissions_delete_all - - -# 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_app_setting_delete --app=$app --key=is_public fi - - # MIGRATION: Switch to a python3 venv -if [[ "$VENV_PY_VERSION" == 2.7 ]] +VENV_PY_VERSION=$(echo ${INSTALL_DIR}/venv/bin/python*.*|sed 's/.*python//')if [[ "$VENV_PY_VERSION" == 2.7 ]] then install_apt_dependencies # Trash py2 venv @@ -66,6 +77,7 @@ fi # MIGRATION: minor Py version has changed ? rebuilt venv # Useful for Py 3.4 → 3.5, Jessie → Stretch, ynh 2.x → 3.x +SYSTEM_PY_VERSION=$(readlink /usr/bin/python3|sed s/.*python//) if [[ "$VENV_PY_VERSION" != '2.7' ]] && [[ "$VENV_PY_VERSION" != "$SYSTEM_PY_VERSION" ]] then mv ${INSTALL_DIR}/venv ${INSTALL_DIR}/venv-old @@ -75,67 +87,139 @@ then configure_nginx "$domain" "$path" fi -#-------------------------------UPGRADE------------------------- - - -# Upgrade code and dependencies -pip_install - - -#-----------------------POST-UPGRADE MIGRATIONS----------------- - - - - -# Python-MySQL is no longer maintained and does not support Py3 -ynh_replace_string "'mysql://" "'mysql+pymysql://" ${ihatemoney_conf_path} - - - -# MIGRATION: Remove old code (from pre-2.x versions, not using pip) - -ynh_secure_remove ${INSTALL_DIR}/src - - - -# 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 - +ynh_secure_remove --file="$final_path/src" # MIGRATION: new-style settings -if [ -e /etc/ihatemoney/settings.py ]; then +if [ -e /etc/$app/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)" + python3 -c "d = open('/etc/$app/settings.py').read().replace('try:\n from settings import *\nexcept ImportError:\n pass\n', ''); open('/etc/$app/settings.py', 'w').write(d)" # Rename - mv /etc/ihatemoney/settings.py ${ihatemoney_conf_path} + mv /etc/$app/settings.py /etc/$app/$app.cfg fi +#================================================= +# STANDARD UPGRADE STEPS +#================================================= +# STOP SUPERVISOR SERVICE +#================================================= +ynh_script_progression --message="Stopping a supervisor service..." +ynh_supervisor_action --service_name="$app" --action="stop" --log_path="systemd" --line_match="stopped: ${app}-horizon" -# MIGRATION: Remove no longer used symlink +#================================================= +# CREATE DEDICATED USER +#================================================= +ynh_script_progression --message="Making sure dedicated system user exists..." -# (ihatemoney now read its conf by default from /etc/ihatemoney/ihatemoney.cfg) -ynh_secure_remove ${INSTALL_DIR}/src/budget/settings.py +# Create a dedicated user (if not existing) +ynh_system_user_create --username=$app --home_dir=$final_path +#================================================= +# DOWNLOAD, CHECK AND UNPACK SOURCE +#================================================= +if [ "$upgrade_type" == "UPGRADE_APP" ] +then + ynh_script_progression --message="Upgrading source files..." -#----------------------------FINALIZATION----------------------- + # Download, check integrity, uncompress and patch the source from app.src + mkdir -p $final_path +fi -# Everything went ok ? Let's keep this new venv. -ynh_secure_remove ${INSTALL_DIR}/venv-old +chmod 750 "$final_path" +chmod -R o-rwx "$final_path" +chown -R $app:$app "$final_path" -# Restart backend -supervisorctl restart budget +#================================================= +# NGINX CONFIGURATION +#================================================= +ynh_script_progression --message="Upgrading NGINX web server configuration..." -# Reload nginx conf -systemctl reload nginx +# Create a dedicated NGINX config +ynh_add_nginx_config + +#================================================= +# UPGRADE DEPENDENCIES +#================================================= +ynh_script_progression --message="Upgrading dependencies..." + +ynh_install_app_dependencies $pkg_dependencies + +#================================================= +# SPECIFIC UPGRADE +#================================================= +# BUILD IHATEMONEY +#================================================= +ynh_script_progression --message="Building $app..." + +pushd $final_path + # Prepare venv + ynh_secure_remove --file="$final_path/venv" + python3 -m venv $final_path/venv + source $final_path/venv/bin/activate + + # PyMySQL ? cryptography ? setuptools>=18.5 + # Required on Jessie, Stretch has setuptools>=18.5 + $final_path/venv/bin/pip install 'setuptools>=18.5' + + # 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. + $final_path/venv/bin/pip install --upgrade \ + 'gunicorn>=19.3.0' \ + 'PyMySQL>=0.9,<0.10' \ + 'ihatemoney>=4,<5' \ + 'SQLAlchemy<1.4' +popd + +#================================================= +# UPDATE A CONFIG FILE +#================================================= +ynh_script_progression --message="Updating a config file..." + +ynh_add_config --template="../conf/gunicorn.conf.py" --destination="/etc/$app/gunicorn.conf.py" + +chmod 644 "/etc/$app/gunicorn.conf.py" +chown $app:$app "/etc/$app/gunicorn.conf.py" + +ynh_add_config --template="../conf/ihatemoney.cfg" --destination="/etc/$app/ihatemoney.cfg" + +chmod 400 "/etc/$app/ihatemoney.cfg" +chown $app:$app "/etc/$app/ihatemoney.cfg" + +#================================================= +# SETUP SUPERVISOR +#================================================= +ynh_script_progression --message="Upgrading supervisor configuration..." + +# Create a dedicated supervisor config +ynh_add_supervisor_config --service="$app" --template=ihatemoney.conf + +#================================================= +# GENERIC FINALIZATION +#================================================= +# INTEGRATE SERVICE IN YUNOHOST +#================================================= +ynh_script_progression --message="Integrating service in YunoHost..." + +yunohost service add "supervisor" --description="Supervisor daemon for $app" --log="/var/log/$app/${app}-horizon.log" + +#================================================= +# START SUPERVISOR SERVICE +#================================================= +ynh_script_progression --message="Starting a supervisor service..." + +ynh_supervisor_action --service_name="$app" --action="start" --log_path="systemd" --line_match="success: ${app}-horizon" + +#================================================= +# 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" diff --git a/scripts/ynh_supervisor b/scripts/ynh_supervisor new file mode 100644 index 0000000..be6d47b --- /dev/null +++ b/scripts/ynh_supervisor @@ -0,0 +1,167 @@ +#!/bin/bash + +# Create a dedicated supervisor config +# +# usage: ynh_add_supervisor_config [--service=service] [--template=template] +# | arg: -s, --service= - Service name (optionnal, `$app` by default) +# | arg: -t, --template= - Name of template file (optionnal, this is 'supervisor' by default, meaning ./conf/supervisor.service will be used as template) +# +# This will use the template `../conf/.service`. +# +# See the documentation of `ynh_add_config` for a description of the template +# format and how placeholders are replaced with actual variables. +# +# Requires YunoHost version 2.7.11 or higher. +ynh_add_supervisor_config () { + # Declare an array to define the options of this helper. + local legacy_args=stv + local -A args_array=( [s]=service= [t]=template= [v]=others_var=) + local service + local template + local others_var + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local service="${service:-$app}" + local template="${template:-supervisor.service}" + others_var="${others_var:-}" + + [[ -z "$others_var" ]] || ynh_print_warn --message="Packagers: using --others_var is unecessary since Yunohost 4.2" + + ynh_add_config --template="$YNH_APP_BASEDIR/conf/$template" --destination="/etc/supervisor/conf.d/$service.conf" + + supervisorctl reread + supervisorctl update +} + +# Remove the dedicated supervisor config +# +# usage: ynh_remove_supervisor_config [--service=service] +# | arg: -s, --service= - Service name (optionnal, $app by default) +# +# Requires YunoHost version 2.7.2 or higher. +ynh_remove_supervisor_config () { + # Declare an array to define the options of this helper. + local legacy_args=s + local -A args_array=( [s]=service= ) + local service + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + local service="${service:-$app}" + + local finalsupervisorconf="/etc/supervisor/conf.d/$service.conf" + if [ -e "$finalsupervisorconf" ] + then + ynh_supervisor_action --service_name=$service --action=stop + ynh_secure_remove --file="$finalsupervisorconf" + supervisorctl reread + supervisorctl update + fi +} + +# Start (or other actions) a service, print a log in case of failure and optionnaly wait until the service is completely started +# +# usage: ynh_supervisor_action [--service_name=service_name] [--action=action] [ [--line_match="line to match"] [--log_path=log_path] [--timeout=300] [--length=20] ] +# | arg: -n, --service_name= - Name of the service to start. Default : `$app` +# | arg: -a, --action= - Action to perform with supervisorctl. Default: start +# | arg: -l, --line_match= - Line to match - The line to find in the log to attest the service have finished to boot. If not defined it don't wait until the service is completely started. +# | arg: -p, --log_path= - Log file - Path to the log file. Default : `/var/log/$app/$app.log` +# | arg: -t, --timeout= - Timeout - The maximum time to wait before ending the watching. Default : 300 seconds. +# | arg: -e, --length= - Length of the error log : Default : 20 +# +# Requires YunoHost version 3.5.0 or higher. +ynh_supervisor_action() { + # Declare an array to define the options of this helper. + local legacy_args=nalpte + declare -Ar args_array=( [n]=service_name= [a]=action= [l]=line_match= [p]=log_path= [t]=timeout= [e]=length= ) + local service_name + local action + local line_match + local length + local log_path + local timeout + # Manage arguments with getopts + ynh_handle_getopts_args "$@" + service_name="${service_name:-$app}" + action=${action:-start} + line_match=${line_match:-} + length=${length:-20} + log_path="${log_path:-/var/log/$service_name/$service_name.log}" + timeout=${timeout:-300} + + # Start to read the log + if [[ -n "$line_match" ]] + then + local templog="$(mktemp)" + # Following the starting of the app in its log + if [ "$log_path" == "systemd" ] + then + # Read the supervisor journal + journalctl --unit=supervisor --follow --since=-0 --quiet > "$templog" & + # Get the PID of the journalctl command + local pid_tail=$! + else + # Read the specified log file + tail --follow=name --retry --lines=0 "$log_path" > "$templog" 2>&1 & + # Get the PID of the tail command + local pid_tail=$! + fi + fi + + # Use reload-or-restart instead of reload. So it wouldn't fail if the service isn't running. + if [ "$action" == "reload" ]; then + action="reload-or-restart" + fi + + # If the service fails to perform the action + if ! supervisorctl $action $service_name + then + # Show syslog for this service + ynh_exec_err journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service_name + # If a log is specified for this service, show also the content of this log + if [ -e "$log_path" ] + then + ynh_exec_err tail --lines=$length "$log_path" + fi + ynh_clean_check_starting + return 1 + fi + + # Start the timeout and try to find line_match + if [[ -n "${line_match:-}" ]] + then + set +x + local i=0 + for i in $(seq 1 $timeout) + do + # Read the log until the sentence is found, that means the app finished to start. Or run until the timeout + if grep --extended-regexp --quiet "$line_match" "$templog" + then + ynh_print_info --message="The service $service_name has correctly executed the action ${action}." + break + fi + if [ $i -eq 3 ]; then + echo -n "Please wait, the service $service_name is ${action}ing" >&2 + fi + if [ $i -ge 3 ]; then + echo -n "." >&2 + fi + sleep 1 + done + set -x + if [ $i -ge 3 ]; then + echo "" >&2 + fi + if [ $i -eq $timeout ] + then + ynh_print_warn --message="The service $service_name didn't fully executed the action ${action} before the timeout." + ynh_print_warn --message="Please find here an extract of the end of the log of the service $service_name:" + ynh_exec_warn journalctl --quiet --no-hostname --no-pager --lines=$length --unit=$service_name + if [ -e "$log_path" ] + then + ynh_print_warn --message="\-\-\-" + ynh_exec_warn tail --lines=$length "$log_path" + fi + fi + ynh_clean_check_starting + fi +}