Skip to content
This repository was archived by the owner on Apr 28, 2022. It is now read-only.

Commit 13f988f

Browse files
committed
Allow enabling developer mode from local package.
[developermode] Implement installing from local package. Contributes to JB#47559 This allows enabling developer mode from local package and thus doesn't require Internet or repository access. The package must not have non-preinstalled requirements. Before installing version in package file name is checked against -preload subpackage. [developermode] Add repositoryAccessRequired property. Contributes to JB#47056 Repository access may be required if the local package does not exist or it can not be installed for some reason. In that case developer mode settings page must ask for repository access (usually some kind of account).
1 parent ceaca90 commit 13f988f

File tree

3 files changed

+153
-62
lines changed

3 files changed

+153
-62
lines changed

rpm/nemo-qml-plugin-systemsettings.spec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Version: 0.5.27
44
Release: 1
55
Group: System/Libraries
66
License: BSD
7-
URL: https://git.merproject.org/mer-core/nemo-qml-plugin-systemsettings
7+
URL: https://git.sailfishos.org/mer-core/nemo-qml-plugin-systemsettings
88
Source0: %{name}-%{version}.tar.bz2
99
Requires(post): /sbin/ldconfig
1010
Requires(postun): /sbin/ldconfig

src/developermodesettings.cpp

Lines changed: 147 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
/* A file that is provided by the developer mode package */
5656
#define DEVELOPER_MODE_PROVIDED_FILE "/usr/bin/devel-su"
5757
#define DEVELOPER_MODE_PACKAGE "jolla-developer-mode"
58+
#define DEVELOPER_MODE_PACKAGE_PRELOAD_DIR "/var/lib/jolla-developer-mode/preloaded/"
5859

5960
/* D-Bus service */
6061
#define USB_MODED_SERVICE "com.meego.usb_moded"
@@ -84,6 +85,17 @@ static QMap<QString,QString> enumerate_network_interfaces()
8485
return result;
8586
}
8687

88+
static QString get_cached_package(const QString &version)
89+
{
90+
QDir dir(DEVELOPER_MODE_PACKAGE_PRELOAD_DIR);
91+
QStringList filters;
92+
filters << QStringLiteral("%1-%2.*.rpm").arg(DEVELOPER_MODE_PACKAGE).arg(version);
93+
auto preloaded = dir.entryList(filters, QDir::Files, QDir::Name);
94+
if (preloaded.empty())
95+
return QString();
96+
return dir.absoluteFilePath(preloaded.last());
97+
}
98+
8799
DeveloperModeSettings::DeveloperModeSettings(QObject *parent)
88100
: QObject(parent)
89101
, m_usbModeDaemon(USB_MODED_SERVICE, USB_MODED_PATH, USB_MODED_INTERFACE, QDBusConnection::systemBus())
@@ -97,6 +109,8 @@ DeveloperModeSettings::DeveloperModeSettings(QObject *parent)
97109
, m_transactionRole(PackageKit::Transaction::RoleUnknown)
98110
, m_transactionStatus(PackageKit::Transaction::StatusUnknown)
99111
, m_refreshedForInstall(false)
112+
, m_localInstallFailed(false)
113+
, m_localDeveloperModePackagePath(get_cached_package(QStringLiteral("*"))) // Initialized to possibly incompatible package
100114
{
101115
int uid = getdef_num("UID_MIN", -1);
102116
struct passwd *pwd;
@@ -106,6 +120,23 @@ DeveloperModeSettings::DeveloperModeSettings(QObject *parent)
106120
qCWarning(lcDeveloperModeLog) << "Failed to return username using getpwuid()";
107121
}
108122

123+
// Resolve and update local package path
124+
if (!m_localDeveloperModePackagePath.isEmpty()) {
125+
PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled);
126+
connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
127+
connect(resolvePackage, &PackageKit::Transaction::package,
128+
this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) {
129+
Q_UNUSED(summary)
130+
Q_ASSERT(info == PackageKit::Transaction::InfoInstalled);
131+
const QString version = PackageKit::Transaction::packageVersion(packageID);
132+
m_localDeveloperModePackagePath = get_cached_package(version);
133+
if (m_localDeveloperModePackagePath.isEmpty()) {
134+
emit repositoryAccessRequiredChanged();
135+
}
136+
qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath;
137+
});
138+
}
139+
109140
refresh();
110141

