From e039f9f061ef6232f2e294904838879792fcac5f Mon Sep 17 00:00:00 2001 From: Sunil Mohan Adapa Date: Thu, 3 Apr 2025 14:20:41 -0700 Subject: [PATCH] upgrades: Show notification before, during, and after a dist upgrade - Show a notification 60 days, 30 days, 1 week, and 1 day before distribution upgrade. If a notification is dismissed for any of these periods don't show again until new period starts. Override any previous notification. - Show a notification just before the distribution upgrade showing that the process has started. Override any previous notification. - Show a notification after the distribution upgrade is completed that it is done. Override any previous notification. Keep this until it is 60 days before next distribution upgrade. If user dismisses the notification, don't show it again. Tests: - Start a bookworm VM. - Disable the auto updates. Set the date to 2025-08-01. Start the service. Notification is not shown when distribution check is done. Enable auto updates. - Set the date to 2025-07-01. Start the service. No notification is shown after distribution upgrade check is run. - Set the date to 2025-08-01. Start the service. Notification is shown when distribution check is done. Clicking on the 'Go to Distribution Update' takes to distribution update page. - Set the date to 2025-08-02. Start the service. Notification is not updated when distribution check is done. Dismiss the notification. - Set the date to 2025-08-03. Start the service. Notification is not shown when distribution check is done. - Set the date to 2025-08-22. Start the service. Notification is shown when distribution check is done. Dismiss the notification. - Set the date to 2025-08-23. Start the service. Notification is not shown when distribution check is done. - Set the date to 2025-09-15. Start the service. Notification is shown when distribution check is done. - Set the date to 2025-09-18 18:00. Start the service. Notification is shown when distribution check is done. - Set the date to 2025-09-19 18:00. Start the service. Notification is shown that distribution update has started. Distribution upgrade has started. Dismiss this notification. Upgrade does not succeed to due timestamp miss matches with release file. - Once the distribution upgrade has started. Start the service. Notification is not shown when distribution check is done. - Rollback to a snapshot before distribution upgrade. Start the distribution upgrade manually and notice that notification is not shown when distribution check is done. - Once the distribution upgrade has completed, start the service. Notification is shown the distribution upgrade has completed when distribution check is done. Dismiss this notification. - Restart the service. Notification is not shown when distribution check is done. Signed-off-by: Sunil Mohan Adapa Reviewed-by: James Valleroy --- plinth/modules/upgrades/__init__.py | 96 ++++++++++++++++++- .../upgrades-dist-upgrade-notification.html | 48 ++++++++++ plinth/modules/upgrades/views.py | 2 + 3 files changed, 145 insertions(+), 1 deletion(-) create mode 100644 plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html diff --git a/plinth/modules/upgrades/__init__.py b/plinth/modules/upgrades/__init__.py index 624c1b9a4..9a9ca1e94 100644 --- a/plinth/modules/upgrades/__init__.py +++ b/plinth/modules/upgrades/__init__.py @@ -1,6 +1,7 @@ # SPDX-License-Identifier: AGPL-3.0-or-later """FreedomBox app for upgrades.""" +import datetime import logging import os import subprocess @@ -215,12 +216,105 @@ def check_dist_upgrade(_): """Check for upgrade to new stable release.""" if is_dist_upgrade_enabled(): status = distupgrade.get_status() - if status['next_action'] in ('continue', 'ready'): + starting = status['next_action'] in ('continue', 'ready') + dist_upgrade_show_notification(status, starting) + if starting: + logger.info('Starting distribution upgrade - %s', status) privileged.start_dist_upgrade() else: logger.info('Not ready for distribution upgrade - %s', status) +def dist_upgrade_show_notification(status: dict, starting: bool): + """Show various notifications regarding distribution upgrade. + + - Show a notification 60 days, 30 days, 1 week, and 1 day before + distribution upgrade. If a notification is dismissed for any of these + periods don't show again until new period starts. Override any previous + notification. + + - Show a notification just before the distribution upgrade showing that the + process has started. Override any previous notification. + + - Show a notification after the distribution upgrade is completed that it + is done. Override any previous notification. Keep this until it is 60 + days before next distribution upgrade. If user dismisses the + notification, don't show it again. + """ + from plinth.notification import Notification + + try: + note = Notification.get('upgrades-dist-upgrade') + data = note.data + except KeyError: + data = {} + + in_days = None + if status['next_action_date']: + in_days = (status['next_action_date'] - + datetime.datetime.now(tz=datetime.timezone.utc)) + + if in_days is None or in_days > datetime.timedelta(days=60): + for_days = None + elif in_days > datetime.timedelta(days=30): + for_days = 60 # 60 day notification + elif in_days > datetime.timedelta(days=7): + for_days = 30 # 30 day notification + elif in_days > datetime.timedelta(days=1): + for_days = 7 # 1 week notification + else: + for_days = 1 # 1 day notification, or overdue notification + + if status['running']: + # Do nothing while the distribution upgrade is running. + return + + state = 'starting' if starting else 'waiting' + if (not for_days and status['current_codename'] + and data.get('next_codename') == status['current_codename']): + # Previously shown notification's codename is current codename. + # Distribution upgrade was successfully completed. + state = 'done' + + if not status['next_action'] and state != 'done': + # There is no upgrade available, don't show any notification. + return + + if not for_days and data.get('state') == 'done': + # Don't remove notification showing upgrade is complete until next + # distribution upgrade is coming up in 2 months or sooner. + return + + if not for_days and state == 'waiting': + # More than 60 days to next distribution update. Don't show + # notification. + return + + if (for_days == data.get('for_days') and state == data.get('state') + and status['next_codename'] == data.get('next_codename')): + # If the notification was shown for same distribution codename, same + # duration, and same state, then don't show it again. + return + + data = { + 'app_name': 'translate:' + gettext_noop('Software Update'), + 'app_icon': 'fa-refresh', + 'current_codename': status['current_codename'], + 'current_version': status['current_version'], + 'next_codename': status['next_codename'], + 'next_version': status['next_version'], + 'state': state, + 'for_days': for_days, + 'in_days': in_days.days if in_days else None, + } + title = gettext_noop('Distribution Update') + note = Notification.update_or_create( + id='upgrades-dist-upgrade', app_id='upgrades', severity='info', + title=title, body_template='upgrades-dist-upgrade-notification.html', + data=data, group='admin') + note.dismiss(should_dismiss=False) + + def is_backports_requested(): """Return whether user has chosen to activate backports.""" return kvstore.get_default(BACKPORTS_REQUESTED_KEY, False) diff --git a/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html b/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html new file mode 100644 index 000000000..eb6cba373 --- /dev/null +++ b/plinth/modules/upgrades/templates/upgrades-dist-upgrade-notification.html @@ -0,0 +1,48 @@ +{% comment %} +# SPDX-License-Identifier: AGPL-3.0-or-later +{% endcomment %} + +{% load i18n %} +{% load static %} + +
+ {% trans "Distribution Update" %} +
+ +

