upgrades: Add first boot step to configure backports

Closes: #1855.

Tests:

- On unstable, first boot step is not shown. Backports are not
  enabled.

- On testing, tested enabling backports at first boot step. Backports
  are enabled.

- On testing, tested not enabling backports. Backports are not enabled
  and can be activated later.

- On testing, confirmed that functional tests can click through the
  first boot step.

- On stable with backports, first boot step is not shown. Backports
  are enabled.

- On stable, tested enabling backports at first boot step. Backports
  are enabled.

- On stable, tested not enabling backports. Backports are not enabled
  and can be activated later.

Signed-off-by: James Valleroy <jvalleroy@mailbox.org>
[sunil: Avoid two different i18n strings with almost same content]
[sunil: Use box_name instead of hardcoded FreedomBox name]
[sunil: Use consistent terminology 'activate' instead of 'enable']
[sunil: Rename the wizard, form, view, url for consistency with existing code]
Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
This commit is contained in:
James Valleroy 2020-07-21 19:41:35 -04:00
parent 2f7b3264d1
commit 65d8f82ae1
No known key found for this signature in database
GPG Key ID: 77C0C75E7B650808
8 changed files with 145 additions and 19 deletions

View File

@ -22,11 +22,12 @@
</p> </p>
<p> <p>
{% blocktrans trimmed %} {% blocktrans trimmed %}
This will allow a very limited set of software, including FreedomBox Frequent feature updates allow a very limited set of software, including
service, to be updated to receive newer features regularly instead of {{box_name}} service, to be updated to receive newer features regularly
once every 2 years or so. Note that packages with frequent feature instead of once every 2 years or so. Note that packages with frequent
updates do not have support from Debian Security Team. They are instead feature updates do not have support from Debian Security Team. They are
maintained by contributors to Debian and the FreedomBox community. instead maintained by contributors to Debian and the {{box_name}}
community.
{% endblocktrans %} {% endblocktrans %}
</p> </p>
{% endif %} {% endif %}

View File

