Fix update discovery and isolate fingerprint installer permissions
This commit is contained in:
@@ -17,6 +17,8 @@ include(KDECompilerSettings NO_POLICY_SCOPE)
|
|||||||
|
|
||||||
find_package(Qt6 6.8 REQUIRED COMPONENTS Core Quick)
|
find_package(Qt6 6.8 REQUIRED COMPONENTS Core Quick)
|
||||||
find_package(KF6 6.0 REQUIRED COMPONENTS Auth Config CoreAddons I18n KCMUtils)
|
find_package(KF6 6.0 REQUIRED COMPONENTS Auth Config CoreAddons I18n KCMUtils)
|
||||||
|
find_package(PkgConfig REQUIRED)
|
||||||
|
pkg_check_modules(RPM REQUIRED IMPORTED_TARGET rpm)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
if(BUILD_TESTING)
|
if(BUILD_TESTING)
|
||||||
|
|||||||
@@ -14,6 +14,11 @@ installation to a narrowly scoped KAuth helper. The helper accepts only valid
|
|||||||
package names that independently resolve to that capability in the
|
package names that independently resolve to that capability in the
|
||||||
`fedora-tools` repository.
|
`fedora-tools` repository.
|
||||||
|
|
||||||
|
Update detection compares repository and installed versions using RPM's version
|
||||||
|
ordering, without requiring access to DNF's privileged transaction state. DNF
|
||||||
|
still checks dependencies when installing an update. The repository file sets a
|
||||||
|
five-minute metadata expiry so Discover's PackageKit backend checks it regularly.
|
||||||
|
|
||||||
Installed tools can also be configured or removed from the module. Package
|
Installed tools can also be configured or removed from the module. Package
|
||||||
operations run one at a time; other actions remain disabled until DNF finishes.
|
operations run one at a time; other actions remain disabled until DNF finishes.
|
||||||
The module currently configures task-group shortcut behaviour, touchpad hold-tap
|
The module currently configures task-group shortcut behaviour, touchpad hold-tap
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: fedora-tools-settings
|
Name: fedora-tools-settings
|
||||||
Version: 0.1.0
|
Version: 0.1.0
|
||||||
Release: 13%{?dist}
|
Release: 14%{?dist}
|
||||||
Summary: Plasma System Settings module for Fedora Tools
|
Summary: Plasma System Settings module for Fedora Tools
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -18,6 +18,7 @@ BuildRequires: kf6-kconfig-devel
|
|||||||
BuildRequires: kf6-ki18n-devel
|
BuildRequires: kf6-ki18n-devel
|
||||||
BuildRequires: kf6-kirigami-devel
|
BuildRequires: kf6-kirigami-devel
|
||||||
BuildRequires: qt6-qtdeclarative-devel
|
BuildRequires: qt6-qtdeclarative-devel
|
||||||
|
BuildRequires: rpm-devel
|
||||||
|
|
||||||
Requires: dnf5
|
Requires: dnf5
|
||||||
Requires: kf6-kauth
|
Requires: kf6-kauth
|
||||||
@@ -74,6 +75,10 @@ done
|
|||||||
%{_datadir}/polkit-1/actions/se.ajpanton.fedoratools.policy
|
%{_datadir}/polkit-1/actions/se.ajpanton.fedoratools.policy
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-14
|
||||||
|
- Detect newer packages without reading privileged DNF state
|
||||||
|
- Report package query failures instead of silently hiding updates
|
||||||
|
|
||||||
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-13
|
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-13
|
||||||
- Share settings validation and report failed configuration writes
|
- Share settings validation and report failed configuration writes
|
||||||
- Simplify ordering and source packaging
|
- Simplify ordering and source packaging
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ kcmutils_add_qml_kcm(kcm_fedora_tools
|
|||||||
toolmodel.h
|
toolmodel.h
|
||||||
)
|
)
|
||||||
target_link_libraries(kcm_fedora_tools PRIVATE
|
target_link_libraries(kcm_fedora_tools PRIVATE
|
||||||
|
PkgConfig::RPM
|
||||||
Qt6::Core
|
Qt6::Core
|
||||||
Qt6::Quick
|
Qt6::Quick
|
||||||
KF6::AuthCore
|
KF6::AuthCore
|
||||||
|
|||||||
@@ -142,7 +142,6 @@ void FedoraToolsKcm::refresh(bool refreshMetadata)
|
|||||||
m_refreshMetadata = refreshMetadata;
|
m_refreshMetadata = refreshMetadata;
|
||||||
m_installedPackages.clear();
|
m_installedPackages.clear();
|
||||||
m_availablePackages.clear();
|
m_availablePackages.clear();
|
||||||
m_updates.clear();
|
|
||||||
setMessage({});
|
setMessage({});
|
||||||
setBusy(true);
|
setBusy(true);
|
||||||
|
|
||||||
@@ -316,8 +315,12 @@ void FedoraToolsKcm::queryFinished(int exitCode, QProcess::ExitStatus exitStatus
|
|||||||
const QByteArray output = m_query.readAllStandardOutput();
|
const QByteArray output = m_query.readAllStandardOutput();
|
||||||
const QByteArray errorOutput = m_query.readAllStandardError();
|
const QByteArray errorOutput = m_query.readAllStandardError();
|
||||||
|
|
||||||
if (exitStatus != QProcess::NormalExit) {
|
// rpm -q reports missing packages on stdout with exit code 1. That is a
|
||||||
setMessage(i18n("Package discovery terminated unexpectedly."), true);
|
// normal discovery result; stderr or a failed repository query is not.
|
||||||
|
const QString details = QString::fromUtf8(errorOutput).trimmed();
|
||||||
|
if (exitStatus != QProcess::NormalExit
|
||||||
|
|| (exitCode != 0 && (m_stage == QueryStage::Available || !details.isEmpty() || exitCode != 1))) {
|
||||||
|
setMessage(details.isEmpty() ? i18n("Package discovery failed (%1).", m_query.program()) : details, true);
|
||||||
m_stage = QueryStage::Idle;
|
m_stage = QueryStage::Idle;
|
||||||
setPackageOperation({}, {});
|
setPackageOperation({}, {});
|
||||||
setBusy(false);
|
setBusy(false);
|
||||||
@@ -342,43 +345,12 @@ void FedoraToolsKcm::queryFinished(int exitCode, QProcess::ExitStatus exitStatus
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (m_stage == QueryStage::Available) {
|
if (m_stage == QueryStage::Available) {
|
||||||
if (exitCode != 0) {
|
|
||||||
m_tools.setPackages(m_installedPackages, {});
|
|
||||||
const QString details = QString::fromUtf8(errorOutput).trimmed();
|
|
||||||
setMessage(details.isEmpty() ? i18n("The Fedora Tools repository is unavailable.") : details, true);
|
|
||||||
updateFingerprintStatus();
|
|
||||||
m_stage = QueryStage::Idle;
|
|
||||||
setPackageOperation({}, {});
|
|
||||||
setBusy(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_availablePackages = parsePackageRecords(output);
|
m_availablePackages = parsePackageRecords(output);
|
||||||
if (m_availablePackages.isEmpty()) {
|
if (m_availablePackages.isEmpty()) {
|
||||||
finishRefresh();
|
finishRefresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
startQuery(QStringLiteral("/usr/bin/dnf5"),
|
|
||||||
{QStringLiteral("--repo=fedora-tools"),
|
|
||||||
QStringLiteral("-q"),
|
|
||||||
QStringLiteral("repoquery"),
|
|
||||||
QStringLiteral("--available"),
|
|
||||||
QStringLiteral("--upgrades"),
|
|
||||||
QStringLiteral("--whatprovides=fedora-tools-tool"),
|
|
||||||
QStringLiteral("--queryformat"),
|
|
||||||
packageFormat},
|
|
||||||
QueryStage::Updates);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_stage == QueryStage::Updates) {
|
|
||||||
if (exitCode == 0) {
|
|
||||||
for (const PackageRecord &package : parsePackageRecords(output)) {
|
|
||||||
m_updates.insert(package.name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList packageNames;
|
QStringList packageNames;
|
||||||
for (const PackageRecord &package : std::as_const(m_availablePackages)) {
|
for (const PackageRecord &package : std::as_const(m_availablePackages)) {
|
||||||
packageNames.append(package.name);
|
packageNames.append(package.name);
|
||||||
@@ -407,7 +379,7 @@ void FedoraToolsKcm::queryFinished(int exitCode, QProcess::ExitStatus exitStatus
|
|||||||
|
|
||||||
void FedoraToolsKcm::finishRefresh()
|
void FedoraToolsKcm::finishRefresh()
|
||||||
{
|
{
|
||||||
m_tools.setPackages(m_installedPackages, m_availablePackages, m_updates);
|
m_tools.setPackages(m_installedPackages, m_availablePackages);
|
||||||
updateFingerprintStatus();
|
updateFingerprintStatus();
|
||||||
m_stage = QueryStage::Idle;
|
m_stage = QueryStage::Idle;
|
||||||
setPackageOperation({}, {});
|
setPackageOperation({}, {});
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
#include <KQuickConfigModule>
|
#include <KQuickConfigModule>
|
||||||
|
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QSet>
|
|
||||||
#include <QUrl>
|
#include <QUrl>
|
||||||
|
|
||||||
class KJob;
|
class KJob;
|
||||||
@@ -75,7 +74,6 @@ private:
|
|||||||
Idle,
|
Idle,
|
||||||
InstalledProviders,
|
InstalledProviders,
|
||||||
Available,
|
Available,
|
||||||
Updates,
|
|
||||||
KnownInstalled,
|
KnownInstalled,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -95,7 +93,6 @@ private:
|
|||||||
QueryStage m_stage = QueryStage::Idle;
|
QueryStage m_stage = QueryStage::Idle;
|
||||||
QList<PackageRecord> m_installedPackages;
|
QList<PackageRecord> m_installedPackages;
|
||||||
QList<PackageRecord> m_availablePackages;
|
QList<PackageRecord> m_availablePackages;
|
||||||
QSet<QString> m_updates;
|
|
||||||
bool m_refreshMetadata = false;
|
bool m_refreshMetadata = false;
|
||||||
bool m_busy = false;
|
bool m_busy = false;
|
||||||
bool m_error = false;
|
bool m_error = false;
|
||||||
|
|||||||
@@ -10,12 +10,26 @@
|
|||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
|
#include <rpm/rpmver.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
bool newerVersion(const QString &available, const QString &installed)
|
||||||
|
{
|
||||||
|
const auto candidate = rpmverParse(available.toUtf8().constData());
|
||||||
|
const auto current = rpmverParse(installed.toUtf8().constData());
|
||||||
|
if (!candidate || !current) {
|
||||||
|
qWarning() << "Cannot compare RPM versions:" << available << installed;
|
||||||
|
}
|
||||||
|
const bool newer = candidate && current && rpmverCmp(candidate, current) > 0;
|
||||||
|
rpmverFree(candidate);
|
||||||
|
rpmverFree(current);
|
||||||
|
return newer;
|
||||||
|
}
|
||||||
|
|
||||||
QStringList readConfigurationCommand(const QString &packageName)
|
QStringList readConfigurationCommand(const QString &packageName)
|
||||||
{
|
{
|
||||||
const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
|
const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation,
|
||||||
@@ -119,8 +133,7 @@ QHash<int, QByteArray> ToolModel::roleNames() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ToolModel::setPackages(const QList<PackageRecord> &installed,
|
void ToolModel::setPackages(const QList<PackageRecord> &installed,
|
||||||
const QList<PackageRecord> &available,
|
const QList<PackageRecord> &available)
|
||||||
const QSet<QString> &updates)
|
|
||||||
{
|
{
|
||||||
QMap<QString, Tool> tools;
|
QMap<QString, Tool> tools;
|
||||||
|
|
||||||
@@ -147,7 +160,8 @@ void ToolModel::setPackages(const QList<PackageRecord> &installed,
|
|||||||
}
|
}
|
||||||
tool.compatibilityError = incompatible;
|
tool.compatibilityError = incompatible;
|
||||||
tool.available = incompatible.isEmpty();
|
tool.available = incompatible.isEmpty();
|
||||||
tool.updateAvailable = tool.available && tool.installed && updates.contains(package.name);
|
tool.updateAvailable = tool.available && tool.installed
|
||||||
|
&& newerVersion(package.version, tool.version);
|
||||||
}
|
}
|
||||||
|
|
||||||
beginResetModel();
|
beginResetModel();
|
||||||
|
|||||||
@@ -5,7 +5,6 @@
|
|||||||
#include "packageutils.h"
|
#include "packageutils.h"
|
||||||
|
|
||||||
#include <QAbstractListModel>
|
#include <QAbstractListModel>
|
||||||
#include <QSet>
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
|
|
||||||
struct Tool
|
struct Tool
|
||||||
@@ -48,8 +47,7 @@ public:
|
|||||||
QHash<int, QByteArray> roleNames() const override;
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
void setPackages(const QList<PackageRecord> &installed,
|
void setPackages(const QList<PackageRecord> &installed,
|
||||||
const QList<PackageRecord> &available,
|
const QList<PackageRecord> &available);
|
||||||
const QSet<QString> &updates = {});
|
|
||||||
bool mayInstall(const QString &packageName) const;
|
bool mayInstall(const QString &packageName) const;
|
||||||
bool mayRemove(const QString &packageName) const;
|
bool mayRemove(const QString &packageName) const;
|
||||||
QStringList configurationCommand(const QString &packageName) const;
|
QStringList configurationCommand(const QString &packageName) const;
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ add_executable(test-toolmodel
|
|||||||
../src/packageutils.cpp
|
../src/packageutils.cpp
|
||||||
)
|
)
|
||||||
target_include_directories(test-toolmodel PRIVATE ../src)
|
target_include_directories(test-toolmodel PRIVATE ../src)
|
||||||
target_link_libraries(test-toolmodel PRIVATE Qt6::Core Qt6::Test)
|
target_link_libraries(test-toolmodel PRIVATE Qt6::Core Qt6::Test PkgConfig::RPM)
|
||||||
add_test(NAME toolmodel COMMAND test-toolmodel)
|
add_test(NAME toolmodel COMMAND test-toolmodel)
|
||||||
|
|
||||||
add_executable(test-qmlresource test-qmlresource.cpp)
|
add_executable(test-qmlresource test-qmlresource.cpp)
|
||||||
|
|||||||
@@ -12,6 +12,38 @@ class ToolModelTest : public QObject
|
|||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
|
void comparesRpmVersions_data()
|
||||||
|
{
|
||||||
|
QTest::addColumn<QString>("installed");
|
||||||
|
QTest::addColumn<QString>("available");
|
||||||
|
QTest::addColumn<bool>("update");
|
||||||
|
QTest::newRow("numeric release") << QStringLiteral("0.1.0-9.fc44") << QStringLiteral("0.1.0-11.fc44") << true;
|
||||||
|
QTest::newRow("same version") << QStringLiteral("0.1.0-11.fc44") << QStringLiteral("0.1.0-11.fc44") << false;
|
||||||
|
QTest::newRow("locally newer") << QStringLiteral("0.1.0-25.fc44") << QStringLiteral("0.1.0-23.fc44") << false;
|
||||||
|
QTest::newRow("epoch wins") << QStringLiteral("2.0-1") << QStringLiteral("1:1.0-1") << true;
|
||||||
|
QTest::newRow("installed epoch wins") << QStringLiteral("1:1.0-1") << QStringLiteral("2.0-1") << false;
|
||||||
|
QTest::newRow("implicit zero epoch") << QStringLiteral("0:1.0-1") << QStringLiteral("1.0-1") << false;
|
||||||
|
QTest::newRow("prerelease") << QStringLiteral("1.0~rc1-1") << QStringLiteral("1.0-1") << true;
|
||||||
|
QTest::newRow("snapshot") << QStringLiteral("1.0-1") << QStringLiteral("1.0^git1-1") << true;
|
||||||
|
QTest::newRow("newer than snapshot") << QStringLiteral("1.0^git1-1") << QStringLiteral("1.0.1-1") << true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void comparesRpmVersions()
|
||||||
|
{
|
||||||
|
QFETCH(QString, installed);
|
||||||
|
QFETCH(QString, available);
|
||||||
|
QFETCH(bool, update);
|
||||||
|
ToolModel model;
|
||||||
|
// Match an older noarch install against its native-architecture update.
|
||||||
|
// No DNF transaction state or privileged files are needed to compare EVRs.
|
||||||
|
const PackageRecord current{QStringLiteral("touchpad-hold-tap"), installed, QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
||||||
|
const PackageRecord candidate{current.name, available, QStringLiteral("x86_64"), current.summary};
|
||||||
|
model.setPackages({current}, {candidate});
|
||||||
|
QCOMPARE(model.data(model.index(0), ToolModel::UpdateAvailableRole).toBool(), update);
|
||||||
|
model.setPackages({}, {candidate});
|
||||||
|
QVERIFY(!model.data(model.index(0), ToolModel::UpdateAvailableRole).toBool());
|
||||||
|
}
|
||||||
|
|
||||||
void incompatibleFrameworkIsOnlyListedWhenInstalled() {
|
void incompatibleFrameworkIsOnlyListedWhenInstalled() {
|
||||||
if (packageCompatibilityError(QStringLiteral("framework-laptop-tools")).isEmpty()) QSKIP("Requires a non-Framework test host");
|
if (packageCompatibilityError(QStringLiteral("framework-laptop-tools")).isEmpty()) QSKIP("Requires a non-Framework test host");
|
||||||
const PackageRecord package{QStringLiteral("framework-laptop-tools"), QStringLiteral("1"), QStringLiteral("x86_64"), QStringLiteral("Hardware controls")};
|
const PackageRecord package{QStringLiteral("framework-laptop-tools"), QStringLiteral("1"), QStringLiteral("x86_64"), QStringLiteral("Hardware controls")};
|
||||||
@@ -55,7 +87,7 @@ private Q_SLOTS:
|
|||||||
const PackageRecord installed{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-5.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
const PackageRecord installed{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-5.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
||||||
const PackageRecord available{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-6.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
const PackageRecord available{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-6.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
||||||
|
|
||||||
model.setPackages({installed}, {available}, {QStringLiteral("touchpad-hold-tap")});
|
model.setPackages({installed}, {available});
|
||||||
|
|
||||||
QCOMPARE(model.rowCount(), 1);
|
QCOMPARE(model.rowCount(), 1);
|
||||||
const QModelIndex index = model.index(0);
|
const QModelIndex index = model.index(0);
|
||||||
@@ -67,7 +99,7 @@ private Q_SLOTS:
|
|||||||
QCOMPARE(model.mayRemove(QStringLiteral("missing-tool")), false);
|
QCOMPARE(model.mayRemove(QStringLiteral("missing-tool")), false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void doesNotGuessUpdatesFromDifferentVersionStrings()
|
void doesNotOfferDowngrades()
|
||||||
{
|
{
|
||||||
ToolModel model;
|
ToolModel model;
|
||||||
const PackageRecord installed{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-7.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
const PackageRecord installed{QStringLiteral("touchpad-hold-tap"), QStringLiteral("0.1.0-7.fc44"), QStringLiteral("noarch"), QStringLiteral("Gesture")};
|
||||||
|
|||||||
@@ -2,5 +2,6 @@
|
|||||||
name=Small tools for Fedora
|
name=Small tools for Fedora
|
||||||
baseurl=https://git.ajpanton.se/api/packages/ajp_anton/rpm/fedora/$releasever
|
baseurl=https://git.ajpanton.se/api/packages/ajp_anton/rpm/fedora/$releasever
|
||||||
enabled=1
|
enabled=1
|
||||||
|
metadata_expire=300
|
||||||
gpgcheck=1
|
gpgcheck=1
|
||||||
gpgkey=https://git.ajpanton.se/api/packages/ajp_anton/rpm/repository.key
|
gpgkey=https://git.ajpanton.se/api/packages/ajp_anton/rpm/repository.key
|
||||||
|
|||||||
@@ -36,6 +36,12 @@ require_fedora() {
|
|||||||
[[ ${ID:-} == fedora ]] || die "this workaround only supports Fedora Linux"
|
[[ ${ID:-} == fedora ]] || die "this workaround only supports Fedora Linux"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
run_dnf() (
|
||||||
|
# The private download's umask must not make DNF's system state root-only.
|
||||||
|
umask 022
|
||||||
|
dnf5 "$@"
|
||||||
|
)
|
||||||
|
|
||||||
cleanup() {
|
cleanup() {
|
||||||
[[ -n $temporary_dir ]] || return
|
[[ -n $temporary_dir ]] || return
|
||||||
rm -f -- "$temporary_dir/kscreenlocker.rpm"
|
rm -f -- "$temporary_dir/kscreenlocker.rpm"
|
||||||
@@ -233,7 +239,7 @@ enable_workaround() {
|
|||||||
|
|
||||||
dnf_args=(install --assumeyes)
|
dnf_args=(install --assumeyes)
|
||||||
(( force )) && dnf_args+=(--allow-downgrade)
|
(( force )) && dnf_args+=(--allow-downgrade)
|
||||||
dnf5 "${dnf_args[@]}" "$candidate"
|
run_dnf "${dnf_args[@]}" "$candidate"
|
||||||
|
|
||||||
is_workaround_package -q kscreenlocker ||
|
is_workaround_package -q kscreenlocker ||
|
||||||
die "DNF completed without enabling the workaround"
|
die "DNF completed without enabling the workaround"
|
||||||
@@ -264,7 +270,7 @@ disable_workaround() {
|
|||||||
printf 'This will replace the workaround with Fedora\x27s current KScreenLocker package.\n'
|
printf 'This will replace the workaround with Fedora\x27s current KScreenLocker package.\n'
|
||||||
confirm
|
confirm
|
||||||
|
|
||||||
dnf5 \
|
run_dnf \
|
||||||
--repo=fedora,updates \
|
--repo=fedora,updates \
|
||||||
--refresh \
|
--refresh \
|
||||||
distro-sync \
|
distro-sync \
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
Name: plasma-fingerprint-workaround
|
Name: plasma-fingerprint-workaround
|
||||||
Version: 0.1.0
|
Version: 0.1.0
|
||||||
Release: 5%{?dist}
|
Release: 6%{?dist}
|
||||||
Summary: Opt-in patched KScreenLocker for fingerprint recovery after suspend
|
Summary: Opt-in patched KScreenLocker for fingerprint recovery after suspend
|
||||||
|
|
||||||
License: MIT
|
License: MIT
|
||||||
@@ -51,6 +51,9 @@ install -D -m 0644 %{SOURCE3} \
|
|||||||
%{_datadir}/plasma-fingerprint-workaround/payload.conf
|
%{_datadir}/plasma-fingerprint-workaround/payload.conf
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-6
|
||||||
|
- Keep private-download permissions from leaking into DNF's system state
|
||||||
|
|
||||||
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-5
|
* Sat Sep 12 2026 fedora-tools contributors - 0.1.0-5
|
||||||
- Simplify status queries and extend invalid-command tests
|
- Simplify status queries and extend invalid-command tests
|
||||||
|
|
||||||
|
|||||||
@@ -29,4 +29,23 @@ source "$payload_config"
|
|||||||
[[ $payload_url == https://*/*.rpm ]]
|
[[ $payload_url == https://*/*.rpm ]]
|
||||||
[[ $payload_sha256 =~ ^[[:xdigit:]]{64}$ ]]
|
[[ $payload_sha256 =~ ^[[:xdigit:]]{64}$ ]]
|
||||||
|
|
||||||
|
# Load the functions through the harmless help command, then replace DNF with
|
||||||
|
# a test function. Never invoke the package manager in this test.
|
||||||
|
(
|
||||||
|
source "$controller" --help >/dev/null
|
||||||
|
dnf5() {
|
||||||
|
[[ $(umask) == 0022 && $1 == test-argument ]]
|
||||||
|
}
|
||||||
|
umask 077
|
||||||
|
run_dnf test-argument
|
||||||
|
[[ $(umask) == 0077 ]]
|
||||||
|
dnf5() { return 23; }
|
||||||
|
if run_dnf test-argument; then
|
||||||
|
printf 'DNF failure was not propagated.\n' >&2
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
[[ $? == 23 ]]
|
||||||
|
fi
|
||||||
|
)
|
||||||
|
|
||||||
printf 'Controller tests passed.\n'
|
printf 'Controller tests passed.\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user