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 <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
Sunil Mohan Adapa 2025-04-03 14:20:41 -07:00 committed by James Valleroy
parent b6f0e7f323
commit e039f9f061
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
3 changed files with 145 additions and 1 deletions

View File

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

View File

@ -0,0 +1,48 @@
{% comment %}
# SPDX-License-Identifier: AGPL-3.0-or-later
{% endcomment %}
{% load i18n %}
{% load static %}
<div class="notification-title">
{% trans "Distribution Update" %}
</div>
<p>
{% 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 <a href="{{ dist_upgrade_url }}">manual</a> 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 <a href="{{ dist_upgrade_url }}">manual</a>
page for expected changes and transitions during the distribution upgrade.
{% endblocktrans %}
{% endif %}
</p>
<p>
<a href="{% url 'upgrades:dist-upgrade' %}"
role="button" class="btn btn-primary">
{% trans "Go to Distribution Update" %}
</a>
<a href="{% url 'notification_dismiss' id=id %}?next={{ request.path|iriencode }}"
role="button" class="btn btn-default">
{% trans "Dismiss" %}
</a>
</p>

View File

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