+ {% url 'help:manual-page' lang='-' page='DebianUpgradeNotes' as dist_upgrade_url %} + {% if data.state == 'starting' %} + {% blocktrans trimmed %} + Distribution update has started. This operation may take several hours. + Most apps will be unavailable during this period. Don't interrupt the + process by shutting down or interrupting power to the machine. + {% endblocktrans %} + {% elif data.state == 'done' %} + {% blocktrans trimmed %} + Distribution update has completed. Reboot the machine, if necessary. + {% endblocktrans %} + {% elif data.for_days == 1 %} + {% blocktrans trimmed %} + Distribution update will start soon. Take a backup of apps and data before + then. See manual page for expected + changes and transitions during the distribution upgrade. + {% endblocktrans %} + {% elif data.for_days %} + {% blocktrans trimmed with in_days=data.in_days %} + Distribution update will start in {{ in_days }} days. Take a backup of + apps and data before then. See manual + page for expected changes and transitions during the distribution upgrade. + {% endblocktrans %} + {% endif %} +

+ +

+ + {% trans "Go to Distribution Update" %} + + + {% trans "Dismiss" %} + +

diff --git a/plinth/modules/upgrades/views.py b/plinth/modules/upgrades/views.py index 7fa4c67c8..67ed3952a 100644 --- a/plinth/modules/upgrades/views.py +++ b/plinth/modules/upgrades/views.py @@ -111,6 +111,8 @@ class DistUpgradeConfirmView(TemplateView): def post(self, request): """Start the distribution upgrade process.""" + status = distupgrade.get_status() + upgrades.dist_upgrade_show_notification(status, starting=True) privileged.start_dist_upgrade() messages.success(request, _('Started distribution update.')) return redirect(reverse_lazy('upgrades:dist-upgrade'))