Repository navigation
08 Architecture
| Path | Content |
|---|---|
bin/bs-update |
The command. |
plasmoid/bsums.xyz.bs-updater/ |
The widget package. |
plasmoid/.../metadata.json |
The identifier, the version and the category of the widget. |
plasmoid/.../contents/ui/main.qml |
The applet. |
plasmoid/.../contents/ui/configGeneral.qml |
The page "General". |
plasmoid/.../contents/ui/configSources.qml |
The page "Update sources". |
plasmoid/.../contents/config/main.xml |
The settings and their defaults. |
plasmoid/.../contents/config/config.qml |
The two settings pages. |
plasmoid/.../contents/icons/ |
The icon for the up-to-date state. |
install.sh |
The installation script. |
make-plasmoid.sh |
The package script for the KDE Store. |
tests/run-tests.sh |
The test suite. |
logo/, screenshots/
|
The material for the KDE Store. |
The directory contents/code/ is not in the repository. install.sh and
make-plasmoid.sh make it and copy bin/bs-update into it. The repository holds
one copy of the command, and the widget package always holds the current
copy.
+-----------------------------+
| Settings pages of the |
| widget |
+--------------+--------------+
| the user selects the tools
v
+-----------------------------+
| main.qml |
| - a timer for the checks |
| - the icon and the tooltip |
+---+------------+--------+---+
| | ^
| writes | starts | reads the counts
v v |
tools.conf bs-update ----+
| |
| reads | starts
+----------->+
v
+-----------------------------+
| pacman, paru, DNF, |
| nobara-sync, APT, Flatpak, |
| Gear Lever |
+--------------+--------------+
| a successful update run
v
last-update ---> main.qml watches this file
bs-update holds two tables as text. Each line has three fields with a
vertical bar between them.
The table TOOLS has the identifier, the package domain and the name for
the user. The sequence of the lines is the sequence of the update run, and
it is also the priority for the conflict rule.
The table DOMAINS has the identifier, the heading for the list output and
the name for the user.
The function field reads one column of one row. Four small functions use
it: tool_domain, tool_name, domain_heading and domain_name.
A new tool must have one new line in TOOLS, one branch in count_tool
and one branch in update_tool. The file configSources.qml holds the same table
for the settings page.
bs-update finds the selection in this sequence:
- The option
--tools, if it is present. - The file
tools.conf, if the command can read it. - The default list
paru-repo paru-aur flatpak gearlever.
The function resolve_tools then examines the list. It removes an unknown
identifier, and it removes each tool with a package domain that an earlier
tool covers. It reports each removal on the error output.
dnf -q check-update
DNF writes one package for each line, in the form
name.arch version repository. The parser must obey three conditions:
- A long package name wraps onto a second line with an indent. Only a line that starts in the first column is a package.
- The first field of a package line always holds a period, because it ends with the architecture.
- The section
Obsoleting Packagesat the end repeats packages from the list above. The parser stops at that heading.
The parser is one awk program with these three conditions. It gives the
correct count with DNF 4 and with DNF 5, and it does not use root
privileges.
For the tool nobara-sync, the parser has a fourth condition. It does not
count a build for an older Fedora release than the system. A Nobara
repository can hold an fc43 build with a higher release number than the
installed fc44 build. DNF shows that build as an update, but
nobara-sync excludes older repository builds and does not install it.
The command rpm -E %fedora gives the release of the system. If that
value is not a number, the parser counts each update.
apt-get -q -s upgrade
The option -s simulates the upgrade. The simulation writes one line
Inst <name> ... for each package that the real upgrade installs. The
parser counts these lines.
Three properties follow from this design:
- The count does not use root privileges, but
apt-get updatemust have them. The count comes from the package lists on the disk. Debian and Ubuntu refresh these lists in the background with the timerapt-daily. The count is as recent as the last refresh. The update run refreshes the lists itself before it installs a package. -
apt-get upgradenever removes a package. A package whose update must have a removal stays back. The simulation does not write anInstline for it, so the count does not hold it. The number on the screen is the number that the update run installs. - The command uses
apt-getand notapt.apt-gethas a stable command line interface.aptwrites a warning that its interface is not for scripts.
For a package that stays back, run this command yourself:
sudo apt-get dist-upgrade
env -u DISPLAY -u WAYLAND_DISPLAY flatpak run it.mijorus.gearlever
The command line interface of Gear Lever also starts the graphical
application. The application then shows a window and a second tray icon.
The two variables DISPLAY and WAYLAND_DISPLAY are absent in the
command above, so only the command line interface runs.
The option --list-updates writes the line No updates available when
there is nothing to do. The parser removes that line before it counts.
The update command is --update --all --yes. The option --all examines
each integrated AppImage and downloads only an AppImage with a newer
version. The option --yes answers the interactive question.
plasmoid-updater updates the items that the dialog "Get New…" of KDE Plasma installed: plasmoids, and also themes, wallpapers and KWin scripts. It comes from https://github.com/uwuclxdy/plasmoid-updater.
| Step | Command |
|---|---|
| The count | plasmoid-updater check |
| The installation |
plasmoid-updater update --no-restart-plasma, with --yes at the levels auto and silent |
plasmoid-updater check writes a line such as
3 updates available. and then a table. The function count_plasmoids reads the number
from that line. It removes the escape codes of the bold table headers first.
No distribution has a package for plasmoid-updater. Thus the function
find_plasmoid_updater looks in the PATH, then in ~/.cargo/bin and
in ~/.local/bin. The tray widget gives its commands a short PATH,
which often does not include ~/.cargo/bin. If the program is absent, the
count is 0, the update fails, and a message tells how to install it.
plasmoid-updater does not restart plasmashell itself. The widget starts
each update run in the systemd unit of plasmashell. That unit stops all its
processes when plasmashell stops, so a restart during the run would also
stop the run. Without the setting restart-plasma=on, the new versions
load at the next start of Plasma.
With restart-plasma=on, a run that updated plasmoids restarts Plasma at
its end. The function restart_plasma starts
systemctl --user restart plasma-plasmashell.service with systemd-run,
in a unit of its own. The restart then does not stop itself.
| Run | Time of the restart |
|---|---|
| In the update window of the widget | After the user closes the window. The run writes the file ~/.cache/bs-updater/restart-plasma, and the window then runs bs-update --restart-plasma-if-pending. |
| In the background | After the notification with the result. |
| In a terminal of the user | At the end of the run. |
~/.cache/bs-updater/last-update
bs-update writes the time in seconds into this file at the end of a
successful update run. The function mark_updated does this.
The widget starts one shell process that watches the file. The process reads the time of the last change every 3 seconds. It exits when the time changes. The widget then shows the up-to-date state and starts the process again.
A poll of one file every 3 seconds is inexpensive. A file monitor is an extra dependency for the widget, so the design uses the poll.
The screen shows one notification <n> updates available at a time. The
function show_notification keeps two files for it:
| File | Content |
|---|---|
~/.cache/bs-updater/notification |
Line 1: the ID of the notification. Line 2: the action of a click. |
~/.cache/bs-updater/notification.pid |
The ID of the notify-send process that waits for a click. |
The option -p makes notify-send write the ID of the notification. The
option -A makes it wait for a click, and it then writes the action.
A check that finds updates does these two steps:
- It stops the
notify-sendprocess of the last notification. - It starts
notify-send -r <ID>, which replaces the last notification.
The command must stop the old process. If it does not, two processes wait for the same notification, and one click opens two terminals.
A check that finds no updates closes the notification. The option
--start also closes it, because the user started the update. The
function close_notification stops the process, then it calls the D-Bus
method CloseNotification with gdbus. If gdbus is absent, the
notification stays until the user closes it.
The widget has one DataSource with the engine executable. Each command
is one source name. The handler onNewData disconnects the source and then
identifies it:
| Source name | Reaction |
|---|---|
| The watcher command | Sets the total to zero, checks again, and watches again after 1 second. |
A command that holds -l
|
Reads the counts from the output. |
The command that writes tools.conf
|
Starts a new check if a check ran before. |
The widget does not connect the watcher again inside onNewData. The
engine removes a disconnected source later, not at once. An immediate new
connection thus gets the old data again, and onNewData runs again. The
calls then recurse until the stack overflows. For this reason, a timer
connects the watcher again after 1 second.
The widget builds the command that writes tools.conf from the eight
settings. QML binds that string to the settings, so a change of a checkbox
makes a new string. The handler onWriteToolsConfigCommandChanged then
writes the file. The file always agrees with the settings.
The widget contains a copy of bs-update in contents/code/. At each
start, before each check and before each update run, the widget compares
that copy with ~/.local/bin/bs-update. A widget update while Plasma runs
replaces the copy in contents/code/, but the widget starts again only
with Plasma. The comparison before each check thus installs the new
command without a new login.
| Condition | Action |
|---|---|
| The two files are equal. | The widget does nothing. |
The file in ~/.local/bin is absent. |
The widget copies the file and sends a notification. |
The file starts with # bs-updater:
|
The widget replaces the file. |
| The file starts with other text. | The widget does nothing. |
The third row holds the command and the widget at the same version after a widget update. The fourth row protects a file of a different program with the same name.
| Decision | Reason |
|---|---|
| The widget never starts a package tool. | One behavior for each origin of a run. |
No count command uses sudo. |
A scheduled check has no password. |
| The command is POSIX shell. | It runs with a small dependency list on each distribution. |
| The two parts speak through files. | No service and no socket are necessary. |
nobara-sync counts with DNF. |
nobara-sync check-updates asks for the root password. |
| The conflict rule uses the first tool. | The result is predictable and the sequence of the table defines it. |
| A failed tool does not stop the run. | The other sources still receive their updates. |
| A failed run writes no state file. | The widget then shows the state "updates available". |
| One update notification at a time. | Scheduled checks do not fill the screen with copies. |
bs-updater technical paper — written in Simplified Technical English, ASD-STE100 Issue 9. See chapter 13.