111142
// TODO: Watch WLAN / USB IP addresses for changes
@@ -146,6 +177,12 @@ int DeveloperModeSettings::workProgress() const
146177
return m_workProgress;
147178
}
148179

180+
bool DeveloperModeSettings::repositoryAccessRequired() const
181+
{
182+
// Aka local-install-of-developer-mode-package-is-not-possible
183+
return m_localInstallFailed || m_localDeveloperModePackagePath.isEmpty();
184+
}
185+
149186
void DeveloperModeSettings::setDeveloperMode(bool enabled)
150187
{
151188
if (m_developerModeEnabled != enabled) {
@@ -233,72 +270,114 @@ void DeveloperModeSettings::refreshPackageCacheAndInstall()
233270
void DeveloperModeSettings::resolveAndExecute(Command command)
234271
{
235272
setWorkStatus(Preparing);
273+
m_workProgress = 0;
236274
m_developerModePackageId.clear(); // might differ between installed/available
237275

238-
PackageKit::Transaction::Filters filters;
239-
if (command == RemoveCommand) {
240-
filters = PackageKit::Transaction::FilterInstalled;
241-
} else {
242-
filters = PackageKit::Transaction::FilterNewest;
243-
}
244-
PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE, filters);
276+
if (command == InstallCommand && !m_localInstallFailed && !m_localDeveloperModePackagePath.isEmpty()) {
277+
// Resolve which version of developer mode package is expected
278+
PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE"-preload", PackageKit::Transaction::FilterInstalled);
279+
connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
280+
connect(resolvePackage, &PackageKit::Transaction::package,
281+
this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) {
282+
Q_UNUSED(summary)
283+
Q_ASSERT(info == PackageKit::Transaction::InfoInstalled);
284+
const QString version = PackageKit::Transaction::packageVersion(packageID);
285+
m_localDeveloperModePackagePath = get_cached_package(version);
286+
emit repositoryAccessRequiredChanged();
287+
qCDebug(lcDeveloperModeLog) << "Preload package version: " << version << ", local package path: " << m_localDeveloperModePackagePath;
288+
});
289+
290+
connect(resolvePackage, &PackageKit::Transaction::finished,
291+
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
292+
Q_UNUSED(runtime)
293+
if (status != PackageKit::Transaction::ExitSuccess || m_localDeveloperModePackagePath.isEmpty()) {
294+
qCDebug(lcDeveloperModeLog) << "Preloaded package not found, must use remote package";
295+
// No cached package => install from repos
296+
resolveAndExecute(InstallCommand);
297+
} else {
298+
PackageKit::Transaction *tx = PackageKit::Daemon::installFiles(QStringList() << m_localDeveloperModePackagePath);
299+
connectCommandSignals(tx);
300+
connect(tx, &PackageKit::Transaction::finished,
301+
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
302+
if (status == PackageKit::Transaction::ExitSuccess) {
303+
qCDebug(lcDeveloperModeLog) << "Developer mode installation from local package transaction done:" << status << runtime;
304+
resetState();
305+
} else if (status == PackageKit::Transaction::ExitFailed) {
306+
qCWarning(lcDeveloperModeLog) << "Developer mode installation from local package failed, trying from repos";
307+
m_localInstallFailed = true;
308+
emit repositoryAccessRequiredChanged();
309+
resolveAndExecute(InstallCommand); // TODO: If repo access is not available this can not bail out
310+
} // else ExitUnknown (ignored)
311+
});
312+
}
313+
});
245314

