mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-01-21 07:55:00 +00:00
- 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>
204 lines
7.2 KiB
Python
204 lines
7.2 KiB
Python
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
"""FreedomBox app for upgrades."""
|
|
|
|
import subprocess
|
|
|
|
from apt.cache import Cache
|
|
from django.contrib import messages
|
|
from django.http import HttpResponseRedirect
|
|
from django.shortcuts import redirect
|
|
from django.urls import reverse_lazy
|
|
from django.utils.translation import gettext as _
|
|
from django.views.generic import TemplateView
|
|
from django.views.generic.edit import FormView
|
|
|
|
from plinth import __version__
|
|
from plinth.modules import first_boot, upgrades
|
|
from plinth.privileged import packages as packages_privileged
|
|
from plinth.views import AppView, messages_error
|
|
|
|
from . import distupgrade, privileged
|
|
from .forms import BackportsFirstbootForm, ConfigureForm
|
|
|
|
|
|
class UpgradesConfigurationView(AppView):
|
|
"""Serve configuration page."""
|
|
|
|
form_class = ConfigureForm
|
|
success_url = reverse_lazy('upgrades:index')
|
|
template_name = "upgrades_configure.html"
|
|
app_id = 'upgrades'
|
|
|
|
def get_initial(self):
|
|
"""Return the initial values for the form."""
|
|
return {
|
|
'auto_upgrades_enabled': privileged.check_auto(),
|
|
'dist_upgrade_enabled': upgrades.is_dist_upgrade_enabled()
|
|
}
|
|
|
|
def get_context_data(self, *args, **kwargs):
|
|
"""Add additional context data for template."""
|
|
context = super().get_context_data(*args, **kwargs)
|
|
context['can_activate_backports'] = upgrades.can_activate_backports()
|
|
context['is_backports_requested'] = upgrades.is_backports_requested()
|
|
context['is_busy'] = (_is_updating()
|
|
or packages_privileged.is_package_manager_busy())
|
|
context['log'] = privileged.get_log()
|
|
context['refresh_page_sec'] = 3 if context['is_busy'] else None
|
|
context['version'] = __version__
|
|
context['new_version'] = is_newer_version_available()
|
|
context['os_release'] = get_os_release()
|
|
return context
|
|
|
|
def form_valid(self, form):
|
|
"""Apply the form changes."""
|
|
old_status = form.initial
|
|
new_status = form.cleaned_data
|
|
|
|
is_changed = False
|
|
|
|
if old_status['auto_upgrades_enabled'] \
|
|
!= new_status['auto_upgrades_enabled']:
|
|
|
|
try:
|
|
if new_status['auto_upgrades_enabled']:
|
|
privileged.enable_auto()
|
|
else:
|
|
privileged.disable_auto()
|
|
|
|
is_changed = True
|
|
except Exception as exception:
|
|
messages_error(self.request,
|
|
_('Error when configuring unattended-upgrades'),
|
|
exception)
|
|
|
|
if old_status['dist_upgrade_enabled'] \
|
|
!= new_status['dist_upgrade_enabled']:
|
|
upgrades.set_dist_upgrade_enabled(
|
|
new_status['dist_upgrade_enabled'])
|
|
is_changed = True
|
|
|
|
if is_changed:
|
|
messages.success(self.request, _('Configuration updated.'))
|
|
|
|
return super().form_valid(form)
|
|
|
|
|
|
class DistUpgradeView(TemplateView):
|
|
"""View to show status of distribution upgrade."""
|
|
template_name = 'upgrades-dist-upgrade.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
"""Return additional context for rendering the template."""
|
|
context = super().get_context_data(**kwargs)
|
|
context['status'] = distupgrade.get_status()
|
|
context['refresh_page_sec'] = None
|
|
if context['status']['running']:
|
|
context['refresh_page_sec'] = 3
|
|
|
|
return context
|
|
|
|
|
|
class DistUpgradeConfirmView(TemplateView):
|
|
"""View to confirm and trigger trigger distribution upgrade."""
|
|
template_name = 'upgrades-dist-upgrade-confirm.html'
|
|
|
|
def get_context_data(self, **kwargs):
|
|
"""Return additional context for rendering the template."""
|
|
context = super().get_context_data(**kwargs)
|
|
context['status'] = distupgrade.get_status()
|
|
return context
|
|
|
|
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'))
|
|
|
|
|
|
def is_newer_version_available():
|
|
"""Return whether a newer Freedombox version is available."""
|
|
cache = Cache()
|
|
freedombox = cache['freedombox']
|
|
return not freedombox.candidate.is_installed
|
|
|
|
|
|
def get_os_release():
|
|
"""Return the Debian release number and name."""
|
|
output = 'Error: Cannot read PRETTY_NAME in /etc/os-release.'
|
|
with open('/etc/os-release', 'r', encoding='utf-8') as release_file:
|
|
for line in release_file:
|
|
if 'PRETTY_NAME=' in line:
|
|
line = line.replace('"', '').strip()
|
|
line = line.split('=')
|
|
output = line[1]
|
|
return output
|
|
|
|
|
|
def _is_updating():
|
|
"""Check if manually triggered update is running."""
|
|
command = ['systemctl', 'is-active', 'freedombox-manual-upgrade']
|
|
result = subprocess.run(command, capture_output=True, text=True,
|
|
check=False)
|
|
return str(result.stdout).startswith('activ') # 'active' or 'activating'
|
|
|
|
|
|
def upgrade(request):
|
|
"""Serve the upgrade page."""
|
|
if request.method == 'POST':
|
|
try:
|
|
privileged.run()
|
|
messages.success(request, _('Upgrade process started.'))
|
|
except Exception:
|
|
messages.error(request, _('Starting upgrade failed.'))
|
|
|
|
return redirect(reverse_lazy('upgrades:index'))
|
|
|
|
|
|
def activate_backports(request):
|
|
"""Activate backports."""
|
|
if request.method == 'POST':
|
|
upgrades.set_backports_requested(True)
|
|
upgrades.setup_repositories(None)
|
|
messages.success(request, _('Frequent feature updates activated.'))
|
|
|
|
return redirect(reverse_lazy('upgrades:index'))
|
|
|
|
|
|
class BackportsFirstbootView(FormView):
|
|
"""View to configure backports during first boot wizard."""
|
|
|
|
template_name = 'backports-firstboot.html'
|
|
form_class = BackportsFirstbootForm
|
|
|
|
def dispatch(self, request, *args, **kwargs):
|
|
"""Show backports configuration form only if it can be activated."""
|
|
if upgrades.is_backports_enabled():
|
|
# Backports is already enabled. Record this preference and
|
|
# skip first boot step.
|
|
upgrades.set_backports_requested(True)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
|
|
|
|
if not upgrades.can_activate_backports():
|
|
# Skip first boot step.
|
|
upgrades.set_backports_requested(False)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
|
|
|
|
return super().dispatch(request, *args, *kwargs)
|
|
|
|
def get_success_url(self):
|
|
"""Return next firstboot step."""
|
|
return reverse_lazy(first_boot.next_step())
|
|
|
|
def form_valid(self, form):
|
|
"""Mark the first wizard step as done, save value and redirect."""
|
|
enabled = form.cleaned_data['backports_enabled']
|
|
upgrades.set_backports_requested(enabled)
|
|
upgrades.setup_repositories(None)
|
|
first_boot.mark_step_done('backports_wizard')
|
|
return super().form_valid(form)
|