Sunil Mohan Adapa 271f88d9ed
backports: When upgrading from older version, assumed requested
- This helps the case where an user installs FreedomBox on Buster and then after
finishing the first boot wizard and before repositories have been setup,
upgrades to latest FreedomBox version.

Signed-off-by: Sunil Mohan Adapa <sunil@medhas.org>
Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
2020-09-11 10:55:49 -04:00

207 lines
6.2 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""
FreedomBox app for upgrades.
"""
import logging
import os
import subprocess
from aptsources import sourceslist
from django.utils.translation import ugettext_lazy as _
from django.utils.translation import ugettext_noop
import plinth
from plinth import actions
from plinth import app as app_module
from plinth import cfg, glib, menu
from .manifest import backup # noqa, pylint: disable=unused-import
version = 7
is_essential = True
managed_packages = ['unattended-upgrades', 'needrestart']
first_boot_steps = [
{
'id': 'backports_wizard',
'url': 'upgrades:backports-firstboot',
'order': 5,
},
]
_description = [
_('Check for and apply the latest software and security updates.'),
_('Updates are run at 06:00 everyday according to local time zone. Set '
'your time zone in Date & Time app. Apps are restarted after update '
'causing them to be unavailable briefly. If system reboot is deemed '
'necessary, it is done automatically at 02:00 causing all apps to be '
'unavailable briefly.')
]
app = None
BACKPORTS_REQUESTED_KEY = 'upgrades_backports_requested'
SOURCES_LIST = '/etc/apt/sources.list.d/freedombox2.list'
logger = logging.getLogger(__name__)
class UpgradesApp(app_module.App):
"""FreedomBox app for software upgrades."""
app_id = 'upgrades'
can_be_disabled = False
def __init__(self):
"""Create components for the app."""
super().__init__()
info = app_module.Info(app_id=self.app_id, version=version,
is_essential=is_essential, name=_('Update'),
icon='fa-refresh', description=_description,
manual_page='Upgrades')
self.add(info)
menu_item = menu.Menu('menu-upgrades', info.name, None, info.icon,
'upgrades:index', parent_url_name='system')
self.add(menu_item)
self._show_new_release_notification()
# Check every day for setting up apt backport sources, every 3 minutes
# in debug mode.
interval = 180 if cfg.develop else 24 * 3600
glib.schedule(interval, setup_repositories)
def _show_new_release_notification(self):
"""When upgraded to new release, show a notification."""
from plinth.notification import Notification
try:
note = Notification.get('upgrades-new-release')
if note.data['version'] == plinth.__version__:
# User already has notification for update to this version. It
# may be dismissed or not yet dismissed
return
# User currently has a notification for an older version, update.
dismiss = False
except KeyError:
# Don't show notification for the first version user runs, create
# but don't show it.
dismiss = True
data = {
'version': plinth.__version__,
'app_name': 'Update',
'app_icon': 'fa-refresh'
}
title = ugettext_noop('FreedomBox Updated')
note = Notification.update_or_create(
id='upgrades-new-release', app_id='upgrades', severity='info',
title=title, body_template='upgrades-new-release.html', data=data,
group='admin')
note.dismiss(should_dismiss=dismiss)
def setup(helper, old_version=None):
"""Install and configure the module."""
helper.install(managed_packages)
# Enable automatic upgrades but only on first install
if not old_version and not cfg.develop:
helper.call('post', actions.superuser_run, 'upgrades', ['enable-auto'])
# Update apt preferences whenever on first install and on version
# increment.
helper.call('post', actions.superuser_run, 'upgrades', ['setup'])
# When upgrading from a version without first boot wizard for backports,
# assume backports have been requested.
if old_version and old_version < 7:
set_backports_requested(can_activate_backports())
# Try to setup apt repositories, if needed, if possible, on first install
# and on version increment.
helper.call('post', setup_repositories, None)
def is_enabled():
"""Return whether the module is enabled."""
output = actions.run('upgrades', ['check-auto'])
return 'True' in output.split()
def enable():
"""Enable the module."""
actions.superuser_run('upgrades', ['enable-auto'])
def disable():
"""Disable the module."""
actions.superuser_run('upgrades', ['disable-auto'])
def setup_repositories(data):
"""Setup apt backport repositories."""
if is_backports_requested():
command = ['setup-repositories']
if cfg.develop:
command += ['--develop']
actions.superuser_run('upgrades', command)
def is_backports_requested():
"""Return whether user has chosen to activate backports."""
from plinth import kvstore
return kvstore.get_default(BACKPORTS_REQUESTED_KEY, False)
def set_backports_requested(requested):
"""Set whether user has chosen to activate backports."""
from plinth import kvstore
kvstore.set(BACKPORTS_REQUESTED_KEY, requested)
logger.info('Backports requested - %s', requested)
def is_backports_enabled():
"""Return whether backports are enabled in the system configuration."""
return os.path.exists(SOURCES_LIST)
def get_current_release():
"""Return current release and codename as a tuple."""
output = subprocess.check_output(
['lsb_release', '--release', '--codename',
'--short']).decode().strip()
lines = output.split('\n')
return lines[0], lines[1]
def is_backports_current():
"""Return whether backports are enabled for the current release."""
if not is_backports_enabled():
return False
_, dist = get_current_release()
dist += '-backports'
sources = sourceslist.SourcesList()
for source in sources:
if source.dist == dist:
return True
return False
def can_activate_backports():
"""Return whether backports can be activated."""
release, _ = get_current_release()
if release == 'unstable' or (release == 'testing' and not cfg.develop):
return False
return True