246-
connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
247-
connect(resolvePackage, &PackageKit::Transaction::package,
248-
this, [this](PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) {
249-
qCDebug(lcDeveloperModeLog) << "Package transaction:" << info << packageId << "summary:" << summary;
250-
m_developerModePackageId = packageId;
251-
});
315+
} else {
316+
PackageKit::Transaction::Filters filters;
317+
if (command == RemoveCommand) {
318+
filters = PackageKit::Transaction::FilterInstalled;
319+
} else {
320+
filters = PackageKit::Transaction::FilterNewest;
321+
}
322+
PackageKit::Transaction *resolvePackage = PackageKit::Daemon::resolve(DEVELOPER_MODE_PACKAGE, filters);
323+
324+
connect(resolvePackage, &PackageKit::Transaction::errorCode, this, &DeveloperModeSettings::reportTransactionErrorCode);
325+
connect(resolvePackage, &PackageKit::Transaction::package,
326+
this, [this](PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) {
327+
qCDebug(lcDeveloperModeLog) << "Package transaction:" << info << packageId << "summary:" << summary;
328+
m_developerModePackageId = packageId;
329+
});
330+
331+
connect(resolvePackage, &PackageKit::Transaction::finished,
332+
this, [this, command](PackageKit::Transaction::Exit status, uint runtime) {
333+
Q_UNUSED(runtime)
334+
335+
if (status != PackageKit::Transaction::ExitSuccess || m_developerModePackageId.isEmpty()) {
336+
if (command == InstallCommand) {
337+
if (m_refreshedForInstall) {
338+
qCWarning(lcDeveloperModeLog) << "Failed to install developer mode, package didn't resolve.";
339+
resetState();
340+
} else {
341+
refreshPackageCacheAndInstall(); // try once if it helps
342+
}
343+
} else if (command == RemoveCommand) {
344+
qCWarning(lcDeveloperModeLog) << "Removing developer mode but package didn't resolve into anything. Shouldn't happen.";
345+
resetState();
346+
}
252347

253-
connect(resolvePackage, &PackageKit::Transaction::finished,
254-
this, [this, command](PackageKit::Transaction::Exit status, uint runtime) {
255-
Q_UNUSED(runtime)
348+
} else if (command == InstallCommand) {
349+
PackageKit::Transaction *tx = PackageKit::Daemon::installPackage(m_developerModePackageId);
350+
connectCommandSignals(tx);
256351

257-
if (status != PackageKit::Transaction::ExitSuccess || m_developerModePackageId.isEmpty()) {
258-
if (command == InstallCommand) {
259352
if (m_refreshedForInstall) {
260-
qCWarning(lcDeveloperModeLog) << "Failed to install developer mode, package didn't resolve.";
261-
resetState();
353+
connect(tx, &PackageKit::Transaction::finished,
354+
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
355+
qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done (with refresh):" << status << runtime;
356+
resetState();
357+
});
262358
} else {
263-
refreshPackageCacheAndInstall(); // try once if it helps
359+
connect(tx, &PackageKit::Transaction::finished,
360+
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
361+
if (status == PackageKit::Transaction::ExitSuccess) {
362+
qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done:" << status << runtime;
363+
resetState();
364+
} else {
365+
qCDebug(lcDeveloperModeLog) << "Developer mode installation failed, trying again after refresh";
366+
refreshPackageCacheAndInstall();
367+
}
368+
});
264369
}
265-
} else if (command == RemoveCommand) {
266-
qCWarning(lcDeveloperModeLog) << "Removing developer mode but package didn't resolve into anything. Shouldn't happen.";
267-
resetState();
268-
}
269-
270-
} else if (command == InstallCommand) {
271-
PackageKit::Transaction *tx = PackageKit::Daemon::installPackage(m_developerModePackageId);
272-
connectCommandSignals(tx);
273-
274-
if (m_refreshedForInstall) {
275-
connect(tx, &PackageKit::Transaction::finished,
276-
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
277-
qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done (with refresh):" << status << runtime;
278-
resetState();
279-
});
280370
} else {
371+
PackageKit::Transaction *tx = PackageKit::Daemon::removePackage(m_developerModePackageId, true, true);
372+
connectCommandSignals(tx);
281373
connect(tx, &PackageKit::Transaction::finished,
282374
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
283-
if (status == PackageKit::Transaction::ExitSuccess) {
284-
qCDebug(lcDeveloperModeLog) << "Developer mode installation transaction done:" << status << runtime;
285-
resetState();
286-
} else {
287-
qCDebug(lcDeveloperModeLog) << "Developer mode installation failed, trying again after refresh";
288-
refreshPackageCacheAndInstall();
289-
}
375+
qCDebug(lcDeveloperModeLog) << "Developer mode removal transaction done:" << status << runtime;
376+
resetState();
290377
});
291378
}
292-
} else {
293-
PackageKit::Transaction *tx = PackageKit::Daemon::removePackage(m_developerModePackageId, true, true);
294-
connectCommandSignals(tx);
295-
connect(tx, &PackageKit::Transaction::finished,
296-
this, [this](PackageKit::Transaction::Exit status, uint runtime) {
297-
qCDebug(lcDeveloperModeLog) << "Developer mode removal transaction done:" << status << runtime;
298-
resetState();
299-
});
300-
}
301-
});
379+
});
380+
}
302381
}
303382

