Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.2.1] - 2026-03-30

### Changed
- GitHub Releases now publish only `x86_64`, `aarch64`, `noarch`, and `src` RPM outputs
- Shared desktop assets and metadata now ship in a dedicated `noarch` companion RPM

## [0.2.0] - 2026-03-30

### Changed
Expand Down
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.22)

project(ro-control
VERSION 0.2.0
VERSION 0.2.1
DESCRIPTION "Smart NVIDIA Driver Manager & System Monitor for Linux"
HOMEPAGE_URL "https://github.com/Project-Ro-ASD/ro-Control"
LANGUAGES CXX
Expand Down
2 changes: 1 addition & 1 deletion data/icons/io.github.projectroasd.rocontrol.metainfo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
</categories>

<releases>
<release version="0.2.0" date="2026-03-30" />
<release version="0.2.1" date="2026-03-30" />
</releases>

<screenshots>
Expand Down
2 changes: 1 addition & 1 deletion docs/man/ro-control.1
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.TH RO-CONTROL 1 "March 2026" "ro-control 0.2.0" "User Commands"
.TH RO-CONTROL 1 "March 2026" "ro-control 0.2.1" "User Commands"
.SH NAME
ro-control \- NVIDIA driver management and diagnostics CLI
.SH SYNOPSIS
Expand Down
2 changes: 1 addition & 1 deletion packaging/rpm/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ macro explicitly:

```bash
rpmbuild -ba packaging/rpm/ro-control.spec \
--define "upstream_version 0.2.0"
--define "upstream_version 0.2.1"
```

If you build from a Git checkout instead of a published source archive, create
Expand Down
6 changes: 5 additions & 1 deletion packaging/rpm/ro-control.spec
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
%global upstream_version %{!?upstream_version:0.2.0}%{?upstream_version}
%global upstream_version %{!?upstream_version:0.2.1}%{?upstream_version}
%global debug_package %{nil}

Name: ro-control
Expand Down Expand Up @@ -89,6 +89,10 @@ tar -xzf %{SOURCE0} --strip-components=1
%{_datadir}/polkit-1/actions/io.github.ProjectRoASD.rocontrol.policy

%changelog
* Mon Mar 30 2026 ro-Control Maintainers <noreply@github.com> - 0.2.1-1
- Limit release outputs to x86_64, aarch64, noarch, and src RPM artifacts
- Split shared desktop assets into a noarch companion package

* Mon Mar 30 2026 ro-Control Maintainers <noreply@github.com> - 0.2.0-1
- Fix installed helper path resolution for privileged operations on system installs
- Activate saved KDE-friendly interface preferences and theme switching in the UI
Expand Down
33 changes: 15 additions & 18 deletions src/backend/monitor/gpumonitor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ bool readIntegerFile(const QString &path, qint64 *value) {
}

bool readFirstTemperatureFromHwmon(const QString &basePath, int *value) {
const QFileInfoList hwmonEntries =
QDir(basePath).entryInfoList({QStringLiteral("hwmon*")},
QDir::Dirs | QDir::NoDotAndDotDot,
QDir::Name);
const QFileInfoList hwmonEntries = QDir(basePath).entryInfoList(
{QStringLiteral("hwmon*")}, QDir::Dirs | QDir::NoDotAndDotDot,
QDir::Name);
for (const QFileInfo &entry : hwmonEntries) {
const QFileInfoList inputs = QDir(entry.absoluteFilePath())
.entryInfoList({QStringLiteral("temp*_input")},
QDir::Files, QDir::Name);
const QFileInfoList inputs =
QDir(entry.absoluteFilePath())
.entryInfoList({QStringLiteral("temp*_input")}, QDir::Files,
QDir::Name);
for (const QFileInfo &input : inputs) {
qint64 milliC = 0;
if (readIntegerFile(input.absoluteFilePath(), &milliC) && milliC > 0) {
Expand All @@ -94,9 +94,9 @@ bool readFirstTemperatureFromHwmon(const QString &basePath, int *value) {
bool readGenericLinuxGpuMetrics(int *temperatureC, int *utilizationPercent,
int *memoryUsedMiB, int *memoryTotalMiB) {
const QFileInfoList cardEntries =
QDir(drmRootPath()).entryInfoList({QStringLiteral("card*")},
QDir::Dirs | QDir::NoDotAndDotDot,
QDir::Name);
QDir(drmRootPath())
.entryInfoList({QStringLiteral("card*")},
QDir::Dirs | QDir::NoDotAndDotDot, QDir::Name);

bool anyMetric = false;
for (const QFileInfo &cardEntry : cardEntries) {
Expand All @@ -117,19 +117,16 @@ bool readGenericLinuxGpuMetrics(int *temperatureC, int *utilizationPercent,
if (utilizationPercent != nullptr &&
readIntegerFile(devicePath + QStringLiteral("/gpu_busy_percent"),
&busyPercent)) {
*utilizationPercent =
std::clamp(static_cast<int>(busyPercent), 0, 100);
*utilizationPercent = std::clamp(static_cast<int>(busyPercent), 0, 100);
anyMetric = true;
}

qint64 usedBytes = 0;
qint64 totalBytes = 0;
const bool usedOk =
readIntegerFile(devicePath + QStringLiteral("/mem_info_vram_used"),
&usedBytes);
const bool totalOk =
readIntegerFile(devicePath + QStringLiteral("/mem_info_vram_total"),
&totalBytes);
const bool usedOk = readIntegerFile(
devicePath + QStringLiteral("/mem_info_vram_used"), &usedBytes);
const bool totalOk = readIntegerFile(
devicePath + QStringLiteral("/mem_info_vram_total"), &totalBytes);
if (usedOk && totalOk && totalBytes > 0) {
if (memoryUsedMiB != nullptr) {
*memoryUsedMiB =
Expand Down
9 changes: 4 additions & 5 deletions src/backend/nvidia/detector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,10 @@ QString NvidiaDetector::activeDriver() const {
}

QString NvidiaDetector::verificationReport() const {
const QString gpuText = m_info.found
? m_info.name
: (m_info.displayAdapterName.isEmpty()
? tr("None")
: m_info.displayAdapterName);
const QString gpuText = m_info.found ? m_info.name
: (m_info.displayAdapterName.isEmpty()
? tr("None")
: m_info.displayAdapterName);
const QString versionText =
m_info.driverVersion.isEmpty() ? tr("None") : m_info.driverVersion;

Expand Down
4 changes: 2 additions & 2 deletions src/backend/nvidia/detector.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class NvidiaDetector : public QObject {

Q_PROPERTY(bool gpuFound READ gpuFound NOTIFY infoChanged)
Q_PROPERTY(QString gpuName READ gpuName NOTIFY infoChanged)
Q_PROPERTY(QString displayAdapterName READ displayAdapterName NOTIFY
infoChanged)
Q_PROPERTY(
QString displayAdapterName READ displayAdapterName NOTIFY infoChanged)
Q_PROPERTY(QString driverVersion READ driverVersion NOTIFY infoChanged)
Q_PROPERTY(bool driverLoaded READ driverLoaded NOTIFY infoChanged)
Q_PROPERTY(bool nouveauActive READ nouveauActive NOTIFY infoChanged)
Expand Down
5 changes: 3 additions & 2 deletions src/backend/system/capabilityprobe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ QString fedoraNvidiaDriverFlowSupportMessage() {
}

return QStringLiteral(
"Fedora NVIDIA driver management is currently supported only on x86_64 "
"and aarch64 builds. The current build architecture is %1.")
"Fedora NVIDIA driver management is currently supported only on "
"x86_64 "
"and aarch64 builds. The current build architecture is %1.")
.arg(normalizedCpuArchitecture());
}

Expand Down
19 changes: 10 additions & 9 deletions src/backend/system/systeminfoprovider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
#include "commandrunner.h"

#include <QFile>
#include <QStringList>
#include <QRegularExpression>
#include <QStringList>
#include <QSysInfo>
#include <QTextStream>

Expand Down Expand Up @@ -67,8 +67,8 @@ QString valueFromOsRelease(const QString &key) {
}

QString value = line.mid(key.size() + 1).trimmed();
if (value.startsWith(QLatin1Char('"')) && value.endsWith(QLatin1Char('"')) &&
value.size() >= 2) {
if (value.startsWith(QLatin1Char('"')) &&
value.endsWith(QLatin1Char('"')) && value.size() >= 2) {
value = value.mid(1, value.size() - 2);
}
return value;
Expand All @@ -77,7 +77,7 @@ QString valueFromOsRelease(const QString &key) {
return {};
}

} // namespace
} // namespace

SystemInfoProvider::SystemInfoProvider(QObject *parent) : QObject(parent) {
refresh();
Expand Down Expand Up @@ -118,7 +118,7 @@ QString SystemInfoProvider::detectOsName() const {

QString SystemInfoProvider::detectKernelVersion() const {
#if defined(Q_OS_UNIX)
utsname name {};
utsname name{};
if (uname(&name) == 0) {
return QString::fromLocal8Bit(name.release);
}
Expand Down Expand Up @@ -173,7 +173,8 @@ QString SystemInfoProvider::detectCpuModel() const {
}

const auto virtResult =
runner.run(QStringLiteral("systemd-detect-virt"), {QStringLiteral("--quiet"), QStringLiteral("--vm")});
runner.run(QStringLiteral("systemd-detect-virt"),
{QStringLiteral("--quiet"), QStringLiteral("--vm")});
if (virtResult.success()) {
const auto virtName = runner.run(QStringLiteral("systemd-detect-virt"));
if (virtName.success()) {
Expand All @@ -188,9 +189,9 @@ QString SystemInfoProvider::detectCpuModel() const {
}
#elif defined(Q_OS_MACOS)
CommandRunner runner;
const auto result =
runner.run(QStringLiteral("sysctl"),
{QStringLiteral("-n"), QStringLiteral("machdep.cpu.brand_string")});
const auto result = runner.run(
QStringLiteral("sysctl"),
{QStringLiteral("-n"), QStringLiteral("machdep.cpu.brand_string")});
if (result.success()) {
const QString value = result.stdout.trimmed();
if (!value.isEmpty()) {
Expand Down
3 changes: 2 additions & 1 deletion src/backend/system/systeminfoprovider.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ class SystemInfoProvider : public QObject {
Q_OBJECT

Q_PROPERTY(QString osName READ osName NOTIFY infoChanged)
Q_PROPERTY(QString desktopEnvironment READ desktopEnvironment NOTIFY infoChanged)
Q_PROPERTY(
QString desktopEnvironment READ desktopEnvironment NOTIFY infoChanged)
Q_PROPERTY(QString kernelVersion READ kernelVersion NOTIFY infoChanged)
Q_PROPERTY(QString cpuModel READ cpuModel NOTIFY infoChanged)

Expand Down
38 changes: 19 additions & 19 deletions tests/test_cli.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ private slots:
RoControlCli::parseArguments({QStringLiteral("ro-control"),
QStringLiteral("--help")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintHelp);
QVERIFY(command.payload.contains(QStringLiteral("driver install")));
Expand All @@ -24,18 +24,18 @@ private slots:
RoControlCli::parseArguments({QStringLiteral("ro-control"),
QStringLiteral("--version")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintVersion);
QCOMPARE(command.payload, QStringLiteral("0.2.0"));
QCOMPARE(command.payload, QStringLiteral("0.2.1"));
}

void testJsonRequiresDiagnostics() {
const auto command =
RoControlCli::parseArguments({QStringLiteral("ro-control"),
QStringLiteral("--json")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::Invalid);
QVERIFY(command.payload.contains(QStringLiteral("--json")));
Expand All @@ -46,7 +46,7 @@ private slots:
RoControlCli::parseArguments({QStringLiteral("ro-control"),
QStringLiteral("status")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintStatusText);
}
Expand All @@ -57,7 +57,7 @@ private slots:
QStringLiteral("status"),
QStringLiteral("--json")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintStatusJson);
}
Expand All @@ -67,7 +67,7 @@ private slots:
RoControlCli::parseArguments({QStringLiteral("ro-control"),
QStringLiteral("diagnostics")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintDiagnosticsText);
}
Expand All @@ -78,7 +78,7 @@ private slots:
QStringLiteral("diagnostics"),
QStringLiteral("--json")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintDiagnosticsJson);
}
Expand All @@ -89,7 +89,7 @@ private slots:
QStringLiteral("--diagnostics"),
QStringLiteral("--json")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::PrintDiagnosticsJson);
}
Expand All @@ -100,7 +100,7 @@ private slots:
QStringLiteral("driver"),
QStringLiteral("install")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action,
RoControlCli::CommandAction::InstallProprietaryDriver);
Expand All @@ -114,7 +114,7 @@ private slots:
QStringLiteral("install"),
QStringLiteral("--open-source")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action,
RoControlCli::CommandAction::InstallOpenSourceDriver);
Expand All @@ -128,7 +128,7 @@ private slots:
QStringLiteral("--proprietary"),
QStringLiteral("--accept-license")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action,
RoControlCli::CommandAction::InstallProprietaryDriver);
Expand All @@ -141,7 +141,7 @@ private slots:
QStringLiteral("driver"),
QStringLiteral("update")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::UpdateDriver);
}
Expand All @@ -153,7 +153,7 @@ private slots:
QStringLiteral("install"),
QStringLiteral("--json")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::Invalid);
QVERIFY(command.payload.contains(QStringLiteral("--json")));
Expand All @@ -167,7 +167,7 @@ private slots:
QStringLiteral("--proprietary"),
QStringLiteral("--open-source")},
QStringLiteral("ro-control"),
QStringLiteral("0.2.0"),
QStringLiteral("0.2.1"),
QStringLiteral("CLI test"));
QCOMPARE(command.action, RoControlCli::CommandAction::Invalid);
QVERIFY(command.payload.contains(QStringLiteral("cannot be used together")));
Expand All @@ -176,7 +176,7 @@ private slots:
void testRenderDiagnosticsText() {
RoControlCli::DiagnosticsSnapshot snapshot;
snapshot.applicationName = QStringLiteral("ro-control");
snapshot.applicationVersion = QStringLiteral("0.2.0");
snapshot.applicationVersion = QStringLiteral("0.2.1");
snapshot.locale = QStringLiteral("en_US");
snapshot.gpuFound = true;
snapshot.gpuName = QStringLiteral("Example GPU");
Expand All @@ -193,7 +193,7 @@ private slots:
void testRenderStatusText() {
RoControlCli::DiagnosticsSnapshot snapshot;
snapshot.applicationName = QStringLiteral("ro-control");
snapshot.applicationVersion = QStringLiteral("0.2.0");
snapshot.applicationVersion = QStringLiteral("0.2.1");
snapshot.activeDriver = QStringLiteral("Proprietary");
snapshot.updateAvailable = true;

Expand All @@ -206,7 +206,7 @@ private slots:
void testRenderDiagnosticsJsonObject() {
RoControlCli::DiagnosticsSnapshot snapshot;
snapshot.applicationName = QStringLiteral("ro-control");
snapshot.applicationVersion = QStringLiteral("0.2.0");
snapshot.applicationVersion = QStringLiteral("0.2.1");
snapshot.gpuFound = true;
snapshot.ramUsagePercent = 42;

Expand All @@ -221,7 +221,7 @@ private slots:
void testRenderStatusJsonObject() {
RoControlCli::DiagnosticsSnapshot snapshot;
snapshot.applicationName = QStringLiteral("ro-control");
snapshot.applicationVersion = QStringLiteral("0.2.0");
snapshot.applicationVersion = QStringLiteral("0.2.1");
snapshot.updateAvailable = true;

const QJsonObject object = RoControlCli::renderStatusJsonObject(snapshot);
Expand Down
Loading
Loading