Compare commits

...

4 Commits

34
debian/changelog vendored

@ -1,3 +1,37 @@
lubuntu-update-notifier (0.5.1~22.04.4) jammy; urgency=medium
* Add support for release upgrading, when all updates are applied (LP: #2038958).
-- Simon Quigley <tsimonq2@ubuntu.com> Tue, 10 Oct 2023 14:06:19 -0500
lubuntu-update-notifier (0.5.1~22.04.3) jammy; urgency=medium
* Don't try to pass a string to QTreeWidgetItem when it expects an
Iterable[str]. (LP: #2012823)
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Sun, 26 Mar 2023 12:21:33 -0500
lubuntu-update-notifier (0.5.1~22.04.2) jammy; urgency=medium
* SRU backport of 0.5.1 to the Jammy stable release.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Sun, 08 Jan 2023 17:56:49 -0600
lubuntu-update-notifier (0.5.1) lunar; urgency=medium
* Added functionality for repairing an interrupted upgrade. (LP: #2002255)
* Fixed an assumption in the config-file-conflict frontend.
* Bumped version number in lubuntu-upgrader.
-- Aaron Rainbolt <arraybolt3@ubuntu.com> Wed, 14 Dec 2022 12:32:48 -0600
lubuntu-update-notifier (0.5) lunar; urgency=medium
* Add a frontend for config-file-conflict.
* Update Standards-version to 4.6.1, no changes needed.
-- Simon Quigley <tsimonq2@ubuntu.com> Wed, 07 Dec 2022 13:26:39 -0600
lubuntu-update-notifier (0.4) hirsute; urgency=medium lubuntu-update-notifier (0.4) hirsute; urgency=medium
* Added internationalization support. * Added internationalization support.

6
debian/control vendored

@ -2,13 +2,13 @@ Source: lubuntu-update-notifier
Section: admin Section: admin
Priority: optional Priority: optional
Maintainer: Hans P Möller <hmollercl@lubuntu.me> Maintainer: Hans P Möller <hmollercl@lubuntu.me>
Build-Depends: debhelper-compat (=13), Build-Depends: debhelper-compat (= 13),
dh-python, dh-python,
gettext, gettext,
python3-all, python3-all,
python3-apt, python3-apt,
python3-setuptools python3-setuptools
Standards-Version: 4.5.0 Standards-Version: 4.6.1
Rules-Requires-Root: no Rules-Requires-Root: no
Testsuite: autopkgtest-pkg-python Testsuite: autopkgtest-pkg-python
@ -19,8 +19,10 @@ Depends: aptdaemon,
lxqt-sudo, lxqt-sudo,
python3, python3,
python3-aptdaemon (>= 0.6.20ubuntu16), python3-aptdaemon (>= 0.6.20ubuntu16),
python3-launchpadlib,
python3-pyqt5, python3-pyqt5,
update-notifier-common, update-notifier-common,
ubuntu-release-upgrader-qt,
${misc:Depends}, ${misc:Depends},
${python3:Depends}, ${python3:Depends},
${shlibs:Depends} ${shlibs:Depends}

5
debian/copyright vendored

@ -2,8 +2,9 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: https://phab.lubuntu.me/source/lubuntu-update-notifier/ Source: https://phab.lubuntu.me/source/lubuntu-update-notifier/
Files: * Files: *
Copyright: © 2019 Lubuntu Team. Copyright: 2019-2022 Lubuntu Team <lubuntu-devel@lists.ubuntu.com>
© 2019 Hans P Möller <hmollercl@lubuntu.me> 2019 Hans P Möller <hmollercl@lubuntu.me>
2022 Simon Quigley <tsimonq2@lubuntu.me>
License: GPL-3+ License: GPL-3+
License: GPL-3+ License: GPL-3+

@ -2,6 +2,7 @@
# coding=utf-8 # coding=utf-8
# Copyright (C) 2019 Hans P. Möller <hmollercl@lubuntu.me> # Copyright (C) 2019 Hans P. Möller <hmollercl@lubuntu.me>
# Copyright (C) 2023 Simon Quigley <tsimonq2@lubuntu.me>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -28,8 +29,10 @@ import gettext
from PyQt5.QtWidgets import (QWidget, QApplication, QLabel, QDialogButtonBox, from PyQt5.QtWidgets import (QWidget, QApplication, QLabel, QDialogButtonBox,
QHBoxLayout, QVBoxLayout, QTreeWidget, QHBoxLayout, QVBoxLayout, QTreeWidget,
QTreeWidgetItem, QHeaderView) QTreeWidgetItem, QHeaderView)
from PyQt5.QtCore import Qt from PyQt5.QtCore import Qt, QThread, pyqtSignal
from PyQt5.QtGui import QIcon from PyQt5.QtGui import QIcon
from launchpadlib.launchpad import Launchpad
import importlib.util import importlib.util
spec = importlib.util.spec_from_file_location( spec = importlib.util.spec_from_file_location(
@ -37,16 +40,35 @@ spec = importlib.util.spec_from_file_location(
apt_check = importlib.util.module_from_spec(spec) apt_check = importlib.util.module_from_spec(spec)
spec.loader.exec_module(apt_check) spec.loader.exec_module(apt_check)
class RunUpgradeThread(QThread):
finished = pyqtSignal()
def __init__(self, cmd):
super().__init__()
self.cmd = cmd
def run(self):
process = subprocess.Popen(self.cmd)
process.wait()
self.finished.emit()
class Dialog(QWidget): class Dialog(QWidget):
''' UI ''' ''' UI '''
def __init__(self, upgrades, security_upgrades, reboot_required, upg_path): def __init__(self, upgrades, security_upgrades, release_upgrade, version, reboot_required, upg_path):
QWidget.__init__(self) QWidget.__init__(self)
self.upgrades = upgrades self.upgrades = upgrades
self.security_upgrades = security_upgrades self.security_upgrades = security_upgrades
self.release_upgrade = release_upgrade
self.version = version
self.upg_path = upg_path self.upg_path = upg_path
self.reboot_required = reboot_required self.reboot_required = reboot_required
try:
self.launchpad = Launchpad.login_anonymously("lubuntu-update-notifier", "production", version="devel")
except:
self.launchpad = None
apt_pkg.init() apt_pkg.init()
try: try:
self.cache = apt_pkg.Cache() self.cache = apt_pkg.Cache()
@ -64,6 +86,8 @@ class Dialog(QWidget):
''' UI initialization ''' ''' UI initialization '''
self.label = QLabel() self.label = QLabel()
self.label.setAlignment(Qt.AlignHCenter) self.label.setAlignment(Qt.AlignHCenter)
self.label.setTextFormat(Qt.RichText)
self.label.setOpenExternalLinks(True)
self.tw = QTreeWidget() self.tw = QTreeWidget()
if self.security_upgrades > 0: if self.security_upgrades > 0:
@ -93,7 +117,7 @@ class Dialog(QWidget):
self.tw.setVisible(False) self.tw.setVisible(False)
if self.upg_path is None: if self.upg_path is None and not self.release_upgrade:
self.buttonBox.button(QDialogButtonBox.Apply).setVisible(False) self.buttonBox.button(QDialogButtonBox.Apply).setVisible(False)
self.setLayout(vbox) self.setLayout(vbox)
@ -129,7 +153,7 @@ class Dialog(QWidget):
if len(pkg_delete) > 0: if len(pkg_delete) > 0:
toDelete = QTreeWidgetItem([_('Remove')]) toDelete = QTreeWidgetItem([_('Remove')])
for p in pkg_delete: for p in pkg_delete:
td_child = QTreeWidgetItem(p.name) td_child = QTreeWidgetItem([p.name])
toDelete.addChild(td_child) toDelete.addChild(td_child)
toDelete.setIcon(0, QIcon.fromTheme("edit-delete")) toDelete.setIcon(0, QIcon.fromTheme("edit-delete"))
self.tw.addTopLevelItem(toDelete) self.tw.addTopLevelItem(toDelete)
@ -163,6 +187,10 @@ class Dialog(QWidget):
td_child.addChild(short) td_child.addChild(short)
toUpgrade.setIcon(0, QIcon.fromTheme("system-software-update")) toUpgrade.setIcon(0, QIcon.fromTheme("system-software-update"))
self.tw.addTopLevelItem(toUpgrade) self.tw.addTopLevelItem(toUpgrade)
elif self.release_upgrade:
self.setWindowTitle("Upgrade Lubuntu")
text = self.new_version_text()
self.buttonBox.clicked.connect(self.call_release_upgrader)
if self.reboot_required: if self.reboot_required:
if text == "": if text == "":
@ -192,43 +220,83 @@ class Dialog(QWidget):
QDialogButtonBox.ApplyRole): QDialogButtonBox.ApplyRole):
''' starts upgrade process ''' ''' starts upgrade process '''
self.label.setText(_("Upgrading...")) self.label.setText(_("Upgrading..."))
# TODO maybe open another thread so notifier won't freeze
cmd = ['lxqt-sudo', self.upg_path, '--full-upgrade']
self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False) self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(False)
self.buttonBox.button(QDialogButtonBox.Apply).setVisible(False) self.buttonBox.button(QDialogButtonBox.Apply).setVisible(False)
self.tw.setVisible(False) self.tw.setVisible(False)
process = subprocess.Popen(cmd)
process.wait()
if self.upg_path == "terminal": cmd = ["lxqt-sudo", self.upg_path, "--full-upgrade"]
text = _("Upgrade finished") self.thread = RunUpgradeThread(cmd)
self.thread.finished.connect(self.on_upgrade_finished)
reboot_required_path = Path("/var/run/reboot-required") self.thread.start()
if reboot_required_path.exists():
text += "\n" + _("Reboot required") def on_upgrade_finished(self):
self.label.setText(text) if self.upg_path == "terminal":
self.closeBtn.setVisible(True) text = _("Upgrade finished")
self.closeBtn.setEnabled(True)
reboot_required_path = Path("/var/run/reboot-required")
if reboot_required_path.exists():
text += "\n" + _("Reboot required")
self.label.setText(text)
self.closeBtn.setVisible(True)
self.closeBtn.setEnabled(True)
elif self.release_upgrade:
self.setWindowTitle("Upgrade Lubuntu")
self.label.setText(self.new_version_text())
self.buttonBox.button(QDialogButtonBox.Apply).setEnabled(True)
self.buttonBox.button(QDialogButtonBox.Apply).setVisible(True)
self.buttonBox.clicked.disconnect(self.call_upgrade)
self.buttonBox.clicked.connect(self.call_release_upgrader)
else:
app.quit()
def call_release_upgrader(self, btnClicked):
if self.buttonBox.buttonRole(btnClicked) == QDialogButtonBox.ApplyRole:
cmd = ["lxqt-sudo", "do-release-upgrade", "-m", "desktop", "-f", "DistUpgradeViewKDE"]
self.thread2 = RunUpgradeThread(cmd)
self.thread2.finished.connect(self.call_reject)
self.thread2.start()
elif self.buttonBox.buttonRole(btnClicked) == QDialogButtonBox.RejectRole:
self.call_reject()
def new_version_text(self):
try:
main_version = '.'.join(self.version.split()[0].split('.')[:2])
codename = self.launchpad.distributions["ubuntu"].getSeries(name_or_version=main_version).name
except:
codename = None
if codename:
url_suffix = ""
point_release = self.version.split(".")[2].split(" ")[0] if "." in self.version[4:] else "0"
if point_release != "0":
url_suffix = f"-{int(point_release)}"
url_suffix += "-released"
text = f"<a href='https://lubuntu.me/{codename}{url_suffix}/'>"
text += _("A new version of Lubuntu") + "</a> "
text += _("is available. Would you like to install it?")
else:
text = _("A new version of Lubuntu is available. Would you like to install it?")
else: return text
app.quit()
class App(QApplication): class App(QApplication):
'''application''' '''application'''
def __init__(self, upgrades, security_upgrades, reboot_required, upg_path, def __init__(self, upgrades, security_upgrades, release_upgrade, version, reboot_required, upg_path,
*args): *args):
QApplication.__init__(self, *args) QApplication.__init__(self, *args)
self.dialog = Dialog(upgrades, security_upgrades, reboot_required, self.dialog = Dialog(upgrades, security_upgrades, release_upgrade, version, reboot_required,
upg_path) upg_path)
self.dialog.show() self.dialog.show()
def main(args, upgrades, security_upgrades, reboot_required, upg_path):
def main(args, upgrades, security_upgrades, release_upgrade, version, reboot_required, upg_path):
'''main''' '''main'''
global app global app
app = App(upgrades, security_upgrades, reboot_required, upg_path, args) app = App(upgrades, security_upgrades, release_upgrade, version, reboot_required, upg_path, args)
app.setWindowIcon(QIcon.fromTheme("system-software-update")) app.setWindowIcon(QIcon.fromTheme("system-software-update"))
app.exec_() app.exec_()
@ -256,12 +324,29 @@ if __name__ == "__main__":
dest="security_upgrades", dest="security_upgrades",
help=_("How many security upgrades are available"), help=_("How many security upgrades are available"),
metavar="APP") metavar="APP")
parser.add_argument("-r",
"--release-upgrade",
dest="release_upgrade",
help=_("Whether a release upgrade is required"),
type=str,
metavar="APP")
parser.add_argument("-v",
"--release-upgrade-version",
dest="version",
help=_("If a release upgrade is available, provide the version"),
type=str,
metavar="APP")
options = parser.parse_args() options = parser.parse_args()
reboot_required_path = Path("/var/run/reboot-required") reboot_required_path = Path("/var/run/reboot-required")
reboot_required = reboot_required_path.exists() reboot_required = reboot_required_path.exists()
if int(options.upgrades) > 0 or reboot_required: if int(options.release_upgrade) == 0:
options.release_upgrade = True
else:
options.release_upgrade = False
if int(options.upgrades) > 0 or reboot_required or options.release_upgrade:
main(sys.argv, int(options.upgrades), int(options.security_upgrades), main(sys.argv, int(options.upgrades), int(options.security_upgrades),
reboot_required, options.upg_path) options.release_upgrade, options.version, reboot_required, options.upg_path)

@ -32,6 +32,12 @@ while true;
j=`expr $j + 1` j=`expr $j + 1`
done done
IFS=$oldIFS IFS=$oldIFS
/usr/libexec/lubuntu-update-notifier/lubuntu-notifier.py -u $UPG -s $SEC -p /usr/bin/lubuntu-upgrader
NEWREL_CHECK=`/usr/bin/do-release-upgrade -c 2>&1`
NEWREL=$?
if [ "$NEWREL" -eq 0 ]; then
VERSION=`echo $NEWREL_CHECK | awk -F\' '/available/{print $2}'`
fi
/usr/libexec/lubuntu-update-notifier/lubuntu-notifier.py -u $UPG -s $SEC -r $NEWREL -v $VERSION -p /usr/bin/lubuntu-upgrader
sleep 86400 sleep 86400
done; done;

@ -2,6 +2,7 @@
# coding=utf-8 # coding=utf-8
# Copyright (C) 2019 Hans P. Möller <hmollercl@lubuntu.me> # Copyright (C) 2019 Hans P. Möller <hmollercl@lubuntu.me>
# Copyright (C) 2022 Simon Quigley <tsimonq2@lubuntu.me>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
@ -64,6 +65,8 @@ class DialogUpg(QWidget):
#stderr=subprocess.PIPE #stderr=subprocess.PIPE
stderr=self.slave)''' stderr=self.slave)'''
self.trans3 = self.apt_client.fix_incomplete_install()
self.repair_install()
if options.fullUpgrade: if options.fullUpgrade:
self.trans2 = self.apt_client.upgrade_system(safe_mode=False) self.trans2 = self.apt_client.upgrade_system(safe_mode=False)
self.setWindowTitle('Full Upgrade') self.setWindowTitle('Full Upgrade')
@ -128,6 +131,12 @@ class DialogUpg(QWidget):
self.progressBar.setValue(progress) self.progressBar.setValue(progress)
self.label.setText(_("Updating cache...")) self.label.setText(_("Updating cache..."))
def repair_progress(self, transaction, progress):
'''upgrade progressbar during update'''
self.progressBar.setVisible(True)
self.progressBar.setValue(progress)
self.label.setText(_("Repairing interrupted upgrade if necessary..."))
def update_progress_download(self, transaction, uri, status, short_desc, def update_progress_download(self, transaction, uri, status, short_desc,
total_size, current_size, msg): total_size, current_size, msg):
'''print update info''' '''print update info'''
@ -312,6 +321,29 @@ class DialogUpg(QWidget):
# print("PTY:" + str(self.slave)) # print("PTY:" + str(self.slave))
print("Status Details:" + details) print("Status Details:" + details)
def config_file_conflict(self, transaction, cur, new):
title = "Conflicting Configuration"
text = "Updating the system will result in the following file being "
text += "overwritten: " + cur + "\n\nWhat would you like to do?"
query = QMessageBox()
query.setWindowTitle(title)
query.setText(text)
query.setIcon(QMessageBox.Question)
query.setStandardButtons(QMessageBox.Yes|QMessageBox.No)
yes = query.button(QMessageBox.Yes)
yes.setText("Overwrite")
no = query.button(QMessageBox.No)
no.setText("Keep Existing")
query.setDefaultButton(no)
query.exec_()
if query.clickedButton() == yes:
answer = "replace"
elif query.clickedButton() == no:
answer = "keep"
transaction.resolve_config_file_conflict(config=cur, answer=answer)
def upgrade(self): def upgrade(self):
'''runs upgrade''' '''runs upgrade'''
try: try:
@ -327,6 +359,8 @@ class DialogUpg(QWidget):
self.trans2.connect("status-details-changed", self.trans2.connect("status-details-changed",
self.status_details_changed) self.status_details_changed)
self.trans2.connect("status-changed", self.status_changed) self.trans2.connect("status-changed", self.status_changed)
self.trans2.connect("config-file-conflict",
self.config_file_conflict)
# TODO make a terminal work to see more info # TODO make a terminal work to see more info
# self.trans2.set_terminal(os.ttyname(self.slave)) # self.trans2.set_terminal(os.ttyname(self.slave))
@ -334,8 +368,6 @@ class DialogUpg(QWidget):
''' '''
# TODO implement this # TODO implement this
self.trans2.connect("medium-required", self._on_medium_required) self.trans2.connect("medium-required", self._on_medium_required)
self.trans2.connect("config-file-conflict",
self._on_config_file_conflict)
remove_obsoleted_depends remove_obsoleted_depends
''' '''
self.trans2.set_debconf_frontend('kde') self.trans2.set_debconf_frontend('kde')
@ -349,6 +381,35 @@ class DialogUpg(QWidget):
'''when close button is pushed, quit''' '''when close button is pushed, quit'''
app.quit() app.quit()
def repair_install(self):
self.closeBtn.setVisible(False)
try:
self.trans3.connect('progress-changed', self.repair_progress)
self.trans3.connect('status-changed', self.status_changed)
self.trans3.connect('status-details-changed',
self.status_details_changed)
self.trans3.connect('finished', self.repair_finish)
self.trans3.connect('error', self.upgrade_error)
self.trans3.set_debconf_frontend('kde')
self.trans3.run()
except (NotAuthorizedError, TransactionFailed) as e:
print("Warning: install transaction not completed successfully:"
+ "{}".format(e))
def repair_finish(self, transaction, exit_state):
'''when repair finish'''
self.label.setText(_("Repair Finished (if repair was needed)"))
if exit_state == EXIT_FAILED:
error_string = get_error_string_from_enum(transaction.error.code)
error_desc = get_error_description_from_enum(
transaction.error.code)
self.plainTextEdit.setEnabled(False)
self.plainTextEdit.moveCursor(QTextCursor.End)
self.plainTextEdit.insertPlainText(error_string + "\n")
self.plainTextEdit.insertPlainText(error_desc + "\n")
self.plainTextEdit.moveCursor(QTextCursor.End)
self.plainTextEdit.setEnabled(True)
class App(QApplication): class App(QApplication):
'''app''' '''app'''
@ -396,7 +457,7 @@ if __name__ == "__main__":
help=_("Full upgrade same as dist-upgrade")) help=_("Full upgrade same as dist-upgrade"))
parser.add_argument('--version', parser.add_argument('--version',
action='version', action='version',
version='%(prog)s 0.4') version='%(prog)s 0.5.1')
options = parser.parse_args() options = parser.parse_args()
# run it # run it

Loading…
Cancel
Save