Sunil Mohan Adapa a998995f36
upgrades: Remove step upgrade during first setup
- Remove the first setup wizard step to run security upgrades. At the time of
its introduction, it was felt that this is very important. Some things have
changed since then:

  - We have mechanism for queuing package operations. Users can now trigger
  software updates and start installing apps before that is completed. Or vice
  versa. Earlier if the software updates were running, app install used to fail
  with an error.

  - There were no notifications. Since then we have added 'first setup'
  notification for important topics such as Privacy. This step can be replaced
  with a notification.

  - Automatic diagnostics and a diagnostic to notify of updated packages also
  helps bring attention to software updates if they are missed during first
  setup.

- A proposed change will re-introduce an advice to run updates in the 'Next
steps' wizard step along with a button trigger it right there.

- The new notification for software updates will bring more attention to running
updates as part of first setup.

- It would be nice not be stuck in the first setup wizard for a long period and
make it look simple. It improves the fun factor of setting up FreedomBox.

- It would present an opportunity to utilize the parallel installation of
apps/updates to the full extent. Although this can also be done by skipping the
progress step after updates are run.

- First wizard steps tend to get less testing.

Tests:

- Run the first setup wizard by removing /var/lib/plinth/plinth.sqlite3 and
running the service. Notice that the software update step is not shown and
wizard completes successfully.

- On stable container, backports step is shown as expected (if not already
enabled).

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Veiko Aasa <veiko17@disroot.org>
2024-10-10 09:02:58 +03:00

179 lines
6.3 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.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 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()
context['can_test_dist_upgrade'] = upgrades.can_test_dist_upgrade()
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)
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)
def test_dist_upgrade(request):
"""Test dist-upgrade from stable to testing."""
if request.method == 'POST':
upgrades.test_dist_upgrade()
messages.success(request, _('Starting distribution upgrade test.'))
return redirect(reverse_lazy('upgrades:index'))