304383
void DeveloperModeSettings::connectCommandSignals(PackageKit::Transaction *transaction)
@@ -319,19 +398,16 @@ void DeveloperModeSettings::connectCommandSignals(PackageKit::Transaction *trans
319398

320399
void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction::Status status, PackageKit::Transaction::Role role)
321400
{
322-
// Do not update progress when finished.
323-
if (status == PackageKit::Transaction::StatusFinished) {
324-
return;
325-
}
326-
327401
// Expected changes from PackageKit when installing packages:
328-
// 1. Change to 'install packages' role
402+
// 1. Change to 'install packages' role or 'install files' if installing from local package file
329403
// 2. Status changes:
330404
// setup -> refresh cache -> query -> resolve deps -> install (refer to as 'Preparing' status)
331405
// -> download ('DownloadingPackages' status)
332406
// -> install ('InstallingPackages' status)
333407
// -> finished
334408
//
409+
// If installing from local package fails, it starts over!
410+
//
335411
// Expected changes from PackageKit when removing packages:
336412
// 1. Change to 'remove packages' role
337413
// 2. Status changes:
@@ -347,10 +423,17 @@ void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction:
347423
m_transactionRole = role;
348424
m_transactionStatus = status;
349425

426+
// Do not update progress when finished or role is unknown.
427+
if (m_transactionStatus == PackageKit::Transaction::StatusFinished
428+
|| m_transactionRole == PackageKit::Transaction::RoleUnknown) {
429+
return;
430+
}
431+
350432
if (percentage >= 0 && percentage <= 100) {
351433
int rangeStart = 0;
352434
int rangeEnd = 0;
353-
if (m_transactionRole == PackageKit::Transaction::RoleInstallPackages) {
435+
if (m_transactionRole == PackageKit::Transaction::RoleInstallPackages
436+
|| m_transactionRole == PackageKit::Transaction::RoleInstallFiles) {
354437
switch (m_transactionStatus) {
355438
case PackageKit::Transaction::StatusRefreshCache: // 0-10 %
356439
rangeStart = 0;
@@ -362,7 +445,10 @@ void DeveloperModeSettings::updateState(int percentage, PackageKit::Transaction:
362445
rangeEnd = 20;
363446
break;
364447
case PackageKit::Transaction::StatusDownload: // 20-60 %
365-
workStatus = DownloadingPackages;
448+
// Skip downloading when installing from local file
449+
if (m_transactionRole != PackageKit::Transaction::RoleInstallFiles) {
450+
workStatus = DownloadingPackages;
451+
}
366452
rangeStart = 20;
367453
rangeEnd = 60;
368454
break;

src/developermodesettings.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
5555
Q_PROPERTY(bool developerModeEnabled READ developerModeEnabled NOTIFY developerModeEnabledChanged)
5656
Q_PROPERTY(enum DeveloperModeSettings::Status workStatus READ workStatus NOTIFY workStatusChanged)
5757
Q_PROPERTY(int workProgress READ workProgress NOTIFY workProgressChanged)
58+
Q_PROPERTY(bool repositoryAccessRequired READ repositoryAccessRequired NOTIFY repositoryAccessRequiredChanged)
5859

5960
public:
6061
explicit DeveloperModeSettings(QObject *parent = NULL);
@@ -74,6 +75,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
7475
bool developerModeEnabled() const;
7576
enum DeveloperModeSettings::Status workStatus() const;
7677
int workProgress() const;
78+
bool repositoryAccessRequired() const;
7779

7880
Q_INVOKABLE void setDeveloperMode(bool enabled);
7981
Q_INVOKABLE void setUsbIpAddress(const QString &usbIpAddress);
@@ -85,6 +87,7 @@ class SYSTEMSETTINGS_EXPORT DeveloperModeSettings : public QObject
8587
void developerModeEnabledChanged();
8688
void workStatusChanged();
8789
void workProgressChanged();
90+
void repositoryAccessRequiredChanged();
8891

8992
private slots:
9093
void reportTransactionErrorCode(PackageKit::Transaction::Error code, const QString &details);
@@ -117,6 +120,8 @@ private slots:
117120
PackageKit::Transaction::Role m_transactionRole;
118121
PackageKit::Transaction::Status m_transactionStatus;
119122
bool m_refreshedForInstall;
123+
bool m_localInstallFailed;
124+
QString m_localDeveloperModePackagePath;
120125
};
121126

122127
Q_DECLARE_METATYPE(DeveloperModeSettings::Status)

0 commit comments

Comments
 (0)