@ -23,6 +23,14 @@ is_essential = True
managed_packages = ['unattended-upgrades', 'needrestart'] managed_packages = ['unattended-upgrades', 'needrestart']
first_boot_steps = [
{
'id': 'backports_wizard',
'url': 'upgrades:backports-firstboot',
'order': 5,
},
]
_description = [ _description = [
_('Check for and apply the latest software and security updates.'), _('Check for and apply the latest software and security updates.'),
_('Updates are run at 06:00 everyday according to local time zone. Set ' _('Updates are run at 06:00 everyday according to local time zone. Set '
@ -34,6 +42,8 @@ _description = [
app = None app = None
BACKPORTS_ENABLED_KEY = 'upgrades_backports_enabled'
SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list' SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list'
@ -129,11 +139,13 @@ def disable():
def setup_repositories(data): def setup_repositories(data):
"""Setup apt backport repositories.""" """Setup apt backport repositories."""
command = ['setup-repositories'] from plinth import kvstore
if cfg.develop: if kvstore.get_default(BACKPORTS_ENABLED_KEY, False):
command += ['--develop'] command = ['setup-repositories']
if cfg.develop:
command += ['--develop']
actions.superuser_run('upgrades', command) actions.superuser_run('upgrades', command)
def is_backports_enabled(): def is_backports_enabled():

View File

@ -12,3 +12,10 @@ class ConfigureForm(forms.Form):
auto_upgrades_enabled = forms.BooleanField( auto_upgrades_enabled = forms.BooleanField(
label=_('Enable auto-update'), required=False, help_text=_( label=_('Enable auto-update'), required=False, help_text=_(
'When enabled, FreedomBox automatically updates once a day.')) 'When enabled, FreedomBox automatically updates once a day.'))
class BackportsFirstbootForm(forms.Form):
"""Form to configure backports during first boot wizard."""
backports_enabled = forms.BooleanField(
label=_('Activate frequent feature updates (recommended)'),
required=False)

View File

@ -0,0 +1,47 @@
{% extends "base_firstboot.html" %}
{% comment %}
# SPDX-License-Identifier: AGPL-3.0-or-later
{% endcomment %}
{% load bootstrap %}
{% load i18n %}
{% load static %}
{% block content %}
<h1>{% trans "Frequent Feature Updates" %}</h1>
<p>
{% blocktrans trimmed %}
Frequent feature updates allow a very limited set of software, including
{{box_name}} service, to be updated to receive newer features regularly
instead of once every 2 years or so. Note that packages with frequent
feature updates do not have support from Debian Security Team. They are
instead maintained by contributors to Debian and the {{box_name}}
community.
{% endblocktrans %}
</p>
<p>
{% blocktrans trimmed %}
It is strongly recommended to activate frequent feature updates. If not
activated now, they can be activated later.
{% endblocktrans %}
</p>
<div class="alert alert-warning" role="alert">
{% blocktrans trimmed %}
<strong>Note:</strong> Once frequent feature updates are activated,
they cannot be deactivated.
{% endblocktrans %}
</div>
<form class="form" method="post">
{% csrf_token %}
{{ form|bootstrap }}
<input type="submit" class="btn btn-primary pull-right" name="next"
value="{% trans "Next" %}"/>
</form>
{% endblock %}

View File

@ -29,11 +29,12 @@
<p> <p>
{% if can_activate_backports or is_backports_enabled %} {% if can_activate_backports or is_backports_enabled %}
{% blocktrans trimmed %} {% blocktrans trimmed %}
This will allow a very limited set of software, including FreedomBox Frequent feature updates allow a very limited set of software, including
service, to be updated to receive newer features regularly instead of {{box_name}} service, to be updated to receive newer features regularly
once every 2 years or so. Note that packages with frequent feature instead of once every 2 years or so. Note that packages with frequent
updates do not have support from Debian Security Team. They are instead feature updates do not have support from Debian Security Team. They are
maintained by contributors to Debian and the FreedomBox community. instead maintained by contributors to Debian and the {{box_name}}
community.
{% endblocktrans %} {% endblocktrans %}
{% endif %} {% endif %}
</p> </p>

View File

@ -12,5 +12,7 @@ urlpatterns = [
name='index'), name='index'),
url(r'^sys/upgrades/activate-backports/$', views.activate_backports, url(r'^sys/upgrades/activate-backports/$', views.activate_backports,
name='activate-backports'), name='activate-backports'),
url(r'^sys/upgrades/firstboot/backports/$',
views.BackportsFirstbootView.as_view(), name='backports-firstboot'),
url(r'^sys/upgrades/upgrade/$', views.upgrade, name='upgrade'), url(r'^sys/upgrades/upgrade/$', views.upgrade, name='upgrade'),
] ]

View File

@ -3,17 +3,23 @@
FreedomBox app for upgrades. FreedomBox app for upgrades.
""" """
import logging
from django.contrib import messages from django.contrib import messages
from django.http import HttpResponseRedirect
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic.edit import FormView
from plinth import actions, package from plinth import actions, kvstore, package
from plinth.errors import ActionError from plinth.errors import ActionError
from plinth.modules import upgrades from plinth.modules import first_boot, upgrades
from plinth.views import AppView from plinth.views import AppView
from .forms import ConfigureForm from .forms import BackportsFirstbootForm, ConfigureForm
logger = logging.getLogger(__name__)
class UpgradesConfigurationView(AppView): class UpgradesConfigurationView(AppView):
@ -84,7 +90,54 @@ def upgrade(request):
def activate_backports(request): def activate_backports(request):
"""Activate backports.""" """Activate backports."""
if request.method == 'POST': if request.method == 'POST':
kvstore.set(upgrades.BACKPORTS_ENABLED_KEY, True)
upgrades.setup_repositories(None) upgrades.setup_repositories(None)
messages.success(request, _('Frequent feature updates activated.')) messages.success(request, _('Frequent feature updates activated.'))
return redirect(reverse_lazy('upgrades:index')) 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.
kvstore.set(upgrades.BACKPORTS_ENABLED_KEY, 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.
first_boot.mark_step_done('backports_wizard')
return HttpResponseRedirect(reverse_lazy(first_boot.next_step()))
return super().dispatch(request, *args, *kwargs)
def get_initial(self):
"""Get initial form data."""
return {
'backports_enabled':
kvstore.get_default(upgrades.BACKPORTS_ENABLED_KEY, True)
}
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']
kvstore.set(upgrades.BACKPORTS_ENABLED_KEY, enabled)
if enabled:
upgrades.setup_repositories(None)
logger.info('Backports enabled.')
else:
logger.info('Backports not enabled.')
first_boot.mark_step_done('backports_wizard')
return super().form_valid(form)

View File

@ -13,8 +13,8 @@ from contextlib import contextmanager
import pytest import pytest
import requests import requests
from selenium.common.exceptions import (WebDriverException, from selenium.common.exceptions import (StaleElementReferenceException,
StaleElementReferenceException) WebDriverException)
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
config = configparser.ConfigParser() config = configparser.ConfigParser()
@ -261,6 +261,9 @@ def login(browser, url, username, password):
if '/internet-connection-type' in browser.url: if '/internet-connection-type' in browser.url:
submit(browser, element=browser.find_by_name('skip')[0]) submit(browser, element=browser.find_by_name('skip')[0])
if '/firstboot/backports' in browser.url:
submit(browser, element=browser.find_by_name('next')[0])
################# #################
# App utilities # # App utilities #